# Execution Engine

所有綁定 session 的進入點（TUI —— v1.2.0 起不再自行執行，改經 Unix socket 交由 daemon 執行 ——、`/v1/send`、pending 續行、Telegram、Discord）走同一套啟動流程：`exec.Prepare` 從輸入比對 skill，接著 `exec.Run` 解析呼叫端指名的 skill、發出 skill 與模型選擇事件、透過 `exec/model.Route` 挑選模型與 fallback，再交給 `exec.Execute()`。Subagent 與 scheduler 以 `isSub=true` 直接呼叫 `exec.Run`，略過 `exec.Prepare`；無狀態的 `/v1/chat/completions` 兩者皆略過，直接呼叫 `exec.Execute()`。`exec.Start` 於 v1.2.1 改名為 `exec.Run`，`ResolveAgent` 也在同一版改為 `exec/model.Route`。自 v1.1.0 起 reasoning 為 per-session 設定：設為 `auto`（或未設定）時，`exec/model.Route` 內的 selector 會依工作類型一併決定 reasoning 等級，全域的 `auto_reasoning` 開關已移除。自動路由永不選用 `pass` tier 模型 —— 即使訊息直接點名也一樣 —— 且這類模型不列入 fallback 清單；只有綁定該模型的 session，或呼叫端以明確的 `model` 指定時才會使用它。

`exec.Execute()` 執行主迴圈，最多 **256 次迭代**（`limits.max_tool_iterations`；v1.1.2 前為 128）。每次迭代：

1. 組裝 messages：`SystemPrompts` + `ContextHistories`（排除 guardrail 拒絕）+ turn context（若有）+ `UserMessage` + `ToolHistories`（`ContextHistories` 與 `UserMessage` 在 v1.2.1 前為 `OldHistories` 與 `UserInput`） —— 依此順序 append，執行中途不改寫，讓 prompt cache 的前綴保持穩定。v1.3.7 起已連線 MCP server 提供的 instructions 改為渲染在主 system prompt 內、guardrail 區塊之前，不再另外作為第二則 system message 送出，system prompt 範本也一律改為英文。v1.2.0 起 turn context 為 `assignTurnContext` 每輪產生的一則 `user` 訊息：工作目錄（無狀態的 `/v1/chat/completions` 執行不帶）、須經 `run_tool` 呼叫的工具名稱（v1.3.7 起 MCP 工具依 server 分組寫成 `mcp__<server>__{a, b}`）、skill 清單（`run_skill` 被排除時不帶）、同一個任務仍在執行中的背景任務（v1.3.7 起，讓接回的 turn 知道哪些結果還在路上）、以 `Prior summary` JSON 區塊呈現的滾動式 summary（v1.3.7 起；先前是夾在歷史與 turn context 之間的獨立訊息），以及 guide 開關開啟時的 `CLAUDE.md` / `AGENTS.md`；這些先前放在 system prompt，每變一次就讓快取重置。permission mode 已不再寫進 turn context 或 system prompt（移除於 v1.3.7）。v1.3.7 起 summary 的 `key_decisions`、`past_discussions` 與 `current_discussion` 條目會先由 decision model 篩到只剩與這次請求相關者——沒有 decision model 或呼叫失敗時全部保留，沒有任何相關條目則不放 summary——其使用規則則寫在 system prompt。v1.3.1 起若設定了 decision model，迴圈開始前還會與上述篩選並行跑兩項判斷：需求所需的能力沒有任何現有工具提供時，加入一行工具缺口提示，要求模型先呼叫 `reasoning_guide(topics=["tool_generate"])`（`reasoning_guide` 或 `edit_tool` 被排除時不判斷）；另一項把判定與需求無關的 skill 從清單移除（判斷失敗則保留全部 skill）
2. 對所選 provider 呼叫 `Agent.Send()`
3. 從 response 解析 `tool_calls`
4. 透過 `toolCall.go` 分派 tool call（見[工具調度](/zh/execution-engine-tools)）
5. 將結果 append 至 `ToolHistories`；工具回傳的圖片接在該輪結果之後，以一則 `user` 訊息 append（v1.1.1 起 —— 先前是注入 `UserInput`，會改寫已快取的前綴）
6. 當無 `tool_calls` 剩餘或撞到迭代上限時停止

v1.3.0 起 TUI 執行可把工作留在背景，v1.3.2 起經由 `background_command` 與 `background_subagents`；請求不是來自 TUI（`cli-` 來源且無 deliver 目標）時，兩者都會從該次執行中排除。`run_command_background` 更名為 `background_command`（移除於 v1.3.2）；`run_websocket` 與 `subagents` 的 `background: true` 模式已移除（移除於 v1.3.2），請改用 `background_command` 與 `background_subagents`。v1.3.8 起即使背景工作仍在跑，任務也照常結束：任務執行中或 pending 時報告會送進該任務；任務結束後，整組完成的背景工作以其 `objective` 與 `next` 另開新任務；任務為背景工作維持 pending 的行為移除於 v1.3.8——見[待決任務與取消](/zh/execution-engine-pending)。

每次 `Send` 會在 SQLite `usage` 表寫入一列（模型、input / output / cache-write / cache-hit tokens、耗時）；自 v1.0.26 起該列另記錄此回應要求的 tool call ID。自 v1.1.0 起完成事件不再附帶 provider 剩餘額度，改由 `GET /v1/providers/quota` 依需求提供。

**無 inter-round delay** — rate-limit 保護來自 provider round-trip 延遲、per-model cooldown 以及各 tool 的 timeout。

## 跨 turn workdir 重置

Workdir 由每次請求提供，而非從歷史沿用：TUI 傳入自身的工作目錄、`/v1/send` 解析 `work_dir` 欄位、subagent 沿用或自行解析。v1.2.0 起每次執行都把它寫進 turn context 而非 system prompt，因此切換目錄不會讓已快取的 system 前綴失效；先前 turn 提過的目錄**不會**經由歷史沿用。

- **Turn context** — `Work directory: ...` 行，說明該目錄為本輪權威基準、忽略歷史中較早的目錄，且 `run_command` 已從該目錄開始。無狀態的 `/v1/chat/completions` 執行則仍將此行放在其自有的 system prompt
- **TUI** — 由使用者以 `Shift+P` 設定目錄（絕對路徑、`~/...` 或相對於目前目錄的路徑，且必須存在），之後每次請求都帶入。移除於 v1.3.0：agent 透過 `run_command` 執行 `cd`，以及把新目錄推回 TUI 的 `workdir` frame；`run_command` 現在會拒絕 `cd`，改提示使用絕對路徑，或單次指令用 `['sh','-c','cd <dir> && <command>']`

逐則訊息的 metadata header（包在每則 user message 外的時間戳與工作目錄）及其 TUI 剝除函式 `stripUserMetaHeader` 已移除；turn context 的工作目錄行是唯一的 workdir 錨點。
