Loop shape
query(params)
yield* queryLoop(params)
mark consumed queued commands complete
queryLoop(params)
initialize deps and runtime config once
state = { messages, tools, assistantMessages, toolResults }
while true:
prepare messagesForQuery and prompt context
stream model response
for each completed assistant/tool_use block:
yield to UI
add tool_use to streaming executor
drain tool results
if no tool follow-up:
run recovery or stop logic
return
refresh tools
state = state plus assistant messages and tool_result messages
QueryParams is the handoff contract
QueryParams carries the conversation, prompt, tools, MCP tools, context callbacks, abort signal, model, permission and tool contexts, and other session options. This is where REPL state becomes model-loop input.
Initialization snapshots dependencies and runtime gates
At startup, the loop picks production dependencies such as the model streaming adapter, compactors, and UUID generator. It also snapshots query config values, initializes mutable loop state, schedules memory prefetching, and enters the turn loop.
Snapshotting configuration up front avoids a single model turn changing behavior because a later render or settings update changed a runtime flag mid-stream.
Each iteration builds the model request
Before calling the model, the loop builds the current message list, prepares skill prefetch state, tracks query metadata, and computes a tool-result budget. The actual model call prepends user context to the messages, passes the system prompt and thinking config, includes current tools and MCP tools, and forwards model and tracking options.
Assistant stream events become UI events and tool jobs
As assistant content arrives, the loop backfills observable tool inputs for display, withholds recoverable errors, appends assistant messages and tool_use blocks, and starts the streaming tool executor. After model streaming finishes, it drains any completed tool results and yields them back to the REPL.
Recovery paths are part of the loop
The loop handles fallback model warnings, prompt-too-long and media recovery, maximum output continuation, stop hooks, aborts, token budget continuation, and max-turn termination. These branches matter because they decide whether the next state should keep going, retry with changed inputs, or complete the user turn.
Tool results create the next model input
If tool_use blocks require follow-up, the loop yields tool result messages and updates context. It also handles queued mid-turn attachments after tool calls, filters slash commands out of that path, attaches memory and skill prefetches, refreshes tools between turns, and creates the next loop state.