Execution Flow
This page traces a task from the moment a user submits a message through DAG resolution, tool dispatch, sub-agent calls, and final result delivery.
High-level flow
Section titled “High-level flow”User message ↓Chat API endpoint (/api/chat) ↓Intent Dispatcher ← Tier 1 routes USER_MESSAGE → DeliverToChat ↓Session Manager ← Loads agent config, memory context ↓ExecutionEngine ↓ ├─ Task planner ← Decomposes goal into tasks if project context │ ↓ │ DAG Resolver ← Resolves task order from dependencies │ ↓ │ Task Executor ← Runs tasks sequentially or in parallel ↓Agent Loop ← Multi-turn conversation with LLM │ ├─ LLM API call ← Anthropic/OpenAI/etc. │ ├─ Tool use response │ ↓ │ ToolDispatcher │ ├─ Built-in tools (bash, file, web_search, browser, ...) │ ├─ MCP tools (mcp__server__tool_name) │ └─ Sub-agent (spawn_sub_agent) │ ↓ │ SubAgentLifecycle │ ↓ │ [Nested agent loop] │ ↓ │ ResultAggregator ↓Result delivery ← SSE stream to UI / channel message ↓Memory update ← Episode storedStep 1: Request ingestion
Section titled “Step 1: Request ingestion”The chat API endpoint receives a POST:
POST /api/chat/{agent_id}{"message": "Analyze the test failures in the CI pipeline"}It:
- Validates auth (API key check)
- Loads the agent config from
agentstable - Creates or resumes a session (
session_id) - Starts an SSE response stream
Step 2: Intent dispatch
Section titled “Step 2: Intent dispatch”Before the session is loaded, the chat API builds an IntentEnvelope
with source=USER_MESSAGE, the room_id from the conversation, and
the user’s agent_hint. The Unified Intent Dispatcher’s
Tier 1 rule for room_id + kind=MESSAGE matches immediately, producing
a DeliverToChat action that resolves to the TurnOrchestrator path
below. Dispatch is fire-and-forget on the streaming path — the dispatch
hot path adds ≤2 ms p50 and never blocks the first SSE byte.
For non-chat producers (channel webhooks, scheduler ticks, hooks,
proactive insights, device events) the same IntentDispatcher.dispatch()
call decides which executor receives the signal. The chat case is the
fast path because the envelope carries explicit hints; ambiguous
envelopes go through Tier 3 (LLM classification).
Step 3: Session setup
Section titled “Step 3: Session setup”The SessionManager:
- Loads recent episodic memory (configurable window, default last 20 episodes)
- Runs semantic retrieval to find relevant past context
- Constructs the system prompt with agent personality + memory context
- Injects any project/task context if the message is part of a project
Step 4: Agent loop (multi-turn)
Section titled “Step 4: Agent loop (multi-turn)”The core execution is an agentic loop:
while True: response = await llm.create( model=agent.model, system=system_prompt, messages=conversation_history, tools=available_tools, )
if response.stop_reason == "end_turn": break # LLM is done
if response.stop_reason == "tool_use": for tool_call in response.tool_uses: result = await tool_dispatcher.dispatch(tool_call) conversation_history.append(tool_result(tool_call.id, result))
# Continue loop with updated historyMaximum turns: configurable per agent (default: 50).
Step 5: Tool dispatch
Section titled “Step 5: Tool dispatch”The ToolDispatcher (not the Intent Dispatcher — see the disambiguation
note in Step 2) routes tool calls to their handler:
tool_name ├─ "bash" → BashTool.execute() ├─ "read_file" → FileTool.read() ├─ "write_file" → FileTool.write() ├─ "web_search" → BraveSearchTool.search() ├─ "browser_navigate" → BrowserManager.navigate() ├─ "spawn_sub_agent" → SubAgentSpawner.spawn() └─ "mcp__*__*" → MCPConnectionManager.call_tool()Tool results flow back into the conversation as tool_result messages.
Step 6: Sub-agent execution
Section titled “Step 6: Sub-agent execution”When the agent calls spawn_sub_agent:
SubAgentSpawner.spawn()validates permissions- Creates a
SubAgentrecord in the database - Starts an isolated session (with filtered tool set)
- Runs the sub-agent’s own agent loop concurrently
- Returns partial or final result to the parent
ResultAggregatormerges results using the configured strategy
Sub-agents can spawn their own sub-agents (up to depth 3).
Step 7: Approval gates
Section titled “Step 7: Approval gates”Certain actions require human approval before proceeding:
- Execution pauses
ApprovalRequestis created and streamed to the UI- Agent waits (up to configurable timeout)
- On approval: continues; on rejection: tool result is “action rejected by user”
Step 8: Memory update
Section titled “Step 8: Memory update”After the session ends (user stops interacting or max turns reached):
- The entire conversation is serialized as episodic memory entries
- If a vector store is configured, embeddings are computed and stored
- The knowledge graph is updated with any new entities discovered
Step 9: Result streaming
Section titled “Step 9: Result streaming”Throughout execution, the SSE stream delivers:
event: deltadata: {"text": "I'll analyze the CI failures...", "session_id": "sess_abc"}
event: tool_usedata: {"tool": "bash", "input": {"command": "cat ci.log | grep FAILED"}}
event: tool_resultdata: {"tool": "bash", "output": "3 tests failed: test_auth, test_payment..."}
event: deltadata: {"text": "Found 3 test failures. The root cause is..."}
event: donedata: {"session_id": "sess_abc", "token_usage": {"input": 1842, "output": 547}}The UI renders these events in real time.
Project task execution
Section titled “Project task execution”When an agent is working on a project (not a free-form chat), the flow changes:
- Project goal is decomposed into tasks by the task planner
- Tasks are arranged in a DAG based on
depends_onrelationships - The
DAGResolvercomputes the execution order - Tasks execute per the project’s
execution_strategy:- sequential: one at a time
- parallel: all independent tasks simultaneously
- dag: topological order with concurrency where possible
- Each task runs its own agent loop
- Project status updates as tasks complete (
in_progress→completed)
Retry and failure handling
Section titled “Retry and failure handling”Tasks that fail are classified by failure type:
| Class | Examples | Default retry |
|---|---|---|
transient | Network timeout, rate limit | Yes — up to 3x with backoff |
tool_error | Bash command failed | Yes — up to 2x |
llm_error | Model API error | Yes — 1x after 10s |
context_limit | Token limit exceeded | Compress context and retry |
permanent | Invalid tool args | No |
human_rejected | User rejected action | No |
Retry delays use exponential backoff: 10s, 30s, 90s.
Concurrency
Section titled “Concurrency”Multiple agent sessions can run concurrently. Each session has its own:
- Conversation history
- Active tool calls
- Sub-agent tree
Limits:
- Max concurrent sessions per agent: 5 (configurable)
- Max concurrent sub-agents globally: 8
- Max concurrent workflow runs: 10 (configurable via
WORKFLOW_MAX_CONCURRENT_RUNS)