Query loop

The query loop is the central coordinator for a model turn. It receives normalized messages and runtime services from the REPL, streams the model response, starts tool execution as soon as complete tool_use blocks arrive, appends tool_result messages, and decides whether to continue or stop.

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.