Execution Engine
Last updated
Every session-backed entry point (TUI, which since v1.2.0 sends its runs to the daemon over the Unix socket instead of executing them itself,
/v1/send, pending resume, Telegram, Discord) goes through the same startup: exec.Prepare matches a skill from the input, then exec.Run resolves a skill named by the caller, emits the skill and model selection events, picks the model and its fallbacks through exec/model.Route, and hands off to exec.Execute(). Subagents and the scheduler call exec.Run directly with isSub=true and skip exec.Prepare; the stateless /v1/chat/completions skips both and calls exec.Execute() itself. exec.Start was renamed exec.Run in v1.2.1, and ResolveAgent became exec/model.Route in the same release. Since v1.1.0 reasoning is a per-session setting: when it is auto (or unset) the selector inside exec/model.Route also decides the reasoning level from the kind of work, and the global auto_reasoning switch was removed. Auto routing never selects a pass-tier model, even when the message names it, and such models are left out of the fallback list; only a session pinned to one, or a caller passing it as the explicit model, runs it.exec.Execute() runs the main loop for up to 256 iterations (limits.max_tool_iterations; 128 before v1.1.2). Each iteration:- Assemble messages:
SystemPrompts+ContextHistories(guardrail refusals dropped) + the turn context, if any +UserMessage+ToolHistories(ContextHistoriesandUserMessagewereOldHistoriesandUserInputbefore v1.2.1) — appended in that order, never rewritten mid-run, so the prompt-cache prefix stays stable. Since v1.3.7 the instructions connected MCP servers send are rendered inside the main system prompt, ahead of the guardrail block, instead of riding as a second system message, and the system prompt templates are English-only. Since v1.2.0 the turn context is oneusermessage built each turn byassignTurnContext: the work directory (not for stateless/v1/chat/completionsruns), the names of the tools to call throughrun_tool(since v1.3.7 MCP tools grouped by server asmcp__<server>__{a, b}), the skill list (omitted whenrun_skillis excluded), the background tasks this same task still has running (since v1.3.7, so a resumed turn knows which results are still on their way), the rolling summary as aPrior summaryJSON block (since v1.3.7; it used to be a separate message between the history and the turn context), and theCLAUDE.md/AGENTS.mdguide when the guide toggle is on; these used to sit in the system prompt, where every change reset the cache. The permission mode is no longer stated there or in the system prompt (Removed in v1.3.7). Since v1.3.7 the summary'skey_decisions,past_discussionsandcurrent_discussionentries are first filtered by the decision model down to those relevant to the request — every entry is kept without a decision model or when the call fails, and the summary is left out when none is relevant — and the rules for using it live in the system prompt. Since v1.3.1, when a decision model is set, two more checks run in parallel with that one before the loop starts: one adds a tool-gap line telling the model to callreasoning_guide(topics=["tool_generate"])first when the request needs a capability no available tool provides (skipped whenreasoning_guideoredit_toolis excluded), and the other drops skills it judges unrelated to the request from the skill list (a failed check keeps every skill) - Call
Agent.Send()on the chosen provider - Parse
tool_callsfrom the response - Dispatch the tool calls through
toolCall.go(see Tool Dispatch) - Append results to
ToolHistories; images returned by tools follow the round's results there as oneusermessage (since v1.1.1 — they used to be injected intoUserInput, which rewrote the cached prefix) - Stop when no
tool_callsremain or the iteration limit is hit
Since v1.3.0 a TUI run can leave work in the background, since v1.3.2 through
background_command and background_subagents; both are excluded from the run unless the request comes from the TUI (cli- origin with no deliver target). run_command_background was renamed background_command (Removed in v1.3.2); run_websocket and the subagents background: true mode were dropped (Removed in v1.3.2): use background_command and background_subagents instead. Since v1.3.8 the task ends normally even while they run: reports reach it while it is running or pending, and once it has ended a finished group runs its objective and next as a new task; the task staying pending for its background work was Removed in v1.3.8 — see Pending Tasks and Cancellation.Each
Send writes one row to the SQLite usage table (model, input / output / cache-write / cache-hit tokens, elapsed time); since v1.0.26 the row also records the IDs of the tool calls that response requested. Since v1.1.0 the completion event no longer carries the remaining provider quota; GET /v1/providers/quota serves it on request.There is no inter-round delay — rate-limit protection comes from provider round-trip latency, per-model cooldown, and per-tool timeouts.
Cross-turn workdir reset
The workdir is supplied per request, not carried over from history: the TUI passes its own working directory,
/v1/send resolves the work_dir field, and subagents inherit or resolve theirs. Since v1.2.0 it is written into the turn context on every run, not into the system prompt, so the cached system prefix survives a directory change; a directory mentioned in an earlier turn does not carry over through history.- Turn context — a
Work directory: ...line stating that this directory is authoritative for this turn, that earlier ones in history are ignored, and thatrun_commandalready starts there. Stateless/v1/chat/completionsruns keep the line in their own system prompt instead - TUI — the user sets the directory with
Shift+P(an absolute path, ~/..., or a path relative to the current one; it must exist), and every later request carries it. Removed in v1.3.0: the agent'scdthroughrun_commandand theworkdirframe that pushed the new directory to the TUI;run_commandnow rejectscdand points to absolute paths or['sh','-c','cd <dir> && <command>']for a single command
The per-message metadata header (timestamp + working directory wrapped around each user message) and its TUI stripper
stripUserMetaHeader were removed; the turn-context work directory line is the only workdir anchor.