How does the agent loop process a user message and stream the assistant response back, including tool dispatch and stop hooks

en
100.0% sentence pass·26/26 cited·35/36 citations valid·94 fn·0 dec·949 sem
Application Bootstrap & EntrypointsAgent & Task OrchestrationTool FrameworkContext & Prompt SystemAPI Client & Model Resolution

Agent loop: user message → streamed response → tool dispatch → stop hooks

Overview

This flow traces how a submitted user prompt drives the main query loop, streams assistant output from the model, dispatches any tool calls, and finally runs Stop hooks before yielding terminal state . The entry point is the SDK/headless path that calls QueryEngine.submitMessage .

Steps

  1. In headless mode, runHeadless loads prior transcript via loadInitialMessages and ultimately drives the engine through its command queue . drainCommandQueue dequeues batched prompt commands and forwards each to the engine .
  2. submitMessage assembles the system prompt via getSystemPrompt, wraps canUseTool to track denials, and builds the initial ProcessUserInputContext before entering the loop . The system prompt is wrapped as an immutable SystemPrompt token .
  3. Control transfers into queryLoop, which destructures mutable State at the top of each iteration and projects messagesForQuery from the compact boundary forward . Token accounting uses tokenCountWithEstimation and may trigger autocompact once the threshold from getAutoCompactThreshold is exceeded .
  4. The loop calls queryModel, which builds betas, filters tools (honoring tool-search and extractDiscoveredToolNames), computes params via paramsFromContext, and yields streaming events plus the final assistant message . The request is wrapped in withRetry to handle 429/529, fast-mode fallback, and FallbackTriggeredError .
  5. On API errors, getAssistantMessageFromError synthesizes a user-visible assistant error message instead of crashing the loop . startsWithApiErrorPrefix is used to detect error sentinels embedded in streamed text .
  6. For each tool_use block in the assistant message, runToolUse resolves the tool by name (including deprecated aliases) and delegates to permission checking . checkPermissionsAndCallTool validates input with the tool's zod schema, runs pre-tool hooks, resolves canUseTool, and finally invokes tool.call .
  7. Tool results are wrapped as user messages via createUserMessage with sourceToolAssistantUUID so the loop can feed them back into the next model turn . Sub-agent tool calls route through AgentTool.call which in turn calls runAgent with forked context .
  8. When the assistant stops requesting tools, the loop invokes handleStopHooks, which executes Stop/SubagentStop hooks, collects blocking errors as meta user messages, and returns a StopHookResult . Blocking errors cause another iteration; otherwise the loop yields Terminal and submitMessage completes the generator .
  9. If autocompact fires mid-loop, compactConversation runs PreCompact hooks, calls streamCompactSummary, and emits a new boundary via createCompactBoundaryMessage, after which markPostCompaction and notifyCompaction reset cache tracking .

State touched

Decisions

The whitelist contains no [decision:...] tokens, so no design-decision citations are available for this flow .