Skip to main content
Zuko’s AI agent runs in-process inside the NestJS backend (apps/backend/src/agent/). It is built on DeepAgents, which extends LangGraph with a hierarchical sub-agent model. There is no separate agent service to start — the agent activates when a user sends a chat message.

Architecture

The root agent handles the conversation turn. When a task falls within a specialist’s responsibility, the root agent delegates to a sub-agent. Each sub-agent has its own system prompt and a scoped set of tools.

Sub-agents

Sub-agents share the same authentication context (org ID, user ID) as the root agent and always operate within the active organization.

Tool reference

CRM tools

These call /api/agents/* endpoints on the backend and write audit log entries with source AI.

Contacts

Companies

Deals

Context


Filesystem tools

These run inside the chat’s sandbox — an isolated execution environment. In production, sandboxes are remote Sprites machines; in development they run locally.
bash and read have a needsApproval check. Commands that match rm -rf or reference .env files are blocked until the user explicitly approves them.

Web tool


Interactive tools


Tool approval (human-in-the-loop)

Tools can declare a needsApproval function. When it returns true, the tool returns { pending: true } instead of executing, and the frontend surfaces an approval prompt to the user. Execution continues only after the user approves. Approval is currently required for:
  • bash commands that include rm -rf
  • bash commands or read calls that reference .env files

Persistent context

Context entities (contacts, companies, deals) attached to a chat thread are stored in LangGraph checkpoint state and persist across turns. The get_conversation_context tool always returns the current set, even after a page reload. This is handled by PersistentContextMiddleware (apps/backend/src/agent/middleware/persistent-context.middleware.ts), which merges context entity arrays via a LangGraph state reducer.

Model selection

The model is selected via the AGENT_MODEL environment variable using the format provider/model:
Supported providers: openai, anthropic. An invalid format throws at startup.

Streaming

The backend streams agent output to the frontend as SSE. The response includes:
  • X-Thread-Id — LangGraph thread ID for this run
  • X-Chat-Id — Zuko chat ID
The raw LangGraph stream (modes: values + messages) is converted to the AI SDK UIMessageStream format via @ai-sdk/langchain, then piped to the browser. To stop a running agent turn, call POST /api/v1/chat/stop. The backend signals cancellation via AbortSignal, which is passed through to all tool execute calls.