From db29e64ab7850f81744442df581f711fe3dfa9f0 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 11:57:53 +0000 Subject: [PATCH 01/46] test(tui): the config command group's guard tests count /connect and /disconnect 0ff4ffd494 added /connect and /disconnect to the config command group but left the two tests that pin that group's registry on the old 17 commands. Hosted CI has been red on Windows since then (run 38129356685, job 114440346849: "18309 tests run: 18307 passed, 2 failed"): - config_policy_keeps_host_registry_order_metadata_and_exact_capabilities expected no connect/disconnect after login; - command_ownership_contract_is_enforced expected exactly 17 commands, got 19. The green run on ed3fbece60 (38129634138) skipped every Rust test as a light change, so it is not evidence for this code. Both tests now name the two commands in their registered position, and the count is 19. The neighbouring comment that claimed the production groups hold exactly 96 commands was already false and no longer states a number. Checks (Ubuntu 24.04 container): nextest -p codewhale-tui --lib -E 'test(/^commands::/)': 1192 tests run: 1177 passed, 15 failed. Both guard tests passed. The 15 failures are all snapshot restore/undo tests that fail on git 2.43's `checkout --end-of-options` (fixed separately; hosted CI runs git 2.55 and is unaffected). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01BsXVvReYca2UxzimQtj7Mj --- crates/tui/src/commands/config_policy_host_tests.rs | 2 ++ crates/tui/src/commands/mod.rs | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/crates/tui/src/commands/config_policy_host_tests.rs b/crates/tui/src/commands/config_policy_host_tests.rs index 73b88639da..41d3097526 100644 --- a/crates/tui/src/commands/config_policy_host_tests.rs +++ b/crates/tui/src/commands/config_policy_host_tests.rs @@ -16,6 +16,8 @@ fn config_policy_keeps_host_registry_order_metadata_and_exact_capabilities() { "import-claude", "permissions", "login", + "connect", + "disconnect", "auth", "workbar", "pet", diff --git a/crates/tui/src/commands/mod.rs b/crates/tui/src/commands/mod.rs index 6a2fadb7d9..ff220b8c89 100644 --- a/crates/tui/src/commands/mod.rs +++ b/crates/tui/src/commands/mod.rs @@ -1081,9 +1081,9 @@ mod tests { has_config = true; assert_eq!( commands.len(), - 17, + 19, "config group (group-local metadata exception) expected \ - exactly 17 commands, got {}", + exactly 19 commands, got {}", commands.len() ); } @@ -1111,8 +1111,8 @@ mod tests { // Consistency: group-iterated command count must match registry. // FEAT-015 registers one test-only contextual command (`/feat015ctx`) - // under `#[cfg(test)]` to prove the dual-path seam (D6); the nine - // production groups remain exactly 96 commands. + // under `#[cfg(test)]` to prove the dual-path seam (D6); it is not + // part of any production group. let test_only_count = command_infos() .iter() .filter(|info| info.name == "feat015ctx") From 42c5dccc9a9323f983cc99e53989fe0dd342169a Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 12:53:28 +0000 Subject: [PATCH 02/46] fix(snapshot): restore and /undo work with git 2.43 Snapshot restore ran `git checkout --end-of-options -- `. git 2.43, which Ubuntu 24.04 ships, rejects that form with "fatal: only one reference expected, 2 given", so /undo and restore failed there. That form shipped in v0.10.1 (593be41dd3). Hosted CI runs git 2.55, so it never saw the failure. Both checkout call sites drop `--end-of-options`. The id passed there is a SnapshotId, which is validated as 40 or 64 hex characters. It can never start with '-', so the option guard protected nothing that reaches it. The `--` separator before the paths stays. Evidence: on this Ubuntu 24.04 container (git 2.43), `nextest run -p codewhale-tui --lib -E 'test(/^commands::/)'` on db29e64ab7 gave `1192 tests run: 1177 passed, 15 failed`. All 15 failures were snapshot restore and undo tests failing with "only one reference expected". The re-run with this change is pending a local test build and hosted CI on codewhale-hq/Codewhale#6957. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01BsXVvReYca2UxzimQtj7Mj --- crates/tui/src/snapshot/repo.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/tui/src/snapshot/repo.rs b/crates/tui/src/snapshot/repo.rs index 7da7284fc6..5e75423e18 100644 --- a/crates/tui/src/snapshot/repo.rs +++ b/crates/tui/src/snapshot/repo.rs @@ -964,7 +964,7 @@ impl SnapshotRepo { let checkout = run_git( &self.git_dir, &self.work_tree, - &["checkout", "--end-of-options", id.as_str(), "--", ":/"], + &["checkout", id.as_str(), "--", ":/"], )?; if !checkout.status.success() { return Err(io_other(format!( @@ -1363,7 +1363,6 @@ impl SnapshotRepo { let mut args: Vec = vec![ "--literal-pathspecs".to_string(), "checkout".to_string(), - "--end-of-options".to_string(), source.as_str().to_string(), "--".to_string(), ]; From 8eb23adb03ebb17b9e2ad8898d174c6e36c0db19 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 12:53:40 +0000 Subject: [PATCH 03/46] fix(shell): a self-cancelled job stays quiet, and an exited job keeps its exit Two session-export defects in the background-shell cancel path: - Cancelling your own background job also queued its completion notification. The session then got a "finished" event for a job it had just killed, and a new model turn could start from it. `ShellManager::kill` now marks a job it killed as `completion_reported`, so the completion drain skips it. - A job that exited between the user's look and the cancel was still killed and reported as "Killed" with exit 0. `BackgroundShell::kill` now polls first and returns without signalling when the process has already finished. The cancel receipt then reports the real status and exit ("... Nothing was killed.") instead of "Canceled background command". Evidence: cargo fmt --all -- --check is clean. The compile and the tools::shell / tool_cancellation tests are pending a local test build and hosted CI on codewhale-hq/Codewhale#6957. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01BsXVvReYca2UxzimQtj7Mj --- crates/tui/src/tools/shell.rs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/crates/tui/src/tools/shell.rs b/crates/tui/src/tools/shell.rs index 6ea187260c..9ddc0bea66 100644 --- a/crates/tui/src/tools/shell.rs +++ b/crates/tui/src/tools/shell.rs @@ -1586,6 +1586,9 @@ impl BackgroundShell { /// Kill the process fn kill(&mut self) -> Result<()> { + if self.poll() && self.status != ShellStatus::Running { + return Ok(()); + } #[cfg(unix)] if self.ownership == ShellOwnership::PersistPending && let Some(process_group_id) = self.child.as_ref().and_then(ShellChild::process_id) @@ -3204,6 +3207,9 @@ impl ShellManager { .ok_or_else(|| anyhow!("Task {task_id} not found"))?; shell.kill()?; + if shell.status == ShellStatus::Killed { + shell.completion_reported = true; + } shell.snapshot() } @@ -7034,10 +7040,14 @@ impl BashTool { .clone() .unwrap_or_else(|| task_id.to_string()); Ok(ToolResult { - content: format!( - "Canceled background command: {task_id}. {}", - kill_outcome_note(&format!("{}{}", result.stdout, result.stderr)) - ), + content: if result.status == ShellStatus::Killed { + format!( + "Canceled background command: {task_id}. {}", + kill_outcome_note(&format!("{}{}", result.stdout, result.stderr)) + ) + } else { + format!("{} Nothing was killed.", wait_timing_line(&result)) + }, success: true, metadata: Some(json!({ "status": format!("{:?}", result.status), From d33dc3861355e1b010135c3b63ece5afc05e6d8d Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 12:53:47 +0000 Subject: [PATCH 04/46] fix(tui): a command that moves to the background after 30 s says so plainly When a foreground bash call outlived its wait and moved to the background, the card copied the model's receipt ("Background task started: shell-42 ... use task_shell_wait ...") into its live output. That told the user how the model should poll, not what happened. Ctrl+B already showed a user notice for the same transition. - A running Exec result no longer seeds `live_output` with the model's receipt. The card keeps its live task identity, and output arrives from the job as before. - The auto-background case pushes the same `ShellWaitReleased` toast as Ctrl+B ("... keep running in /jobs"), at Info level for 6 s. A call the model started with background:true gets no toast, because nothing moved. - canonical_background_bash_keeps_live_task_identity_for_follow_up_actions now asserts the card has no copied receipt. Evidence: cargo fmt --all -- --check is clean. The compile and the ui test are pending a local test build and hosted CI on codewhale-hq/Codewhale#6957. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01BsXVvReYca2UxzimQtj7Mj --- crates/tui/src/tui/tool_routing.rs | 29 ++++++++++++++++------------- crates/tui/src/tui/ui/tests.rs | 6 +----- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/crates/tui/src/tui/tool_routing.rs b/crates/tui/src/tui/tool_routing.rs index a94d33eb76..4036c830e1 100644 --- a/crates/tui/src/tui/tool_routing.rs +++ b/crates/tui/src/tui/tool_routing.rs @@ -714,16 +714,15 @@ pub(super) fn handle_tool_call_complete( // Preserve the execution/audit name while recovering the action-qualified // semantic name from the registered call input. Active entries and // already-flushed history use separate detail stores. - let semantic_name = app - .active_tool_details - .get(id) - .or_else(|| { - app.tool_cells - .get(id) - .and_then(|cell_index| app.tool_details_by_cell.get(cell_index)) - }) + let detail = app.active_tool_details.get(id).or_else(|| { + app.tool_cells + .get(id) + .and_then(|cell_index| app.tool_details_by_cell.get(cell_index)) + }); + let semantic_name = detail .map_or(name, |detail| canonical_action_alias(name, &detail.input)) .to_string(); + let explicit_background = detail.is_some_and(|detail| exec_is_background(&detail.input)); // Roll any child-LLM token usage the tool reports into the // session-cost counter. Runs unconditionally so future tools that @@ -809,6 +808,7 @@ pub(super) fn handle_tool_call_complete( }) .flatten(); let mut workflow_panel_output: Option = None; + let mut moved_to_jobs = false; if let Some(cell) = app.cell_at_virtual_index_mut(cell_index) { match cell { @@ -874,11 +874,8 @@ pub(super) fn handle_tool_call_complete( .as_deref() .map(super::history::summarize_tool_output); exec.live_output = None; - } else if status == ToolStatus::Running - && exec.interaction.is_none() - && !tool_result.content.is_empty() - { - exec.live_output = Some(tool_result.content.clone()); + } else if status == ToolStatus::Running && exec.interaction.is_none() { + moved_to_jobs = !explicit_background; } } else if let Err(err) = result.as_ref() && exec.interaction.is_none() @@ -999,6 +996,12 @@ pub(super) fn handle_tool_call_complete( if let Some(output) = workflow_panel_output.as_deref() { apply_workflow_output_to_panel(app, output); } + if moved_to_jobs { + let notice = app + .tr(codewhale_localization::MessageId::ShellWaitReleased) + .replace("{jobs}", "/jobs"); + app.push_status_toast(notice, crate::tui::app::StatusToastLevel::Info, Some(6_000)); + } // If the mutated cell lived inside the active group, bump the active-cell // revision so the transcript cache re-renders the synthetic tail row. diff --git a/crates/tui/src/tui/ui/tests.rs b/crates/tui/src/tui/ui/tests.rs index 91f0761f52..9e61daf040 100644 --- a/crates/tui/src/tui/ui/tests.rs +++ b/crates/tui/src/tui/ui/tests.rs @@ -1435,11 +1435,7 @@ fn canonical_background_bash_keeps_live_task_identity_for_follow_up_actions() { assert_eq!(exec.shell_task_id.as_deref(), Some("shell-42")); assert_eq!(exec.command, "cargo test --workspace"); assert!(exec.output.is_none()); - assert!( - exec.live_output - .as_deref() - .is_some_and(|output| { output.contains("Background task started: shell-42") }) - ); + assert!(exec.live_output.is_none()); } #[test] From 7b1dcd30dab572d5989f8fd4226ab320cf59b38e Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 12:53:59 +0000 Subject: [PATCH 05/46] fix(engine): todo_write answers the model with a short receipt Every todo_write call returned the whole checklist to the model. A long-running session paid for the full list again on each update, although the model had just written that list in its own call. That was a session-export finding. The structured-result compactor now handles a todo_write result: - it keeps the "Todo list updated (...)" header with its counts; - it adds the item in progress ("In progress: #3 ...") or "No item in progress". - The full list stays in the tool result's metadata, and the TUI's todo panel still renders from it. Model-visible means logged: the receipt is a deterministic projection of the logged tool result and metadata, so the session log reconstructs it. KV-cache: tool results are append-only history; the cached prefix is unchanged. The todo_snapshot doc comment, docs/TOOL_LIFECYCLE.md, docs/SUBAGENTS.md (and their zh_hans copies) and the test_cases_13 comment now say the model learns its list from its own todo_write call, confirmed by the receipt. Evidence: cargo fmt --all -- --check is clean. The compile and the core::engine::context tests are pending a local test build and hosted CI on codewhale-hq/Codewhale#6957. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01BsXVvReYca2UxzimQtj7Mj --- crates/tui/src/core/engine/context.rs | 33 ++++++++++++++++++- .../src/core/engine/tests/test_cases_13.rs | 4 +-- crates/tui/src/todo_snapshot.rs | 11 ++++--- docs/SUBAGENTS.md | 6 ++-- docs/TOOL_LIFECYCLE.md | 5 +-- docs/zh_hans/SUBAGENTS.md | 2 +- docs/zh_hans/TOOL_LIFECYCLE.md | 2 +- 7 files changed, 48 insertions(+), 15 deletions(-) diff --git a/crates/tui/src/core/engine/context.rs b/crates/tui/src/core/engine/context.rs index a78608e0bf..68b9230fc1 100644 --- a/crates/tui/src/core/engine/context.rs +++ b/crates/tui/src/core/engine/context.rs @@ -543,10 +543,41 @@ fn compact_structured_tool_result_for_context( // content actually parses as a gate result, so non-gate `tasks` // results fall through to the generic path unchanged. "task_gate_run" | "tasks" => compact_task_gate_run_result_for_context(raw), - _ => None, + _ => compact_todo_write_result_for_context(raw, metadata), } } +fn compact_todo_write_result_for_context( + raw: &str, + metadata: Option<&Value>, +) -> Option { + let metadata = metadata?; + if metadata.get("canonical_tool").and_then(Value::as_str) != Some("todo_write") { + return None; + } + let header = raw + .lines() + .next() + .filter(|line| line.starts_with("Todo list updated ("))?; + let active = metadata + .pointer("/task_updates/checklist/items")? + .as_array()? + .iter() + .find(|item| item.get("status").and_then(Value::as_str) == Some("in_progress")); + let text = match active { + Some(item) => format!( + "{header}\nIn progress: #{} {}", + item.get("id").and_then(Value::as_u64).unwrap_or_default(), + item.get("content") + .and_then(Value::as_str) + .unwrap_or_default() + .trim(), + ), + None => format!("{header}\nNo item in progress"), + }; + Some(ContextSummary { text, lossy: false }) +} + #[cfg(test)] pub(crate) fn compact_tool_result_for_context( model: &str, diff --git a/crates/tui/src/core/engine/tests/test_cases_13.rs b/crates/tui/src/core/engine/tests/test_cases_13.rs index 41166fc8c4..3e86456eb4 100644 --- a/crates/tui/src/core/engine/tests/test_cases_13.rs +++ b/crates/tui/src/core/engine/tests/test_cases_13.rs @@ -471,8 +471,8 @@ fn messages_with_turn_metadata_returns_stored_session_messages() { // === To-do state reaches the model through its own tool results === // // Codewhale has one To-do list. The model learns what is on it the way it -// learns anything else: from the result its own `work_update` call returned, -// which is ordinary persisted history. No request re-states the list, on any +// learns anything else: from its own `work_update` call and the short receipt +// it returned, which are ordinary persisted history. No request re-states the list, on any // step. The complete list stays visible in the UI, which is a different // surface from the request. diff --git a/crates/tui/src/todo_snapshot.rs b/crates/tui/src/todo_snapshot.rs index 4e4259565b..97d7ecb745 100644 --- a/crates/tui/src/todo_snapshot.rs +++ b/crates/tui/src/todo_snapshot.rs @@ -1,11 +1,12 @@ //! Bounded renderings of a To-do snapshot. //! //! Codewhale has exactly one To-do list. The model learns what is on it the -//! same way it learns anything else: from the tool result its own `todo_write` -//! / `work_update` call returned, which is ordinary persisted transcript -//! state. **Nothing in this module is appended to a provider request**, and no -//! step of a tool loop re-states the list. If the model wants the current -//! list, it reads its own last tool result or calls the tool again. +//! same way it learns anything else: from its own `todo_write` / +//! `work_update` call, whose input is the whole list, confirmed by a short +//! receipt (counts and the item in progress). Both are ordinary persisted +//! transcript state. **Nothing in this module is appended to a provider +//! request**, and no step of a tool loop re-states the list. If the model +//! wants the current list, it reads its own last call or calls the tool again. //! //! What this module owns is the small set of places that render a snapshot //! *once*, at an explicit seam a person asked for: diff --git a/docs/SUBAGENTS.md b/docs/SUBAGENTS.md index 751bf017f1..0433296db4 100644 --- a/docs/SUBAGENTS.md +++ b/docs/SUBAGENTS.md @@ -160,9 +160,9 @@ turn is included. **The list is shown once, at that spawn, and never re-sent.** No sub-agent request re-states a To-do list, and neither does a parent request. Each agent -keeps its own private list (#4810); what it knows about that list comes from the -tool results its own `todo_write` calls returned, which are ordinary messages in -its own transcript. A worker therefore cannot read or write a parent's or a +keeps its own private list (#4810); what it knows about that list comes from its +own `todo_write` calls (each sends the whole list) and the short receipts they +returned, which are ordinary messages in its own transcript. A worker therefore cannot read or write a parent's or a sibling's list, and a forked child cannot mutate the snapshot it was handed or keep reading later parent changes. diff --git a/docs/TOOL_LIFECYCLE.md b/docs/TOOL_LIFECYCLE.md index 0ee9adaa8e..31ef8f73b5 100644 --- a/docs/TOOL_LIFECYCLE.md +++ b/docs/TOOL_LIFECYCLE.md @@ -88,8 +88,9 @@ not a progress surface, must not duplicate To-do items, and plan-only state is never rendered as a To-do snapshot. **No request re-states the list.** The model learns what is on the To-do the way -it learns anything else: from the result its own `todo_write` call returned, -which is ordinary persisted history. Nothing is appended to a parent turn-loop +it learns anything else: from its own `todo_write` call, whose input is the +whole list, and the short receipt it returned (counts and the item in +progress). Both are ordinary persisted history. Nothing is appended to a parent turn-loop step or a sub-agent step. The complete list stays visible in the UI, which is a different surface from the request. diff --git a/docs/zh_hans/SUBAGENTS.md b/docs/zh_hans/SUBAGENTS.md index 6389320c57..3a7a305b7d 100644 --- a/docs/zh_hans/SUBAGENTS.md +++ b/docs/zh_hans/SUBAGENTS.md @@ -56,7 +56,7 @@ Fleet 角色是委派工作面向用户的词汇:父智能体通过 `agent` 分叉状态显示的是父智能体的 To-do 快照——由 `todo_write` 写入的唯一的 Work 界面。子智能体的 `` 块携带 `crates/tui/src/todo_snapshot.rs` 渲染的有界正文,因此分叉是从父智能体真实的进度位置继续,而不是从一段转述继续。这一 To-do 部分在生成发生时解析,所以同一个父回合中更早的 `todo_write` 也会被包含进来。 -**该列表只在生成的那一刻显示一次,之后绝不会重新发送。** 没有哪个子智能体请求会重新陈述 To-do 列表,父智能体的请求也不会。每个智能体都保留自己的私有列表(#4810);它对这个列表的了解,来自它自己的 `todo_write` 调用返回的工具结果,而这些结果是它自己转录中的普通消息。因此 `worker` 无法读取或写入父智能体或兄弟智能体的列表,被分叉出的子智能体也无法修改交给它的快照,或持续读取父智能体之后的变化。 +**该列表只在生成的那一刻显示一次,之后绝不会重新发送。** 没有哪个子智能体请求会重新陈述 To-do 列表,父智能体的请求也不会。每个智能体都保留自己的私有列表(#4810);它对这个列表的了解,来自它自己的 `todo_write` 调用(每次调用都发送完整列表)以及这些调用返回的简短回执,而这些都是它自己转录中的普通消息。因此 `worker` 无法读取或写入父智能体或兄弟智能体的列表,被分叉出的子智能体也无法修改交给它的快照,或持续读取父智能体之后的变化。 子智能体转录内卡片显示的正是这份私有列表。一张委派卡片渲染的是**它自己**那个智能体 To-do 的有界投影——已完成/总数计数、始终包含进行中的条目、最多三行,以及当界限省略其余部分时显式的 `… +N more`——由 `card_todo_projection` 用与面向模型的正文相同的快照、优先级顺序和清洗器构建。卡片只会消费 `agent_id` 与自己匹配的信封,因此父智能体的列表绝不会出现在子智能体名下,也没有兄弟智能体的列表会出现在另一个名下。没有陈述任何工作的智能体完全不显示 To-do 行,而不是显示占位任务;终态卡片保留它的智能体实际发布的最后一份快照。扇出(fanout)卡片保持为圆点网格,不显示子智能体的 To-do:一张卡片背后有许多 `worker` 时,没有一个能如实挂载单一列表的位置。只有当运行时已经把该子智能体表示为它自己的委派卡片时,才会出现子智能体的 To-do。 diff --git a/docs/zh_hans/TOOL_LIFECYCLE.md b/docs/zh_hans/TOOL_LIFECYCLE.md index fe9856ae95..dc4127c10b 100644 --- a/docs/zh_hans/TOOL_LIFECYCLE.md +++ b/docs/zh_hans/TOOL_LIFECYCLE.md @@ -40,7 +40,7 @@ Codewhale 自带庞大的原生工具面。该面的首回合 *active* 分区是 **To-do 是唯一的工作面。** `update_plan` 是对话式推理——面向复杂项目的策略、上下文和路线说明。它不是进度面,不得重复 To-do 条目,仅计划状态永远不会渲染为 To-do 快照。 -**任何请求都不会重述清单。** 模型了解 To-do 上有哪些内容的方式与其他任何信息相同:通过它自己的 `todo_write` 调用返回的结果——那是普通的持久化历史。不会向父回合循环步骤或子智能体步骤追加任何内容。完整清单始终在 UI 中可见,而 UI 是与请求不同的另一个面。 +**任何请求都不会重述清单。** 模型了解 To-do 上有哪些内容的方式与其他任何信息相同:通过它自己的 `todo_write` 调用——调用的输入就是完整清单——以及该调用返回的简短回执(计数和进行中的条目)。两者都是普通的持久化历史。不会向父回合循环步骤或子智能体步骤追加任何内容。完整清单始终在 UI 中可见,而 UI 是与请求不同的另一个面。 `crates/tui/src/todo_snapshot.rs` 渲染那唯一的受限主体——在条目数和字符数上都硬受限,进行中的条目被优先保留,任何省略都被标记——用于三个*一次性*显示快照的接缝,因为是人提出的要求:新分叉的智能体拿到的 `` 块、`/relay` 以及转录内的智能体卡片。 From 85063be0bdf55c5fb00f477b8e0d961ee3fd0094 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 12:54:22 +0000 Subject: [PATCH 06/46] fix(goal): usage counts the turn that completes, yields or blocks the goal A session-export review found that goal usage never moved: `tokens_used` stayed at 0 against 1,309,909 tokens actually spent, and `time_used_seconds` was always 0. Usage was recorded after the turn ended, but only while the goal was still active. A turn that completed, yielded or blocked the goal had already left it inactive by then, and the usual goal turn ends exactly that way. So the turns that did the work were never counted. - `GoalState::record_usage` takes the turn's start `Instant`. It also counts when the goal has an objective and finished at or after that start, so the turn that finished the goal is charged. Earlier turns are not. - `record_goal_usage_for_turn` takes the turn's start (`turn.started_at` or the compaction start) and derives elapsed seconds from it. The now unused `TurnContext::elapsed` is removed. - The existing goal, turn_loop, test_cases_04/15 and preview tests pass `Instant::now()` for the new argument. Evidence: cargo fmt --all -- --check is clean. The compile and the goal tests are pending a local test build and hosted CI on codewhale-hq/Codewhale#6957. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01BsXVvReYca2UxzimQtj7Mj --- crates/tui/src/core/engine.rs | 8 ++++---- crates/tui/src/core/engine/compaction.rs | 2 +- crates/tui/src/core/engine/preview/tests.rs | 6 +++--- crates/tui/src/core/engine/tests/test_cases_04.rs | 4 ++-- crates/tui/src/core/engine/tests/test_cases_15.rs | 2 +- crates/tui/src/core/engine/turn_loop.rs | 7 ++++++- crates/tui/src/core/turn.rs | 7 +------ crates/tui/src/tools/goal.rs | 15 +++++++++------ 8 files changed, 27 insertions(+), 24 deletions(-) diff --git a/crates/tui/src/core/engine.rs b/crates/tui/src/core/engine.rs index 9812f43973..8088475ea0 100644 --- a/crates/tui/src/core/engine.rs +++ b/crates/tui/src/core/engine.rs @@ -4340,15 +4340,15 @@ impl Engine { } } - fn record_goal_usage_for_turn(&self, usage: &Usage, elapsed: std::time::Duration) { + fn record_goal_usage_for_turn(&self, usage: &Usage, started: Instant) { let token_delta = u64::from(usage.input_tokens).saturating_add(u64::from(usage.output_tokens)); - let time_delta_seconds = elapsed.as_secs(); + let time_delta_seconds = started.elapsed().as_secs(); if token_delta == 0 && time_delta_seconds == 0 { return; } match self.config.goal_state.lock() { - Ok(mut state) => state.record_usage(token_delta, time_delta_seconds), + Ok(mut state) => state.record_usage(token_delta, time_delta_seconds, started), Err(err) => tracing::warn!("goal state lock poisoned while recording usage: {err}"), } } @@ -6715,7 +6715,7 @@ impl Engine { // Update session usage self.session.total_usage.add(&turn.usage); if !self.is_acp_turn() { - self.record_goal_usage_for_turn(&turn.usage, turn.elapsed()); + self.record_goal_usage_for_turn(&turn.usage, turn.started_at); } // Cancellation wins until the terminal settlement decision. `run_turn` diff --git a/crates/tui/src/core/engine/compaction.rs b/crates/tui/src/core/engine/compaction.rs index f7390dfe27..8bbefcb60c 100644 --- a/crates/tui/src/core/engine/compaction.rs +++ b/crates/tui/src/core/engine/compaction.rs @@ -358,7 +358,7 @@ impl Engine { ) => Some(result), }; self.session.total_usage.add(&compaction_usage); - self.record_goal_usage_for_turn(&compaction_usage, started.elapsed()); + self.record_goal_usage_for_turn(&compaction_usage, started); self.emit_compaction_usage(&compaction_usage, started.elapsed()) .await; diff --git a/crates/tui/src/core/engine/preview/tests.rs b/crates/tui/src/core/engine/preview/tests.rs index 2a75830e86..6739f9c00b 100644 --- a/crates/tui/src/core/engine/preview/tests.rs +++ b/crates/tui/src/core/engine/preview/tests.rs @@ -1091,7 +1091,7 @@ async fn exhausted_active_goal_remains_previewable() { .goal_state .lock() .expect("goal state") - .record_usage(100, 0); + .record_usage(100, 0, std::time::Instant::now()); let prompt = "continue the release"; let planned = plan(&config, &identity, false, prompt).await; @@ -1117,7 +1117,7 @@ async fn resumed_goal_with_raised_budget_becomes_previewable_again() { ); { let mut state = engine.config.goal_state.lock().expect("goal state"); - state.record_usage(100, 0); + state.record_usage(100, 0, std::time::Instant::now()); state .mark_paused(GoalPauseReason::BudgetLimit) .expect("pause goal"); @@ -1154,7 +1154,7 @@ async fn lowering_active_goal_budget_below_used_tokens_keeps_preview_open() { .goal_state .lock() .expect("goal state") - .record_usage(100, 0); + .record_usage(100, 0, std::time::Instant::now()); sync_goal_state_from_host( &engine.config.goal_state, Some("finish the release"), diff --git a/crates/tui/src/core/engine/tests/test_cases_04.rs b/crates/tui/src/core/engine/tests/test_cases_04.rs index 2a96519cfc..180cccc759 100644 --- a/crates/tui/src/core/engine/tests/test_cases_04.rs +++ b/crates/tui/src/core/engine/tests/test_cases_04.rs @@ -561,7 +561,7 @@ async fn exhausted_goal_reaches_route_failure_without_budget_pause() { }; let (mut engine, handle) = Engine::new(engine_config, &config); let goal_state = engine.config.goal_state.clone(); - goal_state.lock().expect("goal lock").record_usage(11, 0); + goal_state.lock().expect("goal lock").record_usage(11, 0, std::time::Instant::now()); let invalid_route_config = without_named_custom_route(config); engine.authoritative_route_config = @@ -776,7 +776,7 @@ async fn cross_turn_goal_continuation_stops_at_an_enforced_token_budget() { .goal_state .lock() .expect("goal lock") - .record_usage(100, 0); + .record_usage(100, 0, std::time::Instant::now()); let action = engine.goal_continuation_if_active(); if enforce { assert!( diff --git a/crates/tui/src/core/engine/tests/test_cases_15.rs b/crates/tui/src/core/engine/tests/test_cases_15.rs index 4907a2d08d..ec5a9792c7 100644 --- a/crates/tui/src/core/engine/tests/test_cases_15.rs +++ b/crates/tui/src/core/engine/tests/test_cases_15.rs @@ -435,7 +435,7 @@ fn turn_metadata_surfaces_goal_budget_only_while_goal_active() { let mut goal = engine.config.goal_state.lock().expect("goal lock"); goal.create("Finish telemetry visibility".to_string(), Some(2_000)) .expect("create goal"); - goal.record_usage(1_000, 100); + goal.record_usage(1_000, 100, std::time::Instant::now()); } let user_msg = engine diff --git a/crates/tui/src/core/engine/turn_loop.rs b/crates/tui/src/core/engine/turn_loop.rs index e45c70ac29..0c6990831d 100644 --- a/crates/tui/src/core/engine/turn_loop.rs +++ b/crates/tui/src/core/engine/turn_loop.rs @@ -8254,7 +8254,12 @@ mod tests { async fn goal_continuation_publishes_current_usage_without_accruing_twice() { let tmp = tempdir().expect("tempdir"); let (engine, handle) = goal_continuation_cadence_engine(&tmp, 0, true); - engine.config.goal_state.lock().unwrap().record_usage(5, 0); + engine + .config + .goal_state + .lock() + .unwrap() + .record_usage(5, 0, std::time::Instant::now()); let registry = goal_continuation_registry(&engine); let mut count = 0; let usage = Usage { diff --git a/crates/tui/src/core/turn.rs b/crates/tui/src/core/turn.rs index 8c096b6e52..7fe841db54 100644 --- a/crates/tui/src/core/turn.rs +++ b/crates/tui/src/core/turn.rs @@ -18,7 +18,7 @@ use crate::core::events::TurnRoute; use crate::snapshot::{SnapshotRepo, TakenSnapshot}; use codewhale_models::Usage; use std::path::Path; -use std::time::{Duration, Instant}; +use std::time::Instant; /// Which configured limit governs a turn's step budget (#5994). #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -186,11 +186,6 @@ impl TurnContext { self.step } - /// Get the elapsed time - pub fn elapsed(&self) -> Duration { - self.started_at.elapsed() - } - /// Complete the existing request projection with observed turn-exit facts. /// A turn that never prepared a request has no request snapshot to publish. pub(crate) fn terminal_request_snapshot( diff --git a/crates/tui/src/tools/goal.rs b/crates/tui/src/tools/goal.rs index 6fafdb6869..0ac1088795 100644 --- a/crates/tui/src/tools/goal.rs +++ b/crates/tui/src/tools/goal.rs @@ -391,8 +391,11 @@ impl GoalState { } } - pub fn record_usage(&mut self, token_delta: u64, time_delta_seconds: u64) { - if self.is_active() { + pub fn record_usage(&mut self, token_delta: u64, time_delta_seconds: u64, since: Instant) { + if self.is_active() + || (self.objective.is_some() + && self.finished_at.is_some_and(|finished| finished >= since)) + { self.tokens_used = self.tokens_used.saturating_add(token_delta); self.time_used_seconds = self.time_used_seconds.saturating_add(time_delta_seconds); } @@ -1543,7 +1546,7 @@ mod tests { Some(1_200), status, ); - state.record_usage(300, 12); + state.record_usage(300, 12, std::time::Instant::now()); state.record_continuation(); let before = state.snapshot(); @@ -1569,7 +1572,7 @@ mod tests { blocked .create("resume the release goal".to_string(), Some(4_000)) .expect("create blocked fixture"); - blocked.record_usage(750, 44); + blocked.record_usage(750, 44, std::time::Instant::now()); blocked.record_continuation(); blocked .mark_blocked("provider failed".to_string()) @@ -1662,7 +1665,7 @@ mod tests { state .create("finish the first objective".to_string(), Some(1_200)) .expect("create first goal"); - state.record_usage(300, 12); + state.record_usage(300, 12, std::time::Instant::now()); state.record_continuation(); state .mark_complete( @@ -2110,7 +2113,7 @@ mod tests { ); { let mut goal = state.lock().expect("goal lock"); - goal.record_usage(300, 12); + goal.record_usage(300, 12, std::time::Instant::now()); goal.record_continuation(); } From d438379ff54bd2037c11bb24e1ae18fc9361fa50 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 12:54:22 +0000 Subject: [PATCH 07/46] fix(pet): show sub-agents while they run, and let a finished one rest The pet read only `app.agent_roster`, which AgentList refreshes between turns. A sub-agent therefore appeared only after it finished. A finished one stayed "working" until the next refresh. `pet_watch::full::live_rows` overlays live state onto that roster when the pet renders: - `app.subagent_cache` supplies each agent's status (its worker status or the terminal-projection mapping), parked state, model, usage and steps. An agent the roster has not listed yet is added from the cache. - `app.agent_progress` supplies the current activity line, and adds a row for an agent known only by progress. - Elapsed time ticks from `started_at` only while the agent runs. A finished agent keeps its recorded duration. It reads the cache and progress maps directly, so the history is not walked on every animation frame. `subagent_routing::worker_status_for_terminal_projection` becomes pub(crate) for this reader. Evidence: cargo fmt --all -- --check is clean. The compile and the pet_watch tests are pending a local test build and hosted CI on codewhale-hq/Codewhale#6957. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01BsXVvReYca2UxzimQtj7Mj --- crates/tui/src/tui/pet_watch/full.rs | 72 +++++++++++++++++++++++++- crates/tui/src/tui/subagent_routing.rs | 2 +- 2 files changed, 71 insertions(+), 3 deletions(-) diff --git a/crates/tui/src/tui/pet_watch/full.rs b/crates/tui/src/tui/pet_watch/full.rs index 4fef282a63..977018f492 100644 --- a/crates/tui/src/tui/pet_watch/full.rs +++ b/crates/tui/src/tui/pet_watch/full.rs @@ -3,7 +3,7 @@ //! durable world and audio. No prompt, worker or execution authority lives here. use super::{PetWatch, habitat}; use crate::{ - agent_roster::RosterState, + agent_roster::{AgentRosterRow, RosterState}, tools::subagent::AgentWorkerStatus, tui::{ app::App, @@ -41,8 +41,76 @@ fn theme(app: &App) -> Theme { .map_or(theme, |palette| theme.tui_palette(palette)) } +fn live_rows(app: &App) -> Vec { + use crate::tui::subagent_routing::{subagent_is_parked, worker_status_for_terminal_projection}; + let mut rows = app.current_agent_roster().to_vec(); + let name = |id: &str| app.agent_label_map.get(id).cloned(); + let blank = |id: &str, display_name: String| AgentRosterRow { + worker_id: id.to_string(), + display_name, + model: String::new(), + state: RosterState::Running, + status: AgentWorkerStatus::Running, + activity: None, + outcome: None, + millis: None, + input_tokens: None, + output_tokens: None, + cost_microusd: None, + steps_taken: 0, + parent_run_id: None, + run_id: id.to_string(), + }; + for agent in &app.subagent_cache { + let index = rows + .iter() + .position(|row| row.worker_id == agent.agent_id) + .unwrap_or_else(|| { + let display_name = name(&agent.agent_id) + .or_else(|| agent.nickname.clone()) + .unwrap_or_else(|| agent.name.clone()); + let usage = agent.usage.as_ref(); + rows.push(AgentRosterRow { + model: agent.model.clone(), + millis: Some(agent.duration_ms), + input_tokens: usage.and_then(|usage| usage.input_tokens), + output_tokens: usage.and_then(|usage| usage.output_tokens), + cost_microusd: usage.and_then(|usage| usage.cost_microusd), + steps_taken: agent.steps_taken, + parent_run_id: agent.parent_run_id.clone(), + ..blank(&agent.agent_id, display_name) + }); + rows.len() - 1 + }); + let row = &mut rows[index]; + row.status = agent + .worker_status + .unwrap_or_else(|| worker_status_for_terminal_projection(&agent.status)); + row.state = if subagent_is_parked(agent) { + RosterState::Parked + } else { + RosterState::from_worker(row.status) + }; + if row.state == RosterState::Running + && let Some(started) = agent.started_at + { + row.millis = Some(u64::try_from(started.elapsed().as_millis()).unwrap_or(u64::MAX)); + } + } + for (id, progress) in &app.agent_progress { + match rows.iter_mut().find(|row| &row.worker_id == id) { + Some(row) => row.activity = Some(progress.clone()), + None => rows.push(AgentRosterRow { + activity: Some(progress.clone()), + ..blank(id, name(id).unwrap_or_else(|| id.clone())) + }), + } + } + rows +} + pub(super) fn agents(app: &App) -> Vec> { - app.current_agent_roster() + live_rows(app) .iter() .map(|row| { let (state, label) = match row.state { diff --git a/crates/tui/src/tui/subagent_routing.rs b/crates/tui/src/tui/subagent_routing.rs index e8888e2a6a..27693a16a3 100644 --- a/crates/tui/src/tui/subagent_routing.rs +++ b/crates/tui/src/tui/subagent_routing.rs @@ -182,7 +182,7 @@ pub(super) fn apply_subagent_terminal_projection( true } -fn worker_status_for_terminal_projection(status: &SubAgentStatus) -> AgentWorkerStatus { +pub(crate) fn worker_status_for_terminal_projection(status: &SubAgentStatus) -> AgentWorkerStatus { match status { SubAgentStatus::Running => AgentWorkerStatus::Running, SubAgentStatus::Completed => AgentWorkerStatus::Completed, From 985967e4bb5ca0fa8aad44695676bd93a0171828 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 12:54:34 +0000 Subject: [PATCH 08/46] fix(pet): crisp half-block sprites; vendor codewhale-ratatui 0.1.2 The pet looked blurred in the terminal. Upstream 387562b had replaced Sprite's nearest-neighbour lookup with an alpha-weighted box filter, which smears avatar tiles at every scale except 1:1. The whale cove was also rasterized with 4x4 supersampling (2x2 above 64 px), so every edge became a fractional-alpha pixel that Sprite then blended toward the background. Re-vendored from upstream 8f51c6bccadeb11278cda1e2481a116573e5d233 (codewhale-hq/codewhale-ratatui#27): - `Sprite::sample` takes the source pixel under the target pixel's centre. - `whale_motion/pixels.rs` rasterizes one sample per pixel. The cove's deliberate layer opacities keep their translucency; only the anti-aliased rim goes. Upstream, with this change: `cargo test --locked`, 787 passed, 0 failed (46 test binaries). The vendored crate goes to 0.1.2. It is the crates.io publish source, and it gained DotWhale after 0.1.1 was published, so the packaged codewhale-tui cannot compile against crates.io 0.1.1. Also changed: - crates/tui and pet/tui depend on 0.1.2, and both lockfiles are updated (cargo metadata --locked passes for the workspace and for pet/tui); - UPSTREAM.json records the upstream commit and the file hashes; - the vendored CHANGELOG has a 0.1.2 entry; - scripts/release/check-versions.sh allows 0.1.2; - the deconstruction roadmap names 0.1.2. Publishing codewhale-ratatui 0.1.2 to crates.io is a founder step and comes before the codewhale-tui publish. Evidence: cargo fmt --all -- --check is clean. The engine compile and the pet_watch tests are pending a local test build and hosted CI on codewhale-hq/Codewhale#6957. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01BsXVvReYca2UxzimQtj7Mj --- Cargo.lock | 2 +- crates/tui/Cargo.toml | 2 +- docs/design/DECONSTRUCTION_ROADMAP.md | 12 +++--- pet/tui/Cargo.lock | 2 +- pet/tui/Cargo.toml | 2 +- scripts/release/check-versions.sh | 2 +- vendor/codewhale-ratatui/CHANGELOG.md | 9 +++++ vendor/codewhale-ratatui/Cargo.lock | 2 +- vendor/codewhale-ratatui/Cargo.toml | 2 +- vendor/codewhale-ratatui/UPSTREAM.json | 6 +-- vendor/codewhale-ratatui/src/avatar_sprite.rs | 40 ++----------------- .../src/whale_motion/pixels.rs | 5 ++- 12 files changed, 32 insertions(+), 54 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 48e4ae5b90..bcafb838c0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1113,7 +1113,7 @@ dependencies = [ [[package]] name = "codewhale-ratatui" -version = "0.1.1" +version = "0.1.2" dependencies = [ "crossterm", "libc", diff --git a/crates/tui/Cargo.toml b/crates/tui/Cargo.toml index f91866fe1b..6b44c76106 100644 --- a/crates/tui/Cargo.toml +++ b/crates/tui/Cargo.toml @@ -39,7 +39,7 @@ path = "src/lib.rs" [dependencies] # Independent terminal kit, pinned and vendored for reproducible binary builds. -codewhale-ratatui = { path = "../../vendor/codewhale-ratatui", version = "0.1.1" } +codewhale-ratatui = { path = "../../vendor/codewhale-ratatui", version = "0.1.2" } codewhale-sanitize = { path = "../sanitize", version = "0.10.2" } codewhale-memory = { path = "../memory", version = "0.10.2" } ahash = "0.8" diff --git a/docs/design/DECONSTRUCTION_ROADMAP.md b/docs/design/DECONSTRUCTION_ROADMAP.md index f829f93868..a52f3bdfc5 100644 --- a/docs/design/DECONSTRUCTION_ROADMAP.md +++ b/docs/design/DECONSTRUCTION_ROADMAP.md @@ -22,7 +22,7 @@ to a date: at the observed cadence (0.10.0 on 2026-09-22, 0.10.1 on - Test edges and test code are not in the tarball: tests/**, **/tests.rs, **/tests/**, *_tests.rs and test_cases_*.rs are excluded by crates/tui/Cargo.toml. Moving tests therefore frees 0 bytes of tarball. Inline `#[cfg(test)]` blocks in non-excluded files (schema_sanitize.rs from line 470, settings.rs from line 2372, test_support.rs from line 657) do ship. - Cadence is the unmeasured cost. v0.10.0 is dated 2026-09-22 and v0.10.1 2026-10-07 (15 days, measured from tags). The sequencer's 10 patch releases to 0.11.0 is about five months at that rate, with no calendar stated. The shared prefix here is 0.10.2 to 0.10.5, roughly six weeks. Track B items are explicit, optional and uncalendared. - Never widen a trust boundary silently. Every `pub(crate)` to `pub` change (try_lock_file_exclusive, test-support items, execpolicy rule types, credentials) gets a one-paragraph visibility review in the commit message. -- Release-time human gates stay human: tags, crates.io upload, new package names, the publication of codewhale-ratatui. tui's manifest depends on crates.io codewhale-ratatui 0.1.1, which is excluded from the workspace and is not in crates.sh or check-crate-sizes.py. It must already be published before any dry-run or upload that builds tui from a tarball. +- Release-time human gates stay human: tags, crates.io upload, new package names, the publication of codewhale-ratatui. tui's manifest depends on crates.io codewhale-ratatui 0.1.2, which is excluded from the workspace and is not in crates.sh or check-crate-sizes.py. It must already be published before any dry-run or upload that builds tui from a tarball. ## Releases @@ -31,9 +31,9 @@ to a date: at the observed cadence (0.10.0 on 2026-09-22, 0.10.1 on Slices: - Release the stamped candidate. workspace.version is already 0.10.2 in Cargo.toml, and `wip(0.10.2)` and ratatui-0.1.1 release-prep commits are on main. The working tree has 48 dirty entries (CHANGELOG.md, settings.rs, tui/app*, runtime_api/*, core/authority.rs, a staged deletion of full_access_confirm.rs). A structural move on top of an RC with another agent's feature in flight is the largest avoidable risk in the plan. - Non-move rider: write the real per-crate tarball sizes (from the dry-run's check-crate-sizes.py output) into the release notes table. This is the baseline column for every later release. -- Non-move rider: record the codewhale-ratatui 0.1.1 publication receipt in the release notes (out-of-band crate, not in crates.sh). +- Non-move rider: record the codewhale-ratatui 0.1.2 publication receipt in the release notes (out-of-band crate, not in crates.sh). -- **Entry gate:** Founder-owned release process per docs/RELEASE_RUNBOOK.md. `python3 scripts/split/module_graph.py --check` PASS at 11/35/1/1. codewhale-ratatui 0.1.1 is on crates.io (human gate). The dirty full-access and settings work is either in the candidate or excluded by its owner. +- **Entry gate:** Founder-owned release process per docs/RELEASE_RUNBOOK.md. `python3 scripts/split/module_graph.py --check` PASS at 11/35/1/1. codewhale-ratatui 0.1.2 is on crates.io (human gate). The dirty full-access and settings work is either in the candidate or excluded by its owner. - **Exit gate:** `./scripts/release/publish-crates.sh dry-run` passes for all 29 crates, including check-crate-sizes.py. validate-crate-publish-order.py passes. Hosted CI green on the exact SHA (macOS and Windows jobs). Source, local, CI, packaged-artifact and release-state evidence are reported separately. - **Ratchet target:** unchanged: prod 11, test 35, late 1, uilib 1. - **Size budget:** All 29 crates: no growth versus the recorded table. codewhale-tui about 9.47 MiB real (about 9.50 by proxy). Not a gate on structure. @@ -191,7 +191,7 @@ Slices: Slices: - Option A (RS-14, #6941): after 0.10.5, move the remaining runtime half into one new crate with scripts/split/move-modules.py, everything except src/tui, src/commands, composer_*, model_relevance and config_keys; codewhale-tui stays the UI plus composition root. Proxy sizes: UI 2.78 MiB (435 files), runtime remainder 6.89 MiB (714 files, of which extension-host, plugins and assets 1.16 MiB). The sequencer's 2.7 / 6.7 estimates are within 0.2 MiB. - Compatibility event, corrected: crates/cli uses codewhale_tui::run (11 uses), RuntimeOptions (8 uses, ::default and the type), route_preferences (get, is_route_key, set_document, set, selected_route, unset, scrub_root_model_aliases_for_export), config_keys (resolved_stream_config, unread_config_keys, config_key_home) and set_telemetry_preference. The sequencer's list omitted RuntimeOptions and set_telemetry_preference. Keep all re-exported from codewhale-tui. -- Publish impact: crates.sh goes 29 -> 30 (new crate placed after core, config, models, runtime, before tui). codewhale-ratatui 0.1.1 stays out of band, so 'all 30 crates' is 30 plus ratatui. +- Publish impact: crates.sh goes 29 -> 30 (new crate placed after core, config, models, runtime, before tui). codewhale-ratatui 0.1.2 stays out of band, so 'all 30 crates' is 30 plus ratatui. - Option B (founder declines RS-14): continue Track B items and then config-types sections one per release. tui stays about 9.5 MiB and the cap stays the binding constraint. - **Entry gate:** Option A: 0.10.2 to 0.10.5 shipped. Ratchet reads prod 0, test 0, late 0, uilib 0. Founder A/B decision recorded and new crate name approved (human gate). Frozen write window across the workspace and one build owner (a new crate adds a build cache: df -h /Volumes/VIXinSSD first). Dirty tree empty for crates/tui. Option B: Track B through 0.10.15 shipped. @@ -230,7 +230,7 @@ Record at every release, on one machine, at a named source commit with a dirty-f - CHANGED: settings S1: production is 2,372 lines (first cfg(test) at settings.rs:2372), not about 2.9k, and the file imports pricing, so it stays blocked on #6396 for CostCurrency. Moved to the end of Track B (0.10.15). - CHANGED: X1 scope: the move must also take RuntimeProcessOwnerLock::try_lock_exclusive and is_contention (runtime_threads.rs:6855). The sequencer described a pure rename of the free function (runtime_threads.rs:6592) that calls both. Verified production call sites: session_manager.rs:1823, 1879, 1954 and session_reconcile.rs:256; the other five are tests. Marked seam-introducing, not pure rename. - CHANGED: size gates. Removed the per-slice size_budget numbers (9.44, 9.43, 9.42, 9.38, 9.36, 9.34, 9.30) and the per-slice `cargo package` + check-crate-sizes gate. They sit below proxy resolution (about 0.03 MiB) and a single-crate `cargo package` cannot resolve unpublished 0.10.x workspace deps; only the multi-package dry-run can, and it builds all 29 tarballs. Replaced with: no crate grows at the release cut, plus one proxy command for between-release checks. Size-guard question for the founder: keep the 10 MiB hard fail unchanged (recommended); decide whether the fixed 90% warn becomes relative to the last release's recorded size. The warn fires on codewhale-tui every release at 94.7% and is the only noise. Not changed by this plan, and I recommend leaving the script alone until the table of real sizes exists. -- CHANGED: publish-side facts. codewhale-ratatui 0.1.1 is a published dependency of tui that is neither in crates.sh nor checked by check-crate-sizes.py (vendor/codewhale-ratatui is a workspace exclude; check-versions.sh special-cases it). It must be on crates.io before any dry-run that unpacks tui. Added to the 0.10.2 entry gate and to the 0.11.0 'all 30 crates' wording. Founder to confirm that its size is intentionally outside the guard. +- CHANGED: publish-side facts. codewhale-ratatui 0.1.2 is a published dependency of tui that is neither in crates.sh nor checked by check-crate-sizes.py (vendor/codewhale-ratatui is a workspace exclude; check-versions.sh special-cases it). It must be on crates.io before any dry-run that unpacks tui. Added to the 0.10.2 entry gate and to the 0.11.0 'all 30 crates' wording. Founder to confirm that its size is intentionally outside the guard. - CHANGED: coordination. Added pathspec commits. Verified `D crates/tui/src/tui/full_access_confirm.rs` is already staged by another agent, so 'stage only owned paths' is not enough if the commit takes the whole index. Replaced the blanket 'dirty tree blocks the slice' with a named-files rule and a skip-to-next-independent-release rule. - CHANGED: 0.11.0 compatibility list: crates/cli also uses RuntimeOptions (8 uses) and set_telemetry_preference. Proxy sizes validate the sequencer's Option A estimate within 0.2 MiB (UI 2.78 vs 2.7, runtime 6.89 vs 6.7); the budget is raised to 7.2 MiB so a measured 6.89 plus growth does not trip it. - NOT RE-VERIFIED: X7's exact moved set (design table says tool_denied, tool_matches_any_rule, requires_raw_shell, policy_tool_aliases; sequencer says AutoReview* types and HumanDecision), the 9.47 MiB real tarball (only the proxy 9.50 was computed), live state of #6937-#6941, the 12/55/30/62 edge counts in the sequencer's ratchet targets, the 29-crate dry-run build time, any build-time effect. @@ -240,7 +240,7 @@ Record at every release, on one machine, at a named source commit with a dirty-f ## Tracking issues to file (one per slice) - **0.10.5**: A/B decision: RS-14 atomic move vs slice-only path (record in 0.10.5). Decide whether 0.11.0 is the atomic move of the remaining runtime SCC (Option A) or the design doc's slice-only path (Option B). Evidence: with A, X1/X4/X7/X8/P1/P2/settings are not prerequisites; only A moves tui below 9.0 MiB (proxy UI 2.78 MiB / runtime 6.89 MiB). Needs prod/test/late/uilib all 0 first. Linear only, not mirrored. Reuse the #6941 issue if it covers this. -- **0.10.2**: 0.10.2: ship candidate with no structural move; record per-crate tarball table and ratatui 0.1.1 receipt. No move in the candidate. Add real per-crate tarball sizes to the release notes (baseline for later releases) and the codewhale-ratatui 0.1.1 publication receipt (out of band, not in crates.sh). Gate: publish-crates.sh dry-run, validate-crate-publish-order.py, hosted CI green on the SHA. +- **0.10.2**: 0.10.2: ship candidate with no structural move; record per-crate tarball table and ratatui 0.1.2 receipt. No move in the candidate. Add real per-crate tarball sizes to the release notes (baseline for later releases) and the codewhale-ratatui 0.1.2 publication receipt (out of band, not in crates.sh). Gate: publish-crates.sh dry-run, validate-crate-publish-order.py, hosted CI green on the SHA. - **0.10.3**: 0.10.3 X2: command catalog port, finish RS-13 (#6940). runtime_api.rs:4815-4898 and tools/tui_help.rs go through a command_catalog leaf; delete the direct call sites; install the real catalog process-wide for tests. Ratchet prod 11 -> 0. Claim exact files only (runtime_api.rs, tools/tui_help.rs, commands/catalog.rs, extension_host/command.rs). Linear only, not mirrored. - **0.10.4**: 0.10.4 W0-S2: codewhale-test-support (publish=false), env half. Move test_env_lock.rs (300) and the env half of test_support.rs (657 non-test lines) to crates/test/test-support; version.workspace=true; add to root members; keep tui shim and App fixtures; remove shell_dispatcher cfg(test) mount and the tests/integration #[path] include. Critical path for settings and credentials tests. Gate: equal before/after pass counts, one process-global lock copy, crates.sh stays 29. - **0.10.5**: 0.10.5 Test-edge rehoming: test 35 -> 0, late 1 -> 0, uilib 1 -> 0. Rehome tests naming crate::tui, commands or lib.rs (tools|tui 12, client|tui 3, session_manager|tui 3, fleet|lib.rs 3, config|tui 2, core|tui 2, prompts|tui 1), plus test_cases_04.rs:1197 (exec_agent) and roster_routes.rs:798 (crossterm). Test-only; equal pass counts before and after per directory. Prerequisite for any new runtime crate that must not depend on tui or crossterm. diff --git a/pet/tui/Cargo.lock b/pet/tui/Cargo.lock index b02babf380..4128b8aea7 100644 --- a/pet/tui/Cargo.lock +++ b/pet/tui/Cargo.lock @@ -139,7 +139,7 @@ dependencies = [ [[package]] name = "codewhale-ratatui" -version = "0.1.1" +version = "0.1.2" dependencies = [ "crossterm", "libc", diff --git a/pet/tui/Cargo.toml b/pet/tui/Cargo.toml index 60d8713386..1c688588b2 100644 --- a/pet/tui/Cargo.toml +++ b/pet/tui/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2024" [dependencies] -codewhale-ratatui = { path = "../../vendor/codewhale-ratatui", version = "0.1.1" } +codewhale-ratatui = { path = "../../vendor/codewhale-ratatui", version = "0.1.2" } ratatui = "=0.30.2" unicode-width = "=0.2.2" diff --git a/scripts/release/check-versions.sh b/scripts/release/check-versions.sh index 03caa798bb..a6fa9b4ef5 100755 --- a/scripts/release/check-versions.sh +++ b/scripts/release/check-versions.sh @@ -132,7 +132,7 @@ fi # 4) Internal path dependency pins. internal_dep_drift="$( grep -nE 'codewhale-[a-z-]+[[:space:]]*=[[:space:]]*\{[^}]*version[[:space:]]*=[[:space:]]*"' crates/*/Cargo.toml \ - | grep -vF 'codewhale-ratatui = { path = "../../vendor/codewhale-ratatui", version = "0.1.1" }' \ + | grep -vF 'codewhale-ratatui = { path = "../../vendor/codewhale-ratatui", version = "0.1.2" }' \ | grep -v "version[[:space:]]*=[[:space:]]*\"${workspace_version}\"" || true )" if [[ -n "${internal_dep_drift}" ]]; then diff --git a/vendor/codewhale-ratatui/CHANGELOG.md b/vendor/codewhale-ratatui/CHANGELOG.md index 83638324c6..78e4ff5ead 100644 --- a/vendor/codewhale-ratatui/CHANGELOG.md +++ b/vendor/codewhale-ratatui/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## 0.1.2 - 2026-10-11 + +- Let the host paint its prepared pet in `PetMode` (`DotWhale`, + `PetMode::dot_whale`), the API Codewhale 0.10.2 renders the pet through. +- Draw half-block sprites crisply: `Sprite` samples the pixel under each + cell's centre instead of box-filtering, and the whale cove rasterizes one + sample per pixel so shape edges no longer blend into the terminal + background. + ## 0.1.1 - 2026-10-11 - Simplify the native showcase: concise conversation, sparse ambient life, diff --git a/vendor/codewhale-ratatui/Cargo.lock b/vendor/codewhale-ratatui/Cargo.lock index 9509ad9149..878afa3eff 100644 --- a/vendor/codewhale-ratatui/Cargo.lock +++ b/vendor/codewhale-ratatui/Cargo.lock @@ -61,7 +61,7 @@ checksum = "4e7648175b45a9a48536d676f68d918270699102aa8dab5496df06904c914600" [[package]] name = "codewhale-ratatui" -version = "0.1.1" +version = "0.1.2" dependencies = [ "crossterm", "insta", diff --git a/vendor/codewhale-ratatui/Cargo.toml b/vendor/codewhale-ratatui/Cargo.toml index 3acf6e36a1..6cd63b7700 100644 --- a/vendor/codewhale-ratatui/Cargo.toml +++ b/vendor/codewhale-ratatui/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "codewhale-ratatui" -version = "0.1.1" +version = "0.1.2" edition = "2024" rust-version = "1.89" license = "MIT" diff --git a/vendor/codewhale-ratatui/UPSTREAM.json b/vendor/codewhale-ratatui/UPSTREAM.json index 6c7dcb3bea..8003a22fa3 100644 --- a/vendor/codewhale-ratatui/UPSTREAM.json +++ b/vendor/codewhale-ratatui/UPSTREAM.json @@ -187,16 +187,16 @@ "vendor/codewhale-design/tokens.rs": "975b7a4e2cda30f9dfa642283f092b9770529c5ec8c401d05b9bde5b6e55bdd1" }, "pet_update": { - "commit": "00c6505356c5264fa6fd7d01f66458624d03e951", + "commit": "8f51c6bccadeb11278cda1e2481a116573e5d233", "scope": "Selected prepared pet renderer files; non-pet components retain the base snapshot and existing Engine patches.", "files": { - "src/avatar_sprite.rs": "efa14ec46bae22772f01fa28375c4f75cb8fe3696ca29f392b157b6173e1adee", + "src/avatar_sprite.rs": "2d76da6667d24fbcd36a22d5a70c8ef9136eb1b853bb32884cf236ee0cce92b6", "src/color.rs": "96c44be1540c4756f251ed5ffb74aa88a5ebf131b104bf25866ad7ab8e3c6ba8", "src/components/dot_whale.rs": "191bacf808037ba4dd7d4dfffbd06fe9c951a258a31495380591ad6ab4a715fa", "src/components/pet_mode.rs": "35142d709073a09720f1ce1cfb57b18d6ea4880530a5c8d96755c5b9fc203214", "src/components/whale_pet.rs": "bb29442f8db20e8387482e2e1dcb4fa5259ef99c3a37a77bfe9e4b56eaa05aed", "src/whale.rs": "126a6efc8cac550ccd7f9c8edc61745b8c959186de118c2f4d479f5a5c5ffcf7", - "src/whale_motion/pixels.rs": "f232f48a1a9c36339b3dd78e9c5ae5a2043d08749d023977bc39633821ad0bb6" + "src/whale_motion/pixels.rs": "f83113863c7928cc93dbbc7b187d6f9346d1848ffb9636fd3d8551faa9de69d9" }, "merged_exports": { "src/components/mod.rs": "0ea81b474b2137b177c5535e7523ed6b312ea972630ac94f5131883246496e8e" diff --git a/vendor/codewhale-ratatui/src/avatar_sprite.rs b/vendor/codewhale-ratatui/src/avatar_sprite.rs index 5aaf0668f8..c92ceb5794 100644 --- a/vendor/codewhale-ratatui/src/avatar_sprite.rs +++ b/vendor/codewhale-ratatui/src/avatar_sprite.rs @@ -94,42 +94,10 @@ impl<'a> Sprite<'a> { } fn sample(&self, x: usize, y: usize, width: usize, height: usize) -> [u8; 4] { - if width == self.width && height == self.height { - return self.pixel(x, y); - } - let bounds = |position: usize, source: usize, target: usize| { - let scale = source as f64 / target as f64; - let center = (position as f64 + 0.5) * scale; - let radius = scale.max(1.) / 2.; - ( - (center - radius).max(0.), - (center + radius).min(source as f64), - ) - }; - let (left, right) = bounds(x, self.width, width); - let (top, bottom) = bounds(y, self.height, height); - let mut rgba = [0.; 4]; - for sy in top.floor() as usize..(bottom.ceil() as usize).min(self.height) { - let weight_y = (bottom.min(sy as f64 + 1.) - top.max(sy as f64)).max(0.); - for sx in left.floor() as usize..(right.ceil() as usize).min(self.width) { - let weight_x = (right.min(sx as f64 + 1.) - left.max(sx as f64)).max(0.); - let pixel = self.pixel(sx, sy); - let alpha = f64::from(pixel[3]) * weight_x * weight_y; - for channel in 0..3 { - rgba[channel] += f64::from(pixel[channel]) * alpha; - } - rgba[3] += alpha; - } - } - if rgba[3] == 0. { - return [0; 4]; - } - [ - (rgba[0] / rgba[3]).round() as u8, - (rgba[1] / rgba[3]).round() as u8, - (rgba[2] / rgba[3]).round() as u8, - (rgba[3] / ((right - left) * (bottom - top))).round() as u8, - ] + self.pixel( + ((2 * x + 1) * self.width / (2 * width)).min(self.width - 1), + ((2 * y + 1) * self.height / (2 * height)).min(self.height - 1), + ) } } impl Paint for Sprite<'_> { diff --git a/vendor/codewhale-ratatui/src/whale_motion/pixels.rs b/vendor/codewhale-ratatui/src/whale_motion/pixels.rs index 674e0916d6..8569fe448f 100644 --- a/vendor/codewhale-ratatui/src/whale_motion/pixels.rs +++ b/vendor/codewhale-ratatui/src/whale_motion/pixels.rs @@ -80,13 +80,14 @@ impl Canvas { } } -/// Square design space, with extra samples at small sizes for clean curved edges. +/// Square design space at one sample per pixel, so shape edges stay hard on a +/// half-block grid instead of blending into the terminal background. /// Public terminal rectangles never control an unbounded allocation. pub(crate) fn render(scene: &CoveScene, side: usize, dark: bool) -> Vec { if !(1..=MAX_SIDE).contains(&side) { return Vec::new(); } - let samples = if side <= 64 { 4 } else { 2 }; + let samples = 1; let mut canvas = Canvas { side: side * samples, pixels: vec![[0.; 4]; side * side * samples * samples], From 674dcc364dff086e01349718a42b23489575a4c9 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 12:54:53 +0000 Subject: [PATCH 09/46] fix(shell): a silent running command stays running and stoppable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refs #6931. The issue's headline case was a running recursive search shown as "run running · stale · no output 2m 07s". After 60 s without output, `job_snapshot` flagged a live, non-PTY job `stale`. The inspector, the /jobs list, the work dock and the Runtime API all read `stale` as "process gone": - the Stop action disappeared (shell_job_routing `live = Running && !stale`, work_surface `shell_inspector_body`); - the detail said "stale after restart; the process is no longer running"; - "(no output yet)" was suppressed; - the API refused resize as "no longer running" and reported no tty. - A live process is never `stale`. `job_snapshot` sets `stale: false`; only `remember_stale_job` (a restart snapshot, status Killed) sets it, which is the "known-stale" job docs/RUNTIME_API.md describes. The 60 s threshold becomes `QUIET_NO_OUTPUT_AFTER` and only drives presentation. - The exec card reads "running · no output for 2m 07s" instead of "running · stale · no output …". Silence is not shown as hung, which acceptance 2 requires. - `/jobs poll` and `/jobs stdin` no longer consume the model's unread output. Both called `poll_delta_for_session`, which advances the model's stdout cursor (acceptance 4). - `poll` opens the inspector from `inspect_job_for_session`. - `/jobs wait` uses the new `wait_job_for_session`, which waits through the same `wait_while_running` loop `get_output_delta` uses, then inspects. - `stdin` shows the job's one-line status and tail. - `poll_delta_for_session` has no callers left and is removed. - Alt+V with no selection prefers the newest running shell only when it is visible in the transcript viewport. It no longer takes over the completed card the user is looking at (acceptance 1). A click or selection still targets that exact card. - shell_job_routing.rs records its known limitations. The inspector is a snapshot with no follow mode; `/jobs poll` re-reads it. Named PTY sessions live in the terminal dock (#6912), not /jobs. - Tests updated where this change made them wrong: - running_job_snapshot_keeps_silent_live_job_live asserts a silent live job is not stale and still exposes its quiet time; - the stale-running fixture in ui/tests.rs is `stale: false`, and its header assertion uses the new label; - QUIET_NO_OUTPUT_AFTER replaces the old constant name. Still open in #6931: - a follow-mode or auto-refreshing inspector; - listing delegated agents' shells in the agent detail; - the PTY journeys on the installed candidate at normal and narrow widths. Evidence: cargo fmt --all -- --check is clean. The compile and the tools::shell / tui tests are pending a local test build and hosted CI on codewhale-hq/Codewhale#6957. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01BsXVvReYca2UxzimQtj7Mj --- crates/tui/src/tools/shell.rs | 66 ++++++++++++------------ crates/tui/src/tools/shell/tests.rs | 12 ++--- crates/tui/src/tui/history.rs | 2 +- crates/tui/src/tui/shell_job_routing.rs | 29 ++++++++--- crates/tui/src/tui/ui/activity_detail.rs | 3 +- crates/tui/src/tui/ui/handlers.rs | 35 +++++++------ crates/tui/src/tui/ui/task_projection.rs | 7 ++- crates/tui/src/tui/ui/tests.rs | 4 +- 8 files changed, 87 insertions(+), 71 deletions(-) diff --git a/crates/tui/src/tools/shell.rs b/crates/tui/src/tools/shell.rs index 9ddc0bea66..dc7c18fa02 100644 --- a/crates/tui/src/tools/shell.rs +++ b/crates/tui/src/tools/shell.rs @@ -1003,7 +1003,7 @@ fn shared_output_pipe() -> io::Result<(File, File, File)> { } const SYNC_READER_DRAIN_TIMEOUT: Duration = Duration::from_secs(5); -const STALE_NO_OUTPUT_AFTER: Duration = Duration::from_secs(60); +pub(crate) const QUIET_NO_OUTPUT_AFTER: Duration = Duration::from_secs(60); /// Grace between SIGTERM and SIGKILL on the shell kill path (timeout, /// cancel, drop). Bounded so a SIGTERM-ignoring command is force-killed @@ -1717,13 +1717,6 @@ impl BackgroundShell { .unwrap_or((0, String::new())); let elapsed_since_output_ms = (self.status == ShellStatus::Running) .then(|| u64::try_from(self.last_output_at.elapsed().as_millis()).unwrap_or(u64::MAX)); - // A live PTY can wait indefinitely for input. Silence does not mean it - // lost its process owner; marking it stale hides its transport from API - // clients and incorrectly prevents reconnect/resize after one minute. - let stale = self.terminal_size.is_none() - && elapsed_since_output_ms.is_some_and(|elapsed| { - elapsed >= u64::try_from(STALE_NO_OUTPUT_AFTER.as_millis()).unwrap_or(u64::MAX) - }); ShellJobSnapshot { id: self.id.clone(), job_id: self.id.clone(), @@ -1739,7 +1732,7 @@ impl BackgroundShell { stdout_len, stderr_len, stdin_available: self.stdin.is_some() && self.status == ShellStatus::Running, - stale, + stale: false, elapsed_since_output_ms, linked_task_id: self.linked_task_id.clone(), owner_agent_id: self @@ -3127,20 +3120,7 @@ impl ShellManager { .processes .get_mut(task_id) .ok_or_else(|| anyhow!("Task {task_id} not found"))?; - - if wait && shell.status == ShellStatus::Running { - let timeout = Duration::from_millis(timeout_ms.clamp(1000, 600_000)); - let deadline = Instant::now() + timeout; - - while shell.status == ShellStatus::Running && Instant::now() < deadline { - if shell.poll() { - break; - } - std::thread::sleep(Duration::from_millis(100)); - } - } else { - shell.poll(); - } + wait_while_running(shell, wait, timeout_ms); let ( stdout_delta, @@ -3351,17 +3331,6 @@ impl ShellManager { self.get_output_delta(task_id, wait, timeout_ms) } - pub fn poll_delta_for_session( - &mut self, - active_session_id: &str, - task_id: &str, - wait: bool, - timeout_ms: u64, - ) -> Result { - self.require_session_owner(task_id, active_session_id)?; - self.get_output_delta(task_id, wait, timeout_ms) - } - fn get_output_delta_for_session( &mut self, active_session_id: &str, @@ -3494,6 +3463,19 @@ impl ShellManager { self.inspect_job(task_id) } + pub fn wait_job_for_session( + &mut self, + active_session_id: &str, + task_id: &str, + timeout_ms: u64, + ) -> Result { + self.require_session_owner(task_id, active_session_id)?; + if let Some(shell) = self.processes.get_mut(task_id) { + wait_while_running(shell, true, timeout_ms); + } + self.inspect_job(task_id) + } + /// List all live and known-stale background shell jobs for the TUI. pub fn list_jobs(&mut self) -> Vec { for shell in self.processes.values_mut() { @@ -3788,6 +3770,22 @@ impl ShellManager { } } +fn wait_while_running(shell: &mut BackgroundShell, wait: bool, timeout_ms: u64) { + if wait && shell.status == ShellStatus::Running { + let timeout = Duration::from_millis(timeout_ms.clamp(1000, 600_000)); + let deadline = Instant::now() + timeout; + + while shell.status == ShellStatus::Running && Instant::now() < deadline { + if shell.poll() { + break; + } + std::thread::sleep(Duration::from_millis(100)); + } + } else { + shell.poll(); + } +} + fn job_status_rank(status: &ShellStatus, stale: bool) -> u8 { if stale { return 4; diff --git a/crates/tui/src/tools/shell/tests.rs b/crates/tui/src/tools/shell/tests.rs index 57f51e176b..08f4d86bcc 100644 --- a/crates/tui/src/tools/shell/tests.rs +++ b/crates/tui/src/tools/shell/tests.rs @@ -2652,7 +2652,7 @@ fn test_job_list_poll_cancel_and_stale_snapshot() { } #[test] -fn running_job_snapshot_marks_no_output_stale_after_threshold() { +fn running_job_snapshot_keeps_silent_live_job_live() { let tmp = tempdir().expect("tempdir"); let mut manager = ShellManager::new(tmp.path().to_path_buf()); @@ -2662,7 +2662,7 @@ fn running_job_snapshot_marks_no_output_stale_after_threshold() { { let shell = manager.processes.get_mut(&task_id).expect("live shell"); - shell.last_output_at = Instant::now() - STALE_NO_OUTPUT_AFTER - Duration::from_millis(1); + shell.last_output_at = Instant::now() - QUIET_NO_OUTPUT_AFTER - Duration::from_millis(1); } let job = manager @@ -2672,10 +2672,10 @@ fn running_job_snapshot_marks_no_output_stale_after_threshold() { .expect("running job"); assert_eq!(job.status, ShellStatus::Running); - assert!(job.stale, "silent running job should be marked stale"); + assert!(!job.stale, "silence alone must not mark a live job stale"); assert!( job.elapsed_since_output_ms - .is_some_and(|elapsed| elapsed >= STALE_NO_OUTPUT_AFTER.as_millis() as u64), + .is_some_and(|elapsed| elapsed >= QUIET_NO_OUTPUT_AFTER.as_millis() as u64), "elapsed no-output time should be exposed: {job:?}" ); } @@ -5773,14 +5773,14 @@ fn pty_resize_updates_live_terminal_and_rejects_finished_or_pipe_jobs() { // Reopening a client after an idle minute must retain this live PTY's // identity and resize eligibility; output silence is normal at a prompt. manager.processes.get_mut(&id).unwrap().last_output_at = - Instant::now() - STALE_NO_OUTPUT_AFTER - Duration::from_millis(1); + Instant::now() - QUIET_NO_OUTPUT_AFTER - Duration::from_millis(1); let idle = manager.inspect_job(&id).unwrap().snapshot; assert_eq!(idle.status, ShellStatus::Running); assert!(idle.stdin_available); assert!(!idle.stale, "an idle live PTY must remain reconnectable"); assert!( idle.elapsed_since_output_ms - .is_some_and(|ms| ms >= STALE_NO_OUTPUT_AFTER.as_millis() as u64) + .is_some_and(|ms| ms >= QUIET_NO_OUTPUT_AFTER.as_millis() as u64) ); let size = PtyDimensions { rows: 37, diff --git a/crates/tui/src/tui/history.rs b/crates/tui/src/tui/history.rs index 2847f7fe6c..75165b91a5 100644 --- a/crates/tui/src/tui/history.rs +++ b/crates/tui/src/tui/history.rs @@ -2819,7 +2819,7 @@ pub(crate) fn running_status_label_with_elapsed(elapsed_secs: u64) -> String { pub(crate) fn stale_shell_status_label(elapsed_since_output_ms: u64) -> String { format!( - "running · stale · no output {}", + "running · no output for {}", codewhale_command_contract::elapsed::format_elapsed_ms(elapsed_since_output_ms) ) } diff --git a/crates/tui/src/tui/shell_job_routing.rs b/crates/tui/src/tui/shell_job_routing.rs index 64d2c28146..946ac3ca7a 100644 --- a/crates/tui/src/tui/shell_job_routing.rs +++ b/crates/tui/src/tui/shell_job_routing.rs @@ -1,4 +1,9 @@ //! Background shell job-center helpers for slash commands and pagers. +//! +//! Known limitations: the inspector is a snapshot (`/jobs poll` re-reads it, +//! without consuming the model's output cursor); there is no follow mode. +//! Named PTY sessions (`tools/terminal_session.rs`) are not listed here or in +//! `/jobs`; they live in the terminal dock (#6912). use crate::tools::shell::{ShellJobDetail, ShellJobSnapshot, ShellResult, ShellStatus}; use crate::tui::app::App; @@ -139,13 +144,23 @@ pub(crate) fn shell_job_for_cell(app: &App, cell_index: usize) -> Option Option { - (0..app.virtual_cell_count()).rev().find(|&index| { - matches!( - app.cell_at_virtual_index(index), - Some(HistoryCell::Tool(ToolCell::Exec(exec))) if exec.status == ToolStatus::Running - ) - }) +pub(crate) fn newest_visible_running_shell_cell(app: &App) -> Option { + let meta = app.viewport.transcript_cache.line_meta(); + let start = app.viewport.last_transcript_top.min(meta.len()); + let end = start + .saturating_add(app.viewport.last_transcript_visible.max(1)) + .min(meta.len()); + meta[start..end] + .iter() + .rev() + .filter_map(|line| line.cell_line()) + .map(|(rendered, _)| app.original_cell_index_for_rendered(rendered)) + .find(|&index| { + matches!( + app.cell_at_virtual_index(index), + Some(HistoryCell::Tool(ToolCell::Exec(exec))) if exec.status == ToolStatus::Running + ) && shell_job_for_cell(app, index).is_some() + }) } pub(crate) fn open_shell_job_pager(app: &mut App, detail: &ShellJobDetail) { diff --git a/crates/tui/src/tui/ui/activity_detail.rs b/crates/tui/src/tui/ui/activity_detail.rs index 9e09849e5f..f330e92fa8 100644 --- a/crates/tui/src/tui/ui/activity_detail.rs +++ b/crates/tui/src/tui/ui/activity_detail.rs @@ -283,8 +283,7 @@ pub(super) fn open_tool_details_pager(app: &mut App) -> bool { { detail_target_cell_index(app) } else { - crate::tui::shell_job_routing::newest_running_shell_cell(app) - .filter(|&idx| crate::tui::shell_job_routing::shell_job_for_cell(app, idx).is_some()) + crate::tui::shell_job_routing::newest_visible_running_shell_cell(app) .or_else(|| detail_target_cell_index(app)) }; diff --git a/crates/tui/src/tui/ui/handlers.rs b/crates/tui/src/tui/ui/handlers.rs index 7f9c831b19..6c56170e8b 100644 --- a/crates/tui/src/tui/ui/handlers.rs +++ b/crates/tui/src/tui/ui/handlers.rs @@ -1245,29 +1245,30 @@ pub(crate) fn handle_shell_job_action(app: &mut App, action: crate::tui::app::Sh } } crate::tui::app::ShellJobAction::Poll { id, wait } => { - match manager.poll_delta_for_session( - &active_session_id, - &id, - wait, - if wait { 5_000 } else { 1_000 }, - ) { - Ok(delta) => add_shell_job_message(app, format_shell_poll(&delta.result)), + let detail = if wait { + manager.wait_job_for_session(&active_session_id, &id, 5_000) + } else { + manager.inspect_job_for_session(&active_session_id, &id) + }; + match detail { + Ok(detail) => open_shell_job_pager(app, &detail), Err(err) => add_shell_job_message(app, format!("Command poll failed: {err}")), } } crate::tui::app::ShellJobAction::SendStdin { id, input, close } => { match manager.write_stdin_for_session(&active_session_id, &id, &input, close) { - Ok(()) => { - match manager.poll_delta_for_session(&active_session_id, &id, false, 1_000) { - Ok(delta) => add_shell_job_message(app, format_shell_poll(&delta.result)), - Err(err) => { - add_shell_job_message( - app, - format!("Command input sent; poll failed: {err}"), - ); - } + Ok(()) => match manager.inspect_job_for_session(&active_session_id, &id) { + Ok(detail) => add_shell_job_message( + app, + format_shell_job_list(std::slice::from_ref(&detail.snapshot)), + ), + Err(err) => { + add_shell_job_message( + app, + format!("Command input sent; poll failed: {err}"), + ); } - } + }, Err(err) => add_shell_job_message(app, format!("Command input failed: {err}")), } } diff --git a/crates/tui/src/tui/ui/task_projection.rs b/crates/tui/src/tui/ui/task_projection.rs index f71b17565d..bd2c40508e 100644 --- a/crates/tui/src/tui/ui/task_projection.rs +++ b/crates/tui/src/tui/ui/task_projection.rs @@ -625,8 +625,11 @@ pub(super) fn shell_exec_live_update( let next_status = shell_job_tool_status(&job.status); let next_live = shell_job_live_output(job).or_else(|| exec.live_output.clone()); let finalized = !matches!(job.status, ShellStatus::Running); - let stale_elapsed_since_output_ms = if matches!(job.status, ShellStatus::Running) && job.stale { - Some(job.elapsed_since_output_ms.unwrap_or(0)) + let quiet_after_ms = + u64::try_from(crate::tools::shell::QUIET_NO_OUTPUT_AFTER.as_millis()).unwrap_or(u64::MAX); + let stale_elapsed_since_output_ms = if matches!(job.status, ShellStatus::Running) { + job.elapsed_since_output_ms + .filter(|elapsed| *elapsed >= quiet_after_ms) } else { None }; diff --git a/crates/tui/src/tui/ui/tests.rs b/crates/tui/src/tui/ui/tests.rs index 9e61daf040..62c9ec475c 100644 --- a/crates/tui/src/tui/ui/tests.rs +++ b/crates/tui/src/tui/ui/tests.rs @@ -10185,7 +10185,7 @@ fn shell_live_output_update_marks_stale_running_job_static() { stdout_len: 14, stderr_len: 0, stdin_available: false, - stale: true, + stale: false, elapsed_since_output_ms: Some(61_000), linked_task_id: None, owner_agent_id: None, @@ -10228,7 +10228,7 @@ fn shell_live_output_update_marks_stale_running_job_static() { .iter() .map(|span| span.content.as_ref()) .collect::(); - assert!(header_text.contains("running · stale · no output 1m 01s")); + assert!(header_text.contains("running · no output for 1m 01s")); assert!(!header_text.contains("running (")); } From e71255e2b59ab7bc199d9b4417363cc720c9ce2b Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 12:55:08 +0000 Subject: [PATCH 10/46] fix(constitution): ratify checks what you reviewed; preview and wording say what happens MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Session-export review of /constitution. Each item was read on both sides before changing it. - ratify promised it would be "refused if the base changed after you reviewed it", but it re-read the live file and handed that same digest back, so the guard could never fire. `/constitution suggestions` now records the digest the user reviewed on `App`, and ratify passes that to `UserConstitution::ratify`. Ratify without a review says to review first. A ratify after the file moved is refused as StaleBase. The existing ratify test now reviews before it ratifies, and checks the unreviewed refusal. - `preview` claimed to show "the exact block that would be injected", but rendered `source=""`, while the engine injects `render_block(None)` (`source="user-global"`). It now renders the same block. - ratify and migrate said the prompt-cache prefix "moved" or "survives". The constitution reaches the model as a session message appended at turn admission (`record_current_constitution`), outside the cached prefix, as prompts.rs's `user_global_constitution_is_captured_outside_the_stable_prefix` asserts. The messages now say so, and so does the user_constitution module doc. The migrate test checks the new wording. - The setup wizard printed the assembly order (bundled → user-global → repo → AGENTS → memory → current request) under "HIERARCHY OF AUTHORITY". That reverses the base prompt's ranking, which says it "is stated here and nowhere else". The line is removed from all 15 locale templates, and so is the now-unused SetupCheckpointLayerOrder message. "Your direct requests always outrank this document" stays. - The wizard capped the user's own note at 4000 characters, then appended it to a ~300-character template. save_to then trimmed the total to 4000, silently cutting off the end of the user's own text. The note is now capped at MAX_NOTES_LEN - 1000 while typing, so the saved file keeps all of it. The longest template is 335 characters before labels. - zh-Hans used 协作准则 in the /constitution manager and 宪章 (the docs/zh_hans/GLOSSARY.md term) elsewhere, sometimes on the same screen. It now uses 宪章 throughout, including the model-draft status line. - The help usage line and docs/CONFIGURATION.md now list base, suggestions, ratify, migrate and rollback. The step is named "Working agreement" as the wizard shows it. docs/GUIDE.md no longer calls /constitution a way to customize the bundled layer. Not changed here: - A signed-in user still sees the local file as "active", although the account profile is what the model gets. The fix needs an async account check, because the credential store must not be read on the UI loop. - The Runtime API's GET and preview `modelGuidance` shapes differ. - `doctor` reports autonomy from an inactive file. - The context inspector lists the personal constitution as a system-prompt layer. Evidence: cargo fmt --all -- --check is clean, and every edited locale file parses as JSON. The compile and the commands::core::constitution tests are pending a local test build and hosted CI on codewhale-hq/Codewhale#6957. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01BsXVvReYca2UxzimQtj7Mj --- crates/config/src/user_constitution.rs | 9 +- crates/localization/locales/ca.json | 1 - crates/localization/locales/de.json | 1 - crates/localization/locales/en.json | 1 - crates/localization/locales/es-419.json | 1 - crates/localization/locales/fr.json | 1 - crates/localization/locales/hi.json | 1 - crates/localization/locales/id.json | 1 - crates/localization/locales/ja.json | 1 - crates/localization/locales/ko.json | 1 - crates/localization/locales/pt-BR.json | 1 - crates/localization/locales/ru.json | 1 - crates/localization/locales/uk.json | 1 - crates/localization/locales/vi.json | 1 - crates/localization/locales/zh-Hans.json | 1 - crates/localization/locales/zh-Hant.json | 1 - crates/localization/src/lib.rs | 2 - .../src/commands/groups/core/constitution.rs | 142 ++++++++++-------- crates/tui/src/tui/app.rs | 1 + crates/tui/src/tui/app/init.rs | 1 + crates/tui/src/tui/setup/mod.rs | 65 ++++---- crates/tui/src/tui/ui/handlers.rs | 2 +- docs/CONFIGURATION.md | 7 +- docs/GUIDE.md | 2 +- 24 files changed, 128 insertions(+), 118 deletions(-) diff --git a/crates/config/src/user_constitution.rs b/crates/config/src/user_constitution.rs index 2d70d0c594..ade188bf6f 100644 --- a/crates/config/src/user_constitution.rs +++ b/crates/config/src/user_constitution.rs @@ -43,10 +43,11 @@ //! silently. [`UserConstitution::migrate_file`] writes a backup first so //! [`UserConstitution::rollback_file`] can restore the pre-migration bytes. //! - **The prompt projection is byte-stable.** -//! [`UserConstitution::cache_projection`] returns exactly the bytes that enter -//! the cache-stable prompt prefix plus their digest and measures. Suggested -//! clauses, preserved unknown fields, and schema metadata are all outside it, -//! so recording advice never invalidates a prompt cache. +//! [`UserConstitution::cache_projection`] returns exactly the bytes the model +//! receives (as a session message at turn admission, outside the cache-stable +//! prompt prefix) plus their digest and measures. Suggested clauses, +//! preserved unknown fields, and schema metadata are all outside it, so +//! recording advice never changes what the model receives. use std::collections::BTreeMap; use std::fmt::Write; diff --git a/crates/localization/locales/ca.json b/crates/localization/locales/ca.json index ab14a8bf9d..ad0ec51865 100644 --- a/crates/localization/locales/ca.json +++ b/crates/localization/locales/ca.json @@ -1019,7 +1019,6 @@ "SetupStepConstitutionWhy": "Aquest acord guia el comportament, no els permisos d'execució.", "SetupStepVerificationTitle": "Revisió", "SetupStepVerificationWhy": "Resumeix l'estat de Setup i mostra quins passos estan verificats, omesos o encara calen acció.", - "SetupCheckpointLayerOrder": "Ordre de capes: constitució inclosa -> constitució global d'usuari -> constitució del repo -> instruccions AGENTS/del projecte -> memòria/handoff -> petició actual.", "SetupCheckpointDoneBundled": "Punt de control de la constitució complet: s'usa la llei inclosa/per defecte.", "SetupCheckpointDoneGuided": "Constitució ratificada — la teva llei permanent s'ha desat. Esmena-la en qualsevol moment amb /constitution.", "SetupCheckpointDoneKept": "Constitució conservada — la teva llei existent continua vigent, sense canvis. Esmena-la en qualsevol moment amb /constitution.", diff --git a/crates/localization/locales/de.json b/crates/localization/locales/de.json index 861ed824c8..ba1cccf748 100644 --- a/crates/localization/locales/de.json +++ b/crates/localization/locales/de.json @@ -1019,7 +1019,6 @@ "SetupStepConstitutionWhy": "Diese Vereinbarung lenkt das Verhalten, nicht die Laufzeitberechtigungen.", "SetupStepVerificationTitle": "Überprüfung", "SetupStepVerificationWhy": "Fasst den Setup-Status zusammen und zeigt, welche Schritte verifiziert, übersprungen oder noch offen sind.", - "SetupCheckpointLayerOrder": "Ebenenreihenfolge: mitgelieferte Verfassung -> benutzer-globale Verfassung -> Repo-Verfassung -> AGENTS-/Projektanweisungen -> Memory/Handoff -> aktuelle Anfrage.", "SetupCheckpointDoneBundled": "Verfassungs-Checkpoint abgeschlossen: mitgeliefertes/Standard-Recht wird verwendet.", "SetupCheckpointDoneGuided": "Verfassung ratifiziert — dein geltendes Recht ist gespeichert. Jederzeit mit /constitution änderbar.", "SetupCheckpointDoneKept": "Verfassung beibehalten — dein bestehendes Recht gilt unverändert. Jederzeit mit /constitution änderbar.", diff --git a/crates/localization/locales/en.json b/crates/localization/locales/en.json index e7de6d0334..884d99a03c 100644 --- a/crates/localization/locales/en.json +++ b/crates/localization/locales/en.json @@ -1042,7 +1042,6 @@ "SetupStepConstitutionWhy": "This agreement guides behavior, not runtime permissions.", "SetupStepVerificationTitle": "Review", "SetupStepVerificationWhy": "Summarize setup status and show which steps are verified, skipped, or still need action.", - "SetupCheckpointLayerOrder": "Layer order: bundled Constitution -> user-global constitution -> repo constitution -> AGENTS/project instructions -> memory/handoff -> current request.", "SetupCheckpointDoneBundled": "Constitution checkpoint complete: using bundled/default law.", "SetupCheckpointDoneGuided": "Constitution ratified — your standing law is saved. Amend anytime with /constitution.", "SetupCheckpointDoneKept": "Constitution kept — your existing law stands, unchanged. Amend anytime with /constitution.", diff --git a/crates/localization/locales/es-419.json b/crates/localization/locales/es-419.json index 59860bc0cd..add65a18c0 100644 --- a/crates/localization/locales/es-419.json +++ b/crates/localization/locales/es-419.json @@ -1042,7 +1042,6 @@ "SetupStepConstitutionWhy": "Este acuerdo guía el comportamiento, no los permisos de ejecución.", "SetupStepVerificationTitle": "Revisión", "SetupStepVerificationWhy": "Resume el estado del setup y muestra qué pasos están verificados, saltados o aún necesitan acción.", - "SetupCheckpointLayerOrder": "Orden de capas: Constitution incluida -> constitution global de usuario -> constitution del repo -> instrucciones AGENTS/proyecto -> memoria/handoff -> solicitud actual.", "SetupCheckpointDoneBundled": "Checkpoint de constitution completo: usando ley incluida/predeterminada.", "SetupCheckpointDoneGuided": "Constitution ratificada — tu ley permanente fue guardada. Puedes modificarla en cualquier momento con /constitution.", "SetupCheckpointDoneKept": "Constitution conservada — tu ley existente queda sin cambios. Puedes modificarla en cualquier momento con /constitution.", diff --git a/crates/localization/locales/fr.json b/crates/localization/locales/fr.json index b18bbb1940..c76ec25742 100644 --- a/crates/localization/locales/fr.json +++ b/crates/localization/locales/fr.json @@ -1019,7 +1019,6 @@ "SetupStepConstitutionWhy": "Cet accord guide le comportement, pas les permissions d'exécution.", "SetupStepVerificationTitle": "Revue", "SetupStepVerificationWhy": "Résume le statut du setup et montre quelles étapes sont vérifiées, ignorées ou demandent encore une action.", - "SetupCheckpointLayerOrder": "Ordre des couches : Constitution intégrée -> constitution globale utilisateur -> constitution du repo -> instructions AGENTS/projet -> mémoire/handoff -> demande actuelle.", "SetupCheckpointDoneBundled": "Point de contrôle de la constitution terminé : loi intégrée/par défaut utilisée.", "SetupCheckpointDoneGuided": "Constitution ratifiée — votre loi permanente est enregistrée. Amendez-la à tout moment avec /constitution.", "SetupCheckpointDoneKept": "Constitution conservée — votre loi existante reste inchangée. Amendez-la à tout moment avec /constitution.", diff --git a/crates/localization/locales/hi.json b/crates/localization/locales/hi.json index 056fd76a0b..0be7d9bb14 100644 --- a/crates/localization/locales/hi.json +++ b/crates/localization/locales/hi.json @@ -1019,7 +1019,6 @@ "SetupStepConstitutionWhy": "यह समझौता व्यवहार का मार्गदर्शन करता है, रनटाइम अनुमतियों का नहीं।", "SetupStepVerificationTitle": "समीक्षा", "SetupStepVerificationWhy": "सेटअप स्थिति का सार दें और दिखाएँ कौन से चरण सत्यापित, छोड़े गए या कार्रवाई माँगते हैं।", - "SetupCheckpointLayerOrder": "परत क्रम: bundled संविधान -> उपयोगकर्ता-वैश्विक संविधान -> रिपो संविधान -> AGENTS/प्रोजेक्ट निर्देश -> मेमोरी/हैंडऑफ़ -> वर्तमान अनुरोध।", "SetupCheckpointDoneBundled": "संविधान चेकपॉइंट पूर्ण: bundled/डिफ़ॉल्ट कानून उपयोग में।", "SetupCheckpointDoneGuided": "संविधान अंगीकृत — आपका स्थायी कानून सहेजा गया। /constitution से कभी भी संशोधित करें।", "SetupCheckpointDoneKept": "संविधान बरकरार — आपका मौजूदा कानून अपरिवर्तित रहा। /constitution से कभी भी संशोधित करें।", diff --git a/crates/localization/locales/id.json b/crates/localization/locales/id.json index 3ca9024157..a8f77bdc58 100644 --- a/crates/localization/locales/id.json +++ b/crates/localization/locales/id.json @@ -1019,7 +1019,6 @@ "SetupStepConstitutionWhy": "Kesepakatan ini memandu perilaku, bukan izin runtime.", "SetupStepVerificationTitle": "Tinjauan", "SetupStepVerificationWhy": "Ringkas status penyiapan dan tunjukkan langkah mana yang terverifikasi, dilewati, atau masih perlu tindakan.", - "SetupCheckpointLayerOrder": "Urutan lapisan: Konstitusi bawaan -> konstitusi global pengguna -> konstitusi repo -> instruksi AGENTS/proyek -> memori/handoff -> permintaan saat ini.", "SetupCheckpointDoneBundled": "Checkpoint konstitusi selesai: memakai hukum bawaan/default.", "SetupCheckpointDoneGuided": "Konstitusi diratifikasi — hukum tetap Anda tersimpan. Amendemen kapan saja dengan /constitution.", "SetupCheckpointDoneKept": "Konstitusi dipertahankan — hukum Anda yang ada tetap berlaku, tidak berubah. Amendemen kapan saja dengan /constitution.", diff --git a/crates/localization/locales/ja.json b/crates/localization/locales/ja.json index ac3f5c2816..9ba749bde2 100644 --- a/crates/localization/locales/ja.json +++ b/crates/localization/locales/ja.json @@ -1042,7 +1042,6 @@ "SetupStepConstitutionWhy": "この作業合意は行動を導きますが、実行権限は変えません。", "SetupStepVerificationTitle": "レビュー", "SetupStepVerificationWhy": "セットアップ状態を要約し、確認済み、スキップ済み、または対応が必要なステップを表示します。", - "SetupCheckpointLayerOrder": "レイヤー順: 同梱 Constitution -> ユーザーグローバル constitution -> リポジトリ constitution -> AGENTS/プロジェクト指示 -> メモリ/引き継ぎ -> 現在の依頼。", "SetupCheckpointDoneBundled": "Constitution チェックポイント完了: 同梱/既定のルールを使用します。", "SetupCheckpointDoneGuided": "Constitution を承認しました。あなたの常設ルールを保存しました。いつでも /constitution で修正できます。", "SetupCheckpointDoneKept": "Constitution を維持しました。既存のルールは変更されません。いつでも /constitution で修正できます。", diff --git a/crates/localization/locales/ko.json b/crates/localization/locales/ko.json index 62c543366e..4494683292 100644 --- a/crates/localization/locales/ko.json +++ b/crates/localization/locales/ko.json @@ -1042,7 +1042,6 @@ "SetupStepConstitutionWhy": "이 작업 합의는 행동을 안내할 뿐, 실행 권한을 바꾸지 않습니다.", "SetupStepVerificationTitle": "검토", "SetupStepVerificationWhy": "설정 상태를 요약하고 어떤 단계가 확인됨, 건너뜀, 또는 아직 조치가 필요한지 표시합니다.", - "SetupCheckpointLayerOrder": "계층 순서: 내장 헌법 -> 사용자 전역 헌법 -> 저장소 헌법 -> AGENTS/프로젝트 지침 -> 메모리/인계 -> 현재 요청.", "SetupCheckpointDoneBundled": "헌법 체크포인트 완료: 내장/기본 규칙을 사용합니다.", "SetupCheckpointDoneGuided": "헌법이 승인되었습니다 — 상시 규칙이 저장되었습니다. /constitution으로 언제든지 수정할 수 있습니다.", "SetupCheckpointDoneKept": "헌법을 유지했습니다 — 기존 규칙이 변경 없이 그대로 적용됩니다. /constitution으로 언제든지 수정할 수 있습니다.", diff --git a/crates/localization/locales/pt-BR.json b/crates/localization/locales/pt-BR.json index d749131f6e..155ca942c4 100644 --- a/crates/localization/locales/pt-BR.json +++ b/crates/localization/locales/pt-BR.json @@ -1042,7 +1042,6 @@ "SetupStepConstitutionWhy": "Este acordo orienta o comportamento, não as permissões de execução.", "SetupStepVerificationTitle": "Revisão", "SetupStepVerificationWhy": "Resume o status do setup e mostra quais etapas estão verificadas, puladas ou ainda precisam de ação.", - "SetupCheckpointLayerOrder": "Ordem de camadas: Constitution embutida -> constitution global do usuário -> constitution do repo -> instruções AGENTS/projeto -> memória/handoff -> pedido atual.", "SetupCheckpointDoneBundled": "Checkpoint da constitution completo: usando lei embutida/padrão.", "SetupCheckpointDoneGuided": "Constitution ratificada — sua lei permanente foi salva. Altere a qualquer momento com /constitution.", "SetupCheckpointDoneKept": "Constitution mantida — sua lei existente continua sem mudanças. Altere a qualquer momento com /constitution.", diff --git a/crates/localization/locales/ru.json b/crates/localization/locales/ru.json index 4a0ed970dd..83e433bb81 100644 --- a/crates/localization/locales/ru.json +++ b/crates/localization/locales/ru.json @@ -1019,7 +1019,6 @@ "SetupStepConstitutionWhy": "Это соглашение направляет поведение, но не меняет разрешения среды выполнения.", "SetupStepVerificationTitle": "Проверка", "SetupStepVerificationWhy": "Сводка статуса настройки: какие шаги проверены, пропущены или ещё требуют действий.", - "SetupCheckpointLayerOrder": "Порядок слоёв: встроенная конституция -> глобальная конституция пользователя -> конституция репозитория -> инструкции AGENTS/проекта -> память/передача -> текущий запрос.", "SetupCheckpointDoneBundled": "Контрольная точка конституции пройдена: используется встроенный закон по умолчанию.", "SetupCheckpointDoneGuided": "Конституция утверждена — ваш постоянный закон сохранён. Изменить можно в любой момент через /constitution.", "SetupCheckpointDoneKept": "Конституция сохранена — ваш текущий закон остаётся без изменений. Изменить можно в любой момент через /constitution.", diff --git a/crates/localization/locales/uk.json b/crates/localization/locales/uk.json index 59d76d8be9..ee940bd5c1 100644 --- a/crates/localization/locales/uk.json +++ b/crates/localization/locales/uk.json @@ -1019,7 +1019,6 @@ "SetupStepConstitutionWhy": "Ця угода спрямовує поведінку, але не змінює дозволи середовища виконання.", "SetupStepVerificationTitle": "Огляд", "SetupStepVerificationWhy": "Підсумуйте стан налаштування та подивіться, які кроки перевірено, пропущено або які ще потребують дій.", - "SetupCheckpointLayerOrder": "Порядок шарів: вбудована конституція -> глобальна конституція користувача -> конституція репозиторію -> інструкції AGENTS/проєкту -> пам'ять/передача -> поточний запит.", "SetupCheckpointDoneBundled": "Контрольну точку конституції завершено: використовується вбудоване/типове право.", "SetupCheckpointDoneGuided": "Конституцію ратифіковано — ваше чинне право збережено. Змінюйте будь-коли через /constitution.", "SetupCheckpointDoneKept": "Конституцію залишено — ваше чинне право діє без змін. Змінюйте будь-коли через /constitution.", diff --git a/crates/localization/locales/vi.json b/crates/localization/locales/vi.json index 95b1d195ed..e690314efa 100644 --- a/crates/localization/locales/vi.json +++ b/crates/localization/locales/vi.json @@ -1042,7 +1042,6 @@ "SetupStepConstitutionWhy": "Thỏa thuận này hướng dẫn hành vi, không đổi quyền thực thi.", "SetupStepVerificationTitle": "Xem lại", "SetupStepVerificationWhy": "Tóm tắt trạng thái setup và cho biết bước nào đã xác minh, đã bỏ qua hoặc còn cần hành động.", - "SetupCheckpointLayerOrder": "Thứ tự lớp: Constitution tích hợp -> constitution toàn cục của người dùng -> constitution repo -> chỉ dẫn AGENTS/dự án -> bộ nhớ/handoff -> yêu cầu hiện tại.", "SetupCheckpointDoneBundled": "Checkpoint constitution hoàn tất: dùng luật tích hợp/mặc định.", "SetupCheckpointDoneGuided": "Constitution đã được phê chuẩn — luật thường trực của bạn đã được lưu. Có thể sửa bất cứ lúc nào bằng /constitution.", "SetupCheckpointDoneKept": "Constitution được giữ nguyên — luật hiện có không đổi. Có thể sửa bất cứ lúc nào bằng /constitution.", diff --git a/crates/localization/locales/zh-Hans.json b/crates/localization/locales/zh-Hans.json index d1ea4ff2d0..2d12ff30cc 100644 --- a/crates/localization/locales/zh-Hans.json +++ b/crates/localization/locales/zh-Hans.json @@ -1042,7 +1042,6 @@ "SetupStepConstitutionWhy": "此 Codewhale 宪章是工作约定,只指导行为,不会更改运行时权限。", "SetupStepVerificationTitle": "复核", "SetupStepVerificationWhy": "汇总设置状态,并显示哪些步骤已验证、已跳过或仍需操作。", - "SetupCheckpointLayerOrder": "层级顺序:内置基础宪章 -> 用户全局宪章 -> 仓库宪章 -> AGENTS/项目说明 -> 记忆/交接 -> 当前请求。", "SetupCheckpointDoneBundled": "宪章检查点已完成:使用内置/默认宪章。", "SetupCheckpointDoneGuided": "宪章已确认并保存。可随时用 /constitution 修改。", "SetupCheckpointDoneKept": "已保留现有宪章,内容保持不变。可随时用 /constitution 修改。", diff --git a/crates/localization/locales/zh-Hant.json b/crates/localization/locales/zh-Hant.json index e918eee753..d8fc236ff1 100644 --- a/crates/localization/locales/zh-Hant.json +++ b/crates/localization/locales/zh-Hant.json @@ -1040,7 +1040,6 @@ "SetupStepConstitutionWhy": "此工作約定只引導行為,不會更改執行權限。", "SetupStepVerificationTitle": "檢視", "SetupStepVerificationWhy": "總結設定狀態,並顯示哪些步驟已驗證、已略過或仍需操作。", - "SetupCheckpointLayerOrder": "層級順序:內建基礎憲章 -> 使用者全域憲章 -> 儲存庫憲章 -> AGENTS/專案指示 -> 記憶/交接 -> 目前請求。", "SetupCheckpointDoneBundled": "憲章檢查點完成:使用內建/預設憲章。", "SetupCheckpointDoneGuided": "憲章已確認並儲存。可隨時用 /constitution 修訂。", "SetupCheckpointDoneKept": "已保留現有憲章,內容維持不變。可隨時用 /constitution 修訂。", diff --git a/crates/localization/src/lib.rs b/crates/localization/src/lib.rs index 49ad543ae1..7823e0cf55 100644 --- a/crates/localization/src/lib.rs +++ b/crates/localization/src/lib.rs @@ -1262,7 +1262,6 @@ pub enum MessageId { SetupStepConstitutionWhy, SetupStepVerificationTitle, SetupStepVerificationWhy, - SetupCheckpointLayerOrder, SetupCheckpointDoneBundled, SetupCheckpointDoneGuided, SetupCheckpointDoneKept, @@ -3888,7 +3887,6 @@ pub const ALL_MESSAGE_IDS: &[MessageId] = &[ MessageId::SetupStepConstitutionWhy, MessageId::SetupStepVerificationTitle, MessageId::SetupStepVerificationWhy, - MessageId::SetupCheckpointLayerOrder, MessageId::SetupCheckpointDoneBundled, MessageId::SetupCheckpointDoneGuided, MessageId::SetupCheckpointDoneKept, diff --git a/crates/tui/src/commands/groups/core/constitution.rs b/crates/tui/src/commands/groups/core/constitution.rs index 0a712ec26d..4ebf35d8c2 100644 --- a/crates/tui/src/commands/groups/core/constitution.rs +++ b/crates/tui/src/commands/groups/core/constitution.rs @@ -91,7 +91,11 @@ impl RegisterCommand for ConstitutionCmd { .split_whitespace() .map(str::to_string) .collect(); - CommandResult::message(ratify_text(app.ui_locale, &ids)) + CommandResult::message(ratify_text( + app.ui_locale, + app.constitution_reviewed_digest.as_deref(), + &ids, + )) } Some("help") => CommandResult::message(help_text(app.ui_locale)), Some(other) => CommandResult::error(format!( @@ -151,16 +155,18 @@ fn open_explanation(app: &mut App) { /// because unratified model advice is not law. fn open_suggestions(app: &mut App) { let locale = app.ui_locale; - let text = suggestions_text(locale); + let (text, reviewed) = suggestions_text(locale); + app.constitution_reviewed_digest = reviewed; open_pager(app, suggestions_title(locale), &text); } -fn suggestions_text(locale: Locale) -> String { +fn suggestions_text(locale: Locale) -> (String, Option) { let UserConstitutionStatus::Loaded { constitution, .. } = load_user_constitution() else { - return match locale { - Locale::ZhHans => "没有可供审阅的结构化用户全局协作准则。".to_string(), + let text = match locale { + Locale::ZhHans => "没有可供审阅的结构化用户全局宪章。".to_string(), _ => "No structured user-global constitution is available to review.".to_string(), }; + return (text, None); }; let digest = constitution.cache_projection().digest; let suggested: Vec<_> = constitution.suggested_clauses().collect(); @@ -203,13 +209,14 @@ fn suggestions_text(locale: Locale) -> String { } } } - out + (out, Some(digest)) } -/// Explicit ratification (#3930). Fails closed: it re-reads the live file, -/// hands the live digest back to [`UserConstitution::ratify`], and persists only -/// when the clause set is exactly what the user named. -fn ratify_text(locale: Locale, ids: &[String]) -> String { +/// Explicit ratification (#3930). Fails closed: it hands the digest the user +/// reviewed in `/constitution suggestions` to [`UserConstitution::ratify`], +/// which refuses when the live file has moved since, and persists only when the +/// clause set is exactly what the user named. +fn ratify_text(locale: Locale, reviewed: Option<&str>, ids: &[String]) -> String { if ids.is_empty() { return match locale { Locale::ZhHans => { @@ -224,14 +231,21 @@ fn ratify_text(locale: Locale, ids: &[String]) -> String { UserConstitutionStatus::Loaded { path, constitution } => (path, constitution), _ => { return match locale { - Locale::ZhHans => "没有可批准的结构化用户全局协作准则。".to_string(), + Locale::ZhHans => "没有可批准的结构化用户全局宪章。".to_string(), _ => "There is no structured user-global constitution to ratify.".to_string(), }; } }; - let digest = constitution.cache_projection().digest; - match constitution.ratify(&digest, ids, None) { + let Some(reviewed) = reviewed else { + return match locale { + Locale::ZhHans => { + "未批准任何条款:请先用 /constitution suggestions 审阅待批准条款。".to_string() + } + _ => "Nothing was ratified: review the pending clauses with /constitution suggestions first.".to_string(), + }; + }; + match constitution.ratify(reviewed, ids, None) { Err(err) => match locale { Locale::ZhHans => format!("未批准任何条款:{err}"), _ => format!("Nothing was ratified: {err}"), @@ -245,14 +259,14 @@ fn ratify_text(locale: Locale, ids: &[String]) -> String { }, Ok(()) => match locale { Locale::ZhHans => format!( - "已批准 {} 条:{}\n摘要 {} → {}\n这些条款现在会注入模型;提示词缓存前缀已随之变化。", + "已批准 {} 条:{}\n摘要 {} → {}\n这些条款从下一轮起作为会话记录中的消息发送给模型;提示词缓存前缀不变。", ratification.accepted_clause_ids.len(), ratification.accepted_clause_ids.join(", "), ratification.before_digest, ratification.after_digest, ), _ => format!( - "Ratified {} clause(s): {}\nDigest {} → {}\nThey are injected from the next turn; the prompt-cache prefix moved with them.", + "Ratified {} clause(s): {}\nDigest {} → {}\nThey reach the model from the next turn as a message recorded in the session; the prompt-cache prefix is unchanged.", ratification.accepted_clause_ids.len(), ratification.accepted_clause_ids.join(", "), ratification.before_digest, @@ -303,9 +317,9 @@ fn migrate_text(locale: Locale) -> String { receipt.before_digest, receipt.after_digest, if receipt.is_cache_stable() { - "未改变,提示词缓存保持有效" + "未改变,模型下一轮收到相同内容" } else { - "已改变,提示词缓存前缀随之变化" + "已改变,下一轮作为新的会话消息发送" }, ), _ => format!( @@ -315,9 +329,9 @@ fn migrate_text(locale: Locale) -> String { receipt.before_digest, receipt.after_digest, if receipt.is_cache_stable() { - "unchanged, so the prompt cache survives" + "unchanged, so the model receives the same text" } else { - "changed, so the prompt-cache prefix moved" + "changed; the new text is sent as a session message at the next turn" }, ), } @@ -545,7 +559,7 @@ fn preview_text(locale: Locale) -> String { let state = load_setup_state(); let load = load_user_constitution(); match load { - UserConstitutionStatus::Loaded { path, constitution } => { + UserConstitutionStatus::Loaded { constitution, .. } => { let active = user_constitution_is_active(state.as_ref()); let mut text = String::new(); if !active { @@ -554,7 +568,7 @@ fn preview_text(locale: Locale) -> String { } text.push_str( &constitution - .render_block(Some(&path)) + .render_block(None) .unwrap_or_else(|| structured_empty_text(locale).to_string()), ); text @@ -825,24 +839,24 @@ fn ignored_whale_warnings(warnings: &[String]) -> Vec<&str> { fn help_text(locale: Locale) -> String { match locale { Locale::ZhHans => "\ -用法:/constitution [status|preview|bundled|edit|review|repair|repo|explain|posture|help] +用法:/constitution [status|preview|base|suggestions|ratify |migrate|rollback|bundled|edit|review|repair|repo|explain|posture|help] 常用命令: -- /constitution:打开协作准则管理器和当前层级。 -- /constitution preview:显示会注入模型的确切用户全局协作准则块;缺失、空、无效或不可读时显示修复说明。 -- /constitution edit:打开 /setup 的引导式协作准则步骤;用 1-6 调整,按 G 预览,再按 G 保存。 +- /constitution:打开宪章管理器和当前层级。 +- /constitution preview:显示会注入模型的确切用户全局宪章块;缺失、空、无效或不可读时显示修复说明。 +- /constitution edit:打开 /setup 的引导式宪章步骤;用 1-6 调整,按 G 预览,再按 G 保存。 - /constitution repair:说明当前文件状态,然后打开同一个引导式修复步骤。 - /constitution bundled:记录使用内置/默认准则,不创建自定义文件。 - /constitution repo:查看 .codewhale/constitution.json 仓库本地准则。 -- /constitution explain:解释内置基础准则、用户全局协作准则、仓库协作准则、AGENTS.md、记忆和交接的区别。 +- /constitution explain:解释内置基础准则、用户全局宪章、仓库宪章、AGENTS.md、记忆和交接的区别。 - /constitution base:显示下一轮实际组装的确切基础提示词,附来源、摘要和字节/词元度量;只读,不发送请求,也不展开工具目录。 - /constitution suggestions:查看待批准的建议条款;它们不会注入模型,也不影响提示词缓存。 - /constitution ratify :明确批准某条建议条款;若基线在审阅后发生变化则拒绝批准。 -- /constitution migrate:把协作准则文件迁移到当前架构版本并给出回执;被拒绝时文件保持原样。 +- /constitution migrate:把宪章文件迁移到当前架构版本并给出回执;被拒绝时文件保持原样。 - /constitution rollback:恢复迁移前的备份。 -- /constitution posture:打开运行时姿态;协作准则只提供模型指导,不会更改批准、沙盒、Shell、网络、信任或 MCP 权限。", +- /constitution posture:打开运行时姿态;宪章只提供模型指导,不会更改批准、沙盒、Shell、网络、信任或 MCP 权限。", _ => "\ -Usage: /constitution [status|preview|bundled|edit|review|repair|repo|explain|posture|help] +Usage: /constitution [status|preview|base|suggestions|ratify |migrate|rollback|bundled|edit|review|repair|repo|explain|posture|help] Common commands: - /constitution: open the constitution manager and active stack. @@ -879,14 +893,14 @@ fn repair_text(locale: Locale) -> String { match locale { Locale::ZhHans => format!( "\ -用户全局协作准则修复 +用户全局宪章修复 当前文件:{file} 当前状态:{status} 记录选择:{choice} 有效性:{validity} -接下来将打开 /setup 的协作准则步骤。安全修复路径: +接下来将打开 /setup 的宪章步骤。安全修复路径: - 用 1-6 调整引导式草稿,按 G 预览,再按 G 保存新的结构化 constitution.json。 - 按 U 或运行 /constitution bundled 记录使用内置/默认准则;现有无效/空/不可读文件不会被注入。 - 用 /constitution preview 查看当前错误或渲染结果。 @@ -914,42 +928,42 @@ This only repairs the user-global constitution.json. Runtime approval, sandbox, fn manager_title(locale: Locale) -> &'static str { match locale { - Locale::ZhHans => "协作准则", + Locale::ZhHans => "宪章", _ => "Constitution", } } fn review_title(locale: Locale) -> &'static str { match locale { - Locale::ZhHans => "协作准则检查", + Locale::ZhHans => "宪章检查", _ => "Constitution Review", } } fn rendered_title(locale: Locale) -> &'static str { match locale { - Locale::ZhHans => "渲染后的用户协作准则", + Locale::ZhHans => "渲染后的用户宪章", _ => "Rendered User Constitution", } } fn repo_title(locale: Locale) -> &'static str { match locale { - Locale::ZhHans => "仓库本地协作准则", + Locale::ZhHans => "仓库本地宪章", _ => "Repo-Local Constitution", } } fn explanation_title(locale: Locale) -> &'static str { match locale { - Locale::ZhHans => "AGENTS.md 与协作准则", + Locale::ZhHans => "AGENTS.md 与宪章", _ => "AGENTS.md vs Constitution", } } fn no_repo_law_text(locale: Locale) -> &'static str { match locale { - Locale::ZhHans => "此工作区未找到仓库本地协作准则 .codewhale/constitution.json。", + Locale::ZhHans => "此工作区未找到仓库本地宪章 .codewhale/constitution.json。", _ => "No repo-local constitution found at .codewhale/constitution.json for this workspace.", } } @@ -963,7 +977,7 @@ fn inactive_preview_text(locale: Locale) -> &'static str { fn structured_empty_text(locale: Locale) -> &'static str { match locale { - Locale::ZhHans => "结构化协作准则为空。", + Locale::ZhHans => "结构化宪章为空。", _ => "The structured constitution is empty.", } } @@ -971,7 +985,7 @@ fn structured_empty_text(locale: Locale) -> &'static str { fn missing_preview_text(locale: Locale, path: &std::path::Path) -> String { match locale { Locale::ZhHans => format!( - "未在 {} 找到结构化用户全局协作准则。\n\n当前使用内置准则。使用 /constitution edit 创建引导式长期偏好,或使用 /constitution bundled 明确记录内置/默认。", + "未在 {} 找到结构化用户全局宪章。\n\n当前使用内置准则。使用 /constitution edit 创建引导式长期偏好,或使用 /constitution bundled 明确记录内置/默认。", path.display() ), _ => format!( @@ -984,7 +998,7 @@ fn missing_preview_text(locale: Locale, path: &std::path::Path) -> String { fn empty_preview_text(locale: Locale, path: &std::path::Path) -> String { match locale { Locale::ZhHans => format!( - "{} 的结构化用户全局协作准则为空。使用 /constitution repair 返回引导式协作准则步骤。", + "{} 的结构化用户全局宪章为空。使用 /constitution repair 返回引导式宪章步骤。", path.display() ), _ => format!( @@ -997,7 +1011,7 @@ fn empty_preview_text(locale: Locale, path: &std::path::Path) -> String { fn invalid_preview_text(locale: Locale, path: &std::path::Path, error: &str) -> String { match locale { Locale::ZhHans => format!( - "{} 的结构化用户全局协作准则无效,且不会注入。\n\n{error}\n\n使用 /constitution repair 返回引导式协作准则步骤。", + "{} 的结构化用户全局宪章无效,且不会注入。\n\n{error}\n\n使用 /constitution repair 返回引导式宪章步骤。", path.display() ), _ => format!( @@ -1010,7 +1024,7 @@ fn invalid_preview_text(locale: Locale, path: &std::path::Path, error: &str) -> fn unreadable_preview_text(locale: Locale, path: &std::path::Path, error: &str) -> String { match locale { Locale::ZhHans => format!( - "无法读取 {} 的结构化用户全局协作准则,且不会注入。\n\n{error}\n\n使用 /constitution repair 返回引导式协作准则步骤。", + "无法读取 {} 的结构化用户全局宪章,且不会注入。\n\n{error}\n\n使用 /constitution repair 返回引导式宪章步骤。", path.display() ), _ => format!( @@ -1022,7 +1036,7 @@ fn unreadable_preview_text(locale: Locale, path: &std::path::Path, error: &str) fn path_error_preview_text(locale: Locale, error: &str) -> String { match locale { - Locale::ZhHans => format!("无法为用户全局协作准则解析 CODEWHALE_HOME:\n\n{error}"), + Locale::ZhHans => format!("无法为用户全局宪章解析 CODEWHALE_HOME:\n\n{error}"), _ => { format!("Could not resolve CODEWHALE_HOME for the user-global constitution:\n\n{error}") } @@ -1033,19 +1047,19 @@ fn agents_explanation(locale: Locale) -> &'static str { match locale { Locale::ZhHans => { "\ -AGENTS.md 与协作准则 +AGENTS.md 与宪章 内置基础准则是精简的全局判断约定:身份、事实、验证、克制和优先级顺序。 -用户全局协作准则是个人长期偏好。它是结构化数据,确定性渲染,并低于当前用户请求和内置基础准则。 +用户全局宪章是个人长期偏好。它是结构化数据,确定性渲染,并低于当前用户请求和内置基础准则。 -.codewhale/constitution.json 是仓库本地协作准则。它属于某个工作区,并作为独立的仓库协作准则块渲染。 +.codewhale/constitution.json 是仓库本地宪章。它属于某个工作区,并作为独立的仓库宪章块渲染。 AGENTS.md 和项目说明是项目规则/实现指导。它们可以描述构建命令、仓库规范和本地流程;按优先级顺序,它们低于当前用户请求和内置基础准则,高于用户全局长期偏好、记忆和交接。 WHALE.md 已忽略。将普通项目说明迁移到 AGENTS.md,将 Codewhale 专属权限策略迁移到 .codewhale/constitution.json。 -运行时姿态是独立设置。协作准则可以建议主动性,但不会改变批准策略、沙盒、Shell、网络、信任、MCP 权限或默认模式。使用 /constitution posture 查看这些控制。" +运行时姿态是独立设置。宪章可以建议主动性,但不会改变批准策略、沙盒、Shell、网络、信任、MCP 权限或默认模式。使用 /constitution posture 查看这些控制。" } _ => { "\ @@ -1106,17 +1120,17 @@ impl ConstitutionManagerCopy { fn for_locale(locale: Locale) -> Self { match locale { Locale::ZhHans => Self { - manager_header: "协作准则管理器", + manager_header: "宪章管理器", active_stack_header: "生效层级", bundled_active: "内置基础准则:始终生效", - user_global_label: "用户全局协作准则", - repo_local_label: "仓库本地协作准则", + user_global_label: "用户全局宪章", + repo_local_label: "仓库本地宪章", agents_label: "AGENTS/项目说明", legacy_whale_label: "旧版 WHALE.md", memory_handoff_label: "记忆/交接", memory_label: "记忆", handoff_label: "交接", - user_global_header: "用户全局协作准则", + user_global_header: "用户全局宪章", choice_label: "选择", source_label: "来源", file_label: "文件", @@ -1130,8 +1144,8 @@ impl ConstitutionManagerCopy { repo_action: "/constitution repo 会在存在时显示 .codewhale/constitution.json 本地准则。", maintenance_header: "维护", maintenance_actions: &[ - "编辑引导式协作准则:/constitution edit", - "预览渲染后的协作准则:/constitution preview", + "编辑引导式宪章:/constitution edit", + "预览渲染后的宪章:/constitution preview", "查看下一轮的确切基础提示词:/constitution base", "审阅待批准的建议条款:/constitution suggestions", "批准某条建议条款:/constitution ratify ", @@ -1140,7 +1154,7 @@ impl ConstitutionManagerCopy { "查看现有内容:/constitution review", "修复无效/空/不可读文件:/constitution repair", "显示仓库本地准则:/constitution repo", - "解释 AGENTS.md 与协作准则:/constitution explain", + "解释 AGENTS.md 与宪章:/constitution explain", "打开运行时姿态:/constitution posture", ], present: "存在", @@ -1205,7 +1219,7 @@ impl ConstitutionManagerCopy { } fn location_count_unit(&self, count: usize) -> &'static str { - if self.manager_header == "协作准则管理器" { + if self.manager_header == "宪章管理器" { "处" } else if count == 1 { "location" @@ -1215,7 +1229,7 @@ impl ConstitutionManagerCopy { } fn completed_for(&self, version: &str) -> String { - if self.manager_header == "协作准则管理器" { + if self.manager_header == "宪章管理器" { format!("当前(自 {version} 引入)") } else { format!("current (introduced in {version})") @@ -1446,6 +1460,14 @@ mod tests { .expect("usage message"); assert!(bare.contains("Usage: /constitution ratify ")); + let unreviewed = ConstitutionCmd::execute(&mut app, Some("ratify c1")) + .message + .expect("refusal message"); + assert!(unreviewed.contains("Nothing was ratified"), "{unreviewed}"); + + ConstitutionCmd::execute(&mut app, Some("suggestions")); + pop_pager_body(&mut app); + let unknown = ConstitutionCmd::execute(&mut app, Some("ratify nope")) .message .expect("refusal message"); @@ -1484,7 +1506,7 @@ mod tests { "{message}" ); assert!( - message.contains("the prompt cache survives"), + message.contains("the model receives the same text"), "a v1 file carries no clauses, so no model-facing byte moved: {message}" ); @@ -1550,9 +1572,9 @@ mod tests { assert!(result.message.is_none()); let body = pop_pager_body(&mut app); - assert!(body.contains("协作准则管理器")); + assert!(body.contains("宪章管理器")); assert!(body.contains("生效层级")); - assert!(body.contains("用户全局协作准则")); + assert!(body.contains("用户全局宪章")); assert!(body.contains("/constitution preview 会")); assert!(!body.contains("宪法")); assert!(!body.contains("Constitution Manager")); @@ -1588,7 +1610,7 @@ mod tests { assert!(result.message.is_none()); let body = pop_pager_body(&mut app); - assert!(body.contains("AGENTS.md 与协作准则")); + assert!(body.contains("AGENTS.md 与宪章")); assert!(body.contains(".codewhale/constitution.json")); assert!(body.contains("运行时姿态是独立设置")); assert!(!body.contains("宪法")); diff --git a/crates/tui/src/tui/app.rs b/crates/tui/src/tui/app.rs index 8b37404e84..c4c21a6ae6 100644 --- a/crates/tui/src/tui/app.rs +++ b/crates/tui/src/tui/app.rs @@ -1961,6 +1961,7 @@ pub struct App { /// Exact evidence remains attached to each mutation receipt in all modes. pub inline_diff_mode: InlineDiffMode, pub ui_locale: Locale, + pub constitution_reviewed_digest: Option, pub cost_currency: CostCurrency, /// Route payment truth. Model pricing alone cannot distinguish metered /// API calls from OAuth or token-plan quota. diff --git a/crates/tui/src/tui/app/init.rs b/crates/tui/src/tui/app/init.rs index 556ff83d8c..39de31d10f 100644 --- a/crates/tui/src/tui/app/init.rs +++ b/crates/tui/src/tui/app/init.rs @@ -935,6 +935,7 @@ impl App { show_tool_details, inline_diff_mode, ui_locale, + constitution_reviewed_digest: None, cost_currency, billing_presentation: provider_identity.as_ref().map_or( crate::route_billing::BillingPresentation::Unknown, diff --git a/crates/tui/src/tui/setup/mod.rs b/crates/tui/src/tui/setup/mod.rs index 19e8d2d945..c66fce53d7 100644 --- a/crates/tui/src/tui/setup/mod.rs +++ b/crates/tui/src/tui/setup/mod.rs @@ -835,63 +835,63 @@ impl GuidedConstitutionDraft { let own_words = match locale { Locale::Ja => format!( "\nユーザー自由原則:{}", - bounded_freeform_note(note, MAX_NOTES_LEN) + bounded_freeform_note(note, FREEFORM_NOTE_MAX) ), Locale::ZhHans => format!( "\n用户自定义准则:{}", - bounded_freeform_note(note, MAX_NOTES_LEN) + bounded_freeform_note(note, FREEFORM_NOTE_MAX) ), Locale::ZhHant => format!( "\n使用者自由原則:{}", - bounded_freeform_note(note, MAX_NOTES_LEN) + bounded_freeform_note(note, FREEFORM_NOTE_MAX) ), Locale::PtBr => format!( "\nPrincípio livre do usuário: {}", - bounded_freeform_note(note, MAX_NOTES_LEN) + bounded_freeform_note(note, FREEFORM_NOTE_MAX) ), Locale::Es419 => format!( "\nPrincipio libre del usuario: {}", - bounded_freeform_note(note, MAX_NOTES_LEN) + bounded_freeform_note(note, FREEFORM_NOTE_MAX) ), Locale::Vi => format!( "\nNguyên tắc tự do của người dùng: {}", - bounded_freeform_note(note, MAX_NOTES_LEN) + bounded_freeform_note(note, FREEFORM_NOTE_MAX) ), Locale::Ko => format!( "\n사용자 자유 원칙: {}", - bounded_freeform_note(note, MAX_NOTES_LEN) + bounded_freeform_note(note, FREEFORM_NOTE_MAX) ), Locale::Ca => format!( "\nPrincipi lliure de l'usuari: {}", - bounded_freeform_note(note, MAX_NOTES_LEN) + bounded_freeform_note(note, FREEFORM_NOTE_MAX) ), Locale::De => format!( "\nFreitext-Prinzip des Nutzers: {}", - bounded_freeform_note(note, MAX_NOTES_LEN) + bounded_freeform_note(note, FREEFORM_NOTE_MAX) ), Locale::Fr => format!( "\nPrincipe en texte libre de l'utilisateur : {}", - bounded_freeform_note(note, MAX_NOTES_LEN) + bounded_freeform_note(note, FREEFORM_NOTE_MAX) ), Locale::Id => format!( "\nPrinsip bebas pengguna: {}", - bounded_freeform_note(note, MAX_NOTES_LEN) + bounded_freeform_note(note, FREEFORM_NOTE_MAX) ), Locale::Hi => format!( "\nउपयोगकर्ता मुक्त-पाठ सिद्धांत: {}", - bounded_freeform_note(note, MAX_NOTES_LEN) + bounded_freeform_note(note, FREEFORM_NOTE_MAX) ), Locale::Ru => format!( "\nСвободный принцип пользователя: {}", - bounded_freeform_note(note, MAX_NOTES_LEN) + bounded_freeform_note(note, FREEFORM_NOTE_MAX) ), Locale::Uk => format!( "\nВільний принцип користувача: {}", - bounded_freeform_note(note, MAX_NOTES_LEN) + bounded_freeform_note(note, FREEFORM_NOTE_MAX) ), _ => format!( "\nUser freeform principle: {}", - bounded_freeform_note(note, MAX_NOTES_LEN) + bounded_freeform_note(note, FREEFORM_NOTE_MAX) ), }; notes.push_str(&own_words); @@ -2126,6 +2126,8 @@ fn authority_priority(locale: Locale) -> &'static str { } } +const FREEFORM_NOTE_MAX: usize = MAX_NOTES_LEN - 1_000; + fn bounded_freeform_note(input: &str, max_chars: usize) -> String { input .chars() @@ -2896,7 +2898,7 @@ impl SetupWizardView { fn append_freeform_note_text(&mut self, text: &str) { let mut next = self.freeform_note.clone(); next.push_str(text); - self.freeform_note = bounded_freeform_note(&next, MAX_NOTES_LEN); + self.freeform_note = bounded_freeform_note(&next, FREEFORM_NOTE_MAX); self.guided_preview_seen = false; self.model_draft = None; self.model_draft_label = None; @@ -4421,7 +4423,6 @@ fn constitution_ratification_text( Locale::Uk => "Структурована конституція порожня.".to_string(), _ => "The structured constitution is empty.".to_string(), }); - let layer_order = tr(locale, MessageId::SetupCheckpointLayerOrder); match locale { Locale::Ja => { @@ -4454,7 +4455,7 @@ fn constitution_ratification_text( すべての個別判断を裁くのではなく権限と境界を定め、セッションを越えて協働を継続させます。\ ただしこれは記憶ではありません。履歴ではなく原則を保持します。\n\n\ {rendered}\n\n\ - 権限の階層\n{layer_order}\nあなたの直接の指示は常にこの文書より優先されます。\n\n\ + 権限の階層\nあなたの直接の指示は常にこの文書より優先されます。\n\n\ これができないこと\n\ これは行動を導くものです。承認ポリシー、サンドボックス、Shell、ネットワーク、信頼、MCP 権限、\ 既定モード、公開、支出の権限を付与または変更することはできません。これらは実行時にあなたが管理します。\n\n\ @@ -4489,7 +4490,7 @@ fn constitution_ratification_text( 这是 Codewhale 与你协作时长期遵循的偏好和规则。内容应保持简短、便于执行,以持久原则为主,并可随时调整。\ 它界定协作方式与行为边界,而不是替你决定每一种情况;它让协作跨会话延续——但它不是记忆,只保留原则,不保留历史。\n\n\ {rendered}\n\n\ - 权限层级\n{layer_order}\n你的直接指令始终高于本文件。\n\n\ + 权限层级\n你的直接指令始终高于本文件。\n\n\ 它不能做什么\n\ 它只提供行为指导,不能授予或更改审批策略、沙箱、Shell、网络、信任、MCP 权限、默认模式、发布或支出权限——这些始终由你在运行时掌控。\n\n\ 精简核心与可选策略\n\ @@ -4522,7 +4523,7 @@ fn constitution_ratification_text( 這是 Codewhale 與你協作的長期準則。像優秀的憲法一樣:足夠簡短因而可用,由持久原則而非詳盡規則構成,並且可以隨你修訂。\ 它界定權力與邊界,而非裁決每個具體決定;它讓協作跨會話延續,但它不是記憶,它承載的是原則,而非歷史。\n\n\ {rendered}\n\n\ - 權限層級\n{layer_order}\n你的直接指令始終高於本文件。\n\n\ + 權限層級\n你的直接指令始終高於本文件。\n\n\ 它不能做什麼\n\ 它只提供行為指導,不能授予或更改審批策略、沙箱、Shell、網路、信任、MCP 權限、預設模式、發布或支出權限;這些始終由你在執行時掌控。\n\n\ 精簡核心與可選模組\n\ @@ -4560,7 +4561,7 @@ fn constitution_ratification_text( e pode ser emendada conforme você muda. Ela define poderes e limites em vez de decidir cada caso, \ e dá continuidade à colaboração entre sessões. Mas ela não é memória: carrega princípios, não histórico.\n\n\ {rendered}\n\n\ - HIERARQUIA DE AUTORIDADE\n{layer_order}\nSeus pedidos diretos sempre superam este documento.\n\n\ + HIERARQUIA DE AUTORIDADE\nSeus pedidos diretos sempre superam este documento.\n\n\ O QUE ISTO NÃO PODE FAZER\n\ Isto orienta comportamento. Não pode conceder nem alterar política de aprovação, sandbox, shell, rede, \ confiança, permissões MCP, modo padrão, publicação ou autoridade para gastos; isso continua sob seu controle em tempo de execução.\n\n\ @@ -4600,7 +4601,7 @@ fn constitution_ratification_text( y enmendable a medida que cambias. Define poderes y límites en vez de decidir cada caso, \ y da continuidad a la colaboración entre sesiones. Pero no es memoria: lleva principios, no historial.\n\n\ {rendered}\n\n\ - JERARQUÍA DE AUTORIDAD\n{layer_order}\nTus pedidos directos siempre superan este documento.\n\n\ + JERARQUÍA DE AUTORIDAD\nTus pedidos directos siempre superan este documento.\n\n\ LO QUE ESTO NO PUEDE HACER\n\ Orienta comportamiento. No puede conceder ni cambiar política de aprobación, sandbox, shell, red, \ confianza, permisos MCP, modo predeterminado, publicación o autoridad de gasto; eso sigue bajo tu control en tiempo de ejecución.\n\n\ @@ -4640,7 +4641,7 @@ fn constitution_ratification_text( và có thể sửa khi bạn thay đổi. Nó định khung quyền hạn và giới hạn thay vì quyết định từng trường hợp, \ đồng thời giữ sự liên tục giữa các phiên. Nhưng nó không phải bộ nhớ: nó mang nguyên tắc, không mang lịch sử.\n\n\ {rendered}\n\n\ - THỨ BẬC THẨM QUYỀN\n{layer_order}\nYêu cầu trực tiếp của bạn luôn cao hơn tài liệu này.\n\n\ + THỨ BẬC THẨM QUYỀN\nYêu cầu trực tiếp của bạn luôn cao hơn tài liệu này.\n\n\ ĐIỀU NÀY KHÔNG THỂ LÀM\n\ Nó hướng dẫn hành vi. Nó không thể cấp hoặc đổi chính sách phê duyệt, sandbox, shell, mạng, \ độ tin cậy, quyền MCP, chế độ mặc định, xuất bản hoặc quyền chi tiêu; những thứ đó vẫn do bạn kiểm soát lúc chạy.\n\n\ @@ -4680,7 +4681,7 @@ fn constitution_ratification_text( 이는 모든 개별 사례를 판단하는 대신 권한과 한계를 규정하며, 세션을 넘어 협업의 연속성을 부여합니다. \ 다만 이것은 기억이 아닙니다: 이력이 아니라 원칙을 담습니다.\n\n\ {rendered}\n\n\ - 권한 계층\n{layer_order}\n당신의 직접적인 요청은 언제나 이 문서보다 우선합니다.\n\n\ + 권한 계층\n당신의 직접적인 요청은 언제나 이 문서보다 우선합니다.\n\n\ 이것이 할 수 없는 일\n\ 이것은 행동을 안내할 뿐입니다. 승인 정책, 샌드박스, 셸, 네트워크, 신뢰, MCP 권한, 기본 모드, 게시, 지출 권한을 \ 부여하거나 바꿀 수 없습니다; 이는 여전히 런타임에서 당신이 직접 관리합니다.\n\n\ @@ -4720,7 +4721,7 @@ fn constitution_ratification_text( i esmenable a mesura que canvies. Defineix poders i límits en lloc de decidir cada cas, \ i dona continuïtat a la col·laboració entre sessions — però no és memòria: porta principis, no història.\n\n\ {rendered}\n\n\ - JERARQUIA D'AUTORITAT\n{layer_order}\nLes teves peticions directes sempre prevalen sobre aquest document.\n\n\ + JERARQUIA D'AUTORITAT\nLes teves peticions directes sempre prevalen sobre aquest document.\n\n\ EL QUE AIXÒ NO POT FER\n\ Orienta el comportament. No pot concedir ni canviar la política d'aprovació, sandbox, shell, xarxa, \ confiança, permisos MCP, mode per defecte, publicació o autoritat de despesa; això queda sota el teu control en temps d'execució.\n\n\ @@ -4760,7 +4761,7 @@ fn constitution_ratification_text( und lässt sich ändern, wenn du dich änderst. Sie rahmt Befugnisse und Grenzen, statt jeden Einzelfall zu entscheiden, \ und gibt deiner Zusammenarbeit Kontinuität über Sitzungen hinweg — aber sie ist kein Gedächtnis: Sie trägt Prinzipien, nicht Geschichte.\n\n\ {rendered}\n\n\ - HIERARCHIE DER AUTORITÄT\n{layer_order}\nDeine direkten Anweisungen stehen immer über diesem Dokument.\n\n\ + HIERARCHIE DER AUTORITÄT\nDeine direkten Anweisungen stehen immer über diesem Dokument.\n\n\ WAS DIES NICHT KANN\n\ Sie leitet Verhalten. Sie kann keine Freigaberichtlinie, Sandbox, Shell, Netzwerk, \ Vertrauen, MCP-Berechtigungen, Standardmodus, Veröffentlichung oder Ausgabenbefugnis gewähren oder ändern — die bleiben zur Laufzeit in deiner Hand.\n\n\ @@ -4800,7 +4801,7 @@ fn constitution_ratification_text( et amendable à mesure que vous changez. Elle encadre les pouvoirs et les limites plutôt que de trancher chaque cas, \ et donne à votre collaboration une continuité entre les sessions — mais elle n'est pas une mémoire : elle porte des principes, pas un historique.\n\n\ {rendered}\n\n\ - HIÉRARCHIE D'AUTORITÉ\n{layer_order}\nVos demandes directes priment toujours sur ce document.\n\n\ + HIÉRARCHIE D'AUTORITÉ\nVos demandes directes priment toujours sur ce document.\n\n\ CE QU'ELLE NE PEUT PAS FAIRE\n\ Elle guide le comportement. Elle ne peut ni accorder ni modifier la politique d'approbation, le sandbox, le shell, le réseau, \ la confiance, les permissions MCP, le mode par défaut, la publication ou le pouvoir de dépense — ceux-ci restent entre vos mains à l'exécution.\n\n\ @@ -4840,7 +4841,7 @@ fn constitution_ratification_text( dan dapat diamendemen seiring Anda berubah. Ia membingkai wewenang dan batasan alih-alih memutuskan setiap kasus, \ dan memberi kolaborasi Anda kesinambungan lintas sesi — tetapi ia bukan memori: ia membawa prinsip, bukan riwayat.\n\n\ {rendered}\n\n\ - HIERARKI OTORITAS\n{layer_order}\nPermintaan langsung Anda selalu mengungguli dokumen ini.\n\n\ + HIERARKI OTORITAS\nPermintaan langsung Anda selalu mengungguli dokumen ini.\n\n\ APA YANG TIDAK BISA DILAKUKANNYA\n\ Ia memandu perilaku. Ia tidak dapat memberi atau mengubah kebijakan persetujuan, sandbox, shell, jaringan, \ kepercayaan, izin MCP, mode default, publikasi, atau wewenang belanja — semua itu tetap di tangan Anda saat runtime.\n\n\ @@ -4878,7 +4879,7 @@ fn constitution_ratification_text( और आपके बदलने के साथ संशोधनीय है। यह हर मामले का फ़ैसला करने के बजाय शक्तियों और सीमाओं का ढाँचा देता है, \ और आपके सहयोग को सत्रों के पार निरंतरता देता है — लेकिन यह मेमोरी नहीं है: यह इतिहास नहीं, सिद्धांत रखता है।\n\n\ {rendered}\n\n\ - अधिकार पदानुक्रम\n{layer_order}\nआपके प्रत्यक्ष अनुरोध हमेशा इस दस्तावेज़ से ऊपर हैं।\n\n\ + अधिकार पदानुक्रम\nआपके प्रत्यक्ष अनुरोध हमेशा इस दस्तावेज़ से ऊपर हैं।\n\n\ यह क्या नहीं कर सकता\n\ यह व्यवहार का मार्गदर्शन करता है। यह अनुमति नीति, सैंडबॉक्स, शेल, नेटवर्क, \ ट्रस्ट, MCP अनुमतियाँ, डिफ़ॉल्ट मोड, प्रकाशन या खर्च का अधिकार प्रदान या परिवर्तित नहीं कर सकता — वे रनटाइम पर आपके हाथ में रहते हैं।\n\n\ @@ -4918,7 +4919,7 @@ fn constitution_ratification_text( и может изменяться вместе с вами. Она очерчивает полномочия и границы, а не решает каждый случай, \ и придаёт вашему сотрудничеству непрерывность между сессиями — но она не память: она хранит принципы, а не историю.\n\n\ {rendered}\n\n\ - ИЕРАРХИЯ ПОЛНОМОЧИЙ\n{layer_order}\nВаши прямые указания всегда важнее этого документа.\n\n\ + ИЕРАРХИЯ ПОЛНОМОЧИЙ\nВаши прямые указания всегда важнее этого документа.\n\n\ ЧЕГО ОНА НЕ МОЖЕТ\n\ Она направляет поведение. Она не может предоставить или изменить политику одобрения, sandbox, shell, сеть, \ доверие, разрешения MCP, режим по умолчанию, публикацию или право тратить — они остаются в ваших руках во время выполнения.\n\n\ @@ -4958,7 +4959,7 @@ fn constitution_ratification_text( і може змінюватися разом із вами. Вона окреслює повноваження та межі, а не вирішує кожен випадок, \ і надає вашій співпраці неперервність між сесіями — але вона не пам'ять: вона зберігає принципи, а не історію.\n\n\ {rendered}\n\n\ - ІЄРАРХІЯ ПОВНОВАЖЕНЬ\n{layer_order}\nВаші прямі вказівки завжди важливіші за цей документ.\n\n\ + ІЄРАРХІЯ ПОВНОВАЖЕНЬ\nВаші прямі вказівки завжди важливіші за цей документ.\n\n\ ЧОГО ВОНА НЕ МОЖЕ\n\ Вона спрямовує поведінку. Вона не може надати або змінити політику схвалення, sandbox, shell, мережу, \ довіру, дозволи MCP, режим за замовчуванням, публікацію чи право витрачати — вони залишаються у ваших руках під час виконання.\n\n\ @@ -5001,7 +5002,7 @@ fn constitution_ratification_text( continuity across sessions — but it is not memory: it carries principles, \ not history.\n\n\ {rendered}\n\n\ - HIERARCHY OF AUTHORITY\n{layer_order}\nYour direct requests always outrank this document.\n\n\ + HIERARCHY OF AUTHORITY\nYour direct requests always outrank this document.\n\n\ WHAT THIS CANNOT DO\n\ It guides behavior. It cannot grant or change approval policy, sandbox, shell, \ network, trust, MCP permissions, default mode, publishing, or spending \ diff --git a/crates/tui/src/tui/ui/handlers.rs b/crates/tui/src/tui/ui/handlers.rs index 6c56170e8b..0e5e3fdcbc 100644 --- a/crates/tui/src/tui/ui/handlers.rs +++ b/crates/tui/src/tui/ui/handlers.rs @@ -353,7 +353,7 @@ pub(crate) async fn handle_setup_constitution_model_draft( app.status_message = Some(match locale { codewhale_localization::Locale::ZhHans => { format!( - "{model_label} 正在生成协作准则草案……(最多 {}s)", + "{model_label} 正在生成宪章草案……(最多 {}s)", DRAFT_TIMEOUT.as_secs() ) } diff --git a/docs/CONFIGURATION.md b/docs/CONFIGURATION.md index ce1c3ca729..d7f482ede7 100644 --- a/docs/CONFIGURATION.md +++ b/docs/CONFIGURATION.md @@ -77,7 +77,7 @@ required first-run screen. Customize it later through `/constitution` or the progressive `/setup` guide. Provider/model readiness, workspace trust, and runtime posture stay separate from this guidance. -On the **Constitution** step: +On the **Working agreement** (constitution) step: - **`1`–`6`** tune the guided draft. **`G`** previews it, and **`G`** again ratifies and saves a fresh structured `constitution.json`. @@ -91,8 +91,9 @@ On the **Constitution** step: `/constitution` (alias `/law`) is the primary management surface once you are set up. Subcommands: `status` (the default), `preview`, `review`, `repo` (the -repo-local law block), `explain`, `edit`/`guided`, `repair`, `posture`, and -`bundled`. Managing the constitution never changes runtime approval, sandbox, +repo-local law block), `explain`, `base`, `suggestions`, `ratify `, +`migrate`, `rollback`, `edit`/`guided`, `repair`, `posture`, and `bundled`. +Managing the constitution never changes runtime approval, sandbox, shell, network, trust, default mode, or MCP authority — those stay in runtime posture/config. diff --git a/docs/GUIDE.md b/docs/GUIDE.md index 9a862edb71..d56c73d135 100644 --- a/docs/GUIDE.md +++ b/docs/GUIDE.md @@ -94,7 +94,7 @@ server answers. The provider step also has an explicit offline route. Everything optional stays available after that. Use `/setup` for the progressive setup and repair guide, `/settings` for the full typed editor, and -`/constitution` when you want to customize the bundled working agreement. +`/constitution` when you want your own constitution on top of the bundled one. The localized telemetry choice appears only after the workspace is ready and does not block the composer. From 49bd217b4b1271afa8f5ff35c3b09773b77fa666 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 12:55:24 +0000 Subject: [PATCH 11/46] docs: 0.10.2 changelog for the readiness fixes; registry row names /disconnect - The CHANGELOG 0.10.2 section gains Added (`/connect` and `/disconnect`, #6950) and Fixed lines for the slices on this branch: - git 2.43 /undo, shell cancel, the 30 s background notice and the todo_write receipt; - goal usage, pet live rows and crisp sprites, #6931 and /constitution; - the fixes that already landed on main for #6953, #6954, #6955, #6923, #6947 and #6945, plus the Auto-Review guardian budget, skills, MCP reconnect, the pet owner idle exit, tool headers, /jobs and the one-row gap. - Contributors: @LIghtJUNction for #6950, #6947 added to @SparkofSpike, and #6952 added to @Lstarsky0. - crates/tui/CHANGELOG.md was regenerated with scripts/sync-changelog.sh; the tui crate include_str!s it. - docs/features.toml: the plugin-oauth-providers summary names /disconnect next to /connect (111 characters, under the 120 limit). Evidence: ./scripts/release/check-versions.sh --require-dated-release prints "Version state OK: workspace=0.10.2, npm=0.10.2, npm-binary=0.10.2, lockfile in sync" and "Feature release-note receipts OK: 8 linked issue reference(s) checked in v0.10.1..HEAD". Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01BsXVvReYca2UxzimQtj7Mj --- CHANGELOG.md | 59 +++++++++++++++++++++++++++++++++++++++-- crates/tui/CHANGELOG.md | 59 +++++++++++++++++++++++++++++++++++++++-- docs/features.toml | 2 +- 3 files changed, 115 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a85368ab8..342afa0695 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -72,6 +72,10 @@ launcher guidance. ### Added +- `/connect []` signs in to a model provider that a plugin adds, + inside the TUI, and `/disconnect ` removes that sign-in. + `/login` and `/logout` are for your Codewhale account only. Plugins that + ship a shell installer can be installed from the plugin view (#6950). - `codewhale auth claude` and `/auth claude` add Claude subscription sign-in to the existing Anthropic route, with protected owned credentials, refresh before requests, a provider-picker choice and local sign-out. API-key billing @@ -210,6 +214,56 @@ launcher guidance. ### Fixed +- Windows 10 1607 starts again. 0.10.1 stopped at load with 0xC0000139 + because `DeriveCapabilitySidsFromName` is missing there; it is now resolved + at runtime (#6953). +- `codewhale resume` no longer refuses a session whose saved history still + holds a raw sub-agent handoff (#6954). +- A saved session over 8 MiB can be resumed and forked again. Journal entries + off the current branch are archived instead of refusing the session (#6955). +- Snapshot restore and `/undo` work with older git, such as the 2.43 that + Ubuntu 24.04 ships. They failed there with "only one reference expected". +- Cancelling your own background shell job no longer reports it as finished + or starts a new model turn. Cancelling a job that had already exited keeps + its real exit status instead of reading "killed, exit 0". +- A shell command that moves to the background after 30 seconds shows the + same "keep running in /jobs" notice as `Ctrl+B`, and its card no longer + shows the instructions written for the model. +- `todo_write` returns a short receipt (counts and the item in progress) to + the model instead of repeating the whole list on every call. +- Goal usage counts the turn that completes, yields or blocks the goal, so + `tokens_used` and `time_used_seconds` no longer stay at 0. +- A shell command that has printed nothing for a minute still reads as + running ("no output for 1m 05s"), keeps its Stop action and is reachable + through the Runtime API; only a job whose process is gone after a restart + is stale. `/jobs poll` and `/jobs stdin` no longer consume the output the + model has not read yet, and Alt+V prefers a running shell only when it is + on screen (#6931). +- `/constitution`: `ratify` checks the digest you reviewed in `suggestions`, + so a file that changed after your review is refused as documented; `preview` + shows the block exactly as the model receives it; messages no longer claim a + ratify or migrate moves the prompt-cache prefix; the setup wizard no longer + lists an authority order that contradicts the base prompt, and it caps your + own note so saving never cuts its end. The Simplified Chinese manager uses + the glossary term 宪章 throughout. +- The pet shows sub-agents while they run, and a finished one no longer stays + "working". Half-block pet sprites are drawn crisply instead of blurred. +- A provider's retry delay is honored when a 429 carries it in the body + instead of a `Retry-After` header (#6923). +- A Codewhale home reached through a symlink or Windows junction keeps saving + artifacts and compacting (#6947). +- A workflow gate that blocks its own role is rejected when the plan is + admitted instead of deadlocking the run (#6945). +- Auto-Review gives its guardian an output budget a reasoning model can think + inside. +- Skills are deduplicated across roots by file, ignored-key warnings are + grouped, in-workspace skill links are followed, and `doctor` warns about a + dead `always_load` pin or an unrecognized bundled skill. +- MCP servers reconnect when a session sync drops the eager connection pool. +- The shared pet owner exits after ten minutes with no client. +- Tool headers keep their clipped part visible at narrow widths, `/jobs` + prints plain exit codes and a three-line tail, and a collapsed tool output + shows a single hidden row instead of a "1 line omitted" marker. - ChatGPT sign-in omits the unsupported `originator` query parameter that caused an initial `invalid_authorize_request` rejection. PKCE, account binding and granted-scope validation remain in the same login flow @@ -308,10 +362,11 @@ launcher guidance. ### Contributors -- **[@SparkofSpike](https://github.com/SparkofSpike)** — live network-policy updates and goal milestone hand-back ([#6928](https://github.com/codewhale-hq/Codewhale/pull/6928), [#6930](https://github.com/codewhale-hq/Codewhale/pull/6930)). +- **[@LIghtJUNction](https://github.com/LIghtJUNction)** — plugin shell install and in-TUI plugin OAuth sign-in ([#6950](https://github.com/codewhale-hq/Codewhale/pull/6950)). +- **[@SparkofSpike](https://github.com/SparkofSpike)** — live network-policy updates and goal milestone hand-back ([#6928](https://github.com/codewhale-hq/Codewhale/pull/6928), [#6930](https://github.com/codewhale-hq/Codewhale/pull/6930)), and a relocated Codewhale home ([#6947](https://github.com/codewhale-hq/Codewhale/issues/6947)). - **[@dajiaohuang](https://github.com/dajiaohuang)** — cache-write token accounting ([#6913](https://github.com/codewhale-hq/Codewhale/pull/6913)). - **[@gaord](https://github.com/gaord)** — embedder telemetry surface support ([#6916](https://github.com/codewhale-hq/Codewhale/pull/6916)). -- **[@Lstarsky0](https://github.com/Lstarsky0)** — localized profile replies ([#6919](https://github.com/codewhale-hq/Codewhale/pull/6919)). +- **[@Lstarsky0](https://github.com/Lstarsky0)** — localized profile replies ([#6919](https://github.com/codewhale-hq/Codewhale/pull/6919)) and workflow gates that block their own role ([#6952](https://github.com/codewhale-hq/Codewhale/pull/6952)). - **[@jayanthvee](https://github.com/jayanthvee)** — Windows npm launcher guidance ([#6906](https://github.com/codewhale-hq/Codewhale/pull/6906)). - **[@asto18089](https://github.com/asto18089)** — callback-window reference fix for longer browser sign-in waits ([#6865](https://github.com/codewhale-hq/Codewhale/issues/6865)). - **[@BX166](https://github.com/BX166)** — reported three provider failure modes measured on AICraft's own traffic: a cold model that reads as a dead connection, a listed model that fails on every call, and a response cut at the output ceiling that reports `stop` ([#6889](https://github.com/codewhale-hq/Codewhale/issues/6889)). diff --git a/crates/tui/CHANGELOG.md b/crates/tui/CHANGELOG.md index 9f0c7d6a17..781b3a7fde 100644 --- a/crates/tui/CHANGELOG.md +++ b/crates/tui/CHANGELOG.md @@ -72,6 +72,10 @@ launcher guidance. ### Added +- `/connect []` signs in to a model provider that a plugin adds, + inside the TUI, and `/disconnect ` removes that sign-in. + `/login` and `/logout` are for your Codewhale account only. Plugins that + ship a shell installer can be installed from the plugin view (#6950). - `codewhale auth claude` and `/auth claude` add Claude subscription sign-in to the existing Anthropic route, with protected owned credentials, refresh before requests, a provider-picker choice and local sign-out. API-key billing @@ -210,6 +214,56 @@ launcher guidance. ### Fixed +- Windows 10 1607 starts again. 0.10.1 stopped at load with 0xC0000139 + because `DeriveCapabilitySidsFromName` is missing there; it is now resolved + at runtime (#6953). +- `codewhale resume` no longer refuses a session whose saved history still + holds a raw sub-agent handoff (#6954). +- A saved session over 8 MiB can be resumed and forked again. Journal entries + off the current branch are archived instead of refusing the session (#6955). +- Snapshot restore and `/undo` work with older git, such as the 2.43 that + Ubuntu 24.04 ships. They failed there with "only one reference expected". +- Cancelling your own background shell job no longer reports it as finished + or starts a new model turn. Cancelling a job that had already exited keeps + its real exit status instead of reading "killed, exit 0". +- A shell command that moves to the background after 30 seconds shows the + same "keep running in /jobs" notice as `Ctrl+B`, and its card no longer + shows the instructions written for the model. +- `todo_write` returns a short receipt (counts and the item in progress) to + the model instead of repeating the whole list on every call. +- Goal usage counts the turn that completes, yields or blocks the goal, so + `tokens_used` and `time_used_seconds` no longer stay at 0. +- A shell command that has printed nothing for a minute still reads as + running ("no output for 1m 05s"), keeps its Stop action and is reachable + through the Runtime API; only a job whose process is gone after a restart + is stale. `/jobs poll` and `/jobs stdin` no longer consume the output the + model has not read yet, and Alt+V prefers a running shell only when it is + on screen (#6931). +- `/constitution`: `ratify` checks the digest you reviewed in `suggestions`, + so a file that changed after your review is refused as documented; `preview` + shows the block exactly as the model receives it; messages no longer claim a + ratify or migrate moves the prompt-cache prefix; the setup wizard no longer + lists an authority order that contradicts the base prompt, and it caps your + own note so saving never cuts its end. The Simplified Chinese manager uses + the glossary term 宪章 throughout. +- The pet shows sub-agents while they run, and a finished one no longer stays + "working". Half-block pet sprites are drawn crisply instead of blurred. +- A provider's retry delay is honored when a 429 carries it in the body + instead of a `Retry-After` header (#6923). +- A Codewhale home reached through a symlink or Windows junction keeps saving + artifacts and compacting (#6947). +- A workflow gate that blocks its own role is rejected when the plan is + admitted instead of deadlocking the run (#6945). +- Auto-Review gives its guardian an output budget a reasoning model can think + inside. +- Skills are deduplicated across roots by file, ignored-key warnings are + grouped, in-workspace skill links are followed, and `doctor` warns about a + dead `always_load` pin or an unrecognized bundled skill. +- MCP servers reconnect when a session sync drops the eager connection pool. +- The shared pet owner exits after ten minutes with no client. +- Tool headers keep their clipped part visible at narrow widths, `/jobs` + prints plain exit codes and a three-line tail, and a collapsed tool output + shows a single hidden row instead of a "1 line omitted" marker. - ChatGPT sign-in omits the unsupported `originator` query parameter that caused an initial `invalid_authorize_request` rejection. PKCE, account binding and granted-scope validation remain in the same login flow @@ -308,10 +362,11 @@ launcher guidance. ### Contributors -- **[@SparkofSpike](https://github.com/SparkofSpike)** — live network-policy updates and goal milestone hand-back ([#6928](https://github.com/codewhale-hq/Codewhale/pull/6928), [#6930](https://github.com/codewhale-hq/Codewhale/pull/6930)). +- **[@LIghtJUNction](https://github.com/LIghtJUNction)** — plugin shell install and in-TUI plugin OAuth sign-in ([#6950](https://github.com/codewhale-hq/Codewhale/pull/6950)). +- **[@SparkofSpike](https://github.com/SparkofSpike)** — live network-policy updates and goal milestone hand-back ([#6928](https://github.com/codewhale-hq/Codewhale/pull/6928), [#6930](https://github.com/codewhale-hq/Codewhale/pull/6930)), and a relocated Codewhale home ([#6947](https://github.com/codewhale-hq/Codewhale/issues/6947)). - **[@dajiaohuang](https://github.com/dajiaohuang)** — cache-write token accounting ([#6913](https://github.com/codewhale-hq/Codewhale/pull/6913)). - **[@gaord](https://github.com/gaord)** — embedder telemetry surface support ([#6916](https://github.com/codewhale-hq/Codewhale/pull/6916)). -- **[@Lstarsky0](https://github.com/Lstarsky0)** — localized profile replies ([#6919](https://github.com/codewhale-hq/Codewhale/pull/6919)). +- **[@Lstarsky0](https://github.com/Lstarsky0)** — localized profile replies ([#6919](https://github.com/codewhale-hq/Codewhale/pull/6919)) and workflow gates that block their own role ([#6952](https://github.com/codewhale-hq/Codewhale/pull/6952)). - **[@jayanthvee](https://github.com/jayanthvee)** — Windows npm launcher guidance ([#6906](https://github.com/codewhale-hq/Codewhale/pull/6906)). - **[@asto18089](https://github.com/asto18089)** — callback-window reference fix for longer browser sign-in waits ([#6865](https://github.com/codewhale-hq/Codewhale/issues/6865)). - **[@BX166](https://github.com/BX166)** — reported three provider failure modes measured on AICraft's own traffic: a cold model that reads as a dead connection, a listed model that fails on every call, and a response cut at the output ceiling that reports `stop` ([#6889](https://github.com/codewhale-hq/Codewhale/issues/6889)). diff --git a/docs/features.toml b/docs/features.toml index 0bdfec3fd2..ef9509085b 100644 --- a/docs/features.toml +++ b/docs/features.toml @@ -682,7 +682,7 @@ owner = "crates/tui/src/oauth.rs" [[feature]] id = "plugin-oauth-providers" name = "Reviewed plugin OAuth providers" -summary = "Reviewed plugins connect with /connect ; the host owns PKCE, model discovery and per-request approvals." +summary = "Reviewed plugins sign in with /connect and out with /disconnect; the host owns PKCE, models and approvals." status = "experimental" surfaces = { tui = "preview", api = "partial", web = "none" } since = "unreleased" From d220add596273c063fd565fbd36e483fd0e25d34 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 12:58:59 +0000 Subject: [PATCH 12/46] docs(credits): 0.10.2 credits @LIghtJUNction on the website and contributor page CI on 054b122c4c failed "Lint & Type Check": web/lib/public-copy.test.ts:167 compares the 0.10.2 CHANGELOG contributor handles with the website credit arrays and the docs/CONTRIBUTORS.md band. The previous commit (49bd217b4b) added @LIghtJUNction (#6950) to the CHANGELOG. The website still listed them under UNRELEASED_CONTRIBUTORS, which RELEASE_CONTRIBUTORS does not include. - web/lib/release-credits.ts moves @LIghtJUNction from UNRELEASED_CONTRIBUTORS to RELEASE_CONTRIBUTORS, as its own comment describes for the release that carries the work. UNRELEASED_CONTRIBUTORS is now empty. - The docs/CONTRIBUTORS.md v0.10.2 band gains the #6950 line, and adds #6947 to SparkofSpike's entry and #6952 to Lstarsky0's, matching the CHANGELOG. Evidence: a python3 re-implementation of the test's three handle extractions gives the same 8 sorted handles for the CHANGELOG, the website arrays and the doc band (@BX166 @LIghtJUNction @Lstarsky0 @SparkofSpike @asto18089 @dajiaohuang @gaord @jayanthvee). `python3 scripts/check-contributor-credit.py`: "Every contributor in the window is credited on all three surfaces." The vitest run itself is CI's job on this PR. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01BsXVvReYca2UxzimQtj7Mj --- docs/CONTRIBUTORS.md | 5 +++-- web/lib/release-credits.ts | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/CONTRIBUTORS.md b/docs/CONTRIBUTORS.md index 26e5c12167..7cef2ea3bd 100644 --- a/docs/CONTRIBUTORS.md +++ b/docs/CONTRIBUTORS.md @@ -39,10 +39,11 @@ changelog or release notes, and this contributor record. **Contributions** -- **[Sh1Zuku / SparkofSpike](https://github.com/SparkofSpike)** — live network-policy updates and goal milestone hand-back ([#6928](https://github.com/codewhale-hq/Codewhale/pull/6928), [#6930](https://github.com/codewhale-hq/Codewhale/pull/6930)). +- **[Sh1Zuku / SparkofSpike](https://github.com/SparkofSpike)** — live network-policy updates and goal milestone hand-back ([#6928](https://github.com/codewhale-hq/Codewhale/pull/6928), [#6930](https://github.com/codewhale-hq/Codewhale/pull/6930)), and a Codewhale home reached through a symlink or junction ([#6947](https://github.com/codewhale-hq/Codewhale/issues/6947)). - **[dajiaohuang](https://github.com/dajiaohuang)** — cache-write token accounting, with missing and explicit-zero usage preserved ([#6913](https://github.com/codewhale-hq/Codewhale/pull/6913)). - **[gaord](https://github.com/gaord)** — embedder-declared telemetry surface support without changing collection policy ([#6916](https://github.com/codewhale-hq/Codewhale/pull/6916)). -- **[Lstarsky0](https://github.com/Lstarsky0)** — localized `/profile` replies across all 15 complete locale packs ([#6919](https://github.com/codewhale-hq/Codewhale/pull/6919)). +- **[Lstarsky0](https://github.com/Lstarsky0)** — localized `/profile` replies across all 15 complete locale packs ([#6919](https://github.com/codewhale-hq/Codewhale/pull/6919)), and workflow gates that block their own role rejected at plan admission ([#6952](https://github.com/codewhale-hq/Codewhale/pull/6952)). +- **[LIghtJUNction](https://github.com/LIghtJUNction)** — plugin shell install and in-TUI plugin OAuth sign-in with `/connect` and `/disconnect` ([#6950](https://github.com/codewhale-hq/Codewhale/pull/6950)). - **[jayanthvee](https://github.com/jayanthvee)** — Windows npm launcher guidance and targeted process shutdown ([#6906](https://github.com/codewhale-hq/Codewhale/pull/6906)). - **[asto18089](https://github.com/asto18089)** — reference fix for longer browser sign-in callback windows ([#6865](https://github.com/codewhale-hq/Codewhale/issues/6865)). diff --git a/web/lib/release-credits.ts b/web/lib/release-credits.ts index 416e214f4c..a4fbfc34ec 100644 --- a/web/lib/release-credits.ts +++ b/web/lib/release-credits.ts @@ -26,6 +26,7 @@ export const RELEASE_CONTRIBUTORS: string[] = [ "@Lstarsky0", "@jayanthvee", "@asto18089", + "@LIghtJUNction", ]; /** @@ -33,7 +34,7 @@ export const RELEASE_CONTRIBUTORS: string[] = [ * one. scripts/check-contributor-credit.py requires them here now; the release * cut moves them into RELEASE_CONTRIBUTORS with that release's changelog block. */ -export const UNRELEASED_CONTRIBUTORS: string[] = ["@LIghtJUNction"]; +export const UNRELEASED_CONTRIBUTORS: string[] = []; /** * Contributors who helped with reports, reproductions, and verification. From 17a88a9ceb8d5a2843f73bad15123e26d5f8b148 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 13:24:27 +0000 Subject: [PATCH 13/46] fix(shell): only the model's own cancel silences a job; cancel-all skips exited jobs Follow-up to 8eb23adb03, from an adversarial review of that slice. - 8eb23adb03 marked a killed job's completion as reported inside `ShellManager::kill`, which /jobs, the Runtime API and the model's cancel all share. When a person stopped a job the model had started, the model was therefore never told. `kill` is neutral again. The model's own cancel marks the jobs it actually killed through the new `mark_cancel_reported`. A person's cancel still reaches the model as a Killed completion. - Cancel-all picked jobs by their cached status. A job that had exited since the last poll was "canceled": the model's receipt said "Canceled 1 background command" with status Killed, and its real Completed event followed. `/jobs cancel all` said the same. `kill_running_for_session` now polls each of the session's jobs before selecting the running ones, and returns only the jobs it actually killed. Evidence: cargo fmt --all -- --check is clean. Compile and tests are pending: the running local test build predates this edit, and hosted CI on codewhale-hq/Codewhale#6957 runs them on this push. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01BsXVvReYca2UxzimQtj7Mj --- crates/tui/src/tools/shell.rs | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/crates/tui/src/tools/shell.rs b/crates/tui/src/tools/shell.rs index dc7c18fa02..9062f68d04 100644 --- a/crates/tui/src/tools/shell.rs +++ b/crates/tui/src/tools/shell.rs @@ -3187,10 +3187,18 @@ impl ShellManager { .ok_or_else(|| anyhow!("Task {task_id} not found"))?; shell.kill()?; - if shell.status == ShellStatus::Killed { + shell.snapshot() + } + + fn mark_cancel_reported(&mut self, result: &ShellResult) { + if result.status == ShellStatus::Killed + && let Some(shell) = result + .task_id + .as_deref() + .and_then(|id| self.processes.get_mut(id)) + { shell.completion_reported = true; } - shell.snapshot() } pub fn kill_for_session( @@ -3225,18 +3233,20 @@ impl ShellManager { ) -> Result> { let ids = self .processes - .iter() + .iter_mut() .filter(|(_, shell)| { - shell.status == ShellStatus::Running - && !active_session_id.is_empty() - && shell.owner_session_id == active_session_id + !active_session_id.is_empty() && shell.owner_session_id == active_session_id + }) + .filter_map(|(id, shell)| { + shell.poll(); + (shell.status == ShellStatus::Running).then(|| id.clone()) }) - .map(|(id, _)| id.clone()) .collect::>(); let mut results = Vec::with_capacity(ids.len()); for id in ids { results.push(self.kill(&id)?); } + results.retain(|result| result.status == ShellStatus::Killed); Ok(results) } @@ -6997,6 +7007,9 @@ impl BashTool { let results = manager .kill_running_for_session(&context.state_namespace) .map_err(|err| ToolError::execution_failed(err.to_string()))?; + for result in &results { + manager.mark_cancel_reported(result); + } if results.is_empty() { return Ok(ToolResult { content: "No running background commands.".to_string(), @@ -7033,6 +7046,7 @@ impl BashTool { let result = manager .kill_for_session(&context.state_namespace, task_id) .map_err(|err| ToolError::execution_failed(err.to_string()))?; + manager.mark_cancel_reported(&result); let task_id = result .task_id .clone() From ed25a29c3945f14a7e7830d63311f104af60e07a Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 13:24:27 +0000 Subject: [PATCH 14/46] fix(tui): a tty:true command is not "moved to /jobs" Follow-up to d33dc38613, from an adversarial review of that slice. The moved-to-jobs toast is meant only for a foreground wait that ran out, so a call started in the background gets none. The check looked only at `background`, but `tty:true` also starts in the background (the schema says "implies background"). A tty call therefore came back Running at once and showed "... keep running in /jobs" although nothing had moved. `tty:true` now counts as an explicit background start. `exec_is_background` itself is unchanged, because the start-path wait dedupe also uses it. Evidence: cargo fmt --all -- --check is clean. Compile and the tool_routing and ui tests are pending a local build and hosted CI on codewhale-hq/Codewhale#6957. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01BsXVvReYca2UxzimQtj7Mj --- crates/tui/src/tui/tool_routing.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/tui/src/tui/tool_routing.rs b/crates/tui/src/tui/tool_routing.rs index 4036c830e1..410c5bba07 100644 --- a/crates/tui/src/tui/tool_routing.rs +++ b/crates/tui/src/tui/tool_routing.rs @@ -722,7 +722,10 @@ pub(super) fn handle_tool_call_complete( let semantic_name = detail .map_or(name, |detail| canonical_action_alias(name, &detail.input)) .to_string(); - let explicit_background = detail.is_some_and(|detail| exec_is_background(&detail.input)); + let explicit_background = detail.is_some_and(|detail| { + exec_is_background(&detail.input) + || detail.input.get("tty").and_then(serde_json::Value::as_bool) == Some(true) + }); // Roll any child-LLM token usage the tool reports into the // session-cost counter. Runs unconditionally so future tools that From 625f25bc539b21c07e8c07fd81ef3601bf9c2f49 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 13:24:27 +0000 Subject: [PATCH 15/46] fix(constitution): ratifying one clause at a time keeps working Follow-up to e71255e2b5, from an adversarial review of that slice. Ratify now checks the digest reviewed in `/constitution suggestions`, but that digest stayed at its pre-ratify value. The next `/constitution ratify ` was therefore refused as StaleBase, although the only change since the review was the user's own ratification. A repeated `ratify c1` also said StaleBase instead of "already ratified". `ratify_text` now returns the saved file's digest (`ratification.after_digest`) on success, and the command stores it as the reviewed digest. An edit made outside the session is still refused as stale. Evidence: cargo fmt --all -- --check is clean. Compile and the commands::core::constitution tests are pending a local build and hosted CI on codewhale-hq/Codewhale#6957. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01BsXVvReYca2UxzimQtj7Mj --- .../src/commands/groups/core/constitution.rs | 80 +++++++++++-------- 1 file changed, 48 insertions(+), 32 deletions(-) diff --git a/crates/tui/src/commands/groups/core/constitution.rs b/crates/tui/src/commands/groups/core/constitution.rs index 4ebf35d8c2..c611d9af86 100644 --- a/crates/tui/src/commands/groups/core/constitution.rs +++ b/crates/tui/src/commands/groups/core/constitution.rs @@ -91,11 +91,15 @@ impl RegisterCommand for ConstitutionCmd { .split_whitespace() .map(str::to_string) .collect(); - CommandResult::message(ratify_text( + let (message, ratified) = ratify_text( app.ui_locale, app.constitution_reviewed_digest.as_deref(), &ids, - )) + ); + if ratified.is_some() { + app.constitution_reviewed_digest = ratified; + } + CommandResult::message(message) } Some("help") => CommandResult::message(help_text(app.ui_locale)), Some(other) => CommandResult::error(format!( @@ -216,63 +220,75 @@ fn suggestions_text(locale: Locale) -> (String, Option) { /// reviewed in `/constitution suggestions` to [`UserConstitution::ratify`], /// which refuses when the live file has moved since, and persists only when the /// clause set is exactly what the user named. -fn ratify_text(locale: Locale, reviewed: Option<&str>, ids: &[String]) -> String { +fn ratify_text(locale: Locale, reviewed: Option<&str>, ids: &[String]) -> (String, Option) { if ids.is_empty() { - return match locale { + let text = match locale { Locale::ZhHans => { "用法:/constitution ratify [...]。先用 /constitution suggestions 查看待批准条款。" .to_string() } _ => "Usage: /constitution ratify [...]. Run /constitution suggestions first to see what is pending.".to_string(), }; + return (text, None); } let (path, constitution) = match load_user_constitution() { UserConstitutionStatus::Loaded { path, constitution } => (path, constitution), _ => { - return match locale { + let text = match locale { Locale::ZhHans => "没有可批准的结构化用户全局宪章。".to_string(), _ => "There is no structured user-global constitution to ratify.".to_string(), }; + return (text, None); } }; let Some(reviewed) = reviewed else { - return match locale { + let text = match locale { Locale::ZhHans => { "未批准任何条款:请先用 /constitution suggestions 审阅待批准条款。".to_string() } _ => "Nothing was ratified: review the pending clauses with /constitution suggestions first.".to_string(), }; + return (text, None); }; match constitution.ratify(reviewed, ids, None) { - Err(err) => match locale { - Locale::ZhHans => format!("未批准任何条款:{err}"), - _ => format!("Nothing was ratified: {err}"), - }, - Ok(ratification) => match ratification.constitution.save_to(&path) { - Err(err) => match locale { - Locale::ZhHans => format!("批准已计算但保存失败,文件未更改:{err:#}"), - _ => format!( - "Ratification was computed but could not be saved; the file is unchanged: {err:#}" - ), - }, - Ok(()) => match locale { - Locale::ZhHans => format!( - "已批准 {} 条:{}\n摘要 {} → {}\n这些条款从下一轮起作为会话记录中的消息发送给模型;提示词缓存前缀不变。", - ratification.accepted_clause_ids.len(), - ratification.accepted_clause_ids.join(", "), - ratification.before_digest, - ratification.after_digest, - ), - _ => format!( - "Ratified {} clause(s): {}\nDigest {} → {}\nThey reach the model from the next turn as a message recorded in the session; the prompt-cache prefix is unchanged.", - ratification.accepted_clause_ids.len(), - ratification.accepted_clause_ids.join(", "), - ratification.before_digest, - ratification.after_digest, - ), + Err(err) => ( + match locale { + Locale::ZhHans => format!("未批准任何条款:{err}"), + _ => format!("Nothing was ratified: {err}"), }, + None, + ), + Ok(ratification) => match ratification.constitution.save_to(&path) { + Err(err) => ( + match locale { + Locale::ZhHans => format!("批准已计算但保存失败,文件未更改:{err:#}"), + _ => format!( + "Ratification was computed but could not be saved; the file is unchanged: {err:#}" + ), + }, + None, + ), + Ok(()) => ( + match locale { + Locale::ZhHans => format!( + "已批准 {} 条:{}\n摘要 {} → {}\n这些条款从下一轮起作为会话记录中的消息发送给模型;提示词缓存前缀不变。", + ratification.accepted_clause_ids.len(), + ratification.accepted_clause_ids.join(", "), + ratification.before_digest, + ratification.after_digest, + ), + _ => format!( + "Ratified {} clause(s): {}\nDigest {} → {}\nThey reach the model from the next turn as a message recorded in the session; the prompt-cache prefix is unchanged.", + ratification.accepted_clause_ids.len(), + ratification.accepted_clause_ids.join(", "), + ratification.before_digest, + ratification.after_digest, + ), + }, + Some(ratification.after_digest.clone()), + ), }, } } From 210904f44c6b13fc996069d86d44232cb05699e2 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 13:28:09 +0000 Subject: [PATCH 16/46] test(pet): the shared-owner check sends the current appearance schema "Pet conformance / recorder (macos-latest)" failed on codewhale-hq/Codewhale#6957 (run 38141362016, job 114475791414) at pet/scripts/check-shared.py:97: styled=frame_when(d,lambda f:f['appearance']==appearance) AssertionError: Accepted state did not reach the frame projection Cause: main's b0f9cbc4d2 ("/pet style and language, pixel whale body", committed unbuilt) added `body_style` and `text_language` to `Appearance` (pet_watch/appearance.rs:56-59). Both use `#[serde(default)]` and are always serialized. The owner stores the parsed appearance and echoes it whole (owner.rs:665), so the frame carries 10 keys while the fixture sent 8. The strict equality at :97, and again after the crash restart at :107, could never hold. The owner kept answering /v1/frame for the full 5 s, so this is not a stall. The failure is deterministic; Windows runs that step only on macOS (`if: runner.os == 'macOS'`), so it never saw it. Pet conformance runs only on PRs that touch pet paths, so this PR is the first run since b0f9cbc4d2. The product's full echo is intended. shared.html builds and reads both fields, and the persisted and live appearance must agree. So the fixture moves to the current schema instead of the owner hiding defaults. It sends non-default values ('bodyStyle':'pixel', 'textLanguage':'ja'), so the round trip and the restart now also prove the new fields persist. With eventColors False, owner.rs:619-623 still writes `particle` into style.r/g/b for any body style, so the colour assertion at :98 holds. Evidence: `python3 -I -m py_compile pet/scripts/check-shared.py` passes. The script drives the owner binary, so the run itself is the macOS recorder job on this push. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01BsXVvReYca2UxzimQtj7Mj --- pet/scripts/check-shared.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pet/scripts/check-shared.py b/pet/scripts/check-shared.py index 5bb739cf99..6701b39245 100644 --- a/pet/scripts/check-shared.py +++ b/pet/scripts/check-shared.py @@ -90,7 +90,7 @@ def passed(name): checks.append(name);print('PASS '+name,flush=True) request(d,'/v1/audio',{'client':audio_a,'enabled':False}) request(d,'/v1/audio',{'client':audio_b,'enabled':False}) before_style=request(d,'/v1/export') - appearance={'background':[238,239,235],'backgroundTop':[255,255,250],'particle':[32,79,83],'eventColors':False,'brightness':1.4,'dotScale':1.2,'glow':.25,'environment':False} + appearance={'background':[238,239,235],'backgroundTop':[255,255,250],'particle':[32,79,83],'eventColors':False,'brightness':1.4,'dotScale':1.2,'glow':.25,'environment':False,'bodyStyle':'pixel','textLanguage':'ja'} style_action={'identity':d['identity'],'client':str(uuid.uuid4()),'seq':1,'source_revision':b['sourceRevision'],'action':{'kind':'appearance','appearance':appearance}} reject(d,'/v1/action',dict(style_action,action={'kind':'appearance','appearance':dict(appearance,brightness=999)})) request(d,'/v1/action',style_action) From 407cb8ebb3fea034b0c26a08cf966c994e3de314 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 13:52:03 +0000 Subject: [PATCH 17/46] fix(goal): update_goal accepts a progress estimate with yield; the stage report shows A session-export review found update_goal rejected `progress` sent with `status: "yield"` ("progress is only accepted with status not_achieved or advisory"). Yield means a finished stage handed back to the user, which is exactly when a progress estimate is most useful. - update_goal accepts `progress` with yield, and stores it on the yielded goal after `mark_yielded`. `record_progress` only records on an active goal, so it would have dropped it. The refusal text names yield. The schema description says "Optional with not_achieved, advisory or yield". - The TUI shows the progress receipt for a yield too (ui/apply.rs). Paused and complete goals keep their last report silent; a yield is the stage report the user is handed back with. The schema promises the estimate is "shown to the user as reported progress". An adversarial review flagged that without this, a yield's estimate reached only the model. - The existing progress-rejection test expects the new refusal text. - CHANGELOG notes it, and crates/tui/CHANGELOG.md is re-synced. Model-visible and KV-cache: one sentence of update_goal's schema description changes, 7 bytes longer. That changes the session-pinned tool catalog for new sessions only; a running session keeps its pinned prefix. The 12 conformance goldens that pin the catalog were re-recorded with CODEWHALE_CONFORMANCE_UPDATE=1 (CI unset). Normalized, their diff moves only: - the description text, and a fixed-width preview shifted 7 characters; - the sha256 values and the short "frozen: " label; - tools_bytes and payload_json_bytes, each +7; - a `.codewhale/state/.gitignore` entry in the event goldens' workspace_after. That entry comes from main's 491d3df155, which writes that file and was committed with its tests left to CI, so main's golden_turn_events_match was already stale. Evidence, on a local lib test binary (Ubuntu 24.04) built from this branch with the goal.rs change: - `conformance::`: test result: ok. 33 passed; 0 failed. - `tools::goal`: test result: ok. 36 passed; 0 failed. The ui/apply.rs condition was added after that build. cargo fmt --check is clean, and hosted CI compiles and runs it. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01BsXVvReYca2UxzimQtj7Mj --- CHANGELOG.md | 3 ++- crates/tui/CHANGELOG.md | 3 ++- crates/tui/src/tools/goal.rs | 11 +++++++---- crates/tui/src/tui/ui/apply.rs | 6 ++++-- .../conformance/events/approval_denied.golden.jsonl | 10 +++++----- .../conformance/events/cancel_midstream.golden.jsonl | 8 ++++---- .../conformance/events/one_tool_call.golden.jsonl | 10 +++++----- .../events/parallel_tool_calls.golden.jsonl | 10 +++++----- .../conformance/events/plain_answer.golden.jsonl | 8 ++++---- .../provider_error_after_tool_call.golden.jsonl | 8 ++++---- .../conformance/prompt/agent_suggest.golden.json | 6 +++--- .../prompt/agent_suggest.tools.golden.json | 2 +- .../fixtures/conformance/prompt/plan_mode.golden.json | 6 +++--- .../conformance/prompt/plan_mode.tools.golden.json | 2 +- .../prompt/trusted_project_instructions.golden.json | 6 +++--- .../trusted_project_instructions.tools.golden.json | 2 +- 16 files changed, 54 insertions(+), 47 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 342afa0695..d23aa0c166 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -232,7 +232,8 @@ launcher guidance. - `todo_write` returns a short receipt (counts and the item in progress) to the model instead of repeating the whole list on every call. - Goal usage counts the turn that completes, yields or blocks the goal, so - `tokens_used` and `time_used_seconds` no longer stay at 0. + `tokens_used` and `time_used_seconds` no longer stay at 0. `update_goal` + accepts a progress estimate with `yield`. - A shell command that has printed nothing for a minute still reads as running ("no output for 1m 05s"), keeps its Stop action and is reachable through the Runtime API; only a job whose process is gone after a restart diff --git a/crates/tui/CHANGELOG.md b/crates/tui/CHANGELOG.md index 781b3a7fde..694c557e91 100644 --- a/crates/tui/CHANGELOG.md +++ b/crates/tui/CHANGELOG.md @@ -232,7 +232,8 @@ launcher guidance. - `todo_write` returns a short receipt (counts and the item in progress) to the model instead of repeating the whole list on every call. - Goal usage counts the turn that completes, yields or blocks the goal, so - `tokens_used` and `time_used_seconds` no longer stay at 0. + `tokens_used` and `time_used_seconds` no longer stay at 0. `update_goal` + accepts a progress estimate with `yield`. - A shell command that has printed nothing for a minute still reads as running ("no output for 1m 05s"), keeps its Stop action and is reachable through the Runtime API; only a job whose process is gone after a restart diff --git a/crates/tui/src/tools/goal.rs b/crates/tui/src/tools/goal.rs index 0ac1088795..00222fd3a2 100644 --- a/crates/tui/src/tools/goal.rs +++ b/crates/tui/src/tools/goal.rs @@ -1160,7 +1160,7 @@ impl ToolSpec for UpdateGoalTool { }, "progress": { "type": "object", - "description": "Optional with not_achieved or advisory: your current best estimate of overall completion, shown to the user as reported progress. Keep percent honest — it is an estimate, never a verified fraction.", + "description": "Optional with not_achieved, advisory or yield: your current best estimate of overall completion, shown to the user as reported progress. Keep percent honest — it is an estimate, never a verified fraction.", "properties": { "percent": { "type": "integer", @@ -1211,9 +1211,9 @@ impl ToolSpec for UpdateGoalTool { } let status = required_str(&input, "status")?.trim().to_ascii_lowercase(); let progress = parse_progress_report(&input)?; - if progress.is_some() && !matches!(status.as_str(), "not_achieved" | "advisory") { + if progress.is_some() && !matches!(status.as_str(), "not_achieved" | "advisory" | "yield") { return Err(ToolError::invalid_input( - "progress is only accepted with status not_achieved or advisory", + "progress is only accepted with status not_achieved, advisory or yield", )); } let snapshot = { @@ -1272,6 +1272,9 @@ impl ToolSpec for UpdateGoalTool { } "yield" => { state.mark_yielded().map_err(ToolError::invalid_input)?; + if progress.is_some() { + state.progress = progress; + } } "advisory" => { let advisory = input @@ -2342,7 +2345,7 @@ mod tests { .await .expect_err("progress is not terminal evidence"); assert!( - err.to_string().contains("not_achieved or advisory"), + err.to_string().contains("not_achieved, advisory or yield"), "{err}" ); diff --git a/crates/tui/src/tui/ui/apply.rs b/crates/tui/src/tui/ui/apply.rs index 45d2670ba2..66f987b81b 100644 --- a/crates/tui/src/tui/ui/apply.rs +++ b/crates/tui/src/tui/ui/apply.rs @@ -688,9 +688,11 @@ pub(crate) fn apply_goal_snapshot_to_app(app: &mut App, snapshot: &GoalSnapshot) // A fresh reported-progress receipt reads like the model's own status // line: percent with a bar, then the optional now/next lines it wrote. // Paused/complete goals keep their last report silent — the lifecycle - // receipt already spoke. + // receipt already spoke — except a yield, whose progress is the stage + // report the user is handed back with. if progress_changed - && verdict == GoalStatus::Active + && (verdict == GoalStatus::Active + || snapshot.pause_reason == Some(crate::tools::goal::GoalPauseReason::Yielded)) && let Some(progress) = snapshot.progress.as_ref() { let mut content = app diff --git a/crates/tui/tests/fixtures/conformance/events/approval_denied.golden.jsonl b/crates/tui/tests/fixtures/conformance/events/approval_denied.golden.jsonl index d5e054ccb7..4d586bac04 100644 --- a/crates/tui/tests/fixtures/conformance/events/approval_denied.golden.jsonl +++ b/crates/tui/tests/fixtures/conformance/events/approval_denied.golden.jsonl @@ -1,8 +1,8 @@ {"created_at":"","event":"turn_started","route":{"auto_model":false,"base_url":"https://api.deepseek.com/beta","billing_product":{"kind":"unproven"},"model":"deepseek-v4-pro","provider":"deepseek","provider_identity":"deepseek"},"turn_id":""} {"engine_session_id":"conformance-session","event":"session_updated","messages":[{"content":[{"text":"Create denied.txt with touch.","type":"text"},{"text":"\nCurrent local date: \nCurrent workspace: \nCurrent permission posture: Ask\nCurrent sandbox posture: workspace-write (writes inside the workspace; network blocked) (local OS sandbox applied)\n## Repo Working Set\nKey files: README.md\nWhen in doubt, use tools to verify and keep changes focused on the working set.\n","type":"text"}],"role":"user"}],"model":"deepseek-v4-pro","system_prompt":"","workspace":""} {"changed":false,"context_updates":0,"description":"","event":"prefix_cache_change","last_miss_reason":"","pin_reason":"initial","pinned_combined_hash":"","stability_pct":100,"system_prompt_changed":false,"tools_changed":false} -{"changed":false,"context_updates":0,"description":"frozen: 54603624f5a8","event":"prefix_cache_change","last_miss_reason":"","pin_reason":"initial","pinned_combined_hash":"","stability_pct":100,"system_prompt_changed":false,"tools_changed":false} -{"event":"tool_request_snapshot","snapshot":{"active_tool_catalog_sha256":"18c6d652f11c0c195e7b586852ab46b47987939aaa316b39ef8b692255a7e077","capture_source":"prepared model-client request","delivery_status":"unknown (capture does not prove provider delivery)","omitted_tool_count":0,"payload_json_bytes":14890,"payload_measurement_status":"exact (within 1048576-byte measurement bound)","provider":{"model":"deepseek-v4-pro","provider":"Deepseek","status":"available"},"registry_facts_present":true,"registry_only_tools":{"status":"known","value":{"count":40,"omitted":8,"rendered":[{"truncated":false,"value":"Git"},{"truncated":false,"value":"Run"},{"truncated":false,"value":"Web"},{"truncated":false,"value":"apply_patch"},{"truncated":false,"value":"automation"},{"truncated":false,"value":"diagnostics"},{"truncated":false,"value":"finance"},{"truncated":false,"value":"github"},{"truncated":false,"value":"grep_files"},{"truncated":false,"value":"handle_read"},{"truncated":false,"value":"harness"},{"truncated":false,"value":"image_ocr"},{"truncated":false,"value":"list_dir"},{"truncated":false,"value":"lsp"},{"truncated":false,"value":"note"},{"truncated":false,"value":"notify"},{"truncated":false,"value":"pandoc_convert"},{"truncated":false,"value":"project_map"},{"truncated":false,"value":"registry_sync"},{"truncated":false,"value":"request_plugin_install"},{"truncated":false,"value":"request_user_input"},{"truncated":false,"value":"retrieve_tool_result"},{"truncated":false,"value":"revert_turn"},{"truncated":false,"value":"review"},{"truncated":false,"value":"send_later"},{"truncated":false,"value":"session_get"},{"truncated":false,"value":"session_search"},{"truncated":false,"value":"start_mcp_server"},{"truncated":false,"value":"start_registry_mcp_server"},{"truncated":false,"value":"task_shell_wait"},{"truncated":false,"value":"tasks"},{"truncated":false,"value":"terminal/cancel"}]}},"registry_tool_count":{"status":"known","value":62},"rendered_tool_count":12,"schema_version":1,"step":0,"tool_count":12,"tools":[{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Required"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"ExecutesCode"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Execute a shell command in the workspace and return stdout and stderr. Output keeps the last 2000 lines or 50KB. An optional timeout is the seconds to wait in the foreground (default 30 seconds); a command still running then is never killed for time: it moves to the background by itself and you get its output so far, how long ago it last printed, its build phase, and a task_id, so long builds and tests need no special timeout. Find task_shell_wait with tool_search to read more or wait for it. In Ask, after "},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The command to execute. Actual execution shell: `/bin/bash`. Use Bash syntax: pipelines, redirections, $(command), and && / || are supported. Quote paths and variable expansions, such as \\\"$path\\\"; use single quotes for literal text. For literal multiline input, use a quoted heredoc delimiter (<<'EOF') with its closing delimiter on a separate line. Use only installed programs; do not assume GNU-specific flags on macOS/BSD. Example: printf '%s\\\\n' 'sample'.\",\"type\":\"string\"},\"justification\":{\"description\":\"Required with sandbox_permissions: one sentence explaining why this exact command needs wider access.\",\"type\":\"string\"},\"read_only\":{\"description\":\"Set true to run analysis code (including Python/SQLite) with mandatory native filesystem read-only isolation and no network. Available during peer writes. Refused when native enforcement is unavailable; no background, stdin, external backend, or sandbox escalation.\",\"type\":\"boolean\"},\"sandbox_permissions\":{\"description\":\"The wider sandbox mode this exact command needs. Use only as a one-shot retry after a sandbox denial; requires justification and user approval in Ask.\",\"enum\":[\"workspace-write\",\"danger-full-access\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional seconds to wait in the foreground (default 30 seconds). A command still running then is not killed: it moves to the background by itself and you get its output so far, how long ago it last printed, its build phase, and a task_id. Find task_shell_wait with tool_search to read more or wait for it.\",\"type\":\"number\"}},\"required\":[\"command\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"bash"},"ordinal":1,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":0,"omitted":0,"rendered":[]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Create the session's one persistent goal: a completion objective Codewhale keeps working toward across turns until it is verified complete, blocked, or the user stops it. A goal is for a durable objective that outlasts one turn: a multi-step outcome the user wants continued and verified. A question, a greeting, or a one-shot edit completes as an ordinary turn. When the user explicitly asks to use `/goal` or to make something the goal, `create_goal` is what records it; acknowledging it in prose records nothi"},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"objective\":{\"description\":\"The full objective to pursue. Keep the complete user goal, not a shortened one-turn version.\",\"type\":\"string\"},\"token_budget\":{\"description\":\"Optional soft token budget for the goal.\",\"minimum\":0,\"type\":\"integer\"}},\"required\":[\"objective\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"create_goal"},"ordinal":2,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Suggest"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Edit one file with targeted text replacements. Every edits[].oldText must identify a unique, non-overlapping region of the original file. Merge nearby or overlapping changes into one edit."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"edits\":{\"description\":\"One or more disjoint replacements, all matched against the original file.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"newText\":{\"description\":\"Replacement text; may be empty to delete the matched span.\",\"type\":\"string\"},\"oldText\":{\"description\":\"Text identifying one unique region to replace.\",\"type\":\"string\"}},\"required\":[\"newText\",\"oldText\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to the file to edit (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"edits\",\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"edit"},"ordinal":3,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":2,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"},{"truncated":false,"value":"Sandboxable"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Find workspace files by name, path, or glob (e.g. src/**/*.rs). Space-separated terms must all match. Literal matches rank above fuzzy matches; results are capped. Respects ignore files and skips build/dependency directories by default. Use extensions to filter suffixes."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"properties\":{\"exclude\":{\"description\":\"Optional replacement exclusion globs (e.g. [\\\"**/target/**\\\", \\\"*.lock\\\"]); [] disables default exclusions.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"extensions\":{\"description\":\"Optional list of file extensions to include (e.g. [\\\"rs\\\", \\\"md\\\"]).\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"limit\":{\"description\":\"Maximum number of ranked results to return (default: 20, maximum: 200).\",\"type\":\"integer\"},\"path\":{\"description\":\"Optional base path to search (relative to workspace).\",\"type\":\"string\"},\"query\":{\"description\":\"File name, path fragment, space-separated terms, or glob (e.g. **/engine*.rs).\",\"type\":\"string\"}},\"required\":[\"query\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"file_search"},"ordinal":4,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Inspect the current runtime goal state, including objective, status, token budget, elapsed time, evidence, and blocker."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{},\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"get_goal"},"ordinal":5,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Load a named skill's SKILL.md body and companion file list into this turn. Use when the user names a skill, or when an entry in the system prompt's `## Skills` index matches the task -- load it before starting the work, not after. Pass query=\"...\" to search names and descriptions, or name=\"list\" for the whole catalogue. Resolves global and plugin skills that `read` cannot reach."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"name\":{\"description\":\"Skill id to load. Omit or pass \\\"list\\\" to see all available skills.\",\"type\":\"string\"},\"query\":{\"description\":\"Search term matched against skill names and descriptions. Use when the index was truncated or no name is known.\",\"type\":\"string\"}},\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"load_skill"},"ordinal":6,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":2,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"},{"truncated":false,"value":"Sandboxable"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Read a file. A text file comes back whole in one call when it fits this call's output budget — 100000 bytes by default, raisable to 500000 with max_bytes. There is no line cap. Use offset and limit for an exact line range; when output is budget-limited the footer names the exact offset to continue from. Every text response reports the file's byte size, line count, and whether output was truncated. A PNG, JPEG, GIF or WebP image comes back as image content you can see (large images are downscaled), including"},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of lines to read.\",\"type\":\"number\"},\"max_bytes\":{\"description\":\"Output budget in bytes for this one call. Defaults to 100000; values above the 500000 maximum are clamped down rather than rejected, and a value below the active default leaves the default in place.\",\"type\":\"number\"},\"offset\":{\"description\":\"Line number to start reading from (1-indexed).\",\"type\":\"number\"},\"path\":{\"description\":\"Path to the file to read (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"read"},"ordinal":7,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Replace the To-do list the user watches. For any task with three or more steps or more than one file, write the list before you start, keep exactly one item in_progress, and mark items done as you finish them. Skip it only for a single quick answer or edit."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"properties\":{\"todos\":{\"description\":\"The complete list of To-do items. This replaces the existing list.\",\"items\":{\"properties\":{\"content\":{\"description\":\"The task description\",\"type\":\"string\"},\"status\":{\"description\":\"Task status\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"required\":[\"todos\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"todo_write"},"ordinal":8,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":0,"omitted":0,"rendered":[]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Update the runtime goal completion gate by calling this tool; a prose status in your answer does not change the goal or stop continuation. Critical verification may seal one immutable completion contract. Advisory review is append-only context and never completes, blocks, or pauses the goal. Mark blocked when progress requires user input. Mark yield when you finished a stage and the next step needs the user's decision: the goal stays unfinished, the turn ends, and their next message resumes it."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":true,"value":"{\"additionalProperties\":false,\"properties\":{\"advisory\":{\"description\":\"Required when status is advisory. Appended separately from the judged completion contract.\",\"type\":\"string\"},\"blocker\":{\"description\":\"Required when status is blocked. Explain the condition preventing progress.\",\"type\":\"string\"},\"evidence\":{\"description\":\"Required when status is complete. Briefly cite the proof that the goal is done.\",\"type\":\"string\"},\"progress\":{\"additionalProperties\":false,\"description\":\"Optional with not_achieved or advisory: your current best estimate of overall completion, shown to the user as reported progress. Keep percent honest — it is an estimate, never a verified fraction.\",\"properties\":{\"next\":{\"description\":\"One short line: what comes next.\",\"type\":\"string\"},\"now\":{\"description\":\"One short line: what is being worked on right now.\",\"type\":\"string\"},\"percent\":{\"description\":\"Estimated percent complete, 0-100.\",\"maximum\":100,\"minimum\":0,\"type\":\"integer\"}},\"required\":[\"percent\"],\"type\":\"object\"},\"status\":{\"description\":\"Use complete only when a critical verifier proves the goal; not_achieved to record verifier gaps; blocked when meaningful progress cannot continue; yield when a stage is finished and the next step is the user's call; advisory to append best-effort context without changing lifecycle state.\",\"enum\":[\"complete\",\"blocked\",\"not_achieved\",\"advisory\",\"yield\"],\"type\":\"string\"},\"verification\":{\"additionalProperties\":false,\"description\":\"Required when status is complete or not_achieved. A verifier-as-judge receipt from a concrete check, such as Run action=\\\"verifiers\\\" or an equivalent project-specific gate.\",\"properties\":{\"check\":{\"description\":\"The verifier/check that passed.\",\"type\":\"string\"},\"gaps\":{\"description\":\"Concrete remaining gaps. Required for critical not_achieved reviews; order and duplicate wording do not affect the stall fingerprint.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"role\":{\"description\":\"Critical reviews may satisfy the judged completion contract. Advisory reviews are fail-open and c"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"update_goal"},"ordinal":9,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Suggest"}},"cache_control_type":{"status":"known","value":{"truncated":false,"value":"ephemeral"}},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Write content to a file. Creates the file if it does not exist, overwrites it if it does, and creates parent directories automatically."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Content to write to the file.\",\"type\":\"string\"},\"path\":{\"description\":\"Path to the file to write (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"content\",\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"write"},"ordinal":10,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"reason":"tool is not in the registry","status":"unknown"},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"reason":"tool is not in the registry","status":"unknown"},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Run a JavaScript program that composes tool calls with `await tools.call(name, args)` and returns a bounded JSON result. Prefer it whenever you would make several dependent or repetitive calls, MCP and plugin tools included: intermediate results stay in the program and only what you return reaches the conversation. Every nested call passes the same permission checks as a direct call; a call that needs approval pauses the program until the user decides, and a denied or refused call throws inside the program "},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"type\":\"object\",\"properties\":{\"code\":{\"type\":\"string\",\"description\":\"JavaScript program. The return value (or thrown error) becomes the result; use tools.call(name, argsObject) for tool calls.\"}},\"required\":[\"code\"]}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"reason":"tool is not in the registry","status":"unknown"},"name":{"truncated":false,"value":"execute_tools"},"ordinal":11,"provenance":{"status":"known","value":"synthetic"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"status":"known","value":{"truncated":false,"value":"execute_tools_20260918"}},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"reason":"tool is not in the registry","status":"unknown"},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"reason":"tool is not in the registry","status":"unknown"},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Search deferred tool definitions and return matching tool references."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\",\"description\":\"Search query for tool discovery.\"},\"match\":{\"type\":\"string\",\"enum\":[\"bm25\",\"regex\"],\"default\":\"bm25\",\"description\":\"Matching algorithm: bm25 for natural-language matching, regex for a regular expression over tool names/descriptions/schema.\"},\"max_results\":{\"type\":\"integer\",\"minimum\":1,\"maximum\":8,\"default\":8,\"description\":\"Maximum number of matching tool references to return.\"}},\"required\":[\"query\"]}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"reason":"tool is not in the registry","status":"unknown"},"name":{"truncated":false,"value":"tool_search"},"ordinal":12,"provenance":{"status":"known","value":"synthetic"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"status":"known","value":{"truncated":false,"value":"tool_search_20251119"}},"visibility":"active"}],"tools_field_present":true,"turn_id":{"truncated":false,"value":""},"unavailable_for_this_request":["provider_wire_payload"]}} +{"changed":false,"context_updates":0,"description":"frozen: bcbbcfbebef5","event":"prefix_cache_change","last_miss_reason":"","pin_reason":"initial","pinned_combined_hash":"","stability_pct":100,"system_prompt_changed":false,"tools_changed":false} +{"event":"tool_request_snapshot","snapshot":{"active_tool_catalog_sha256":"ba34408b0c03b810ba1ef6190c41d215f58e88822148991099b7d70d444992fc","capture_source":"prepared model-client request","delivery_status":"unknown (capture does not prove provider delivery)","omitted_tool_count":0,"payload_json_bytes":14897,"payload_measurement_status":"exact (within 1048576-byte measurement bound)","provider":{"model":"deepseek-v4-pro","provider":"Deepseek","status":"available"},"registry_facts_present":true,"registry_only_tools":{"status":"known","value":{"count":40,"omitted":8,"rendered":[{"truncated":false,"value":"Git"},{"truncated":false,"value":"Run"},{"truncated":false,"value":"Web"},{"truncated":false,"value":"apply_patch"},{"truncated":false,"value":"automation"},{"truncated":false,"value":"diagnostics"},{"truncated":false,"value":"finance"},{"truncated":false,"value":"github"},{"truncated":false,"value":"grep_files"},{"truncated":false,"value":"handle_read"},{"truncated":false,"value":"harness"},{"truncated":false,"value":"image_ocr"},{"truncated":false,"value":"list_dir"},{"truncated":false,"value":"lsp"},{"truncated":false,"value":"note"},{"truncated":false,"value":"notify"},{"truncated":false,"value":"pandoc_convert"},{"truncated":false,"value":"project_map"},{"truncated":false,"value":"registry_sync"},{"truncated":false,"value":"request_plugin_install"},{"truncated":false,"value":"request_user_input"},{"truncated":false,"value":"retrieve_tool_result"},{"truncated":false,"value":"revert_turn"},{"truncated":false,"value":"review"},{"truncated":false,"value":"send_later"},{"truncated":false,"value":"session_get"},{"truncated":false,"value":"session_search"},{"truncated":false,"value":"start_mcp_server"},{"truncated":false,"value":"start_registry_mcp_server"},{"truncated":false,"value":"task_shell_wait"},{"truncated":false,"value":"tasks"},{"truncated":false,"value":"terminal/cancel"}]}},"registry_tool_count":{"status":"known","value":62},"rendered_tool_count":12,"schema_version":1,"step":0,"tool_count":12,"tools":[{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Required"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"ExecutesCode"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Execute a shell command in the workspace and return stdout and stderr. Output keeps the last 2000 lines or 50KB. An optional timeout is the seconds to wait in the foreground (default 30 seconds); a command still running then is never killed for time: it moves to the background by itself and you get its output so far, how long ago it last printed, its build phase, and a task_id, so long builds and tests need no special timeout. Find task_shell_wait with tool_search to read more or wait for it. In Ask, after "},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The command to execute. Actual execution shell: `/bin/bash`. Use Bash syntax: pipelines, redirections, $(command), and && / || are supported. Quote paths and variable expansions, such as \\\"$path\\\"; use single quotes for literal text. For literal multiline input, use a quoted heredoc delimiter (<<'EOF') with its closing delimiter on a separate line. Use only installed programs; do not assume GNU-specific flags on macOS/BSD. Example: printf '%s\\\\n' 'sample'.\",\"type\":\"string\"},\"justification\":{\"description\":\"Required with sandbox_permissions: one sentence explaining why this exact command needs wider access.\",\"type\":\"string\"},\"read_only\":{\"description\":\"Set true to run analysis code (including Python/SQLite) with mandatory native filesystem read-only isolation and no network. Available during peer writes. Refused when native enforcement is unavailable; no background, stdin, external backend, or sandbox escalation.\",\"type\":\"boolean\"},\"sandbox_permissions\":{\"description\":\"The wider sandbox mode this exact command needs. Use only as a one-shot retry after a sandbox denial; requires justification and user approval in Ask.\",\"enum\":[\"workspace-write\",\"danger-full-access\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional seconds to wait in the foreground (default 30 seconds). A command still running then is not killed: it moves to the background by itself and you get its output so far, how long ago it last printed, its build phase, and a task_id. Find task_shell_wait with tool_search to read more or wait for it.\",\"type\":\"number\"}},\"required\":[\"command\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"bash"},"ordinal":1,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":0,"omitted":0,"rendered":[]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Create the session's one persistent goal: a completion objective Codewhale keeps working toward across turns until it is verified complete, blocked, or the user stops it. A goal is for a durable objective that outlasts one turn: a multi-step outcome the user wants continued and verified. A question, a greeting, or a one-shot edit completes as an ordinary turn. When the user explicitly asks to use `/goal` or to make something the goal, `create_goal` is what records it; acknowledging it in prose records nothi"},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"objective\":{\"description\":\"The full objective to pursue. Keep the complete user goal, not a shortened one-turn version.\",\"type\":\"string\"},\"token_budget\":{\"description\":\"Optional soft token budget for the goal.\",\"minimum\":0,\"type\":\"integer\"}},\"required\":[\"objective\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"create_goal"},"ordinal":2,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Suggest"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Edit one file with targeted text replacements. Every edits[].oldText must identify a unique, non-overlapping region of the original file. Merge nearby or overlapping changes into one edit."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"edits\":{\"description\":\"One or more disjoint replacements, all matched against the original file.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"newText\":{\"description\":\"Replacement text; may be empty to delete the matched span.\",\"type\":\"string\"},\"oldText\":{\"description\":\"Text identifying one unique region to replace.\",\"type\":\"string\"}},\"required\":[\"newText\",\"oldText\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to the file to edit (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"edits\",\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"edit"},"ordinal":3,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":2,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"},{"truncated":false,"value":"Sandboxable"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Find workspace files by name, path, or glob (e.g. src/**/*.rs). Space-separated terms must all match. Literal matches rank above fuzzy matches; results are capped. Respects ignore files and skips build/dependency directories by default. Use extensions to filter suffixes."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"properties\":{\"exclude\":{\"description\":\"Optional replacement exclusion globs (e.g. [\\\"**/target/**\\\", \\\"*.lock\\\"]); [] disables default exclusions.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"extensions\":{\"description\":\"Optional list of file extensions to include (e.g. [\\\"rs\\\", \\\"md\\\"]).\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"limit\":{\"description\":\"Maximum number of ranked results to return (default: 20, maximum: 200).\",\"type\":\"integer\"},\"path\":{\"description\":\"Optional base path to search (relative to workspace).\",\"type\":\"string\"},\"query\":{\"description\":\"File name, path fragment, space-separated terms, or glob (e.g. **/engine*.rs).\",\"type\":\"string\"}},\"required\":[\"query\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"file_search"},"ordinal":4,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Inspect the current runtime goal state, including objective, status, token budget, elapsed time, evidence, and blocker."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{},\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"get_goal"},"ordinal":5,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Load a named skill's SKILL.md body and companion file list into this turn. Use when the user names a skill, or when an entry in the system prompt's `## Skills` index matches the task -- load it before starting the work, not after. Pass query=\"...\" to search names and descriptions, or name=\"list\" for the whole catalogue. Resolves global and plugin skills that `read` cannot reach."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"name\":{\"description\":\"Skill id to load. Omit or pass \\\"list\\\" to see all available skills.\",\"type\":\"string\"},\"query\":{\"description\":\"Search term matched against skill names and descriptions. Use when the index was truncated or no name is known.\",\"type\":\"string\"}},\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"load_skill"},"ordinal":6,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":2,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"},{"truncated":false,"value":"Sandboxable"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Read a file. A text file comes back whole in one call when it fits this call's output budget — 100000 bytes by default, raisable to 500000 with max_bytes. There is no line cap. Use offset and limit for an exact line range; when output is budget-limited the footer names the exact offset to continue from. Every text response reports the file's byte size, line count, and whether output was truncated. A PNG, JPEG, GIF or WebP image comes back as image content you can see (large images are downscaled), including"},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of lines to read.\",\"type\":\"number\"},\"max_bytes\":{\"description\":\"Output budget in bytes for this one call. Defaults to 100000; values above the 500000 maximum are clamped down rather than rejected, and a value below the active default leaves the default in place.\",\"type\":\"number\"},\"offset\":{\"description\":\"Line number to start reading from (1-indexed).\",\"type\":\"number\"},\"path\":{\"description\":\"Path to the file to read (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"read"},"ordinal":7,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Replace the To-do list the user watches. For any task with three or more steps or more than one file, write the list before you start, keep exactly one item in_progress, and mark items done as you finish them. Skip it only for a single quick answer or edit."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"properties\":{\"todos\":{\"description\":\"The complete list of To-do items. This replaces the existing list.\",\"items\":{\"properties\":{\"content\":{\"description\":\"The task description\",\"type\":\"string\"},\"status\":{\"description\":\"Task status\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"required\":[\"todos\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"todo_write"},"ordinal":8,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":0,"omitted":0,"rendered":[]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Update the runtime goal completion gate by calling this tool; a prose status in your answer does not change the goal or stop continuation. Critical verification may seal one immutable completion contract. Advisory review is append-only context and never completes, blocks, or pauses the goal. Mark blocked when progress requires user input. Mark yield when you finished a stage and the next step needs the user's decision: the goal stays unfinished, the turn ends, and their next message resumes it."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":true,"value":"{\"additionalProperties\":false,\"properties\":{\"advisory\":{\"description\":\"Required when status is advisory. Appended separately from the judged completion contract.\",\"type\":\"string\"},\"blocker\":{\"description\":\"Required when status is blocked. Explain the condition preventing progress.\",\"type\":\"string\"},\"evidence\":{\"description\":\"Required when status is complete. Briefly cite the proof that the goal is done.\",\"type\":\"string\"},\"progress\":{\"additionalProperties\":false,\"description\":\"Optional with not_achieved, advisory or yield: your current best estimate of overall completion, shown to the user as reported progress. Keep percent honest — it is an estimate, never a verified fraction.\",\"properties\":{\"next\":{\"description\":\"One short line: what comes next.\",\"type\":\"string\"},\"now\":{\"description\":\"One short line: what is being worked on right now.\",\"type\":\"string\"},\"percent\":{\"description\":\"Estimated percent complete, 0-100.\",\"maximum\":100,\"minimum\":0,\"type\":\"integer\"}},\"required\":[\"percent\"],\"type\":\"object\"},\"status\":{\"description\":\"Use complete only when a critical verifier proves the goal; not_achieved to record verifier gaps; blocked when meaningful progress cannot continue; yield when a stage is finished and the next step is the user's call; advisory to append best-effort context without changing lifecycle state.\",\"enum\":[\"complete\",\"blocked\",\"not_achieved\",\"advisory\",\"yield\"],\"type\":\"string\"},\"verification\":{\"additionalProperties\":false,\"description\":\"Required when status is complete or not_achieved. A verifier-as-judge receipt from a concrete check, such as Run action=\\\"verifiers\\\" or an equivalent project-specific gate.\",\"properties\":{\"check\":{\"description\":\"The verifier/check that passed.\",\"type\":\"string\"},\"gaps\":{\"description\":\"Concrete remaining gaps. Required for critical not_achieved reviews; order and duplicate wording do not affect the stall fingerprint.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"role\":{\"description\":\"Critical reviews may satisfy the judged completion contract. Advisory reviews are fail-ope"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"update_goal"},"ordinal":9,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Suggest"}},"cache_control_type":{"status":"known","value":{"truncated":false,"value":"ephemeral"}},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Write content to a file. Creates the file if it does not exist, overwrites it if it does, and creates parent directories automatically."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Content to write to the file.\",\"type\":\"string\"},\"path\":{\"description\":\"Path to the file to write (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"content\",\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"write"},"ordinal":10,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"reason":"tool is not in the registry","status":"unknown"},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"reason":"tool is not in the registry","status":"unknown"},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Run a JavaScript program that composes tool calls with `await tools.call(name, args)` and returns a bounded JSON result. Prefer it whenever you would make several dependent or repetitive calls, MCP and plugin tools included: intermediate results stay in the program and only what you return reaches the conversation. Every nested call passes the same permission checks as a direct call; a call that needs approval pauses the program until the user decides, and a denied or refused call throws inside the program "},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"type\":\"object\",\"properties\":{\"code\":{\"type\":\"string\",\"description\":\"JavaScript program. The return value (or thrown error) becomes the result; use tools.call(name, argsObject) for tool calls.\"}},\"required\":[\"code\"]}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"reason":"tool is not in the registry","status":"unknown"},"name":{"truncated":false,"value":"execute_tools"},"ordinal":11,"provenance":{"status":"known","value":"synthetic"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"status":"known","value":{"truncated":false,"value":"execute_tools_20260918"}},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"reason":"tool is not in the registry","status":"unknown"},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"reason":"tool is not in the registry","status":"unknown"},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Search deferred tool definitions and return matching tool references."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\",\"description\":\"Search query for tool discovery.\"},\"match\":{\"type\":\"string\",\"enum\":[\"bm25\",\"regex\"],\"default\":\"bm25\",\"description\":\"Matching algorithm: bm25 for natural-language matching, regex for a regular expression over tool names/descriptions/schema.\"},\"max_results\":{\"type\":\"integer\",\"minimum\":1,\"maximum\":8,\"default\":8,\"description\":\"Maximum number of matching tool references to return.\"}},\"required\":[\"query\"]}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"reason":"tool is not in the registry","status":"unknown"},"name":{"truncated":false,"value":"tool_search"},"ordinal":12,"provenance":{"status":"known","value":"synthetic"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"status":"known","value":{"truncated":false,"value":"tool_search_20251119"}},"visibility":"active"}],"tools_field_present":true,"turn_id":{"truncated":false,"value":""},"unavailable_for_this_request":["provider_wire_payload"]}} {"event":"route_dispatched","route":{"auto_model":false,"base_url":"https://api.deepseek.com/beta","billing":{"billing_mode":"metered","dispatched_at":""},"billing_product":{"kind":"unproven"},"model":"deepseek-v4-pro","provider":"deepseek","provider_identity":"deepseek"},"turn_id":""} {"duration_ms":"","event":"turn_usage","first_token_ms":"","request_ms":"","usage":{"input_tokens":150,"output_tokens":15}} {"event":"tool_call_started","input":{"command":"touch denied.txt"},"tool_call_id":"","tool_name":"Bash"} @@ -12,12 +12,12 @@ {"engine_session_id":"conformance-session","event":"session_updated","messages":[{"content":[{"text":"Create denied.txt with touch.","type":"text"},{"text":"\nCurrent local date: \nCurrent workspace: \nCurrent permission posture: Ask\nCurrent sandbox posture: workspace-write (writes inside the workspace; network blocked) (local OS sandbox applied)\n## Repo Working Set\nKey files: README.md\nWhen in doubt, use tools to verify and keep changes focused on the working set.\n","type":"text"}],"role":"user"},{"content":[{"execution_id":"","id":"call_touch","input":{"command":"touch denied.txt"},"name":"Bash","type":"tool_use"}],"role":"assistant"},{"content":[{"content":"Error: Tool 'Bash' denied by user — the call was not approved. Do not retry the same call; present what you intended and wait for the user's approval or new instructions.","execution_id":"","is_error":true,"tool_use_id":"call_touch","type":"tool_result"}],"role":"user"}],"model":"deepseek-v4-pro","system_prompt":"","workspace":""} {"event":"status","message":"Continuing — tool results"} {"changed":false,"context_updates":0,"description":"","event":"prefix_cache_change","last_miss_reason":"","pin_reason":"initial","pinned_combined_hash":"","stability_pct":100,"system_prompt_changed":false,"tools_changed":false} -{"event":"tool_request_snapshot","snapshot":{"active_tool_catalog_sha256":"18c6d652f11c0c195e7b586852ab46b47987939aaa316b39ef8b692255a7e077","capture_source":"prepared model-client request","delivery_status":"unknown (capture does not prove provider delivery)","omitted_tool_count":0,"payload_json_bytes":14890,"payload_measurement_status":"exact (within 1048576-byte measurement bound)","provider":{"model":"deepseek-v4-pro","provider":"Deepseek","status":"available"},"registry_facts_present":true,"registry_only_tools":{"status":"known","value":{"count":40,"omitted":8,"rendered":[{"truncated":false,"value":"Git"},{"truncated":false,"value":"Run"},{"truncated":false,"value":"Web"},{"truncated":false,"value":"apply_patch"},{"truncated":false,"value":"automation"},{"truncated":false,"value":"diagnostics"},{"truncated":false,"value":"finance"},{"truncated":false,"value":"github"},{"truncated":false,"value":"grep_files"},{"truncated":false,"value":"handle_read"},{"truncated":false,"value":"harness"},{"truncated":false,"value":"image_ocr"},{"truncated":false,"value":"list_dir"},{"truncated":false,"value":"lsp"},{"truncated":false,"value":"note"},{"truncated":false,"value":"notify"},{"truncated":false,"value":"pandoc_convert"},{"truncated":false,"value":"project_map"},{"truncated":false,"value":"registry_sync"},{"truncated":false,"value":"request_plugin_install"},{"truncated":false,"value":"request_user_input"},{"truncated":false,"value":"retrieve_tool_result"},{"truncated":false,"value":"revert_turn"},{"truncated":false,"value":"review"},{"truncated":false,"value":"send_later"},{"truncated":false,"value":"session_get"},{"truncated":false,"value":"session_search"},{"truncated":false,"value":"start_mcp_server"},{"truncated":false,"value":"start_registry_mcp_server"},{"truncated":false,"value":"task_shell_wait"},{"truncated":false,"value":"tasks"},{"truncated":false,"value":"terminal/cancel"}]}},"registry_tool_count":{"status":"known","value":62},"rendered_tool_count":12,"schema_version":1,"step":1,"tool_count":12,"tools":[{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Required"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"ExecutesCode"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Execute a shell command in the workspace and return stdout and stderr. Output keeps the last 2000 lines or 50KB. An optional timeout is the seconds to wait in the foreground (default 30 seconds); a command still running then is never killed for time: it moves to the background by itself and you get its output so far, how long ago it last printed, its build phase, and a task_id, so long builds and tests need no special timeout. Find task_shell_wait with tool_search to read more or wait for it. In Ask, after "},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The command to execute. Actual execution shell: `/bin/bash`. Use Bash syntax: pipelines, redirections, $(command), and && / || are supported. Quote paths and variable expansions, such as \\\"$path\\\"; use single quotes for literal text. For literal multiline input, use a quoted heredoc delimiter (<<'EOF') with its closing delimiter on a separate line. Use only installed programs; do not assume GNU-specific flags on macOS/BSD. Example: printf '%s\\\\n' 'sample'.\",\"type\":\"string\"},\"justification\":{\"description\":\"Required with sandbox_permissions: one sentence explaining why this exact command needs wider access.\",\"type\":\"string\"},\"read_only\":{\"description\":\"Set true to run analysis code (including Python/SQLite) with mandatory native filesystem read-only isolation and no network. Available during peer writes. Refused when native enforcement is unavailable; no background, stdin, external backend, or sandbox escalation.\",\"type\":\"boolean\"},\"sandbox_permissions\":{\"description\":\"The wider sandbox mode this exact command needs. Use only as a one-shot retry after a sandbox denial; requires justification and user approval in Ask.\",\"enum\":[\"workspace-write\",\"danger-full-access\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional seconds to wait in the foreground (default 30 seconds). A command still running then is not killed: it moves to the background by itself and you get its output so far, how long ago it last printed, its build phase, and a task_id. Find task_shell_wait with tool_search to read more or wait for it.\",\"type\":\"number\"}},\"required\":[\"command\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"bash"},"ordinal":1,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":0,"omitted":0,"rendered":[]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Create the session's one persistent goal: a completion objective Codewhale keeps working toward across turns until it is verified complete, blocked, or the user stops it. A goal is for a durable objective that outlasts one turn: a multi-step outcome the user wants continued and verified. A question, a greeting, or a one-shot edit completes as an ordinary turn. When the user explicitly asks to use `/goal` or to make something the goal, `create_goal` is what records it; acknowledging it in prose records nothi"},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"objective\":{\"description\":\"The full objective to pursue. Keep the complete user goal, not a shortened one-turn version.\",\"type\":\"string\"},\"token_budget\":{\"description\":\"Optional soft token budget for the goal.\",\"minimum\":0,\"type\":\"integer\"}},\"required\":[\"objective\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"create_goal"},"ordinal":2,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Suggest"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Edit one file with targeted text replacements. Every edits[].oldText must identify a unique, non-overlapping region of the original file. Merge nearby or overlapping changes into one edit."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"edits\":{\"description\":\"One or more disjoint replacements, all matched against the original file.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"newText\":{\"description\":\"Replacement text; may be empty to delete the matched span.\",\"type\":\"string\"},\"oldText\":{\"description\":\"Text identifying one unique region to replace.\",\"type\":\"string\"}},\"required\":[\"newText\",\"oldText\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to the file to edit (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"edits\",\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"edit"},"ordinal":3,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":2,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"},{"truncated":false,"value":"Sandboxable"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Find workspace files by name, path, or glob (e.g. src/**/*.rs). Space-separated terms must all match. Literal matches rank above fuzzy matches; results are capped. Respects ignore files and skips build/dependency directories by default. Use extensions to filter suffixes."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"properties\":{\"exclude\":{\"description\":\"Optional replacement exclusion globs (e.g. [\\\"**/target/**\\\", \\\"*.lock\\\"]); [] disables default exclusions.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"extensions\":{\"description\":\"Optional list of file extensions to include (e.g. [\\\"rs\\\", \\\"md\\\"]).\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"limit\":{\"description\":\"Maximum number of ranked results to return (default: 20, maximum: 200).\",\"type\":\"integer\"},\"path\":{\"description\":\"Optional base path to search (relative to workspace).\",\"type\":\"string\"},\"query\":{\"description\":\"File name, path fragment, space-separated terms, or glob (e.g. **/engine*.rs).\",\"type\":\"string\"}},\"required\":[\"query\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"file_search"},"ordinal":4,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Inspect the current runtime goal state, including objective, status, token budget, elapsed time, evidence, and blocker."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{},\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"get_goal"},"ordinal":5,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Load a named skill's SKILL.md body and companion file list into this turn. Use when the user names a skill, or when an entry in the system prompt's `## Skills` index matches the task -- load it before starting the work, not after. Pass query=\"...\" to search names and descriptions, or name=\"list\" for the whole catalogue. Resolves global and plugin skills that `read` cannot reach."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"name\":{\"description\":\"Skill id to load. Omit or pass \\\"list\\\" to see all available skills.\",\"type\":\"string\"},\"query\":{\"description\":\"Search term matched against skill names and descriptions. Use when the index was truncated or no name is known.\",\"type\":\"string\"}},\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"load_skill"},"ordinal":6,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":2,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"},{"truncated":false,"value":"Sandboxable"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Read a file. A text file comes back whole in one call when it fits this call's output budget — 100000 bytes by default, raisable to 500000 with max_bytes. There is no line cap. Use offset and limit for an exact line range; when output is budget-limited the footer names the exact offset to continue from. Every text response reports the file's byte size, line count, and whether output was truncated. A PNG, JPEG, GIF or WebP image comes back as image content you can see (large images are downscaled), including"},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of lines to read.\",\"type\":\"number\"},\"max_bytes\":{\"description\":\"Output budget in bytes for this one call. Defaults to 100000; values above the 500000 maximum are clamped down rather than rejected, and a value below the active default leaves the default in place.\",\"type\":\"number\"},\"offset\":{\"description\":\"Line number to start reading from (1-indexed).\",\"type\":\"number\"},\"path\":{\"description\":\"Path to the file to read (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"read"},"ordinal":7,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Replace the To-do list the user watches. For any task with three or more steps or more than one file, write the list before you start, keep exactly one item in_progress, and mark items done as you finish them. Skip it only for a single quick answer or edit."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"properties\":{\"todos\":{\"description\":\"The complete list of To-do items. This replaces the existing list.\",\"items\":{\"properties\":{\"content\":{\"description\":\"The task description\",\"type\":\"string\"},\"status\":{\"description\":\"Task status\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"required\":[\"todos\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"todo_write"},"ordinal":8,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":0,"omitted":0,"rendered":[]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Update the runtime goal completion gate by calling this tool; a prose status in your answer does not change the goal or stop continuation. Critical verification may seal one immutable completion contract. Advisory review is append-only context and never completes, blocks, or pauses the goal. Mark blocked when progress requires user input. Mark yield when you finished a stage and the next step needs the user's decision: the goal stays unfinished, the turn ends, and their next message resumes it."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":true,"value":"{\"additionalProperties\":false,\"properties\":{\"advisory\":{\"description\":\"Required when status is advisory. Appended separately from the judged completion contract.\",\"type\":\"string\"},\"blocker\":{\"description\":\"Required when status is blocked. Explain the condition preventing progress.\",\"type\":\"string\"},\"evidence\":{\"description\":\"Required when status is complete. Briefly cite the proof that the goal is done.\",\"type\":\"string\"},\"progress\":{\"additionalProperties\":false,\"description\":\"Optional with not_achieved or advisory: your current best estimate of overall completion, shown to the user as reported progress. Keep percent honest — it is an estimate, never a verified fraction.\",\"properties\":{\"next\":{\"description\":\"One short line: what comes next.\",\"type\":\"string\"},\"now\":{\"description\":\"One short line: what is being worked on right now.\",\"type\":\"string\"},\"percent\":{\"description\":\"Estimated percent complete, 0-100.\",\"maximum\":100,\"minimum\":0,\"type\":\"integer\"}},\"required\":[\"percent\"],\"type\":\"object\"},\"status\":{\"description\":\"Use complete only when a critical verifier proves the goal; not_achieved to record verifier gaps; blocked when meaningful progress cannot continue; yield when a stage is finished and the next step is the user's call; advisory to append best-effort context without changing lifecycle state.\",\"enum\":[\"complete\",\"blocked\",\"not_achieved\",\"advisory\",\"yield\"],\"type\":\"string\"},\"verification\":{\"additionalProperties\":false,\"description\":\"Required when status is complete or not_achieved. A verifier-as-judge receipt from a concrete check, such as Run action=\\\"verifiers\\\" or an equivalent project-specific gate.\",\"properties\":{\"check\":{\"description\":\"The verifier/check that passed.\",\"type\":\"string\"},\"gaps\":{\"description\":\"Concrete remaining gaps. Required for critical not_achieved reviews; order and duplicate wording do not affect the stall fingerprint.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"role\":{\"description\":\"Critical reviews may satisfy the judged completion contract. Advisory reviews are fail-open and c"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"update_goal"},"ordinal":9,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Suggest"}},"cache_control_type":{"status":"known","value":{"truncated":false,"value":"ephemeral"}},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Write content to a file. Creates the file if it does not exist, overwrites it if it does, and creates parent directories automatically."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Content to write to the file.\",\"type\":\"string\"},\"path\":{\"description\":\"Path to the file to write (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"content\",\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"write"},"ordinal":10,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"reason":"tool is not in the registry","status":"unknown"},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"reason":"tool is not in the registry","status":"unknown"},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Run a JavaScript program that composes tool calls with `await tools.call(name, args)` and returns a bounded JSON result. Prefer it whenever you would make several dependent or repetitive calls, MCP and plugin tools included: intermediate results stay in the program and only what you return reaches the conversation. Every nested call passes the same permission checks as a direct call; a call that needs approval pauses the program until the user decides, and a denied or refused call throws inside the program "},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"type\":\"object\",\"properties\":{\"code\":{\"type\":\"string\",\"description\":\"JavaScript program. The return value (or thrown error) becomes the result; use tools.call(name, argsObject) for tool calls.\"}},\"required\":[\"code\"]}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"reason":"tool is not in the registry","status":"unknown"},"name":{"truncated":false,"value":"execute_tools"},"ordinal":11,"provenance":{"status":"known","value":"synthetic"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"status":"known","value":{"truncated":false,"value":"execute_tools_20260918"}},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"reason":"tool is not in the registry","status":"unknown"},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"reason":"tool is not in the registry","status":"unknown"},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Search deferred tool definitions and return matching tool references."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\",\"description\":\"Search query for tool discovery.\"},\"match\":{\"type\":\"string\",\"enum\":[\"bm25\",\"regex\"],\"default\":\"bm25\",\"description\":\"Matching algorithm: bm25 for natural-language matching, regex for a regular expression over tool names/descriptions/schema.\"},\"max_results\":{\"type\":\"integer\",\"minimum\":1,\"maximum\":8,\"default\":8,\"description\":\"Maximum number of matching tool references to return.\"}},\"required\":[\"query\"]}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"reason":"tool is not in the registry","status":"unknown"},"name":{"truncated":false,"value":"tool_search"},"ordinal":12,"provenance":{"status":"known","value":"synthetic"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"status":"known","value":{"truncated":false,"value":"tool_search_20251119"}},"visibility":"active"}],"tools_field_present":true,"turn_id":{"truncated":false,"value":""},"unavailable_for_this_request":["provider_wire_payload"]}} +{"event":"tool_request_snapshot","snapshot":{"active_tool_catalog_sha256":"ba34408b0c03b810ba1ef6190c41d215f58e88822148991099b7d70d444992fc","capture_source":"prepared model-client request","delivery_status":"unknown (capture does not prove provider delivery)","omitted_tool_count":0,"payload_json_bytes":14897,"payload_measurement_status":"exact (within 1048576-byte measurement bound)","provider":{"model":"deepseek-v4-pro","provider":"Deepseek","status":"available"},"registry_facts_present":true,"registry_only_tools":{"status":"known","value":{"count":40,"omitted":8,"rendered":[{"truncated":false,"value":"Git"},{"truncated":false,"value":"Run"},{"truncated":false,"value":"Web"},{"truncated":false,"value":"apply_patch"},{"truncated":false,"value":"automation"},{"truncated":false,"value":"diagnostics"},{"truncated":false,"value":"finance"},{"truncated":false,"value":"github"},{"truncated":false,"value":"grep_files"},{"truncated":false,"value":"handle_read"},{"truncated":false,"value":"harness"},{"truncated":false,"value":"image_ocr"},{"truncated":false,"value":"list_dir"},{"truncated":false,"value":"lsp"},{"truncated":false,"value":"note"},{"truncated":false,"value":"notify"},{"truncated":false,"value":"pandoc_convert"},{"truncated":false,"value":"project_map"},{"truncated":false,"value":"registry_sync"},{"truncated":false,"value":"request_plugin_install"},{"truncated":false,"value":"request_user_input"},{"truncated":false,"value":"retrieve_tool_result"},{"truncated":false,"value":"revert_turn"},{"truncated":false,"value":"review"},{"truncated":false,"value":"send_later"},{"truncated":false,"value":"session_get"},{"truncated":false,"value":"session_search"},{"truncated":false,"value":"start_mcp_server"},{"truncated":false,"value":"start_registry_mcp_server"},{"truncated":false,"value":"task_shell_wait"},{"truncated":false,"value":"tasks"},{"truncated":false,"value":"terminal/cancel"}]}},"registry_tool_count":{"status":"known","value":62},"rendered_tool_count":12,"schema_version":1,"step":1,"tool_count":12,"tools":[{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Required"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"ExecutesCode"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Execute a shell command in the workspace and return stdout and stderr. Output keeps the last 2000 lines or 50KB. An optional timeout is the seconds to wait in the foreground (default 30 seconds); a command still running then is never killed for time: it moves to the background by itself and you get its output so far, how long ago it last printed, its build phase, and a task_id, so long builds and tests need no special timeout. Find task_shell_wait with tool_search to read more or wait for it. In Ask, after "},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The command to execute. Actual execution shell: `/bin/bash`. Use Bash syntax: pipelines, redirections, $(command), and && / || are supported. Quote paths and variable expansions, such as \\\"$path\\\"; use single quotes for literal text. For literal multiline input, use a quoted heredoc delimiter (<<'EOF') with its closing delimiter on a separate line. Use only installed programs; do not assume GNU-specific flags on macOS/BSD. Example: printf '%s\\\\n' 'sample'.\",\"type\":\"string\"},\"justification\":{\"description\":\"Required with sandbox_permissions: one sentence explaining why this exact command needs wider access.\",\"type\":\"string\"},\"read_only\":{\"description\":\"Set true to run analysis code (including Python/SQLite) with mandatory native filesystem read-only isolation and no network. Available during peer writes. Refused when native enforcement is unavailable; no background, stdin, external backend, or sandbox escalation.\",\"type\":\"boolean\"},\"sandbox_permissions\":{\"description\":\"The wider sandbox mode this exact command needs. Use only as a one-shot retry after a sandbox denial; requires justification and user approval in Ask.\",\"enum\":[\"workspace-write\",\"danger-full-access\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional seconds to wait in the foreground (default 30 seconds). A command still running then is not killed: it moves to the background by itself and you get its output so far, how long ago it last printed, its build phase, and a task_id. Find task_shell_wait with tool_search to read more or wait for it.\",\"type\":\"number\"}},\"required\":[\"command\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"bash"},"ordinal":1,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":0,"omitted":0,"rendered":[]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Create the session's one persistent goal: a completion objective Codewhale keeps working toward across turns until it is verified complete, blocked, or the user stops it. A goal is for a durable objective that outlasts one turn: a multi-step outcome the user wants continued and verified. A question, a greeting, or a one-shot edit completes as an ordinary turn. When the user explicitly asks to use `/goal` or to make something the goal, `create_goal` is what records it; acknowledging it in prose records nothi"},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"objective\":{\"description\":\"The full objective to pursue. Keep the complete user goal, not a shortened one-turn version.\",\"type\":\"string\"},\"token_budget\":{\"description\":\"Optional soft token budget for the goal.\",\"minimum\":0,\"type\":\"integer\"}},\"required\":[\"objective\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"create_goal"},"ordinal":2,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Suggest"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Edit one file with targeted text replacements. Every edits[].oldText must identify a unique, non-overlapping region of the original file. Merge nearby or overlapping changes into one edit."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"edits\":{\"description\":\"One or more disjoint replacements, all matched against the original file.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"newText\":{\"description\":\"Replacement text; may be empty to delete the matched span.\",\"type\":\"string\"},\"oldText\":{\"description\":\"Text identifying one unique region to replace.\",\"type\":\"string\"}},\"required\":[\"newText\",\"oldText\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to the file to edit (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"edits\",\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"edit"},"ordinal":3,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":2,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"},{"truncated":false,"value":"Sandboxable"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Find workspace files by name, path, or glob (e.g. src/**/*.rs). Space-separated terms must all match. Literal matches rank above fuzzy matches; results are capped. Respects ignore files and skips build/dependency directories by default. Use extensions to filter suffixes."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"properties\":{\"exclude\":{\"description\":\"Optional replacement exclusion globs (e.g. [\\\"**/target/**\\\", \\\"*.lock\\\"]); [] disables default exclusions.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"extensions\":{\"description\":\"Optional list of file extensions to include (e.g. [\\\"rs\\\", \\\"md\\\"]).\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"limit\":{\"description\":\"Maximum number of ranked results to return (default: 20, maximum: 200).\",\"type\":\"integer\"},\"path\":{\"description\":\"Optional base path to search (relative to workspace).\",\"type\":\"string\"},\"query\":{\"description\":\"File name, path fragment, space-separated terms, or glob (e.g. **/engine*.rs).\",\"type\":\"string\"}},\"required\":[\"query\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"file_search"},"ordinal":4,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Inspect the current runtime goal state, including objective, status, token budget, elapsed time, evidence, and blocker."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{},\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"get_goal"},"ordinal":5,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Load a named skill's SKILL.md body and companion file list into this turn. Use when the user names a skill, or when an entry in the system prompt's `## Skills` index matches the task -- load it before starting the work, not after. Pass query=\"...\" to search names and descriptions, or name=\"list\" for the whole catalogue. Resolves global and plugin skills that `read` cannot reach."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"name\":{\"description\":\"Skill id to load. Omit or pass \\\"list\\\" to see all available skills.\",\"type\":\"string\"},\"query\":{\"description\":\"Search term matched against skill names and descriptions. Use when the index was truncated or no name is known.\",\"type\":\"string\"}},\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"load_skill"},"ordinal":6,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":2,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"},{"truncated":false,"value":"Sandboxable"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Read a file. A text file comes back whole in one call when it fits this call's output budget — 100000 bytes by default, raisable to 500000 with max_bytes. There is no line cap. Use offset and limit for an exact line range; when output is budget-limited the footer names the exact offset to continue from. Every text response reports the file's byte size, line count, and whether output was truncated. A PNG, JPEG, GIF or WebP image comes back as image content you can see (large images are downscaled), including"},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of lines to read.\",\"type\":\"number\"},\"max_bytes\":{\"description\":\"Output budget in bytes for this one call. Defaults to 100000; values above the 500000 maximum are clamped down rather than rejected, and a value below the active default leaves the default in place.\",\"type\":\"number\"},\"offset\":{\"description\":\"Line number to start reading from (1-indexed).\",\"type\":\"number\"},\"path\":{\"description\":\"Path to the file to read (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"read"},"ordinal":7,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Replace the To-do list the user watches. For any task with three or more steps or more than one file, write the list before you start, keep exactly one item in_progress, and mark items done as you finish them. Skip it only for a single quick answer or edit."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"properties\":{\"todos\":{\"description\":\"The complete list of To-do items. This replaces the existing list.\",\"items\":{\"properties\":{\"content\":{\"description\":\"The task description\",\"type\":\"string\"},\"status\":{\"description\":\"Task status\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"required\":[\"todos\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"todo_write"},"ordinal":8,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":0,"omitted":0,"rendered":[]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Update the runtime goal completion gate by calling this tool; a prose status in your answer does not change the goal or stop continuation. Critical verification may seal one immutable completion contract. Advisory review is append-only context and never completes, blocks, or pauses the goal. Mark blocked when progress requires user input. Mark yield when you finished a stage and the next step needs the user's decision: the goal stays unfinished, the turn ends, and their next message resumes it."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":true,"value":"{\"additionalProperties\":false,\"properties\":{\"advisory\":{\"description\":\"Required when status is advisory. Appended separately from the judged completion contract.\",\"type\":\"string\"},\"blocker\":{\"description\":\"Required when status is blocked. Explain the condition preventing progress.\",\"type\":\"string\"},\"evidence\":{\"description\":\"Required when status is complete. Briefly cite the proof that the goal is done.\",\"type\":\"string\"},\"progress\":{\"additionalProperties\":false,\"description\":\"Optional with not_achieved, advisory or yield: your current best estimate of overall completion, shown to the user as reported progress. Keep percent honest — it is an estimate, never a verified fraction.\",\"properties\":{\"next\":{\"description\":\"One short line: what comes next.\",\"type\":\"string\"},\"now\":{\"description\":\"One short line: what is being worked on right now.\",\"type\":\"string\"},\"percent\":{\"description\":\"Estimated percent complete, 0-100.\",\"maximum\":100,\"minimum\":0,\"type\":\"integer\"}},\"required\":[\"percent\"],\"type\":\"object\"},\"status\":{\"description\":\"Use complete only when a critical verifier proves the goal; not_achieved to record verifier gaps; blocked when meaningful progress cannot continue; yield when a stage is finished and the next step is the user's call; advisory to append best-effort context without changing lifecycle state.\",\"enum\":[\"complete\",\"blocked\",\"not_achieved\",\"advisory\",\"yield\"],\"type\":\"string\"},\"verification\":{\"additionalProperties\":false,\"description\":\"Required when status is complete or not_achieved. A verifier-as-judge receipt from a concrete check, such as Run action=\\\"verifiers\\\" or an equivalent project-specific gate.\",\"properties\":{\"check\":{\"description\":\"The verifier/check that passed.\",\"type\":\"string\"},\"gaps\":{\"description\":\"Concrete remaining gaps. Required for critical not_achieved reviews; order and duplicate wording do not affect the stall fingerprint.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"role\":{\"description\":\"Critical reviews may satisfy the judged completion contract. Advisory reviews are fail-ope"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"update_goal"},"ordinal":9,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Suggest"}},"cache_control_type":{"status":"known","value":{"truncated":false,"value":"ephemeral"}},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Write content to a file. Creates the file if it does not exist, overwrites it if it does, and creates parent directories automatically."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Content to write to the file.\",\"type\":\"string\"},\"path\":{\"description\":\"Path to the file to write (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"content\",\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"write"},"ordinal":10,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"reason":"tool is not in the registry","status":"unknown"},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"reason":"tool is not in the registry","status":"unknown"},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Run a JavaScript program that composes tool calls with `await tools.call(name, args)` and returns a bounded JSON result. Prefer it whenever you would make several dependent or repetitive calls, MCP and plugin tools included: intermediate results stay in the program and only what you return reaches the conversation. Every nested call passes the same permission checks as a direct call; a call that needs approval pauses the program until the user decides, and a denied or refused call throws inside the program "},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"type\":\"object\",\"properties\":{\"code\":{\"type\":\"string\",\"description\":\"JavaScript program. The return value (or thrown error) becomes the result; use tools.call(name, argsObject) for tool calls.\"}},\"required\":[\"code\"]}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"reason":"tool is not in the registry","status":"unknown"},"name":{"truncated":false,"value":"execute_tools"},"ordinal":11,"provenance":{"status":"known","value":"synthetic"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"status":"known","value":{"truncated":false,"value":"execute_tools_20260918"}},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"reason":"tool is not in the registry","status":"unknown"},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"reason":"tool is not in the registry","status":"unknown"},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Search deferred tool definitions and return matching tool references."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\",\"description\":\"Search query for tool discovery.\"},\"match\":{\"type\":\"string\",\"enum\":[\"bm25\",\"regex\"],\"default\":\"bm25\",\"description\":\"Matching algorithm: bm25 for natural-language matching, regex for a regular expression over tool names/descriptions/schema.\"},\"max_results\":{\"type\":\"integer\",\"minimum\":1,\"maximum\":8,\"default\":8,\"description\":\"Maximum number of matching tool references to return.\"}},\"required\":[\"query\"]}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"reason":"tool is not in the registry","status":"unknown"},"name":{"truncated":false,"value":"tool_search"},"ordinal":12,"provenance":{"status":"known","value":"synthetic"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"status":"known","value":{"truncated":false,"value":"tool_search_20251119"}},"visibility":"active"}],"tools_field_present":true,"turn_id":{"truncated":false,"value":""},"unavailable_for_this_request":["provider_wire_payload"]}} {"event":"message_started","index":0} {"delta":"Understood, I did not create it.","event":"response_delta","index":0} {"duration_ms":"","event":"turn_usage","first_token_ms":"","request_ms":"","usage":{"input_tokens":200,"output_tokens":6}} {"event":"message_complete","index":0} {"engine_session_id":"conformance-session","event":"session_updated","messages":[{"content":[{"text":"Create denied.txt with touch.","type":"text"},{"text":"\nCurrent local date: \nCurrent workspace: \nCurrent permission posture: Ask\nCurrent sandbox posture: workspace-write (writes inside the workspace; network blocked) (local OS sandbox applied)\n## Repo Working Set\nKey files: README.md\nWhen in doubt, use tools to verify and keep changes focused on the working set.\n","type":"text"}],"role":"user"},{"content":[{"execution_id":"","id":"call_touch","input":{"command":"touch denied.txt"},"name":"Bash","type":"tool_use"}],"role":"assistant"},{"content":[{"content":"Error: Tool 'Bash' denied by user — the call was not approved. Do not retry the same call; present what you intended and wait for the user's approval or new instructions.","execution_id":"","is_error":true,"tool_use_id":"call_touch","type":"tool_result"}],"role":"user"},{"content":[{"text":"Understood, I did not create it.","type":"text"}],"role":"assistant"}],"model":"deepseek-v4-pro","system_prompt":"","workspace":""} -{"event":"tool_request_snapshot","snapshot":{"active_tool_catalog_sha256":"18c6d652f11c0c195e7b586852ab46b47987939aaa316b39ef8b692255a7e077","capture_source":"prepared model-client request","delivery_status":"unknown (capture does not prove provider delivery)","omitted_tool_count":0,"payload_json_bytes":14890,"payload_measurement_status":"exact (within 1048576-byte measurement bound)","provider":{"model":"deepseek-v4-pro","provider":"Deepseek","status":"available"},"registry_facts_present":true,"registry_only_tools":{"status":"known","value":{"count":40,"omitted":8,"rendered":[{"truncated":false,"value":"Git"},{"truncated":false,"value":"Run"},{"truncated":false,"value":"Web"},{"truncated":false,"value":"apply_patch"},{"truncated":false,"value":"automation"},{"truncated":false,"value":"diagnostics"},{"truncated":false,"value":"finance"},{"truncated":false,"value":"github"},{"truncated":false,"value":"grep_files"},{"truncated":false,"value":"handle_read"},{"truncated":false,"value":"harness"},{"truncated":false,"value":"image_ocr"},{"truncated":false,"value":"list_dir"},{"truncated":false,"value":"lsp"},{"truncated":false,"value":"note"},{"truncated":false,"value":"notify"},{"truncated":false,"value":"pandoc_convert"},{"truncated":false,"value":"project_map"},{"truncated":false,"value":"registry_sync"},{"truncated":false,"value":"request_plugin_install"},{"truncated":false,"value":"request_user_input"},{"truncated":false,"value":"retrieve_tool_result"},{"truncated":false,"value":"revert_turn"},{"truncated":false,"value":"review"},{"truncated":false,"value":"send_later"},{"truncated":false,"value":"session_get"},{"truncated":false,"value":"session_search"},{"truncated":false,"value":"start_mcp_server"},{"truncated":false,"value":"start_registry_mcp_server"},{"truncated":false,"value":"task_shell_wait"},{"truncated":false,"value":"tasks"},{"truncated":false,"value":"terminal/cancel"}]}},"registry_tool_count":{"status":"known","value":62},"rendered_tool_count":12,"schema_version":1,"step":1,"terminal":{"automatic_compaction_attempts":0,"effective_max_steps":null,"emergency_compaction_attempts":0,"empty_stop_retries":0,"final_report_requested":false,"last_prepared_output_limit_tokens":65536,"last_provider_finish_reason":{"truncated":false,"value":"end_turn"},"last_reported_input_tokens":200,"last_response_tool_calls":0,"last_response_tool_calls_suppressed":0,"model_requests_started":2,"model_step_index":1,"permission_denial_rounds_without_progress":0,"permission_strategy_switches":0,"reason":"provider_no_tool_call","reasoning_only_reprompts":0,"route_context_window_tokens":1000000,"soft_landing_sent":false,"status":"completed","step_budget_source":"max_steps","stream_resumes":0,"transparent_stream_retries":0,"transport_retries":0},"tool_count":12,"tools":[{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Required"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"ExecutesCode"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Execute a shell command in the workspace and return stdout and stderr. Output keeps the last 2000 lines or 50KB. An optional timeout is the seconds to wait in the foreground (default 30 seconds); a command still running then is never killed for time: it moves to the background by itself and you get its output so far, how long ago it last printed, its build phase, and a task_id, so long builds and tests need no special timeout. Find task_shell_wait with tool_search to read more or wait for it. In Ask, after "},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The command to execute. Actual execution shell: `/bin/bash`. Use Bash syntax: pipelines, redirections, $(command), and && / || are supported. Quote paths and variable expansions, such as \\\"$path\\\"; use single quotes for literal text. For literal multiline input, use a quoted heredoc delimiter (<<'EOF') with its closing delimiter on a separate line. Use only installed programs; do not assume GNU-specific flags on macOS/BSD. Example: printf '%s\\\\n' 'sample'.\",\"type\":\"string\"},\"justification\":{\"description\":\"Required with sandbox_permissions: one sentence explaining why this exact command needs wider access.\",\"type\":\"string\"},\"read_only\":{\"description\":\"Set true to run analysis code (including Python/SQLite) with mandatory native filesystem read-only isolation and no network. Available during peer writes. Refused when native enforcement is unavailable; no background, stdin, external backend, or sandbox escalation.\",\"type\":\"boolean\"},\"sandbox_permissions\":{\"description\":\"The wider sandbox mode this exact command needs. Use only as a one-shot retry after a sandbox denial; requires justification and user approval in Ask.\",\"enum\":[\"workspace-write\",\"danger-full-access\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional seconds to wait in the foreground (default 30 seconds). A command still running then is not killed: it moves to the background by itself and you get its output so far, how long ago it last printed, its build phase, and a task_id. Find task_shell_wait with tool_search to read more or wait for it.\",\"type\":\"number\"}},\"required\":[\"command\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"bash"},"ordinal":1,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":0,"omitted":0,"rendered":[]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Create the session's one persistent goal: a completion objective Codewhale keeps working toward across turns until it is verified complete, blocked, or the user stops it. A goal is for a durable objective that outlasts one turn: a multi-step outcome the user wants continued and verified. A question, a greeting, or a one-shot edit completes as an ordinary turn. When the user explicitly asks to use `/goal` or to make something the goal, `create_goal` is what records it; acknowledging it in prose records nothi"},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"objective\":{\"description\":\"The full objective to pursue. Keep the complete user goal, not a shortened one-turn version.\",\"type\":\"string\"},\"token_budget\":{\"description\":\"Optional soft token budget for the goal.\",\"minimum\":0,\"type\":\"integer\"}},\"required\":[\"objective\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"create_goal"},"ordinal":2,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Suggest"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Edit one file with targeted text replacements. Every edits[].oldText must identify a unique, non-overlapping region of the original file. Merge nearby or overlapping changes into one edit."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"edits\":{\"description\":\"One or more disjoint replacements, all matched against the original file.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"newText\":{\"description\":\"Replacement text; may be empty to delete the matched span.\",\"type\":\"string\"},\"oldText\":{\"description\":\"Text identifying one unique region to replace.\",\"type\":\"string\"}},\"required\":[\"newText\",\"oldText\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to the file to edit (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"edits\",\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"edit"},"ordinal":3,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":2,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"},{"truncated":false,"value":"Sandboxable"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Find workspace files by name, path, or glob (e.g. src/**/*.rs). Space-separated terms must all match. Literal matches rank above fuzzy matches; results are capped. Respects ignore files and skips build/dependency directories by default. Use extensions to filter suffixes."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"properties\":{\"exclude\":{\"description\":\"Optional replacement exclusion globs (e.g. [\\\"**/target/**\\\", \\\"*.lock\\\"]); [] disables default exclusions.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"extensions\":{\"description\":\"Optional list of file extensions to include (e.g. [\\\"rs\\\", \\\"md\\\"]).\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"limit\":{\"description\":\"Maximum number of ranked results to return (default: 20, maximum: 200).\",\"type\":\"integer\"},\"path\":{\"description\":\"Optional base path to search (relative to workspace).\",\"type\":\"string\"},\"query\":{\"description\":\"File name, path fragment, space-separated terms, or glob (e.g. **/engine*.rs).\",\"type\":\"string\"}},\"required\":[\"query\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"file_search"},"ordinal":4,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Inspect the current runtime goal state, including objective, status, token budget, elapsed time, evidence, and blocker."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{},\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"get_goal"},"ordinal":5,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Load a named skill's SKILL.md body and companion file list into this turn. Use when the user names a skill, or when an entry in the system prompt's `## Skills` index matches the task -- load it before starting the work, not after. Pass query=\"...\" to search names and descriptions, or name=\"list\" for the whole catalogue. Resolves global and plugin skills that `read` cannot reach."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"name\":{\"description\":\"Skill id to load. Omit or pass \\\"list\\\" to see all available skills.\",\"type\":\"string\"},\"query\":{\"description\":\"Search term matched against skill names and descriptions. Use when the index was truncated or no name is known.\",\"type\":\"string\"}},\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"load_skill"},"ordinal":6,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":2,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"},{"truncated":false,"value":"Sandboxable"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Read a file. A text file comes back whole in one call when it fits this call's output budget — 100000 bytes by default, raisable to 500000 with max_bytes. There is no line cap. Use offset and limit for an exact line range; when output is budget-limited the footer names the exact offset to continue from. Every text response reports the file's byte size, line count, and whether output was truncated. A PNG, JPEG, GIF or WebP image comes back as image content you can see (large images are downscaled), including"},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of lines to read.\",\"type\":\"number\"},\"max_bytes\":{\"description\":\"Output budget in bytes for this one call. Defaults to 100000; values above the 500000 maximum are clamped down rather than rejected, and a value below the active default leaves the default in place.\",\"type\":\"number\"},\"offset\":{\"description\":\"Line number to start reading from (1-indexed).\",\"type\":\"number\"},\"path\":{\"description\":\"Path to the file to read (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"read"},"ordinal":7,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Replace the To-do list the user watches. For any task with three or more steps or more than one file, write the list before you start, keep exactly one item in_progress, and mark items done as you finish them. Skip it only for a single quick answer or edit."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"properties\":{\"todos\":{\"description\":\"The complete list of To-do items. This replaces the existing list.\",\"items\":{\"properties\":{\"content\":{\"description\":\"The task description\",\"type\":\"string\"},\"status\":{\"description\":\"Task status\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"required\":[\"todos\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"todo_write"},"ordinal":8,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":0,"omitted":0,"rendered":[]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Update the runtime goal completion gate by calling this tool; a prose status in your answer does not change the goal or stop continuation. Critical verification may seal one immutable completion contract. Advisory review is append-only context and never completes, blocks, or pauses the goal. Mark blocked when progress requires user input. Mark yield when you finished a stage and the next step needs the user's decision: the goal stays unfinished, the turn ends, and their next message resumes it."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":true,"value":"{\"additionalProperties\":false,\"properties\":{\"advisory\":{\"description\":\"Required when status is advisory. Appended separately from the judged completion contract.\",\"type\":\"string\"},\"blocker\":{\"description\":\"Required when status is blocked. Explain the condition preventing progress.\",\"type\":\"string\"},\"evidence\":{\"description\":\"Required when status is complete. Briefly cite the proof that the goal is done.\",\"type\":\"string\"},\"progress\":{\"additionalProperties\":false,\"description\":\"Optional with not_achieved or advisory: your current best estimate of overall completion, shown to the user as reported progress. Keep percent honest — it is an estimate, never a verified fraction.\",\"properties\":{\"next\":{\"description\":\"One short line: what comes next.\",\"type\":\"string\"},\"now\":{\"description\":\"One short line: what is being worked on right now.\",\"type\":\"string\"},\"percent\":{\"description\":\"Estimated percent complete, 0-100.\",\"maximum\":100,\"minimum\":0,\"type\":\"integer\"}},\"required\":[\"percent\"],\"type\":\"object\"},\"status\":{\"description\":\"Use complete only when a critical verifier proves the goal; not_achieved to record verifier gaps; blocked when meaningful progress cannot continue; yield when a stage is finished and the next step is the user's call; advisory to append best-effort context without changing lifecycle state.\",\"enum\":[\"complete\",\"blocked\",\"not_achieved\",\"advisory\",\"yield\"],\"type\":\"string\"},\"verification\":{\"additionalProperties\":false,\"description\":\"Required when status is complete or not_achieved. A verifier-as-judge receipt from a concrete check, such as Run action=\\\"verifiers\\\" or an equivalent project-specific gate.\",\"properties\":{\"check\":{\"description\":\"The verifier/check that passed.\",\"type\":\"string\"},\"gaps\":{\"description\":\"Concrete remaining gaps. Required for critical not_achieved reviews; order and duplicate wording do not affect the stall fingerprint.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"role\":{\"description\":\"Critical reviews may satisfy the judged completion contract. Advisory reviews are fail-open and c"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"update_goal"},"ordinal":9,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Suggest"}},"cache_control_type":{"status":"known","value":{"truncated":false,"value":"ephemeral"}},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Write content to a file. Creates the file if it does not exist, overwrites it if it does, and creates parent directories automatically."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Content to write to the file.\",\"type\":\"string\"},\"path\":{\"description\":\"Path to the file to write (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"content\",\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"write"},"ordinal":10,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"reason":"tool is not in the registry","status":"unknown"},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"reason":"tool is not in the registry","status":"unknown"},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Run a JavaScript program that composes tool calls with `await tools.call(name, args)` and returns a bounded JSON result. Prefer it whenever you would make several dependent or repetitive calls, MCP and plugin tools included: intermediate results stay in the program and only what you return reaches the conversation. Every nested call passes the same permission checks as a direct call; a call that needs approval pauses the program until the user decides, and a denied or refused call throws inside the program "},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"type\":\"object\",\"properties\":{\"code\":{\"type\":\"string\",\"description\":\"JavaScript program. The return value (or thrown error) becomes the result; use tools.call(name, argsObject) for tool calls.\"}},\"required\":[\"code\"]}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"reason":"tool is not in the registry","status":"unknown"},"name":{"truncated":false,"value":"execute_tools"},"ordinal":11,"provenance":{"status":"known","value":"synthetic"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"status":"known","value":{"truncated":false,"value":"execute_tools_20260918"}},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"reason":"tool is not in the registry","status":"unknown"},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"reason":"tool is not in the registry","status":"unknown"},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Search deferred tool definitions and return matching tool references."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\",\"description\":\"Search query for tool discovery.\"},\"match\":{\"type\":\"string\",\"enum\":[\"bm25\",\"regex\"],\"default\":\"bm25\",\"description\":\"Matching algorithm: bm25 for natural-language matching, regex for a regular expression over tool names/descriptions/schema.\"},\"max_results\":{\"type\":\"integer\",\"minimum\":1,\"maximum\":8,\"default\":8,\"description\":\"Maximum number of matching tool references to return.\"}},\"required\":[\"query\"]}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"reason":"tool is not in the registry","status":"unknown"},"name":{"truncated":false,"value":"tool_search"},"ordinal":12,"provenance":{"status":"known","value":"synthetic"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"status":"known","value":{"truncated":false,"value":"tool_search_20251119"}},"visibility":"active"}],"tools_field_present":true,"turn_id":{"truncated":false,"value":""},"unavailable_for_this_request":["provider_wire_payload"]}} +{"event":"tool_request_snapshot","snapshot":{"active_tool_catalog_sha256":"ba34408b0c03b810ba1ef6190c41d215f58e88822148991099b7d70d444992fc","capture_source":"prepared model-client request","delivery_status":"unknown (capture does not prove provider delivery)","omitted_tool_count":0,"payload_json_bytes":14897,"payload_measurement_status":"exact (within 1048576-byte measurement bound)","provider":{"model":"deepseek-v4-pro","provider":"Deepseek","status":"available"},"registry_facts_present":true,"registry_only_tools":{"status":"known","value":{"count":40,"omitted":8,"rendered":[{"truncated":false,"value":"Git"},{"truncated":false,"value":"Run"},{"truncated":false,"value":"Web"},{"truncated":false,"value":"apply_patch"},{"truncated":false,"value":"automation"},{"truncated":false,"value":"diagnostics"},{"truncated":false,"value":"finance"},{"truncated":false,"value":"github"},{"truncated":false,"value":"grep_files"},{"truncated":false,"value":"handle_read"},{"truncated":false,"value":"harness"},{"truncated":false,"value":"image_ocr"},{"truncated":false,"value":"list_dir"},{"truncated":false,"value":"lsp"},{"truncated":false,"value":"note"},{"truncated":false,"value":"notify"},{"truncated":false,"value":"pandoc_convert"},{"truncated":false,"value":"project_map"},{"truncated":false,"value":"registry_sync"},{"truncated":false,"value":"request_plugin_install"},{"truncated":false,"value":"request_user_input"},{"truncated":false,"value":"retrieve_tool_result"},{"truncated":false,"value":"revert_turn"},{"truncated":false,"value":"review"},{"truncated":false,"value":"send_later"},{"truncated":false,"value":"session_get"},{"truncated":false,"value":"session_search"},{"truncated":false,"value":"start_mcp_server"},{"truncated":false,"value":"start_registry_mcp_server"},{"truncated":false,"value":"task_shell_wait"},{"truncated":false,"value":"tasks"},{"truncated":false,"value":"terminal/cancel"}]}},"registry_tool_count":{"status":"known","value":62},"rendered_tool_count":12,"schema_version":1,"step":1,"terminal":{"automatic_compaction_attempts":0,"effective_max_steps":null,"emergency_compaction_attempts":0,"empty_stop_retries":0,"final_report_requested":false,"last_prepared_output_limit_tokens":65536,"last_provider_finish_reason":{"truncated":false,"value":"end_turn"},"last_reported_input_tokens":200,"last_response_tool_calls":0,"last_response_tool_calls_suppressed":0,"model_requests_started":2,"model_step_index":1,"permission_denial_rounds_without_progress":0,"permission_strategy_switches":0,"reason":"provider_no_tool_call","reasoning_only_reprompts":0,"route_context_window_tokens":1000000,"soft_landing_sent":false,"status":"completed","step_budget_source":"max_steps","stream_resumes":0,"transparent_stream_retries":0,"transport_retries":0},"tool_count":12,"tools":[{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Required"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"ExecutesCode"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Execute a shell command in the workspace and return stdout and stderr. Output keeps the last 2000 lines or 50KB. An optional timeout is the seconds to wait in the foreground (default 30 seconds); a command still running then is never killed for time: it moves to the background by itself and you get its output so far, how long ago it last printed, its build phase, and a task_id, so long builds and tests need no special timeout. Find task_shell_wait with tool_search to read more or wait for it. In Ask, after "},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The command to execute. Actual execution shell: `/bin/bash`. Use Bash syntax: pipelines, redirections, $(command), and && / || are supported. Quote paths and variable expansions, such as \\\"$path\\\"; use single quotes for literal text. For literal multiline input, use a quoted heredoc delimiter (<<'EOF') with its closing delimiter on a separate line. Use only installed programs; do not assume GNU-specific flags on macOS/BSD. Example: printf '%s\\\\n' 'sample'.\",\"type\":\"string\"},\"justification\":{\"description\":\"Required with sandbox_permissions: one sentence explaining why this exact command needs wider access.\",\"type\":\"string\"},\"read_only\":{\"description\":\"Set true to run analysis code (including Python/SQLite) with mandatory native filesystem read-only isolation and no network. Available during peer writes. Refused when native enforcement is unavailable; no background, stdin, external backend, or sandbox escalation.\",\"type\":\"boolean\"},\"sandbox_permissions\":{\"description\":\"The wider sandbox mode this exact command needs. Use only as a one-shot retry after a sandbox denial; requires justification and user approval in Ask.\",\"enum\":[\"workspace-write\",\"danger-full-access\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional seconds to wait in the foreground (default 30 seconds). A command still running then is not killed: it moves to the background by itself and you get its output so far, how long ago it last printed, its build phase, and a task_id. Find task_shell_wait with tool_search to read more or wait for it.\",\"type\":\"number\"}},\"required\":[\"command\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"bash"},"ordinal":1,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":0,"omitted":0,"rendered":[]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Create the session's one persistent goal: a completion objective Codewhale keeps working toward across turns until it is verified complete, blocked, or the user stops it. A goal is for a durable objective that outlasts one turn: a multi-step outcome the user wants continued and verified. A question, a greeting, or a one-shot edit completes as an ordinary turn. When the user explicitly asks to use `/goal` or to make something the goal, `create_goal` is what records it; acknowledging it in prose records nothi"},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"objective\":{\"description\":\"The full objective to pursue. Keep the complete user goal, not a shortened one-turn version.\",\"type\":\"string\"},\"token_budget\":{\"description\":\"Optional soft token budget for the goal.\",\"minimum\":0,\"type\":\"integer\"}},\"required\":[\"objective\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"create_goal"},"ordinal":2,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Suggest"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Edit one file with targeted text replacements. Every edits[].oldText must identify a unique, non-overlapping region of the original file. Merge nearby or overlapping changes into one edit."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"edits\":{\"description\":\"One or more disjoint replacements, all matched against the original file.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"newText\":{\"description\":\"Replacement text; may be empty to delete the matched span.\",\"type\":\"string\"},\"oldText\":{\"description\":\"Text identifying one unique region to replace.\",\"type\":\"string\"}},\"required\":[\"newText\",\"oldText\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to the file to edit (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"edits\",\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"edit"},"ordinal":3,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":2,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"},{"truncated":false,"value":"Sandboxable"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Find workspace files by name, path, or glob (e.g. src/**/*.rs). Space-separated terms must all match. Literal matches rank above fuzzy matches; results are capped. Respects ignore files and skips build/dependency directories by default. Use extensions to filter suffixes."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"properties\":{\"exclude\":{\"description\":\"Optional replacement exclusion globs (e.g. [\\\"**/target/**\\\", \\\"*.lock\\\"]); [] disables default exclusions.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"extensions\":{\"description\":\"Optional list of file extensions to include (e.g. [\\\"rs\\\", \\\"md\\\"]).\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"limit\":{\"description\":\"Maximum number of ranked results to return (default: 20, maximum: 200).\",\"type\":\"integer\"},\"path\":{\"description\":\"Optional base path to search (relative to workspace).\",\"type\":\"string\"},\"query\":{\"description\":\"File name, path fragment, space-separated terms, or glob (e.g. **/engine*.rs).\",\"type\":\"string\"}},\"required\":[\"query\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"file_search"},"ordinal":4,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Inspect the current runtime goal state, including objective, status, token budget, elapsed time, evidence, and blocker."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{},\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"get_goal"},"ordinal":5,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Load a named skill's SKILL.md body and companion file list into this turn. Use when the user names a skill, or when an entry in the system prompt's `## Skills` index matches the task -- load it before starting the work, not after. Pass query=\"...\" to search names and descriptions, or name=\"list\" for the whole catalogue. Resolves global and plugin skills that `read` cannot reach."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"name\":{\"description\":\"Skill id to load. Omit or pass \\\"list\\\" to see all available skills.\",\"type\":\"string\"},\"query\":{\"description\":\"Search term matched against skill names and descriptions. Use when the index was truncated or no name is known.\",\"type\":\"string\"}},\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"load_skill"},"ordinal":6,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":2,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"},{"truncated":false,"value":"Sandboxable"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Read a file. A text file comes back whole in one call when it fits this call's output budget — 100000 bytes by default, raisable to 500000 with max_bytes. There is no line cap. Use offset and limit for an exact line range; when output is budget-limited the footer names the exact offset to continue from. Every text response reports the file's byte size, line count, and whether output was truncated. A PNG, JPEG, GIF or WebP image comes back as image content you can see (large images are downscaled), including"},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of lines to read.\",\"type\":\"number\"},\"max_bytes\":{\"description\":\"Output budget in bytes for this one call. Defaults to 100000; values above the 500000 maximum are clamped down rather than rejected, and a value below the active default leaves the default in place.\",\"type\":\"number\"},\"offset\":{\"description\":\"Line number to start reading from (1-indexed).\",\"type\":\"number\"},\"path\":{\"description\":\"Path to the file to read (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"read"},"ordinal":7,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Replace the To-do list the user watches. For any task with three or more steps or more than one file, write the list before you start, keep exactly one item in_progress, and mark items done as you finish them. Skip it only for a single quick answer or edit."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"properties\":{\"todos\":{\"description\":\"The complete list of To-do items. This replaces the existing list.\",\"items\":{\"properties\":{\"content\":{\"description\":\"The task description\",\"type\":\"string\"},\"status\":{\"description\":\"Task status\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"required\":[\"todos\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"todo_write"},"ordinal":8,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":0,"omitted":0,"rendered":[]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Update the runtime goal completion gate by calling this tool; a prose status in your answer does not change the goal or stop continuation. Critical verification may seal one immutable completion contract. Advisory review is append-only context and never completes, blocks, or pauses the goal. Mark blocked when progress requires user input. Mark yield when you finished a stage and the next step needs the user's decision: the goal stays unfinished, the turn ends, and their next message resumes it."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":true,"value":"{\"additionalProperties\":false,\"properties\":{\"advisory\":{\"description\":\"Required when status is advisory. Appended separately from the judged completion contract.\",\"type\":\"string\"},\"blocker\":{\"description\":\"Required when status is blocked. Explain the condition preventing progress.\",\"type\":\"string\"},\"evidence\":{\"description\":\"Required when status is complete. Briefly cite the proof that the goal is done.\",\"type\":\"string\"},\"progress\":{\"additionalProperties\":false,\"description\":\"Optional with not_achieved, advisory or yield: your current best estimate of overall completion, shown to the user as reported progress. Keep percent honest — it is an estimate, never a verified fraction.\",\"properties\":{\"next\":{\"description\":\"One short line: what comes next.\",\"type\":\"string\"},\"now\":{\"description\":\"One short line: what is being worked on right now.\",\"type\":\"string\"},\"percent\":{\"description\":\"Estimated percent complete, 0-100.\",\"maximum\":100,\"minimum\":0,\"type\":\"integer\"}},\"required\":[\"percent\"],\"type\":\"object\"},\"status\":{\"description\":\"Use complete only when a critical verifier proves the goal; not_achieved to record verifier gaps; blocked when meaningful progress cannot continue; yield when a stage is finished and the next step is the user's call; advisory to append best-effort context without changing lifecycle state.\",\"enum\":[\"complete\",\"blocked\",\"not_achieved\",\"advisory\",\"yield\"],\"type\":\"string\"},\"verification\":{\"additionalProperties\":false,\"description\":\"Required when status is complete or not_achieved. A verifier-as-judge receipt from a concrete check, such as Run action=\\\"verifiers\\\" or an equivalent project-specific gate.\",\"properties\":{\"check\":{\"description\":\"The verifier/check that passed.\",\"type\":\"string\"},\"gaps\":{\"description\":\"Concrete remaining gaps. Required for critical not_achieved reviews; order and duplicate wording do not affect the stall fingerprint.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"role\":{\"description\":\"Critical reviews may satisfy the judged completion contract. Advisory reviews are fail-ope"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"update_goal"},"ordinal":9,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Suggest"}},"cache_control_type":{"status":"known","value":{"truncated":false,"value":"ephemeral"}},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Write content to a file. Creates the file if it does not exist, overwrites it if it does, and creates parent directories automatically."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Content to write to the file.\",\"type\":\"string\"},\"path\":{\"description\":\"Path to the file to write (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"content\",\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"write"},"ordinal":10,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"reason":"tool is not in the registry","status":"unknown"},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"reason":"tool is not in the registry","status":"unknown"},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Run a JavaScript program that composes tool calls with `await tools.call(name, args)` and returns a bounded JSON result. Prefer it whenever you would make several dependent or repetitive calls, MCP and plugin tools included: intermediate results stay in the program and only what you return reaches the conversation. Every nested call passes the same permission checks as a direct call; a call that needs approval pauses the program until the user decides, and a denied or refused call throws inside the program "},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"type\":\"object\",\"properties\":{\"code\":{\"type\":\"string\",\"description\":\"JavaScript program. The return value (or thrown error) becomes the result; use tools.call(name, argsObject) for tool calls.\"}},\"required\":[\"code\"]}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"reason":"tool is not in the registry","status":"unknown"},"name":{"truncated":false,"value":"execute_tools"},"ordinal":11,"provenance":{"status":"known","value":"synthetic"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"status":"known","value":{"truncated":false,"value":"execute_tools_20260918"}},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"reason":"tool is not in the registry","status":"unknown"},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"reason":"tool is not in the registry","status":"unknown"},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Search deferred tool definitions and return matching tool references."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\",\"description\":\"Search query for tool discovery.\"},\"match\":{\"type\":\"string\",\"enum\":[\"bm25\",\"regex\"],\"default\":\"bm25\",\"description\":\"Matching algorithm: bm25 for natural-language matching, regex for a regular expression over tool names/descriptions/schema.\"},\"max_results\":{\"type\":\"integer\",\"minimum\":1,\"maximum\":8,\"default\":8,\"description\":\"Maximum number of matching tool references to return.\"}},\"required\":[\"query\"]}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"reason":"tool is not in the registry","status":"unknown"},"name":{"truncated":false,"value":"tool_search"},"ordinal":12,"provenance":{"status":"known","value":"synthetic"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"status":"known","value":{"truncated":false,"value":"tool_search_20251119"}},"visibility":"active"}],"tools_field_present":true,"turn_id":{"truncated":false,"value":""},"unavailable_for_this_request":["provider_wire_payload"]}} {"event":"turn_complete","parent_route_usage":{"input_tokens":350,"output_tokens":21},"routed_usage_dropped_records":0,"status":"completed","tool_catalog":"","usage":{"input_tokens":350,"output_tokens":21}} -{"harness_summary":{"model_requests":2,"non_streaming_requests":0,"workspace_after":{".codewhale/state/subagents.v1.lock":"e3b0c44298fc1c14","README.md":"41f49bffb38de5ba"}}} +{"harness_summary":{"model_requests":2,"non_streaming_requests":0,"workspace_after":{".codewhale/state/.gitignore":"cdbcae15105d6b78",".codewhale/state/subagents.v1.lock":"e3b0c44298fc1c14","README.md":"41f49bffb38de5ba"}}} diff --git a/crates/tui/tests/fixtures/conformance/events/cancel_midstream.golden.jsonl b/crates/tui/tests/fixtures/conformance/events/cancel_midstream.golden.jsonl index 65f17cbaf7..46d85be322 100644 --- a/crates/tui/tests/fixtures/conformance/events/cancel_midstream.golden.jsonl +++ b/crates/tui/tests/fixtures/conformance/events/cancel_midstream.golden.jsonl @@ -1,15 +1,15 @@ {"created_at":"","event":"turn_started","route":{"auto_model":false,"base_url":"https://api.deepseek.com/beta","billing_product":{"kind":"unproven"},"model":"deepseek-v4-pro","provider":"deepseek","provider_identity":"deepseek"},"turn_id":""} {"engine_session_id":"conformance-session","event":"session_updated","messages":[{"content":[{"text":"Tell me a long story.","type":"text"},{"text":"\nCurrent local date: \nCurrent workspace: \nCurrent permission posture: Ask\nCurrent sandbox posture: workspace-write (writes inside the workspace; network blocked) (local OS sandbox applied)\n## Repo Working Set\nKey files: README.md\nWhen in doubt, use tools to verify and keep changes focused on the working set.\n","type":"text"}],"role":"user"}],"model":"deepseek-v4-pro","system_prompt":"","workspace":""} {"changed":false,"context_updates":0,"description":"","event":"prefix_cache_change","last_miss_reason":"","pin_reason":"initial","pinned_combined_hash":"","stability_pct":100,"system_prompt_changed":false,"tools_changed":false} -{"changed":false,"context_updates":0,"description":"frozen: 8b8238c51355","event":"prefix_cache_change","last_miss_reason":"","pin_reason":"initial","pinned_combined_hash":"","stability_pct":100,"system_prompt_changed":false,"tools_changed":false} -{"event":"tool_request_snapshot","snapshot":{"active_tool_catalog_sha256":"18c6d652f11c0c195e7b586852ab46b47987939aaa316b39ef8b692255a7e077","capture_source":"prepared model-client request","delivery_status":"unknown (capture does not prove provider delivery)","omitted_tool_count":0,"payload_json_bytes":14890,"payload_measurement_status":"exact (within 1048576-byte measurement bound)","provider":{"model":"deepseek-v4-pro","provider":"Deepseek","status":"available"},"registry_facts_present":true,"registry_only_tools":{"status":"known","value":{"count":40,"omitted":8,"rendered":[{"truncated":false,"value":"Git"},{"truncated":false,"value":"Run"},{"truncated":false,"value":"Web"},{"truncated":false,"value":"apply_patch"},{"truncated":false,"value":"automation"},{"truncated":false,"value":"diagnostics"},{"truncated":false,"value":"finance"},{"truncated":false,"value":"github"},{"truncated":false,"value":"grep_files"},{"truncated":false,"value":"handle_read"},{"truncated":false,"value":"harness"},{"truncated":false,"value":"image_ocr"},{"truncated":false,"value":"list_dir"},{"truncated":false,"value":"lsp"},{"truncated":false,"value":"note"},{"truncated":false,"value":"notify"},{"truncated":false,"value":"pandoc_convert"},{"truncated":false,"value":"project_map"},{"truncated":false,"value":"registry_sync"},{"truncated":false,"value":"request_plugin_install"},{"truncated":false,"value":"request_user_input"},{"truncated":false,"value":"retrieve_tool_result"},{"truncated":false,"value":"revert_turn"},{"truncated":false,"value":"review"},{"truncated":false,"value":"send_later"},{"truncated":false,"value":"session_get"},{"truncated":false,"value":"session_search"},{"truncated":false,"value":"start_mcp_server"},{"truncated":false,"value":"start_registry_mcp_server"},{"truncated":false,"value":"task_shell_wait"},{"truncated":false,"value":"tasks"},{"truncated":false,"value":"terminal/cancel"}]}},"registry_tool_count":{"status":"known","value":62},"rendered_tool_count":12,"schema_version":1,"step":0,"tool_count":12,"tools":[{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Required"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"ExecutesCode"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Execute a shell command in the workspace and return stdout and stderr. Output keeps the last 2000 lines or 50KB. An optional timeout is the seconds to wait in the foreground (default 30 seconds); a command still running then is never killed for time: it moves to the background by itself and you get its output so far, how long ago it last printed, its build phase, and a task_id, so long builds and tests need no special timeout. Find task_shell_wait with tool_search to read more or wait for it. In Ask, after "},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The command to execute. Actual execution shell: `/bin/bash`. Use Bash syntax: pipelines, redirections, $(command), and && / || are supported. Quote paths and variable expansions, such as \\\"$path\\\"; use single quotes for literal text. For literal multiline input, use a quoted heredoc delimiter (<<'EOF') with its closing delimiter on a separate line. Use only installed programs; do not assume GNU-specific flags on macOS/BSD. Example: printf '%s\\\\n' 'sample'.\",\"type\":\"string\"},\"justification\":{\"description\":\"Required with sandbox_permissions: one sentence explaining why this exact command needs wider access.\",\"type\":\"string\"},\"read_only\":{\"description\":\"Set true to run analysis code (including Python/SQLite) with mandatory native filesystem read-only isolation and no network. Available during peer writes. Refused when native enforcement is unavailable; no background, stdin, external backend, or sandbox escalation.\",\"type\":\"boolean\"},\"sandbox_permissions\":{\"description\":\"The wider sandbox mode this exact command needs. Use only as a one-shot retry after a sandbox denial; requires justification and user approval in Ask.\",\"enum\":[\"workspace-write\",\"danger-full-access\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional seconds to wait in the foreground (default 30 seconds). A command still running then is not killed: it moves to the background by itself and you get its output so far, how long ago it last printed, its build phase, and a task_id. Find task_shell_wait with tool_search to read more or wait for it.\",\"type\":\"number\"}},\"required\":[\"command\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"bash"},"ordinal":1,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":0,"omitted":0,"rendered":[]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Create the session's one persistent goal: a completion objective Codewhale keeps working toward across turns until it is verified complete, blocked, or the user stops it. A goal is for a durable objective that outlasts one turn: a multi-step outcome the user wants continued and verified. A question, a greeting, or a one-shot edit completes as an ordinary turn. When the user explicitly asks to use `/goal` or to make something the goal, `create_goal` is what records it; acknowledging it in prose records nothi"},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"objective\":{\"description\":\"The full objective to pursue. Keep the complete user goal, not a shortened one-turn version.\",\"type\":\"string\"},\"token_budget\":{\"description\":\"Optional soft token budget for the goal.\",\"minimum\":0,\"type\":\"integer\"}},\"required\":[\"objective\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"create_goal"},"ordinal":2,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Suggest"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Edit one file with targeted text replacements. Every edits[].oldText must identify a unique, non-overlapping region of the original file. Merge nearby or overlapping changes into one edit."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"edits\":{\"description\":\"One or more disjoint replacements, all matched against the original file.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"newText\":{\"description\":\"Replacement text; may be empty to delete the matched span.\",\"type\":\"string\"},\"oldText\":{\"description\":\"Text identifying one unique region to replace.\",\"type\":\"string\"}},\"required\":[\"newText\",\"oldText\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to the file to edit (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"edits\",\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"edit"},"ordinal":3,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":2,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"},{"truncated":false,"value":"Sandboxable"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Find workspace files by name, path, or glob (e.g. src/**/*.rs). Space-separated terms must all match. Literal matches rank above fuzzy matches; results are capped. Respects ignore files and skips build/dependency directories by default. Use extensions to filter suffixes."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"properties\":{\"exclude\":{\"description\":\"Optional replacement exclusion globs (e.g. [\\\"**/target/**\\\", \\\"*.lock\\\"]); [] disables default exclusions.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"extensions\":{\"description\":\"Optional list of file extensions to include (e.g. [\\\"rs\\\", \\\"md\\\"]).\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"limit\":{\"description\":\"Maximum number of ranked results to return (default: 20, maximum: 200).\",\"type\":\"integer\"},\"path\":{\"description\":\"Optional base path to search (relative to workspace).\",\"type\":\"string\"},\"query\":{\"description\":\"File name, path fragment, space-separated terms, or glob (e.g. **/engine*.rs).\",\"type\":\"string\"}},\"required\":[\"query\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"file_search"},"ordinal":4,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Inspect the current runtime goal state, including objective, status, token budget, elapsed time, evidence, and blocker."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{},\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"get_goal"},"ordinal":5,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Load a named skill's SKILL.md body and companion file list into this turn. Use when the user names a skill, or when an entry in the system prompt's `## Skills` index matches the task -- load it before starting the work, not after. Pass query=\"...\" to search names and descriptions, or name=\"list\" for the whole catalogue. Resolves global and plugin skills that `read` cannot reach."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"name\":{\"description\":\"Skill id to load. Omit or pass \\\"list\\\" to see all available skills.\",\"type\":\"string\"},\"query\":{\"description\":\"Search term matched against skill names and descriptions. Use when the index was truncated or no name is known.\",\"type\":\"string\"}},\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"load_skill"},"ordinal":6,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":2,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"},{"truncated":false,"value":"Sandboxable"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Read a file. A text file comes back whole in one call when it fits this call's output budget — 100000 bytes by default, raisable to 500000 with max_bytes. There is no line cap. Use offset and limit for an exact line range; when output is budget-limited the footer names the exact offset to continue from. Every text response reports the file's byte size, line count, and whether output was truncated. A PNG, JPEG, GIF or WebP image comes back as image content you can see (large images are downscaled), including"},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of lines to read.\",\"type\":\"number\"},\"max_bytes\":{\"description\":\"Output budget in bytes for this one call. Defaults to 100000; values above the 500000 maximum are clamped down rather than rejected, and a value below the active default leaves the default in place.\",\"type\":\"number\"},\"offset\":{\"description\":\"Line number to start reading from (1-indexed).\",\"type\":\"number\"},\"path\":{\"description\":\"Path to the file to read (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"read"},"ordinal":7,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Replace the To-do list the user watches. For any task with three or more steps or more than one file, write the list before you start, keep exactly one item in_progress, and mark items done as you finish them. Skip it only for a single quick answer or edit."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"properties\":{\"todos\":{\"description\":\"The complete list of To-do items. This replaces the existing list.\",\"items\":{\"properties\":{\"content\":{\"description\":\"The task description\",\"type\":\"string\"},\"status\":{\"description\":\"Task status\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"required\":[\"todos\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"todo_write"},"ordinal":8,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":0,"omitted":0,"rendered":[]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Update the runtime goal completion gate by calling this tool; a prose status in your answer does not change the goal or stop continuation. Critical verification may seal one immutable completion contract. Advisory review is append-only context and never completes, blocks, or pauses the goal. Mark blocked when progress requires user input. Mark yield when you finished a stage and the next step needs the user's decision: the goal stays unfinished, the turn ends, and their next message resumes it."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":true,"value":"{\"additionalProperties\":false,\"properties\":{\"advisory\":{\"description\":\"Required when status is advisory. Appended separately from the judged completion contract.\",\"type\":\"string\"},\"blocker\":{\"description\":\"Required when status is blocked. Explain the condition preventing progress.\",\"type\":\"string\"},\"evidence\":{\"description\":\"Required when status is complete. Briefly cite the proof that the goal is done.\",\"type\":\"string\"},\"progress\":{\"additionalProperties\":false,\"description\":\"Optional with not_achieved or advisory: your current best estimate of overall completion, shown to the user as reported progress. Keep percent honest — it is an estimate, never a verified fraction.\",\"properties\":{\"next\":{\"description\":\"One short line: what comes next.\",\"type\":\"string\"},\"now\":{\"description\":\"One short line: what is being worked on right now.\",\"type\":\"string\"},\"percent\":{\"description\":\"Estimated percent complete, 0-100.\",\"maximum\":100,\"minimum\":0,\"type\":\"integer\"}},\"required\":[\"percent\"],\"type\":\"object\"},\"status\":{\"description\":\"Use complete only when a critical verifier proves the goal; not_achieved to record verifier gaps; blocked when meaningful progress cannot continue; yield when a stage is finished and the next step is the user's call; advisory to append best-effort context without changing lifecycle state.\",\"enum\":[\"complete\",\"blocked\",\"not_achieved\",\"advisory\",\"yield\"],\"type\":\"string\"},\"verification\":{\"additionalProperties\":false,\"description\":\"Required when status is complete or not_achieved. A verifier-as-judge receipt from a concrete check, such as Run action=\\\"verifiers\\\" or an equivalent project-specific gate.\",\"properties\":{\"check\":{\"description\":\"The verifier/check that passed.\",\"type\":\"string\"},\"gaps\":{\"description\":\"Concrete remaining gaps. Required for critical not_achieved reviews; order and duplicate wording do not affect the stall fingerprint.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"role\":{\"description\":\"Critical reviews may satisfy the judged completion contract. Advisory reviews are fail-open and c"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"update_goal"},"ordinal":9,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Suggest"}},"cache_control_type":{"status":"known","value":{"truncated":false,"value":"ephemeral"}},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Write content to a file. Creates the file if it does not exist, overwrites it if it does, and creates parent directories automatically."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Content to write to the file.\",\"type\":\"string\"},\"path\":{\"description\":\"Path to the file to write (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"content\",\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"write"},"ordinal":10,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"reason":"tool is not in the registry","status":"unknown"},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"reason":"tool is not in the registry","status":"unknown"},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Run a JavaScript program that composes tool calls with `await tools.call(name, args)` and returns a bounded JSON result. Prefer it whenever you would make several dependent or repetitive calls, MCP and plugin tools included: intermediate results stay in the program and only what you return reaches the conversation. Every nested call passes the same permission checks as a direct call; a call that needs approval pauses the program until the user decides, and a denied or refused call throws inside the program "},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"type\":\"object\",\"properties\":{\"code\":{\"type\":\"string\",\"description\":\"JavaScript program. The return value (or thrown error) becomes the result; use tools.call(name, argsObject) for tool calls.\"}},\"required\":[\"code\"]}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"reason":"tool is not in the registry","status":"unknown"},"name":{"truncated":false,"value":"execute_tools"},"ordinal":11,"provenance":{"status":"known","value":"synthetic"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"status":"known","value":{"truncated":false,"value":"execute_tools_20260918"}},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"reason":"tool is not in the registry","status":"unknown"},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"reason":"tool is not in the registry","status":"unknown"},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Search deferred tool definitions and return matching tool references."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\",\"description\":\"Search query for tool discovery.\"},\"match\":{\"type\":\"string\",\"enum\":[\"bm25\",\"regex\"],\"default\":\"bm25\",\"description\":\"Matching algorithm: bm25 for natural-language matching, regex for a regular expression over tool names/descriptions/schema.\"},\"max_results\":{\"type\":\"integer\",\"minimum\":1,\"maximum\":8,\"default\":8,\"description\":\"Maximum number of matching tool references to return.\"}},\"required\":[\"query\"]}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"reason":"tool is not in the registry","status":"unknown"},"name":{"truncated":false,"value":"tool_search"},"ordinal":12,"provenance":{"status":"known","value":"synthetic"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"status":"known","value":{"truncated":false,"value":"tool_search_20251119"}},"visibility":"active"}],"tools_field_present":true,"turn_id":{"truncated":false,"value":""},"unavailable_for_this_request":["provider_wire_payload"]}} +{"changed":false,"context_updates":0,"description":"frozen: e6c0d3cc59cd","event":"prefix_cache_change","last_miss_reason":"","pin_reason":"initial","pinned_combined_hash":"","stability_pct":100,"system_prompt_changed":false,"tools_changed":false} +{"event":"tool_request_snapshot","snapshot":{"active_tool_catalog_sha256":"ba34408b0c03b810ba1ef6190c41d215f58e88822148991099b7d70d444992fc","capture_source":"prepared model-client request","delivery_status":"unknown (capture does not prove provider delivery)","omitted_tool_count":0,"payload_json_bytes":14897,"payload_measurement_status":"exact (within 1048576-byte measurement bound)","provider":{"model":"deepseek-v4-pro","provider":"Deepseek","status":"available"},"registry_facts_present":true,"registry_only_tools":{"status":"known","value":{"count":40,"omitted":8,"rendered":[{"truncated":false,"value":"Git"},{"truncated":false,"value":"Run"},{"truncated":false,"value":"Web"},{"truncated":false,"value":"apply_patch"},{"truncated":false,"value":"automation"},{"truncated":false,"value":"diagnostics"},{"truncated":false,"value":"finance"},{"truncated":false,"value":"github"},{"truncated":false,"value":"grep_files"},{"truncated":false,"value":"handle_read"},{"truncated":false,"value":"harness"},{"truncated":false,"value":"image_ocr"},{"truncated":false,"value":"list_dir"},{"truncated":false,"value":"lsp"},{"truncated":false,"value":"note"},{"truncated":false,"value":"notify"},{"truncated":false,"value":"pandoc_convert"},{"truncated":false,"value":"project_map"},{"truncated":false,"value":"registry_sync"},{"truncated":false,"value":"request_plugin_install"},{"truncated":false,"value":"request_user_input"},{"truncated":false,"value":"retrieve_tool_result"},{"truncated":false,"value":"revert_turn"},{"truncated":false,"value":"review"},{"truncated":false,"value":"send_later"},{"truncated":false,"value":"session_get"},{"truncated":false,"value":"session_search"},{"truncated":false,"value":"start_mcp_server"},{"truncated":false,"value":"start_registry_mcp_server"},{"truncated":false,"value":"task_shell_wait"},{"truncated":false,"value":"tasks"},{"truncated":false,"value":"terminal/cancel"}]}},"registry_tool_count":{"status":"known","value":62},"rendered_tool_count":12,"schema_version":1,"step":0,"tool_count":12,"tools":[{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Required"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"ExecutesCode"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Execute a shell command in the workspace and return stdout and stderr. Output keeps the last 2000 lines or 50KB. An optional timeout is the seconds to wait in the foreground (default 30 seconds); a command still running then is never killed for time: it moves to the background by itself and you get its output so far, how long ago it last printed, its build phase, and a task_id, so long builds and tests need no special timeout. Find task_shell_wait with tool_search to read more or wait for it. In Ask, after "},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The command to execute. Actual execution shell: `/bin/bash`. Use Bash syntax: pipelines, redirections, $(command), and && / || are supported. Quote paths and variable expansions, such as \\\"$path\\\"; use single quotes for literal text. For literal multiline input, use a quoted heredoc delimiter (<<'EOF') with its closing delimiter on a separate line. Use only installed programs; do not assume GNU-specific flags on macOS/BSD. Example: printf '%s\\\\n' 'sample'.\",\"type\":\"string\"},\"justification\":{\"description\":\"Required with sandbox_permissions: one sentence explaining why this exact command needs wider access.\",\"type\":\"string\"},\"read_only\":{\"description\":\"Set true to run analysis code (including Python/SQLite) with mandatory native filesystem read-only isolation and no network. Available during peer writes. Refused when native enforcement is unavailable; no background, stdin, external backend, or sandbox escalation.\",\"type\":\"boolean\"},\"sandbox_permissions\":{\"description\":\"The wider sandbox mode this exact command needs. Use only as a one-shot retry after a sandbox denial; requires justification and user approval in Ask.\",\"enum\":[\"workspace-write\",\"danger-full-access\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional seconds to wait in the foreground (default 30 seconds). A command still running then is not killed: it moves to the background by itself and you get its output so far, how long ago it last printed, its build phase, and a task_id. Find task_shell_wait with tool_search to read more or wait for it.\",\"type\":\"number\"}},\"required\":[\"command\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"bash"},"ordinal":1,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":0,"omitted":0,"rendered":[]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Create the session's one persistent goal: a completion objective Codewhale keeps working toward across turns until it is verified complete, blocked, or the user stops it. A goal is for a durable objective that outlasts one turn: a multi-step outcome the user wants continued and verified. A question, a greeting, or a one-shot edit completes as an ordinary turn. When the user explicitly asks to use `/goal` or to make something the goal, `create_goal` is what records it; acknowledging it in prose records nothi"},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"objective\":{\"description\":\"The full objective to pursue. Keep the complete user goal, not a shortened one-turn version.\",\"type\":\"string\"},\"token_budget\":{\"description\":\"Optional soft token budget for the goal.\",\"minimum\":0,\"type\":\"integer\"}},\"required\":[\"objective\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"create_goal"},"ordinal":2,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Suggest"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Edit one file with targeted text replacements. Every edits[].oldText must identify a unique, non-overlapping region of the original file. Merge nearby or overlapping changes into one edit."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"edits\":{\"description\":\"One or more disjoint replacements, all matched against the original file.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"newText\":{\"description\":\"Replacement text; may be empty to delete the matched span.\",\"type\":\"string\"},\"oldText\":{\"description\":\"Text identifying one unique region to replace.\",\"type\":\"string\"}},\"required\":[\"newText\",\"oldText\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to the file to edit (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"edits\",\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"edit"},"ordinal":3,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":2,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"},{"truncated":false,"value":"Sandboxable"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Find workspace files by name, path, or glob (e.g. src/**/*.rs). Space-separated terms must all match. Literal matches rank above fuzzy matches; results are capped. Respects ignore files and skips build/dependency directories by default. Use extensions to filter suffixes."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"properties\":{\"exclude\":{\"description\":\"Optional replacement exclusion globs (e.g. [\\\"**/target/**\\\", \\\"*.lock\\\"]); [] disables default exclusions.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"extensions\":{\"description\":\"Optional list of file extensions to include (e.g. [\\\"rs\\\", \\\"md\\\"]).\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"limit\":{\"description\":\"Maximum number of ranked results to return (default: 20, maximum: 200).\",\"type\":\"integer\"},\"path\":{\"description\":\"Optional base path to search (relative to workspace).\",\"type\":\"string\"},\"query\":{\"description\":\"File name, path fragment, space-separated terms, or glob (e.g. **/engine*.rs).\",\"type\":\"string\"}},\"required\":[\"query\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"file_search"},"ordinal":4,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Inspect the current runtime goal state, including objective, status, token budget, elapsed time, evidence, and blocker."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{},\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"get_goal"},"ordinal":5,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Load a named skill's SKILL.md body and companion file list into this turn. Use when the user names a skill, or when an entry in the system prompt's `## Skills` index matches the task -- load it before starting the work, not after. Pass query=\"...\" to search names and descriptions, or name=\"list\" for the whole catalogue. Resolves global and plugin skills that `read` cannot reach."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"name\":{\"description\":\"Skill id to load. Omit or pass \\\"list\\\" to see all available skills.\",\"type\":\"string\"},\"query\":{\"description\":\"Search term matched against skill names and descriptions. Use when the index was truncated or no name is known.\",\"type\":\"string\"}},\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"load_skill"},"ordinal":6,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":2,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"},{"truncated":false,"value":"Sandboxable"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Read a file. A text file comes back whole in one call when it fits this call's output budget — 100000 bytes by default, raisable to 500000 with max_bytes. There is no line cap. Use offset and limit for an exact line range; when output is budget-limited the footer names the exact offset to continue from. Every text response reports the file's byte size, line count, and whether output was truncated. A PNG, JPEG, GIF or WebP image comes back as image content you can see (large images are downscaled), including"},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of lines to read.\",\"type\":\"number\"},\"max_bytes\":{\"description\":\"Output budget in bytes for this one call. Defaults to 100000; values above the 500000 maximum are clamped down rather than rejected, and a value below the active default leaves the default in place.\",\"type\":\"number\"},\"offset\":{\"description\":\"Line number to start reading from (1-indexed).\",\"type\":\"number\"},\"path\":{\"description\":\"Path to the file to read (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"read"},"ordinal":7,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Replace the To-do list the user watches. For any task with three or more steps or more than one file, write the list before you start, keep exactly one item in_progress, and mark items done as you finish them. Skip it only for a single quick answer or edit."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"properties\":{\"todos\":{\"description\":\"The complete list of To-do items. This replaces the existing list.\",\"items\":{\"properties\":{\"content\":{\"description\":\"The task description\",\"type\":\"string\"},\"status\":{\"description\":\"Task status\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"required\":[\"todos\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"todo_write"},"ordinal":8,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":0,"omitted":0,"rendered":[]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Update the runtime goal completion gate by calling this tool; a prose status in your answer does not change the goal or stop continuation. Critical verification may seal one immutable completion contract. Advisory review is append-only context and never completes, blocks, or pauses the goal. Mark blocked when progress requires user input. Mark yield when you finished a stage and the next step needs the user's decision: the goal stays unfinished, the turn ends, and their next message resumes it."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":true,"value":"{\"additionalProperties\":false,\"properties\":{\"advisory\":{\"description\":\"Required when status is advisory. Appended separately from the judged completion contract.\",\"type\":\"string\"},\"blocker\":{\"description\":\"Required when status is blocked. Explain the condition preventing progress.\",\"type\":\"string\"},\"evidence\":{\"description\":\"Required when status is complete. Briefly cite the proof that the goal is done.\",\"type\":\"string\"},\"progress\":{\"additionalProperties\":false,\"description\":\"Optional with not_achieved, advisory or yield: your current best estimate of overall completion, shown to the user as reported progress. Keep percent honest — it is an estimate, never a verified fraction.\",\"properties\":{\"next\":{\"description\":\"One short line: what comes next.\",\"type\":\"string\"},\"now\":{\"description\":\"One short line: what is being worked on right now.\",\"type\":\"string\"},\"percent\":{\"description\":\"Estimated percent complete, 0-100.\",\"maximum\":100,\"minimum\":0,\"type\":\"integer\"}},\"required\":[\"percent\"],\"type\":\"object\"},\"status\":{\"description\":\"Use complete only when a critical verifier proves the goal; not_achieved to record verifier gaps; blocked when meaningful progress cannot continue; yield when a stage is finished and the next step is the user's call; advisory to append best-effort context without changing lifecycle state.\",\"enum\":[\"complete\",\"blocked\",\"not_achieved\",\"advisory\",\"yield\"],\"type\":\"string\"},\"verification\":{\"additionalProperties\":false,\"description\":\"Required when status is complete or not_achieved. A verifier-as-judge receipt from a concrete check, such as Run action=\\\"verifiers\\\" or an equivalent project-specific gate.\",\"properties\":{\"check\":{\"description\":\"The verifier/check that passed.\",\"type\":\"string\"},\"gaps\":{\"description\":\"Concrete remaining gaps. Required for critical not_achieved reviews; order and duplicate wording do not affect the stall fingerprint.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"role\":{\"description\":\"Critical reviews may satisfy the judged completion contract. Advisory reviews are fail-ope"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"update_goal"},"ordinal":9,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Suggest"}},"cache_control_type":{"status":"known","value":{"truncated":false,"value":"ephemeral"}},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Write content to a file. Creates the file if it does not exist, overwrites it if it does, and creates parent directories automatically."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Content to write to the file.\",\"type\":\"string\"},\"path\":{\"description\":\"Path to the file to write (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"content\",\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"write"},"ordinal":10,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"reason":"tool is not in the registry","status":"unknown"},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"reason":"tool is not in the registry","status":"unknown"},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Run a JavaScript program that composes tool calls with `await tools.call(name, args)` and returns a bounded JSON result. Prefer it whenever you would make several dependent or repetitive calls, MCP and plugin tools included: intermediate results stay in the program and only what you return reaches the conversation. Every nested call passes the same permission checks as a direct call; a call that needs approval pauses the program until the user decides, and a denied or refused call throws inside the program "},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"type\":\"object\",\"properties\":{\"code\":{\"type\":\"string\",\"description\":\"JavaScript program. The return value (or thrown error) becomes the result; use tools.call(name, argsObject) for tool calls.\"}},\"required\":[\"code\"]}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"reason":"tool is not in the registry","status":"unknown"},"name":{"truncated":false,"value":"execute_tools"},"ordinal":11,"provenance":{"status":"known","value":"synthetic"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"status":"known","value":{"truncated":false,"value":"execute_tools_20260918"}},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"reason":"tool is not in the registry","status":"unknown"},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"reason":"tool is not in the registry","status":"unknown"},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Search deferred tool definitions and return matching tool references."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\",\"description\":\"Search query for tool discovery.\"},\"match\":{\"type\":\"string\",\"enum\":[\"bm25\",\"regex\"],\"default\":\"bm25\",\"description\":\"Matching algorithm: bm25 for natural-language matching, regex for a regular expression over tool names/descriptions/schema.\"},\"max_results\":{\"type\":\"integer\",\"minimum\":1,\"maximum\":8,\"default\":8,\"description\":\"Maximum number of matching tool references to return.\"}},\"required\":[\"query\"]}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"reason":"tool is not in the registry","status":"unknown"},"name":{"truncated":false,"value":"tool_search"},"ordinal":12,"provenance":{"status":"known","value":"synthetic"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"status":"known","value":{"truncated":false,"value":"tool_search_20251119"}},"visibility":"active"}],"tools_field_present":true,"turn_id":{"truncated":false,"value":""},"unavailable_for_this_request":["provider_wire_payload"]}} {"event":"route_dispatched","route":{"auto_model":false,"base_url":"https://api.deepseek.com/beta","billing":{"billing_mode":"metered","dispatched_at":""},"billing_product":{"kind":"unproven"},"model":"deepseek-v4-pro","provider":"deepseek","provider_identity":"deepseek"},"turn_id":""} {"event":"message_started","index":0} {"delta":"Here is a partial","event":"response_delta","index":0} {"duration_ms":"","event":"turn_usage","first_token_ms":"","request_ms":"","usage":{"input_tokens":120,"output_tokens":0}} {"event":"status","message":"Request cancelled"} {"engine_session_id":"conformance-session","event":"session_updated","messages":[{"content":[{"text":"Tell me a long story.","type":"text"},{"text":"\nCurrent local date: \nCurrent workspace: \nCurrent permission posture: Ask\nCurrent sandbox posture: workspace-write (writes inside the workspace; network blocked) (local OS sandbox applied)\n## Repo Working Set\nKey files: README.md\nWhen in doubt, use tools to verify and keep changes focused on the working set.\n","type":"text"}],"role":"user"},{"content":[{"text":"Here is a partial","type":"text"}],"role":"assistant_interrupted"}],"model":"deepseek-v4-pro","system_prompt":"","workspace":""} -{"event":"tool_request_snapshot","snapshot":{"active_tool_catalog_sha256":"18c6d652f11c0c195e7b586852ab46b47987939aaa316b39ef8b692255a7e077","capture_source":"prepared model-client request","delivery_status":"unknown (capture does not prove provider delivery)","omitted_tool_count":0,"payload_json_bytes":14890,"payload_measurement_status":"exact (within 1048576-byte measurement bound)","provider":{"model":"deepseek-v4-pro","provider":"Deepseek","status":"available"},"registry_facts_present":true,"registry_only_tools":{"status":"known","value":{"count":40,"omitted":8,"rendered":[{"truncated":false,"value":"Git"},{"truncated":false,"value":"Run"},{"truncated":false,"value":"Web"},{"truncated":false,"value":"apply_patch"},{"truncated":false,"value":"automation"},{"truncated":false,"value":"diagnostics"},{"truncated":false,"value":"finance"},{"truncated":false,"value":"github"},{"truncated":false,"value":"grep_files"},{"truncated":false,"value":"handle_read"},{"truncated":false,"value":"harness"},{"truncated":false,"value":"image_ocr"},{"truncated":false,"value":"list_dir"},{"truncated":false,"value":"lsp"},{"truncated":false,"value":"note"},{"truncated":false,"value":"notify"},{"truncated":false,"value":"pandoc_convert"},{"truncated":false,"value":"project_map"},{"truncated":false,"value":"registry_sync"},{"truncated":false,"value":"request_plugin_install"},{"truncated":false,"value":"request_user_input"},{"truncated":false,"value":"retrieve_tool_result"},{"truncated":false,"value":"revert_turn"},{"truncated":false,"value":"review"},{"truncated":false,"value":"send_later"},{"truncated":false,"value":"session_get"},{"truncated":false,"value":"session_search"},{"truncated":false,"value":"start_mcp_server"},{"truncated":false,"value":"start_registry_mcp_server"},{"truncated":false,"value":"task_shell_wait"},{"truncated":false,"value":"tasks"},{"truncated":false,"value":"terminal/cancel"}]}},"registry_tool_count":{"status":"known","value":62},"rendered_tool_count":12,"schema_version":1,"step":0,"terminal":{"automatic_compaction_attempts":0,"effective_max_steps":null,"emergency_compaction_attempts":0,"empty_stop_retries":0,"final_report_requested":false,"last_prepared_output_limit_tokens":65536,"last_provider_finish_reason":null,"last_reported_input_tokens":120,"last_response_tool_calls":0,"last_response_tool_calls_suppressed":null,"model_requests_started":1,"model_step_index":0,"permission_denial_rounds_without_progress":0,"permission_strategy_switches":0,"reason":"interrupted","reasoning_only_reprompts":0,"route_context_window_tokens":1000000,"soft_landing_sent":false,"status":"interrupted","step_budget_source":"max_steps","stream_resumes":0,"transparent_stream_retries":0,"transport_retries":0},"tool_count":12,"tools":[{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Required"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"ExecutesCode"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Execute a shell command in the workspace and return stdout and stderr. Output keeps the last 2000 lines or 50KB. An optional timeout is the seconds to wait in the foreground (default 30 seconds); a command still running then is never killed for time: it moves to the background by itself and you get its output so far, how long ago it last printed, its build phase, and a task_id, so long builds and tests need no special timeout. Find task_shell_wait with tool_search to read more or wait for it. In Ask, after "},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The command to execute. Actual execution shell: `/bin/bash`. Use Bash syntax: pipelines, redirections, $(command), and && / || are supported. Quote paths and variable expansions, such as \\\"$path\\\"; use single quotes for literal text. For literal multiline input, use a quoted heredoc delimiter (<<'EOF') with its closing delimiter on a separate line. Use only installed programs; do not assume GNU-specific flags on macOS/BSD. Example: printf '%s\\\\n' 'sample'.\",\"type\":\"string\"},\"justification\":{\"description\":\"Required with sandbox_permissions: one sentence explaining why this exact command needs wider access.\",\"type\":\"string\"},\"read_only\":{\"description\":\"Set true to run analysis code (including Python/SQLite) with mandatory native filesystem read-only isolation and no network. Available during peer writes. Refused when native enforcement is unavailable; no background, stdin, external backend, or sandbox escalation.\",\"type\":\"boolean\"},\"sandbox_permissions\":{\"description\":\"The wider sandbox mode this exact command needs. Use only as a one-shot retry after a sandbox denial; requires justification and user approval in Ask.\",\"enum\":[\"workspace-write\",\"danger-full-access\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional seconds to wait in the foreground (default 30 seconds). A command still running then is not killed: it moves to the background by itself and you get its output so far, how long ago it last printed, its build phase, and a task_id. Find task_shell_wait with tool_search to read more or wait for it.\",\"type\":\"number\"}},\"required\":[\"command\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"bash"},"ordinal":1,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":0,"omitted":0,"rendered":[]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Create the session's one persistent goal: a completion objective Codewhale keeps working toward across turns until it is verified complete, blocked, or the user stops it. A goal is for a durable objective that outlasts one turn: a multi-step outcome the user wants continued and verified. A question, a greeting, or a one-shot edit completes as an ordinary turn. When the user explicitly asks to use `/goal` or to make something the goal, `create_goal` is what records it; acknowledging it in prose records nothi"},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"objective\":{\"description\":\"The full objective to pursue. Keep the complete user goal, not a shortened one-turn version.\",\"type\":\"string\"},\"token_budget\":{\"description\":\"Optional soft token budget for the goal.\",\"minimum\":0,\"type\":\"integer\"}},\"required\":[\"objective\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"create_goal"},"ordinal":2,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Suggest"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Edit one file with targeted text replacements. Every edits[].oldText must identify a unique, non-overlapping region of the original file. Merge nearby or overlapping changes into one edit."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"edits\":{\"description\":\"One or more disjoint replacements, all matched against the original file.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"newText\":{\"description\":\"Replacement text; may be empty to delete the matched span.\",\"type\":\"string\"},\"oldText\":{\"description\":\"Text identifying one unique region to replace.\",\"type\":\"string\"}},\"required\":[\"newText\",\"oldText\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to the file to edit (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"edits\",\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"edit"},"ordinal":3,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":2,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"},{"truncated":false,"value":"Sandboxable"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Find workspace files by name, path, or glob (e.g. src/**/*.rs). Space-separated terms must all match. Literal matches rank above fuzzy matches; results are capped. Respects ignore files and skips build/dependency directories by default. Use extensions to filter suffixes."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"properties\":{\"exclude\":{\"description\":\"Optional replacement exclusion globs (e.g. [\\\"**/target/**\\\", \\\"*.lock\\\"]); [] disables default exclusions.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"extensions\":{\"description\":\"Optional list of file extensions to include (e.g. [\\\"rs\\\", \\\"md\\\"]).\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"limit\":{\"description\":\"Maximum number of ranked results to return (default: 20, maximum: 200).\",\"type\":\"integer\"},\"path\":{\"description\":\"Optional base path to search (relative to workspace).\",\"type\":\"string\"},\"query\":{\"description\":\"File name, path fragment, space-separated terms, or glob (e.g. **/engine*.rs).\",\"type\":\"string\"}},\"required\":[\"query\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"file_search"},"ordinal":4,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Inspect the current runtime goal state, including objective, status, token budget, elapsed time, evidence, and blocker."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{},\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"get_goal"},"ordinal":5,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Load a named skill's SKILL.md body and companion file list into this turn. Use when the user names a skill, or when an entry in the system prompt's `## Skills` index matches the task -- load it before starting the work, not after. Pass query=\"...\" to search names and descriptions, or name=\"list\" for the whole catalogue. Resolves global and plugin skills that `read` cannot reach."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"name\":{\"description\":\"Skill id to load. Omit or pass \\\"list\\\" to see all available skills.\",\"type\":\"string\"},\"query\":{\"description\":\"Search term matched against skill names and descriptions. Use when the index was truncated or no name is known.\",\"type\":\"string\"}},\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"load_skill"},"ordinal":6,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":2,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"},{"truncated":false,"value":"Sandboxable"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Read a file. A text file comes back whole in one call when it fits this call's output budget — 100000 bytes by default, raisable to 500000 with max_bytes. There is no line cap. Use offset and limit for an exact line range; when output is budget-limited the footer names the exact offset to continue from. Every text response reports the file's byte size, line count, and whether output was truncated. A PNG, JPEG, GIF or WebP image comes back as image content you can see (large images are downscaled), including"},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of lines to read.\",\"type\":\"number\"},\"max_bytes\":{\"description\":\"Output budget in bytes for this one call. Defaults to 100000; values above the 500000 maximum are clamped down rather than rejected, and a value below the active default leaves the default in place.\",\"type\":\"number\"},\"offset\":{\"description\":\"Line number to start reading from (1-indexed).\",\"type\":\"number\"},\"path\":{\"description\":\"Path to the file to read (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"read"},"ordinal":7,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Replace the To-do list the user watches. For any task with three or more steps or more than one file, write the list before you start, keep exactly one item in_progress, and mark items done as you finish them. Skip it only for a single quick answer or edit."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"properties\":{\"todos\":{\"description\":\"The complete list of To-do items. This replaces the existing list.\",\"items\":{\"properties\":{\"content\":{\"description\":\"The task description\",\"type\":\"string\"},\"status\":{\"description\":\"Task status\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"required\":[\"todos\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"todo_write"},"ordinal":8,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":0,"omitted":0,"rendered":[]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Update the runtime goal completion gate by calling this tool; a prose status in your answer does not change the goal or stop continuation. Critical verification may seal one immutable completion contract. Advisory review is append-only context and never completes, blocks, or pauses the goal. Mark blocked when progress requires user input. Mark yield when you finished a stage and the next step needs the user's decision: the goal stays unfinished, the turn ends, and their next message resumes it."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":true,"value":"{\"additionalProperties\":false,\"properties\":{\"advisory\":{\"description\":\"Required when status is advisory. Appended separately from the judged completion contract.\",\"type\":\"string\"},\"blocker\":{\"description\":\"Required when status is blocked. Explain the condition preventing progress.\",\"type\":\"string\"},\"evidence\":{\"description\":\"Required when status is complete. Briefly cite the proof that the goal is done.\",\"type\":\"string\"},\"progress\":{\"additionalProperties\":false,\"description\":\"Optional with not_achieved or advisory: your current best estimate of overall completion, shown to the user as reported progress. Keep percent honest — it is an estimate, never a verified fraction.\",\"properties\":{\"next\":{\"description\":\"One short line: what comes next.\",\"type\":\"string\"},\"now\":{\"description\":\"One short line: what is being worked on right now.\",\"type\":\"string\"},\"percent\":{\"description\":\"Estimated percent complete, 0-100.\",\"maximum\":100,\"minimum\":0,\"type\":\"integer\"}},\"required\":[\"percent\"],\"type\":\"object\"},\"status\":{\"description\":\"Use complete only when a critical verifier proves the goal; not_achieved to record verifier gaps; blocked when meaningful progress cannot continue; yield when a stage is finished and the next step is the user's call; advisory to append best-effort context without changing lifecycle state.\",\"enum\":[\"complete\",\"blocked\",\"not_achieved\",\"advisory\",\"yield\"],\"type\":\"string\"},\"verification\":{\"additionalProperties\":false,\"description\":\"Required when status is complete or not_achieved. A verifier-as-judge receipt from a concrete check, such as Run action=\\\"verifiers\\\" or an equivalent project-specific gate.\",\"properties\":{\"check\":{\"description\":\"The verifier/check that passed.\",\"type\":\"string\"},\"gaps\":{\"description\":\"Concrete remaining gaps. Required for critical not_achieved reviews; order and duplicate wording do not affect the stall fingerprint.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"role\":{\"description\":\"Critical reviews may satisfy the judged completion contract. Advisory reviews are fail-open and c"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"update_goal"},"ordinal":9,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Suggest"}},"cache_control_type":{"status":"known","value":{"truncated":false,"value":"ephemeral"}},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Write content to a file. Creates the file if it does not exist, overwrites it if it does, and creates parent directories automatically."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Content to write to the file.\",\"type\":\"string\"},\"path\":{\"description\":\"Path to the file to write (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"content\",\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"write"},"ordinal":10,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"reason":"tool is not in the registry","status":"unknown"},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"reason":"tool is not in the registry","status":"unknown"},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Run a JavaScript program that composes tool calls with `await tools.call(name, args)` and returns a bounded JSON result. Prefer it whenever you would make several dependent or repetitive calls, MCP and plugin tools included: intermediate results stay in the program and only what you return reaches the conversation. Every nested call passes the same permission checks as a direct call; a call that needs approval pauses the program until the user decides, and a denied or refused call throws inside the program "},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"type\":\"object\",\"properties\":{\"code\":{\"type\":\"string\",\"description\":\"JavaScript program. The return value (or thrown error) becomes the result; use tools.call(name, argsObject) for tool calls.\"}},\"required\":[\"code\"]}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"reason":"tool is not in the registry","status":"unknown"},"name":{"truncated":false,"value":"execute_tools"},"ordinal":11,"provenance":{"status":"known","value":"synthetic"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"status":"known","value":{"truncated":false,"value":"execute_tools_20260918"}},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"reason":"tool is not in the registry","status":"unknown"},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"reason":"tool is not in the registry","status":"unknown"},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Search deferred tool definitions and return matching tool references."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\",\"description\":\"Search query for tool discovery.\"},\"match\":{\"type\":\"string\",\"enum\":[\"bm25\",\"regex\"],\"default\":\"bm25\",\"description\":\"Matching algorithm: bm25 for natural-language matching, regex for a regular expression over tool names/descriptions/schema.\"},\"max_results\":{\"type\":\"integer\",\"minimum\":1,\"maximum\":8,\"default\":8,\"description\":\"Maximum number of matching tool references to return.\"}},\"required\":[\"query\"]}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"reason":"tool is not in the registry","status":"unknown"},"name":{"truncated":false,"value":"tool_search"},"ordinal":12,"provenance":{"status":"known","value":"synthetic"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"status":"known","value":{"truncated":false,"value":"tool_search_20251119"}},"visibility":"active"}],"tools_field_present":true,"turn_id":{"truncated":false,"value":""},"unavailable_for_this_request":["provider_wire_payload"]}} +{"event":"tool_request_snapshot","snapshot":{"active_tool_catalog_sha256":"ba34408b0c03b810ba1ef6190c41d215f58e88822148991099b7d70d444992fc","capture_source":"prepared model-client request","delivery_status":"unknown (capture does not prove provider delivery)","omitted_tool_count":0,"payload_json_bytes":14897,"payload_measurement_status":"exact (within 1048576-byte measurement bound)","provider":{"model":"deepseek-v4-pro","provider":"Deepseek","status":"available"},"registry_facts_present":true,"registry_only_tools":{"status":"known","value":{"count":40,"omitted":8,"rendered":[{"truncated":false,"value":"Git"},{"truncated":false,"value":"Run"},{"truncated":false,"value":"Web"},{"truncated":false,"value":"apply_patch"},{"truncated":false,"value":"automation"},{"truncated":false,"value":"diagnostics"},{"truncated":false,"value":"finance"},{"truncated":false,"value":"github"},{"truncated":false,"value":"grep_files"},{"truncated":false,"value":"handle_read"},{"truncated":false,"value":"harness"},{"truncated":false,"value":"image_ocr"},{"truncated":false,"value":"list_dir"},{"truncated":false,"value":"lsp"},{"truncated":false,"value":"note"},{"truncated":false,"value":"notify"},{"truncated":false,"value":"pandoc_convert"},{"truncated":false,"value":"project_map"},{"truncated":false,"value":"registry_sync"},{"truncated":false,"value":"request_plugin_install"},{"truncated":false,"value":"request_user_input"},{"truncated":false,"value":"retrieve_tool_result"},{"truncated":false,"value":"revert_turn"},{"truncated":false,"value":"review"},{"truncated":false,"value":"send_later"},{"truncated":false,"value":"session_get"},{"truncated":false,"value":"session_search"},{"truncated":false,"value":"start_mcp_server"},{"truncated":false,"value":"start_registry_mcp_server"},{"truncated":false,"value":"task_shell_wait"},{"truncated":false,"value":"tasks"},{"truncated":false,"value":"terminal/cancel"}]}},"registry_tool_count":{"status":"known","value":62},"rendered_tool_count":12,"schema_version":1,"step":0,"terminal":{"automatic_compaction_attempts":0,"effective_max_steps":null,"emergency_compaction_attempts":0,"empty_stop_retries":0,"final_report_requested":false,"last_prepared_output_limit_tokens":65536,"last_provider_finish_reason":null,"last_reported_input_tokens":120,"last_response_tool_calls":0,"last_response_tool_calls_suppressed":null,"model_requests_started":1,"model_step_index":0,"permission_denial_rounds_without_progress":0,"permission_strategy_switches":0,"reason":"interrupted","reasoning_only_reprompts":0,"route_context_window_tokens":1000000,"soft_landing_sent":false,"status":"interrupted","step_budget_source":"max_steps","stream_resumes":0,"transparent_stream_retries":0,"transport_retries":0},"tool_count":12,"tools":[{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Required"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"ExecutesCode"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Execute a shell command in the workspace and return stdout and stderr. Output keeps the last 2000 lines or 50KB. An optional timeout is the seconds to wait in the foreground (default 30 seconds); a command still running then is never killed for time: it moves to the background by itself and you get its output so far, how long ago it last printed, its build phase, and a task_id, so long builds and tests need no special timeout. Find task_shell_wait with tool_search to read more or wait for it. In Ask, after "},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The command to execute. Actual execution shell: `/bin/bash`. Use Bash syntax: pipelines, redirections, $(command), and && / || are supported. Quote paths and variable expansions, such as \\\"$path\\\"; use single quotes for literal text. For literal multiline input, use a quoted heredoc delimiter (<<'EOF') with its closing delimiter on a separate line. Use only installed programs; do not assume GNU-specific flags on macOS/BSD. Example: printf '%s\\\\n' 'sample'.\",\"type\":\"string\"},\"justification\":{\"description\":\"Required with sandbox_permissions: one sentence explaining why this exact command needs wider access.\",\"type\":\"string\"},\"read_only\":{\"description\":\"Set true to run analysis code (including Python/SQLite) with mandatory native filesystem read-only isolation and no network. Available during peer writes. Refused when native enforcement is unavailable; no background, stdin, external backend, or sandbox escalation.\",\"type\":\"boolean\"},\"sandbox_permissions\":{\"description\":\"The wider sandbox mode this exact command needs. Use only as a one-shot retry after a sandbox denial; requires justification and user approval in Ask.\",\"enum\":[\"workspace-write\",\"danger-full-access\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional seconds to wait in the foreground (default 30 seconds). A command still running then is not killed: it moves to the background by itself and you get its output so far, how long ago it last printed, its build phase, and a task_id. Find task_shell_wait with tool_search to read more or wait for it.\",\"type\":\"number\"}},\"required\":[\"command\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"bash"},"ordinal":1,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":0,"omitted":0,"rendered":[]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Create the session's one persistent goal: a completion objective Codewhale keeps working toward across turns until it is verified complete, blocked, or the user stops it. A goal is for a durable objective that outlasts one turn: a multi-step outcome the user wants continued and verified. A question, a greeting, or a one-shot edit completes as an ordinary turn. When the user explicitly asks to use `/goal` or to make something the goal, `create_goal` is what records it; acknowledging it in prose records nothi"},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"objective\":{\"description\":\"The full objective to pursue. Keep the complete user goal, not a shortened one-turn version.\",\"type\":\"string\"},\"token_budget\":{\"description\":\"Optional soft token budget for the goal.\",\"minimum\":0,\"type\":\"integer\"}},\"required\":[\"objective\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"create_goal"},"ordinal":2,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Suggest"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Edit one file with targeted text replacements. Every edits[].oldText must identify a unique, non-overlapping region of the original file. Merge nearby or overlapping changes into one edit."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"edits\":{\"description\":\"One or more disjoint replacements, all matched against the original file.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"newText\":{\"description\":\"Replacement text; may be empty to delete the matched span.\",\"type\":\"string\"},\"oldText\":{\"description\":\"Text identifying one unique region to replace.\",\"type\":\"string\"}},\"required\":[\"newText\",\"oldText\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to the file to edit (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"edits\",\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"edit"},"ordinal":3,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":2,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"},{"truncated":false,"value":"Sandboxable"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Find workspace files by name, path, or glob (e.g. src/**/*.rs). Space-separated terms must all match. Literal matches rank above fuzzy matches; results are capped. Respects ignore files and skips build/dependency directories by default. Use extensions to filter suffixes."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"properties\":{\"exclude\":{\"description\":\"Optional replacement exclusion globs (e.g. [\\\"**/target/**\\\", \\\"*.lock\\\"]); [] disables default exclusions.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"extensions\":{\"description\":\"Optional list of file extensions to include (e.g. [\\\"rs\\\", \\\"md\\\"]).\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"limit\":{\"description\":\"Maximum number of ranked results to return (default: 20, maximum: 200).\",\"type\":\"integer\"},\"path\":{\"description\":\"Optional base path to search (relative to workspace).\",\"type\":\"string\"},\"query\":{\"description\":\"File name, path fragment, space-separated terms, or glob (e.g. **/engine*.rs).\",\"type\":\"string\"}},\"required\":[\"query\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"file_search"},"ordinal":4,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Inspect the current runtime goal state, including objective, status, token budget, elapsed time, evidence, and blocker."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{},\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"get_goal"},"ordinal":5,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Load a named skill's SKILL.md body and companion file list into this turn. Use when the user names a skill, or when an entry in the system prompt's `## Skills` index matches the task -- load it before starting the work, not after. Pass query=\"...\" to search names and descriptions, or name=\"list\" for the whole catalogue. Resolves global and plugin skills that `read` cannot reach."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"name\":{\"description\":\"Skill id to load. Omit or pass \\\"list\\\" to see all available skills.\",\"type\":\"string\"},\"query\":{\"description\":\"Search term matched against skill names and descriptions. Use when the index was truncated or no name is known.\",\"type\":\"string\"}},\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"load_skill"},"ordinal":6,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":2,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"},{"truncated":false,"value":"Sandboxable"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Read a file. A text file comes back whole in one call when it fits this call's output budget — 100000 bytes by default, raisable to 500000 with max_bytes. There is no line cap. Use offset and limit for an exact line range; when output is budget-limited the footer names the exact offset to continue from. Every text response reports the file's byte size, line count, and whether output was truncated. A PNG, JPEG, GIF or WebP image comes back as image content you can see (large images are downscaled), including"},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of lines to read.\",\"type\":\"number\"},\"max_bytes\":{\"description\":\"Output budget in bytes for this one call. Defaults to 100000; values above the 500000 maximum are clamped down rather than rejected, and a value below the active default leaves the default in place.\",\"type\":\"number\"},\"offset\":{\"description\":\"Line number to start reading from (1-indexed).\",\"type\":\"number\"},\"path\":{\"description\":\"Path to the file to read (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"read"},"ordinal":7,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Replace the To-do list the user watches. For any task with three or more steps or more than one file, write the list before you start, keep exactly one item in_progress, and mark items done as you finish them. Skip it only for a single quick answer or edit."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"properties\":{\"todos\":{\"description\":\"The complete list of To-do items. This replaces the existing list.\",\"items\":{\"properties\":{\"content\":{\"description\":\"The task description\",\"type\":\"string\"},\"status\":{\"description\":\"Task status\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"required\":[\"todos\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"todo_write"},"ordinal":8,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":0,"omitted":0,"rendered":[]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Update the runtime goal completion gate by calling this tool; a prose status in your answer does not change the goal or stop continuation. Critical verification may seal one immutable completion contract. Advisory review is append-only context and never completes, blocks, or pauses the goal. Mark blocked when progress requires user input. Mark yield when you finished a stage and the next step needs the user's decision: the goal stays unfinished, the turn ends, and their next message resumes it."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":true,"value":"{\"additionalProperties\":false,\"properties\":{\"advisory\":{\"description\":\"Required when status is advisory. Appended separately from the judged completion contract.\",\"type\":\"string\"},\"blocker\":{\"description\":\"Required when status is blocked. Explain the condition preventing progress.\",\"type\":\"string\"},\"evidence\":{\"description\":\"Required when status is complete. Briefly cite the proof that the goal is done.\",\"type\":\"string\"},\"progress\":{\"additionalProperties\":false,\"description\":\"Optional with not_achieved, advisory or yield: your current best estimate of overall completion, shown to the user as reported progress. Keep percent honest — it is an estimate, never a verified fraction.\",\"properties\":{\"next\":{\"description\":\"One short line: what comes next.\",\"type\":\"string\"},\"now\":{\"description\":\"One short line: what is being worked on right now.\",\"type\":\"string\"},\"percent\":{\"description\":\"Estimated percent complete, 0-100.\",\"maximum\":100,\"minimum\":0,\"type\":\"integer\"}},\"required\":[\"percent\"],\"type\":\"object\"},\"status\":{\"description\":\"Use complete only when a critical verifier proves the goal; not_achieved to record verifier gaps; blocked when meaningful progress cannot continue; yield when a stage is finished and the next step is the user's call; advisory to append best-effort context without changing lifecycle state.\",\"enum\":[\"complete\",\"blocked\",\"not_achieved\",\"advisory\",\"yield\"],\"type\":\"string\"},\"verification\":{\"additionalProperties\":false,\"description\":\"Required when status is complete or not_achieved. A verifier-as-judge receipt from a concrete check, such as Run action=\\\"verifiers\\\" or an equivalent project-specific gate.\",\"properties\":{\"check\":{\"description\":\"The verifier/check that passed.\",\"type\":\"string\"},\"gaps\":{\"description\":\"Concrete remaining gaps. Required for critical not_achieved reviews; order and duplicate wording do not affect the stall fingerprint.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"role\":{\"description\":\"Critical reviews may satisfy the judged completion contract. Advisory reviews are fail-ope"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"update_goal"},"ordinal":9,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Suggest"}},"cache_control_type":{"status":"known","value":{"truncated":false,"value":"ephemeral"}},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Write content to a file. Creates the file if it does not exist, overwrites it if it does, and creates parent directories automatically."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Content to write to the file.\",\"type\":\"string\"},\"path\":{\"description\":\"Path to the file to write (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"content\",\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"write"},"ordinal":10,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"reason":"tool is not in the registry","status":"unknown"},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"reason":"tool is not in the registry","status":"unknown"},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Run a JavaScript program that composes tool calls with `await tools.call(name, args)` and returns a bounded JSON result. Prefer it whenever you would make several dependent or repetitive calls, MCP and plugin tools included: intermediate results stay in the program and only what you return reaches the conversation. Every nested call passes the same permission checks as a direct call; a call that needs approval pauses the program until the user decides, and a denied or refused call throws inside the program "},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"type\":\"object\",\"properties\":{\"code\":{\"type\":\"string\",\"description\":\"JavaScript program. The return value (or thrown error) becomes the result; use tools.call(name, argsObject) for tool calls.\"}},\"required\":[\"code\"]}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"reason":"tool is not in the registry","status":"unknown"},"name":{"truncated":false,"value":"execute_tools"},"ordinal":11,"provenance":{"status":"known","value":"synthetic"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"status":"known","value":{"truncated":false,"value":"execute_tools_20260918"}},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"reason":"tool is not in the registry","status":"unknown"},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"reason":"tool is not in the registry","status":"unknown"},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Search deferred tool definitions and return matching tool references."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\",\"description\":\"Search query for tool discovery.\"},\"match\":{\"type\":\"string\",\"enum\":[\"bm25\",\"regex\"],\"default\":\"bm25\",\"description\":\"Matching algorithm: bm25 for natural-language matching, regex for a regular expression over tool names/descriptions/schema.\"},\"max_results\":{\"type\":\"integer\",\"minimum\":1,\"maximum\":8,\"default\":8,\"description\":\"Maximum number of matching tool references to return.\"}},\"required\":[\"query\"]}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"reason":"tool is not in the registry","status":"unknown"},"name":{"truncated":false,"value":"tool_search"},"ordinal":12,"provenance":{"status":"known","value":"synthetic"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"status":"known","value":{"truncated":false,"value":"tool_search_20251119"}},"visibility":"active"}],"tools_field_present":true,"turn_id":{"truncated":false,"value":""},"unavailable_for_this_request":["provider_wire_payload"]}} {"event":"turn_complete","parent_route_usage":{"input_tokens":120,"output_tokens":0},"routed_usage_dropped_records":0,"status":"interrupted","tool_catalog":"","usage":{"input_tokens":120,"output_tokens":0}} {"event":"status","message":"Turn interrupted."} -{"harness_summary":{"model_requests":1,"non_streaming_requests":0,"workspace_after":{".codewhale/state/subagents.v1.lock":"e3b0c44298fc1c14","NOTES.md":"f957b19529906961","README.md":"41f49bffb38de5ba"}}} +{"harness_summary":{"model_requests":1,"non_streaming_requests":0,"workspace_after":{".codewhale/state/.gitignore":"cdbcae15105d6b78",".codewhale/state/subagents.v1.lock":"e3b0c44298fc1c14","NOTES.md":"f957b19529906961","README.md":"41f49bffb38de5ba"}}} diff --git a/crates/tui/tests/fixtures/conformance/events/one_tool_call.golden.jsonl b/crates/tui/tests/fixtures/conformance/events/one_tool_call.golden.jsonl index 0b16ad8f05..f30f81b017 100644 --- a/crates/tui/tests/fixtures/conformance/events/one_tool_call.golden.jsonl +++ b/crates/tui/tests/fixtures/conformance/events/one_tool_call.golden.jsonl @@ -1,8 +1,8 @@ {"created_at":"","event":"turn_started","route":{"auto_model":false,"base_url":"https://api.deepseek.com/beta","billing_product":{"kind":"unproven"},"model":"deepseek-v4-pro","provider":"deepseek","provider_identity":"deepseek"},"turn_id":""} {"engine_session_id":"conformance-session","event":"session_updated","messages":[{"content":[{"text":"What does README.md say?","type":"text"},{"text":"\nCurrent local date: \nCurrent workspace: \nCurrent permission posture: Ask\nCurrent sandbox posture: workspace-write (writes inside the workspace; network blocked) (local OS sandbox applied)\n## Repo Working Set\nKey files: README.md\nActive paths (prioritize these):\n- README.md (file)\nWhen in doubt, use tools to verify and keep changes focused on the working set.\n","type":"text"}],"role":"user"}],"model":"deepseek-v4-pro","system_prompt":"","workspace":""} {"changed":false,"context_updates":0,"description":"","event":"prefix_cache_change","last_miss_reason":"","pin_reason":"initial","pinned_combined_hash":"","stability_pct":100,"system_prompt_changed":false,"tools_changed":false} -{"changed":false,"context_updates":0,"description":"frozen: 8b8238c51355","event":"prefix_cache_change","last_miss_reason":"","pin_reason":"initial","pinned_combined_hash":"","stability_pct":100,"system_prompt_changed":false,"tools_changed":false} -{"event":"tool_request_snapshot","snapshot":{"active_tool_catalog_sha256":"18c6d652f11c0c195e7b586852ab46b47987939aaa316b39ef8b692255a7e077","capture_source":"prepared model-client request","delivery_status":"unknown (capture does not prove provider delivery)","omitted_tool_count":0,"payload_json_bytes":14890,"payload_measurement_status":"exact (within 1048576-byte measurement bound)","provider":{"model":"deepseek-v4-pro","provider":"Deepseek","status":"available"},"registry_facts_present":true,"registry_only_tools":{"status":"known","value":{"count":40,"omitted":8,"rendered":[{"truncated":false,"value":"Git"},{"truncated":false,"value":"Run"},{"truncated":false,"value":"Web"},{"truncated":false,"value":"apply_patch"},{"truncated":false,"value":"automation"},{"truncated":false,"value":"diagnostics"},{"truncated":false,"value":"finance"},{"truncated":false,"value":"github"},{"truncated":false,"value":"grep_files"},{"truncated":false,"value":"handle_read"},{"truncated":false,"value":"harness"},{"truncated":false,"value":"image_ocr"},{"truncated":false,"value":"list_dir"},{"truncated":false,"value":"lsp"},{"truncated":false,"value":"note"},{"truncated":false,"value":"notify"},{"truncated":false,"value":"pandoc_convert"},{"truncated":false,"value":"project_map"},{"truncated":false,"value":"registry_sync"},{"truncated":false,"value":"request_plugin_install"},{"truncated":false,"value":"request_user_input"},{"truncated":false,"value":"retrieve_tool_result"},{"truncated":false,"value":"revert_turn"},{"truncated":false,"value":"review"},{"truncated":false,"value":"send_later"},{"truncated":false,"value":"session_get"},{"truncated":false,"value":"session_search"},{"truncated":false,"value":"start_mcp_server"},{"truncated":false,"value":"start_registry_mcp_server"},{"truncated":false,"value":"task_shell_wait"},{"truncated":false,"value":"tasks"},{"truncated":false,"value":"terminal/cancel"}]}},"registry_tool_count":{"status":"known","value":62},"rendered_tool_count":12,"schema_version":1,"step":0,"tool_count":12,"tools":[{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Required"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"ExecutesCode"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Execute a shell command in the workspace and return stdout and stderr. Output keeps the last 2000 lines or 50KB. An optional timeout is the seconds to wait in the foreground (default 30 seconds); a command still running then is never killed for time: it moves to the background by itself and you get its output so far, how long ago it last printed, its build phase, and a task_id, so long builds and tests need no special timeout. Find task_shell_wait with tool_search to read more or wait for it. In Ask, after "},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The command to execute. Actual execution shell: `/bin/bash`. Use Bash syntax: pipelines, redirections, $(command), and && / || are supported. Quote paths and variable expansions, such as \\\"$path\\\"; use single quotes for literal text. For literal multiline input, use a quoted heredoc delimiter (<<'EOF') with its closing delimiter on a separate line. Use only installed programs; do not assume GNU-specific flags on macOS/BSD. Example: printf '%s\\\\n' 'sample'.\",\"type\":\"string\"},\"justification\":{\"description\":\"Required with sandbox_permissions: one sentence explaining why this exact command needs wider access.\",\"type\":\"string\"},\"read_only\":{\"description\":\"Set true to run analysis code (including Python/SQLite) with mandatory native filesystem read-only isolation and no network. Available during peer writes. Refused when native enforcement is unavailable; no background, stdin, external backend, or sandbox escalation.\",\"type\":\"boolean\"},\"sandbox_permissions\":{\"description\":\"The wider sandbox mode this exact command needs. Use only as a one-shot retry after a sandbox denial; requires justification and user approval in Ask.\",\"enum\":[\"workspace-write\",\"danger-full-access\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional seconds to wait in the foreground (default 30 seconds). A command still running then is not killed: it moves to the background by itself and you get its output so far, how long ago it last printed, its build phase, and a task_id. Find task_shell_wait with tool_search to read more or wait for it.\",\"type\":\"number\"}},\"required\":[\"command\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"bash"},"ordinal":1,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":0,"omitted":0,"rendered":[]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Create the session's one persistent goal: a completion objective Codewhale keeps working toward across turns until it is verified complete, blocked, or the user stops it. A goal is for a durable objective that outlasts one turn: a multi-step outcome the user wants continued and verified. A question, a greeting, or a one-shot edit completes as an ordinary turn. When the user explicitly asks to use `/goal` or to make something the goal, `create_goal` is what records it; acknowledging it in prose records nothi"},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"objective\":{\"description\":\"The full objective to pursue. Keep the complete user goal, not a shortened one-turn version.\",\"type\":\"string\"},\"token_budget\":{\"description\":\"Optional soft token budget for the goal.\",\"minimum\":0,\"type\":\"integer\"}},\"required\":[\"objective\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"create_goal"},"ordinal":2,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Suggest"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Edit one file with targeted text replacements. Every edits[].oldText must identify a unique, non-overlapping region of the original file. Merge nearby or overlapping changes into one edit."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"edits\":{\"description\":\"One or more disjoint replacements, all matched against the original file.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"newText\":{\"description\":\"Replacement text; may be empty to delete the matched span.\",\"type\":\"string\"},\"oldText\":{\"description\":\"Text identifying one unique region to replace.\",\"type\":\"string\"}},\"required\":[\"newText\",\"oldText\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to the file to edit (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"edits\",\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"edit"},"ordinal":3,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":2,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"},{"truncated":false,"value":"Sandboxable"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Find workspace files by name, path, or glob (e.g. src/**/*.rs). Space-separated terms must all match. Literal matches rank above fuzzy matches; results are capped. Respects ignore files and skips build/dependency directories by default. Use extensions to filter suffixes."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"properties\":{\"exclude\":{\"description\":\"Optional replacement exclusion globs (e.g. [\\\"**/target/**\\\", \\\"*.lock\\\"]); [] disables default exclusions.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"extensions\":{\"description\":\"Optional list of file extensions to include (e.g. [\\\"rs\\\", \\\"md\\\"]).\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"limit\":{\"description\":\"Maximum number of ranked results to return (default: 20, maximum: 200).\",\"type\":\"integer\"},\"path\":{\"description\":\"Optional base path to search (relative to workspace).\",\"type\":\"string\"},\"query\":{\"description\":\"File name, path fragment, space-separated terms, or glob (e.g. **/engine*.rs).\",\"type\":\"string\"}},\"required\":[\"query\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"file_search"},"ordinal":4,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Inspect the current runtime goal state, including objective, status, token budget, elapsed time, evidence, and blocker."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{},\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"get_goal"},"ordinal":5,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Load a named skill's SKILL.md body and companion file list into this turn. Use when the user names a skill, or when an entry in the system prompt's `## Skills` index matches the task -- load it before starting the work, not after. Pass query=\"...\" to search names and descriptions, or name=\"list\" for the whole catalogue. Resolves global and plugin skills that `read` cannot reach."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"name\":{\"description\":\"Skill id to load. Omit or pass \\\"list\\\" to see all available skills.\",\"type\":\"string\"},\"query\":{\"description\":\"Search term matched against skill names and descriptions. Use when the index was truncated or no name is known.\",\"type\":\"string\"}},\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"load_skill"},"ordinal":6,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":2,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"},{"truncated":false,"value":"Sandboxable"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Read a file. A text file comes back whole in one call when it fits this call's output budget — 100000 bytes by default, raisable to 500000 with max_bytes. There is no line cap. Use offset and limit for an exact line range; when output is budget-limited the footer names the exact offset to continue from. Every text response reports the file's byte size, line count, and whether output was truncated. A PNG, JPEG, GIF or WebP image comes back as image content you can see (large images are downscaled), including"},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of lines to read.\",\"type\":\"number\"},\"max_bytes\":{\"description\":\"Output budget in bytes for this one call. Defaults to 100000; values above the 500000 maximum are clamped down rather than rejected, and a value below the active default leaves the default in place.\",\"type\":\"number\"},\"offset\":{\"description\":\"Line number to start reading from (1-indexed).\",\"type\":\"number\"},\"path\":{\"description\":\"Path to the file to read (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"read"},"ordinal":7,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Replace the To-do list the user watches. For any task with three or more steps or more than one file, write the list before you start, keep exactly one item in_progress, and mark items done as you finish them. Skip it only for a single quick answer or edit."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"properties\":{\"todos\":{\"description\":\"The complete list of To-do items. This replaces the existing list.\",\"items\":{\"properties\":{\"content\":{\"description\":\"The task description\",\"type\":\"string\"},\"status\":{\"description\":\"Task status\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"required\":[\"todos\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"todo_write"},"ordinal":8,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":0,"omitted":0,"rendered":[]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Update the runtime goal completion gate by calling this tool; a prose status in your answer does not change the goal or stop continuation. Critical verification may seal one immutable completion contract. Advisory review is append-only context and never completes, blocks, or pauses the goal. Mark blocked when progress requires user input. Mark yield when you finished a stage and the next step needs the user's decision: the goal stays unfinished, the turn ends, and their next message resumes it."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":true,"value":"{\"additionalProperties\":false,\"properties\":{\"advisory\":{\"description\":\"Required when status is advisory. Appended separately from the judged completion contract.\",\"type\":\"string\"},\"blocker\":{\"description\":\"Required when status is blocked. Explain the condition preventing progress.\",\"type\":\"string\"},\"evidence\":{\"description\":\"Required when status is complete. Briefly cite the proof that the goal is done.\",\"type\":\"string\"},\"progress\":{\"additionalProperties\":false,\"description\":\"Optional with not_achieved or advisory: your current best estimate of overall completion, shown to the user as reported progress. Keep percent honest — it is an estimate, never a verified fraction.\",\"properties\":{\"next\":{\"description\":\"One short line: what comes next.\",\"type\":\"string\"},\"now\":{\"description\":\"One short line: what is being worked on right now.\",\"type\":\"string\"},\"percent\":{\"description\":\"Estimated percent complete, 0-100.\",\"maximum\":100,\"minimum\":0,\"type\":\"integer\"}},\"required\":[\"percent\"],\"type\":\"object\"},\"status\":{\"description\":\"Use complete only when a critical verifier proves the goal; not_achieved to record verifier gaps; blocked when meaningful progress cannot continue; yield when a stage is finished and the next step is the user's call; advisory to append best-effort context without changing lifecycle state.\",\"enum\":[\"complete\",\"blocked\",\"not_achieved\",\"advisory\",\"yield\"],\"type\":\"string\"},\"verification\":{\"additionalProperties\":false,\"description\":\"Required when status is complete or not_achieved. A verifier-as-judge receipt from a concrete check, such as Run action=\\\"verifiers\\\" or an equivalent project-specific gate.\",\"properties\":{\"check\":{\"description\":\"The verifier/check that passed.\",\"type\":\"string\"},\"gaps\":{\"description\":\"Concrete remaining gaps. Required for critical not_achieved reviews; order and duplicate wording do not affect the stall fingerprint.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"role\":{\"description\":\"Critical reviews may satisfy the judged completion contract. Advisory reviews are fail-open and c"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"update_goal"},"ordinal":9,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Suggest"}},"cache_control_type":{"status":"known","value":{"truncated":false,"value":"ephemeral"}},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Write content to a file. Creates the file if it does not exist, overwrites it if it does, and creates parent directories automatically."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Content to write to the file.\",\"type\":\"string\"},\"path\":{\"description\":\"Path to the file to write (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"content\",\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"write"},"ordinal":10,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"reason":"tool is not in the registry","status":"unknown"},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"reason":"tool is not in the registry","status":"unknown"},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Run a JavaScript program that composes tool calls with `await tools.call(name, args)` and returns a bounded JSON result. Prefer it whenever you would make several dependent or repetitive calls, MCP and plugin tools included: intermediate results stay in the program and only what you return reaches the conversation. Every nested call passes the same permission checks as a direct call; a call that needs approval pauses the program until the user decides, and a denied or refused call throws inside the program "},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"type\":\"object\",\"properties\":{\"code\":{\"type\":\"string\",\"description\":\"JavaScript program. The return value (or thrown error) becomes the result; use tools.call(name, argsObject) for tool calls.\"}},\"required\":[\"code\"]}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"reason":"tool is not in the registry","status":"unknown"},"name":{"truncated":false,"value":"execute_tools"},"ordinal":11,"provenance":{"status":"known","value":"synthetic"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"status":"known","value":{"truncated":false,"value":"execute_tools_20260918"}},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"reason":"tool is not in the registry","status":"unknown"},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"reason":"tool is not in the registry","status":"unknown"},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Search deferred tool definitions and return matching tool references."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\",\"description\":\"Search query for tool discovery.\"},\"match\":{\"type\":\"string\",\"enum\":[\"bm25\",\"regex\"],\"default\":\"bm25\",\"description\":\"Matching algorithm: bm25 for natural-language matching, regex for a regular expression over tool names/descriptions/schema.\"},\"max_results\":{\"type\":\"integer\",\"minimum\":1,\"maximum\":8,\"default\":8,\"description\":\"Maximum number of matching tool references to return.\"}},\"required\":[\"query\"]}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"reason":"tool is not in the registry","status":"unknown"},"name":{"truncated":false,"value":"tool_search"},"ordinal":12,"provenance":{"status":"known","value":"synthetic"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"status":"known","value":{"truncated":false,"value":"tool_search_20251119"}},"visibility":"active"}],"tools_field_present":true,"turn_id":{"truncated":false,"value":""},"unavailable_for_this_request":["provider_wire_payload"]}} +{"changed":false,"context_updates":0,"description":"frozen: e6c0d3cc59cd","event":"prefix_cache_change","last_miss_reason":"","pin_reason":"initial","pinned_combined_hash":"","stability_pct":100,"system_prompt_changed":false,"tools_changed":false} +{"event":"tool_request_snapshot","snapshot":{"active_tool_catalog_sha256":"ba34408b0c03b810ba1ef6190c41d215f58e88822148991099b7d70d444992fc","capture_source":"prepared model-client request","delivery_status":"unknown (capture does not prove provider delivery)","omitted_tool_count":0,"payload_json_bytes":14897,"payload_measurement_status":"exact (within 1048576-byte measurement bound)","provider":{"model":"deepseek-v4-pro","provider":"Deepseek","status":"available"},"registry_facts_present":true,"registry_only_tools":{"status":"known","value":{"count":40,"omitted":8,"rendered":[{"truncated":false,"value":"Git"},{"truncated":false,"value":"Run"},{"truncated":false,"value":"Web"},{"truncated":false,"value":"apply_patch"},{"truncated":false,"value":"automation"},{"truncated":false,"value":"diagnostics"},{"truncated":false,"value":"finance"},{"truncated":false,"value":"github"},{"truncated":false,"value":"grep_files"},{"truncated":false,"value":"handle_read"},{"truncated":false,"value":"harness"},{"truncated":false,"value":"image_ocr"},{"truncated":false,"value":"list_dir"},{"truncated":false,"value":"lsp"},{"truncated":false,"value":"note"},{"truncated":false,"value":"notify"},{"truncated":false,"value":"pandoc_convert"},{"truncated":false,"value":"project_map"},{"truncated":false,"value":"registry_sync"},{"truncated":false,"value":"request_plugin_install"},{"truncated":false,"value":"request_user_input"},{"truncated":false,"value":"retrieve_tool_result"},{"truncated":false,"value":"revert_turn"},{"truncated":false,"value":"review"},{"truncated":false,"value":"send_later"},{"truncated":false,"value":"session_get"},{"truncated":false,"value":"session_search"},{"truncated":false,"value":"start_mcp_server"},{"truncated":false,"value":"start_registry_mcp_server"},{"truncated":false,"value":"task_shell_wait"},{"truncated":false,"value":"tasks"},{"truncated":false,"value":"terminal/cancel"}]}},"registry_tool_count":{"status":"known","value":62},"rendered_tool_count":12,"schema_version":1,"step":0,"tool_count":12,"tools":[{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Required"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"ExecutesCode"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Execute a shell command in the workspace and return stdout and stderr. Output keeps the last 2000 lines or 50KB. An optional timeout is the seconds to wait in the foreground (default 30 seconds); a command still running then is never killed for time: it moves to the background by itself and you get its output so far, how long ago it last printed, its build phase, and a task_id, so long builds and tests need no special timeout. Find task_shell_wait with tool_search to read more or wait for it. In Ask, after "},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The command to execute. Actual execution shell: `/bin/bash`. Use Bash syntax: pipelines, redirections, $(command), and && / || are supported. Quote paths and variable expansions, such as \\\"$path\\\"; use single quotes for literal text. For literal multiline input, use a quoted heredoc delimiter (<<'EOF') with its closing delimiter on a separate line. Use only installed programs; do not assume GNU-specific flags on macOS/BSD. Example: printf '%s\\\\n' 'sample'.\",\"type\":\"string\"},\"justification\":{\"description\":\"Required with sandbox_permissions: one sentence explaining why this exact command needs wider access.\",\"type\":\"string\"},\"read_only\":{\"description\":\"Set true to run analysis code (including Python/SQLite) with mandatory native filesystem read-only isolation and no network. Available during peer writes. Refused when native enforcement is unavailable; no background, stdin, external backend, or sandbox escalation.\",\"type\":\"boolean\"},\"sandbox_permissions\":{\"description\":\"The wider sandbox mode this exact command needs. Use only as a one-shot retry after a sandbox denial; requires justification and user approval in Ask.\",\"enum\":[\"workspace-write\",\"danger-full-access\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional seconds to wait in the foreground (default 30 seconds). A command still running then is not killed: it moves to the background by itself and you get its output so far, how long ago it last printed, its build phase, and a task_id. Find task_shell_wait with tool_search to read more or wait for it.\",\"type\":\"number\"}},\"required\":[\"command\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"bash"},"ordinal":1,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":0,"omitted":0,"rendered":[]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Create the session's one persistent goal: a completion objective Codewhale keeps working toward across turns until it is verified complete, blocked, or the user stops it. A goal is for a durable objective that outlasts one turn: a multi-step outcome the user wants continued and verified. A question, a greeting, or a one-shot edit completes as an ordinary turn. When the user explicitly asks to use `/goal` or to make something the goal, `create_goal` is what records it; acknowledging it in prose records nothi"},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"objective\":{\"description\":\"The full objective to pursue. Keep the complete user goal, not a shortened one-turn version.\",\"type\":\"string\"},\"token_budget\":{\"description\":\"Optional soft token budget for the goal.\",\"minimum\":0,\"type\":\"integer\"}},\"required\":[\"objective\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"create_goal"},"ordinal":2,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Suggest"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Edit one file with targeted text replacements. Every edits[].oldText must identify a unique, non-overlapping region of the original file. Merge nearby or overlapping changes into one edit."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"edits\":{\"description\":\"One or more disjoint replacements, all matched against the original file.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"newText\":{\"description\":\"Replacement text; may be empty to delete the matched span.\",\"type\":\"string\"},\"oldText\":{\"description\":\"Text identifying one unique region to replace.\",\"type\":\"string\"}},\"required\":[\"newText\",\"oldText\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to the file to edit (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"edits\",\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"edit"},"ordinal":3,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":2,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"},{"truncated":false,"value":"Sandboxable"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Find workspace files by name, path, or glob (e.g. src/**/*.rs). Space-separated terms must all match. Literal matches rank above fuzzy matches; results are capped. Respects ignore files and skips build/dependency directories by default. Use extensions to filter suffixes."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"properties\":{\"exclude\":{\"description\":\"Optional replacement exclusion globs (e.g. [\\\"**/target/**\\\", \\\"*.lock\\\"]); [] disables default exclusions.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"extensions\":{\"description\":\"Optional list of file extensions to include (e.g. [\\\"rs\\\", \\\"md\\\"]).\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"limit\":{\"description\":\"Maximum number of ranked results to return (default: 20, maximum: 200).\",\"type\":\"integer\"},\"path\":{\"description\":\"Optional base path to search (relative to workspace).\",\"type\":\"string\"},\"query\":{\"description\":\"File name, path fragment, space-separated terms, or glob (e.g. **/engine*.rs).\",\"type\":\"string\"}},\"required\":[\"query\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"file_search"},"ordinal":4,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Inspect the current runtime goal state, including objective, status, token budget, elapsed time, evidence, and blocker."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{},\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"get_goal"},"ordinal":5,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Load a named skill's SKILL.md body and companion file list into this turn. Use when the user names a skill, or when an entry in the system prompt's `## Skills` index matches the task -- load it before starting the work, not after. Pass query=\"...\" to search names and descriptions, or name=\"list\" for the whole catalogue. Resolves global and plugin skills that `read` cannot reach."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"name\":{\"description\":\"Skill id to load. Omit or pass \\\"list\\\" to see all available skills.\",\"type\":\"string\"},\"query\":{\"description\":\"Search term matched against skill names and descriptions. Use when the index was truncated or no name is known.\",\"type\":\"string\"}},\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"load_skill"},"ordinal":6,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":2,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"},{"truncated":false,"value":"Sandboxable"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Read a file. A text file comes back whole in one call when it fits this call's output budget — 100000 bytes by default, raisable to 500000 with max_bytes. There is no line cap. Use offset and limit for an exact line range; when output is budget-limited the footer names the exact offset to continue from. Every text response reports the file's byte size, line count, and whether output was truncated. A PNG, JPEG, GIF or WebP image comes back as image content you can see (large images are downscaled), including"},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of lines to read.\",\"type\":\"number\"},\"max_bytes\":{\"description\":\"Output budget in bytes for this one call. Defaults to 100000; values above the 500000 maximum are clamped down rather than rejected, and a value below the active default leaves the default in place.\",\"type\":\"number\"},\"offset\":{\"description\":\"Line number to start reading from (1-indexed).\",\"type\":\"number\"},\"path\":{\"description\":\"Path to the file to read (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"read"},"ordinal":7,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Replace the To-do list the user watches. For any task with three or more steps or more than one file, write the list before you start, keep exactly one item in_progress, and mark items done as you finish them. Skip it only for a single quick answer or edit."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"properties\":{\"todos\":{\"description\":\"The complete list of To-do items. This replaces the existing list.\",\"items\":{\"properties\":{\"content\":{\"description\":\"The task description\",\"type\":\"string\"},\"status\":{\"description\":\"Task status\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"required\":[\"todos\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"todo_write"},"ordinal":8,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":0,"omitted":0,"rendered":[]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Update the runtime goal completion gate by calling this tool; a prose status in your answer does not change the goal or stop continuation. Critical verification may seal one immutable completion contract. Advisory review is append-only context and never completes, blocks, or pauses the goal. Mark blocked when progress requires user input. Mark yield when you finished a stage and the next step needs the user's decision: the goal stays unfinished, the turn ends, and their next message resumes it."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":true,"value":"{\"additionalProperties\":false,\"properties\":{\"advisory\":{\"description\":\"Required when status is advisory. Appended separately from the judged completion contract.\",\"type\":\"string\"},\"blocker\":{\"description\":\"Required when status is blocked. Explain the condition preventing progress.\",\"type\":\"string\"},\"evidence\":{\"description\":\"Required when status is complete. Briefly cite the proof that the goal is done.\",\"type\":\"string\"},\"progress\":{\"additionalProperties\":false,\"description\":\"Optional with not_achieved, advisory or yield: your current best estimate of overall completion, shown to the user as reported progress. Keep percent honest — it is an estimate, never a verified fraction.\",\"properties\":{\"next\":{\"description\":\"One short line: what comes next.\",\"type\":\"string\"},\"now\":{\"description\":\"One short line: what is being worked on right now.\",\"type\":\"string\"},\"percent\":{\"description\":\"Estimated percent complete, 0-100.\",\"maximum\":100,\"minimum\":0,\"type\":\"integer\"}},\"required\":[\"percent\"],\"type\":\"object\"},\"status\":{\"description\":\"Use complete only when a critical verifier proves the goal; not_achieved to record verifier gaps; blocked when meaningful progress cannot continue; yield when a stage is finished and the next step is the user's call; advisory to append best-effort context without changing lifecycle state.\",\"enum\":[\"complete\",\"blocked\",\"not_achieved\",\"advisory\",\"yield\"],\"type\":\"string\"},\"verification\":{\"additionalProperties\":false,\"description\":\"Required when status is complete or not_achieved. A verifier-as-judge receipt from a concrete check, such as Run action=\\\"verifiers\\\" or an equivalent project-specific gate.\",\"properties\":{\"check\":{\"description\":\"The verifier/check that passed.\",\"type\":\"string\"},\"gaps\":{\"description\":\"Concrete remaining gaps. Required for critical not_achieved reviews; order and duplicate wording do not affect the stall fingerprint.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"role\":{\"description\":\"Critical reviews may satisfy the judged completion contract. Advisory reviews are fail-ope"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"update_goal"},"ordinal":9,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Suggest"}},"cache_control_type":{"status":"known","value":{"truncated":false,"value":"ephemeral"}},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Write content to a file. Creates the file if it does not exist, overwrites it if it does, and creates parent directories automatically."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Content to write to the file.\",\"type\":\"string\"},\"path\":{\"description\":\"Path to the file to write (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"content\",\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"write"},"ordinal":10,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"reason":"tool is not in the registry","status":"unknown"},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"reason":"tool is not in the registry","status":"unknown"},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Run a JavaScript program that composes tool calls with `await tools.call(name, args)` and returns a bounded JSON result. Prefer it whenever you would make several dependent or repetitive calls, MCP and plugin tools included: intermediate results stay in the program and only what you return reaches the conversation. Every nested call passes the same permission checks as a direct call; a call that needs approval pauses the program until the user decides, and a denied or refused call throws inside the program "},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"type\":\"object\",\"properties\":{\"code\":{\"type\":\"string\",\"description\":\"JavaScript program. The return value (or thrown error) becomes the result; use tools.call(name, argsObject) for tool calls.\"}},\"required\":[\"code\"]}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"reason":"tool is not in the registry","status":"unknown"},"name":{"truncated":false,"value":"execute_tools"},"ordinal":11,"provenance":{"status":"known","value":"synthetic"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"status":"known","value":{"truncated":false,"value":"execute_tools_20260918"}},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"reason":"tool is not in the registry","status":"unknown"},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"reason":"tool is not in the registry","status":"unknown"},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Search deferred tool definitions and return matching tool references."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\",\"description\":\"Search query for tool discovery.\"},\"match\":{\"type\":\"string\",\"enum\":[\"bm25\",\"regex\"],\"default\":\"bm25\",\"description\":\"Matching algorithm: bm25 for natural-language matching, regex for a regular expression over tool names/descriptions/schema.\"},\"max_results\":{\"type\":\"integer\",\"minimum\":1,\"maximum\":8,\"default\":8,\"description\":\"Maximum number of matching tool references to return.\"}},\"required\":[\"query\"]}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"reason":"tool is not in the registry","status":"unknown"},"name":{"truncated":false,"value":"tool_search"},"ordinal":12,"provenance":{"status":"known","value":"synthetic"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"status":"known","value":{"truncated":false,"value":"tool_search_20251119"}},"visibility":"active"}],"tools_field_present":true,"turn_id":{"truncated":false,"value":""},"unavailable_for_this_request":["provider_wire_payload"]}} {"event":"route_dispatched","route":{"auto_model":false,"base_url":"https://api.deepseek.com/beta","billing":{"billing_mode":"metered","dispatched_at":""},"billing_product":{"kind":"unproven"},"model":"deepseek-v4-pro","provider":"deepseek","provider_identity":"deepseek"},"turn_id":""} {"duration_ms":"","event":"turn_usage","first_token_ms":"","request_ms":"","usage":{"input_tokens":150,"output_tokens":20}} {"event":"tool_call_started","input":{"action":"read","path":"README.md"},"tool_call_id":"","tool_name":"File"} @@ -13,12 +13,12 @@ {"engine_session_id":"conformance-session","event":"session_updated","messages":[{"content":[{"text":"What does README.md say?","type":"text"},{"text":"\nCurrent local date: \nCurrent workspace: \nCurrent permission posture: Ask\nCurrent sandbox posture: workspace-write (writes inside the workspace; network blocked) (local OS sandbox applied)\n## Repo Working Set\nKey files: README.md\nActive paths (prioritize these):\n- README.md (file)\nWhen in doubt, use tools to verify and keep changes focused on the working set.\n","type":"text"}],"role":"user"},{"content":[{"execution_id":"","id":"call_read_readme","input":{"action":"read","path":"README.md"},"name":"File","type":"tool_use"}],"role":"assistant"},{"content":[{"content":"content_hash=\"sha256:41f49bffb38de5ba88945f09121ffc590f9d7f6ff80fdb9ea8ad10224fecea61\"\nconformance fixture","execution_id":"","tool_use_id":"call_read_readme","type":"tool_result"}],"role":"user"}],"model":"deepseek-v4-pro","system_prompt":"","workspace":""} {"event":"status","message":"Continuing — tool results"} {"changed":false,"context_updates":0,"description":"","event":"prefix_cache_change","last_miss_reason":"","pin_reason":"initial","pinned_combined_hash":"","stability_pct":100,"system_prompt_changed":false,"tools_changed":false} -{"event":"tool_request_snapshot","snapshot":{"active_tool_catalog_sha256":"18c6d652f11c0c195e7b586852ab46b47987939aaa316b39ef8b692255a7e077","capture_source":"prepared model-client request","delivery_status":"unknown (capture does not prove provider delivery)","omitted_tool_count":0,"payload_json_bytes":14890,"payload_measurement_status":"exact (within 1048576-byte measurement bound)","provider":{"model":"deepseek-v4-pro","provider":"Deepseek","status":"available"},"registry_facts_present":true,"registry_only_tools":{"status":"known","value":{"count":40,"omitted":8,"rendered":[{"truncated":false,"value":"Git"},{"truncated":false,"value":"Run"},{"truncated":false,"value":"Web"},{"truncated":false,"value":"apply_patch"},{"truncated":false,"value":"automation"},{"truncated":false,"value":"diagnostics"},{"truncated":false,"value":"finance"},{"truncated":false,"value":"github"},{"truncated":false,"value":"grep_files"},{"truncated":false,"value":"handle_read"},{"truncated":false,"value":"harness"},{"truncated":false,"value":"image_ocr"},{"truncated":false,"value":"list_dir"},{"truncated":false,"value":"lsp"},{"truncated":false,"value":"note"},{"truncated":false,"value":"notify"},{"truncated":false,"value":"pandoc_convert"},{"truncated":false,"value":"project_map"},{"truncated":false,"value":"registry_sync"},{"truncated":false,"value":"request_plugin_install"},{"truncated":false,"value":"request_user_input"},{"truncated":false,"value":"retrieve_tool_result"},{"truncated":false,"value":"revert_turn"},{"truncated":false,"value":"review"},{"truncated":false,"value":"send_later"},{"truncated":false,"value":"session_get"},{"truncated":false,"value":"session_search"},{"truncated":false,"value":"start_mcp_server"},{"truncated":false,"value":"start_registry_mcp_server"},{"truncated":false,"value":"task_shell_wait"},{"truncated":false,"value":"tasks"},{"truncated":false,"value":"terminal/cancel"}]}},"registry_tool_count":{"status":"known","value":62},"rendered_tool_count":12,"schema_version":1,"step":1,"tool_count":12,"tools":[{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Required"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"ExecutesCode"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Execute a shell command in the workspace and return stdout and stderr. Output keeps the last 2000 lines or 50KB. An optional timeout is the seconds to wait in the foreground (default 30 seconds); a command still running then is never killed for time: it moves to the background by itself and you get its output so far, how long ago it last printed, its build phase, and a task_id, so long builds and tests need no special timeout. Find task_shell_wait with tool_search to read more or wait for it. In Ask, after "},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The command to execute. Actual execution shell: `/bin/bash`. Use Bash syntax: pipelines, redirections, $(command), and && / || are supported. Quote paths and variable expansions, such as \\\"$path\\\"; use single quotes for literal text. For literal multiline input, use a quoted heredoc delimiter (<<'EOF') with its closing delimiter on a separate line. Use only installed programs; do not assume GNU-specific flags on macOS/BSD. Example: printf '%s\\\\n' 'sample'.\",\"type\":\"string\"},\"justification\":{\"description\":\"Required with sandbox_permissions: one sentence explaining why this exact command needs wider access.\",\"type\":\"string\"},\"read_only\":{\"description\":\"Set true to run analysis code (including Python/SQLite) with mandatory native filesystem read-only isolation and no network. Available during peer writes. Refused when native enforcement is unavailable; no background, stdin, external backend, or sandbox escalation.\",\"type\":\"boolean\"},\"sandbox_permissions\":{\"description\":\"The wider sandbox mode this exact command needs. Use only as a one-shot retry after a sandbox denial; requires justification and user approval in Ask.\",\"enum\":[\"workspace-write\",\"danger-full-access\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional seconds to wait in the foreground (default 30 seconds). A command still running then is not killed: it moves to the background by itself and you get its output so far, how long ago it last printed, its build phase, and a task_id. Find task_shell_wait with tool_search to read more or wait for it.\",\"type\":\"number\"}},\"required\":[\"command\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"bash"},"ordinal":1,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":0,"omitted":0,"rendered":[]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Create the session's one persistent goal: a completion objective Codewhale keeps working toward across turns until it is verified complete, blocked, or the user stops it. A goal is for a durable objective that outlasts one turn: a multi-step outcome the user wants continued and verified. A question, a greeting, or a one-shot edit completes as an ordinary turn. When the user explicitly asks to use `/goal` or to make something the goal, `create_goal` is what records it; acknowledging it in prose records nothi"},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"objective\":{\"description\":\"The full objective to pursue. Keep the complete user goal, not a shortened one-turn version.\",\"type\":\"string\"},\"token_budget\":{\"description\":\"Optional soft token budget for the goal.\",\"minimum\":0,\"type\":\"integer\"}},\"required\":[\"objective\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"create_goal"},"ordinal":2,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Suggest"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Edit one file with targeted text replacements. Every edits[].oldText must identify a unique, non-overlapping region of the original file. Merge nearby or overlapping changes into one edit."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"edits\":{\"description\":\"One or more disjoint replacements, all matched against the original file.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"newText\":{\"description\":\"Replacement text; may be empty to delete the matched span.\",\"type\":\"string\"},\"oldText\":{\"description\":\"Text identifying one unique region to replace.\",\"type\":\"string\"}},\"required\":[\"newText\",\"oldText\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to the file to edit (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"edits\",\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"edit"},"ordinal":3,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":2,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"},{"truncated":false,"value":"Sandboxable"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Find workspace files by name, path, or glob (e.g. src/**/*.rs). Space-separated terms must all match. Literal matches rank above fuzzy matches; results are capped. Respects ignore files and skips build/dependency directories by default. Use extensions to filter suffixes."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"properties\":{\"exclude\":{\"description\":\"Optional replacement exclusion globs (e.g. [\\\"**/target/**\\\", \\\"*.lock\\\"]); [] disables default exclusions.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"extensions\":{\"description\":\"Optional list of file extensions to include (e.g. [\\\"rs\\\", \\\"md\\\"]).\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"limit\":{\"description\":\"Maximum number of ranked results to return (default: 20, maximum: 200).\",\"type\":\"integer\"},\"path\":{\"description\":\"Optional base path to search (relative to workspace).\",\"type\":\"string\"},\"query\":{\"description\":\"File name, path fragment, space-separated terms, or glob (e.g. **/engine*.rs).\",\"type\":\"string\"}},\"required\":[\"query\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"file_search"},"ordinal":4,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Inspect the current runtime goal state, including objective, status, token budget, elapsed time, evidence, and blocker."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{},\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"get_goal"},"ordinal":5,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Load a named skill's SKILL.md body and companion file list into this turn. Use when the user names a skill, or when an entry in the system prompt's `## Skills` index matches the task -- load it before starting the work, not after. Pass query=\"...\" to search names and descriptions, or name=\"list\" for the whole catalogue. Resolves global and plugin skills that `read` cannot reach."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"name\":{\"description\":\"Skill id to load. Omit or pass \\\"list\\\" to see all available skills.\",\"type\":\"string\"},\"query\":{\"description\":\"Search term matched against skill names and descriptions. Use when the index was truncated or no name is known.\",\"type\":\"string\"}},\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"load_skill"},"ordinal":6,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":2,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"},{"truncated":false,"value":"Sandboxable"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Read a file. A text file comes back whole in one call when it fits this call's output budget — 100000 bytes by default, raisable to 500000 with max_bytes. There is no line cap. Use offset and limit for an exact line range; when output is budget-limited the footer names the exact offset to continue from. Every text response reports the file's byte size, line count, and whether output was truncated. A PNG, JPEG, GIF or WebP image comes back as image content you can see (large images are downscaled), including"},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of lines to read.\",\"type\":\"number\"},\"max_bytes\":{\"description\":\"Output budget in bytes for this one call. Defaults to 100000; values above the 500000 maximum are clamped down rather than rejected, and a value below the active default leaves the default in place.\",\"type\":\"number\"},\"offset\":{\"description\":\"Line number to start reading from (1-indexed).\",\"type\":\"number\"},\"path\":{\"description\":\"Path to the file to read (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"read"},"ordinal":7,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Replace the To-do list the user watches. For any task with three or more steps or more than one file, write the list before you start, keep exactly one item in_progress, and mark items done as you finish them. Skip it only for a single quick answer or edit."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"properties\":{\"todos\":{\"description\":\"The complete list of To-do items. This replaces the existing list.\",\"items\":{\"properties\":{\"content\":{\"description\":\"The task description\",\"type\":\"string\"},\"status\":{\"description\":\"Task status\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"required\":[\"todos\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"todo_write"},"ordinal":8,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":0,"omitted":0,"rendered":[]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Update the runtime goal completion gate by calling this tool; a prose status in your answer does not change the goal or stop continuation. Critical verification may seal one immutable completion contract. Advisory review is append-only context and never completes, blocks, or pauses the goal. Mark blocked when progress requires user input. Mark yield when you finished a stage and the next step needs the user's decision: the goal stays unfinished, the turn ends, and their next message resumes it."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":true,"value":"{\"additionalProperties\":false,\"properties\":{\"advisory\":{\"description\":\"Required when status is advisory. Appended separately from the judged completion contract.\",\"type\":\"string\"},\"blocker\":{\"description\":\"Required when status is blocked. Explain the condition preventing progress.\",\"type\":\"string\"},\"evidence\":{\"description\":\"Required when status is complete. Briefly cite the proof that the goal is done.\",\"type\":\"string\"},\"progress\":{\"additionalProperties\":false,\"description\":\"Optional with not_achieved or advisory: your current best estimate of overall completion, shown to the user as reported progress. Keep percent honest — it is an estimate, never a verified fraction.\",\"properties\":{\"next\":{\"description\":\"One short line: what comes next.\",\"type\":\"string\"},\"now\":{\"description\":\"One short line: what is being worked on right now.\",\"type\":\"string\"},\"percent\":{\"description\":\"Estimated percent complete, 0-100.\",\"maximum\":100,\"minimum\":0,\"type\":\"integer\"}},\"required\":[\"percent\"],\"type\":\"object\"},\"status\":{\"description\":\"Use complete only when a critical verifier proves the goal; not_achieved to record verifier gaps; blocked when meaningful progress cannot continue; yield when a stage is finished and the next step is the user's call; advisory to append best-effort context without changing lifecycle state.\",\"enum\":[\"complete\",\"blocked\",\"not_achieved\",\"advisory\",\"yield\"],\"type\":\"string\"},\"verification\":{\"additionalProperties\":false,\"description\":\"Required when status is complete or not_achieved. A verifier-as-judge receipt from a concrete check, such as Run action=\\\"verifiers\\\" or an equivalent project-specific gate.\",\"properties\":{\"check\":{\"description\":\"The verifier/check that passed.\",\"type\":\"string\"},\"gaps\":{\"description\":\"Concrete remaining gaps. Required for critical not_achieved reviews; order and duplicate wording do not affect the stall fingerprint.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"role\":{\"description\":\"Critical reviews may satisfy the judged completion contract. Advisory reviews are fail-open and c"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"update_goal"},"ordinal":9,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Suggest"}},"cache_control_type":{"status":"known","value":{"truncated":false,"value":"ephemeral"}},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Write content to a file. Creates the file if it does not exist, overwrites it if it does, and creates parent directories automatically."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Content to write to the file.\",\"type\":\"string\"},\"path\":{\"description\":\"Path to the file to write (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"content\",\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"write"},"ordinal":10,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"reason":"tool is not in the registry","status":"unknown"},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"reason":"tool is not in the registry","status":"unknown"},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Run a JavaScript program that composes tool calls with `await tools.call(name, args)` and returns a bounded JSON result. Prefer it whenever you would make several dependent or repetitive calls, MCP and plugin tools included: intermediate results stay in the program and only what you return reaches the conversation. Every nested call passes the same permission checks as a direct call; a call that needs approval pauses the program until the user decides, and a denied or refused call throws inside the program "},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"type\":\"object\",\"properties\":{\"code\":{\"type\":\"string\",\"description\":\"JavaScript program. The return value (or thrown error) becomes the result; use tools.call(name, argsObject) for tool calls.\"}},\"required\":[\"code\"]}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"reason":"tool is not in the registry","status":"unknown"},"name":{"truncated":false,"value":"execute_tools"},"ordinal":11,"provenance":{"status":"known","value":"synthetic"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"status":"known","value":{"truncated":false,"value":"execute_tools_20260918"}},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"reason":"tool is not in the registry","status":"unknown"},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"reason":"tool is not in the registry","status":"unknown"},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Search deferred tool definitions and return matching tool references."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\",\"description\":\"Search query for tool discovery.\"},\"match\":{\"type\":\"string\",\"enum\":[\"bm25\",\"regex\"],\"default\":\"bm25\",\"description\":\"Matching algorithm: bm25 for natural-language matching, regex for a regular expression over tool names/descriptions/schema.\"},\"max_results\":{\"type\":\"integer\",\"minimum\":1,\"maximum\":8,\"default\":8,\"description\":\"Maximum number of matching tool references to return.\"}},\"required\":[\"query\"]}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"reason":"tool is not in the registry","status":"unknown"},"name":{"truncated":false,"value":"tool_search"},"ordinal":12,"provenance":{"status":"known","value":"synthetic"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"status":"known","value":{"truncated":false,"value":"tool_search_20251119"}},"visibility":"active"}],"tools_field_present":true,"turn_id":{"truncated":false,"value":""},"unavailable_for_this_request":["provider_wire_payload"]}} +{"event":"tool_request_snapshot","snapshot":{"active_tool_catalog_sha256":"ba34408b0c03b810ba1ef6190c41d215f58e88822148991099b7d70d444992fc","capture_source":"prepared model-client request","delivery_status":"unknown (capture does not prove provider delivery)","omitted_tool_count":0,"payload_json_bytes":14897,"payload_measurement_status":"exact (within 1048576-byte measurement bound)","provider":{"model":"deepseek-v4-pro","provider":"Deepseek","status":"available"},"registry_facts_present":true,"registry_only_tools":{"status":"known","value":{"count":40,"omitted":8,"rendered":[{"truncated":false,"value":"Git"},{"truncated":false,"value":"Run"},{"truncated":false,"value":"Web"},{"truncated":false,"value":"apply_patch"},{"truncated":false,"value":"automation"},{"truncated":false,"value":"diagnostics"},{"truncated":false,"value":"finance"},{"truncated":false,"value":"github"},{"truncated":false,"value":"grep_files"},{"truncated":false,"value":"handle_read"},{"truncated":false,"value":"harness"},{"truncated":false,"value":"image_ocr"},{"truncated":false,"value":"list_dir"},{"truncated":false,"value":"lsp"},{"truncated":false,"value":"note"},{"truncated":false,"value":"notify"},{"truncated":false,"value":"pandoc_convert"},{"truncated":false,"value":"project_map"},{"truncated":false,"value":"registry_sync"},{"truncated":false,"value":"request_plugin_install"},{"truncated":false,"value":"request_user_input"},{"truncated":false,"value":"retrieve_tool_result"},{"truncated":false,"value":"revert_turn"},{"truncated":false,"value":"review"},{"truncated":false,"value":"send_later"},{"truncated":false,"value":"session_get"},{"truncated":false,"value":"session_search"},{"truncated":false,"value":"start_mcp_server"},{"truncated":false,"value":"start_registry_mcp_server"},{"truncated":false,"value":"task_shell_wait"},{"truncated":false,"value":"tasks"},{"truncated":false,"value":"terminal/cancel"}]}},"registry_tool_count":{"status":"known","value":62},"rendered_tool_count":12,"schema_version":1,"step":1,"tool_count":12,"tools":[{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Required"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"ExecutesCode"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Execute a shell command in the workspace and return stdout and stderr. Output keeps the last 2000 lines or 50KB. An optional timeout is the seconds to wait in the foreground (default 30 seconds); a command still running then is never killed for time: it moves to the background by itself and you get its output so far, how long ago it last printed, its build phase, and a task_id, so long builds and tests need no special timeout. Find task_shell_wait with tool_search to read more or wait for it. In Ask, after "},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The command to execute. Actual execution shell: `/bin/bash`. Use Bash syntax: pipelines, redirections, $(command), and && / || are supported. Quote paths and variable expansions, such as \\\"$path\\\"; use single quotes for literal text. For literal multiline input, use a quoted heredoc delimiter (<<'EOF') with its closing delimiter on a separate line. Use only installed programs; do not assume GNU-specific flags on macOS/BSD. Example: printf '%s\\\\n' 'sample'.\",\"type\":\"string\"},\"justification\":{\"description\":\"Required with sandbox_permissions: one sentence explaining why this exact command needs wider access.\",\"type\":\"string\"},\"read_only\":{\"description\":\"Set true to run analysis code (including Python/SQLite) with mandatory native filesystem read-only isolation and no network. Available during peer writes. Refused when native enforcement is unavailable; no background, stdin, external backend, or sandbox escalation.\",\"type\":\"boolean\"},\"sandbox_permissions\":{\"description\":\"The wider sandbox mode this exact command needs. Use only as a one-shot retry after a sandbox denial; requires justification and user approval in Ask.\",\"enum\":[\"workspace-write\",\"danger-full-access\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional seconds to wait in the foreground (default 30 seconds). A command still running then is not killed: it moves to the background by itself and you get its output so far, how long ago it last printed, its build phase, and a task_id. Find task_shell_wait with tool_search to read more or wait for it.\",\"type\":\"number\"}},\"required\":[\"command\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"bash"},"ordinal":1,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":0,"omitted":0,"rendered":[]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Create the session's one persistent goal: a completion objective Codewhale keeps working toward across turns until it is verified complete, blocked, or the user stops it. A goal is for a durable objective that outlasts one turn: a multi-step outcome the user wants continued and verified. A question, a greeting, or a one-shot edit completes as an ordinary turn. When the user explicitly asks to use `/goal` or to make something the goal, `create_goal` is what records it; acknowledging it in prose records nothi"},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"objective\":{\"description\":\"The full objective to pursue. Keep the complete user goal, not a shortened one-turn version.\",\"type\":\"string\"},\"token_budget\":{\"description\":\"Optional soft token budget for the goal.\",\"minimum\":0,\"type\":\"integer\"}},\"required\":[\"objective\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"create_goal"},"ordinal":2,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Suggest"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Edit one file with targeted text replacements. Every edits[].oldText must identify a unique, non-overlapping region of the original file. Merge nearby or overlapping changes into one edit."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"edits\":{\"description\":\"One or more disjoint replacements, all matched against the original file.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"newText\":{\"description\":\"Replacement text; may be empty to delete the matched span.\",\"type\":\"string\"},\"oldText\":{\"description\":\"Text identifying one unique region to replace.\",\"type\":\"string\"}},\"required\":[\"newText\",\"oldText\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to the file to edit (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"edits\",\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"edit"},"ordinal":3,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":2,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"},{"truncated":false,"value":"Sandboxable"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Find workspace files by name, path, or glob (e.g. src/**/*.rs). Space-separated terms must all match. Literal matches rank above fuzzy matches; results are capped. Respects ignore files and skips build/dependency directories by default. Use extensions to filter suffixes."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"properties\":{\"exclude\":{\"description\":\"Optional replacement exclusion globs (e.g. [\\\"**/target/**\\\", \\\"*.lock\\\"]); [] disables default exclusions.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"extensions\":{\"description\":\"Optional list of file extensions to include (e.g. [\\\"rs\\\", \\\"md\\\"]).\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"limit\":{\"description\":\"Maximum number of ranked results to return (default: 20, maximum: 200).\",\"type\":\"integer\"},\"path\":{\"description\":\"Optional base path to search (relative to workspace).\",\"type\":\"string\"},\"query\":{\"description\":\"File name, path fragment, space-separated terms, or glob (e.g. **/engine*.rs).\",\"type\":\"string\"}},\"required\":[\"query\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"file_search"},"ordinal":4,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Inspect the current runtime goal state, including objective, status, token budget, elapsed time, evidence, and blocker."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{},\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"get_goal"},"ordinal":5,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Load a named skill's SKILL.md body and companion file list into this turn. Use when the user names a skill, or when an entry in the system prompt's `## Skills` index matches the task -- load it before starting the work, not after. Pass query=\"...\" to search names and descriptions, or name=\"list\" for the whole catalogue. Resolves global and plugin skills that `read` cannot reach."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"name\":{\"description\":\"Skill id to load. Omit or pass \\\"list\\\" to see all available skills.\",\"type\":\"string\"},\"query\":{\"description\":\"Search term matched against skill names and descriptions. Use when the index was truncated or no name is known.\",\"type\":\"string\"}},\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"load_skill"},"ordinal":6,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":2,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"},{"truncated":false,"value":"Sandboxable"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Read a file. A text file comes back whole in one call when it fits this call's output budget — 100000 bytes by default, raisable to 500000 with max_bytes. There is no line cap. Use offset and limit for an exact line range; when output is budget-limited the footer names the exact offset to continue from. Every text response reports the file's byte size, line count, and whether output was truncated. A PNG, JPEG, GIF or WebP image comes back as image content you can see (large images are downscaled), including"},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of lines to read.\",\"type\":\"number\"},\"max_bytes\":{\"description\":\"Output budget in bytes for this one call. Defaults to 100000; values above the 500000 maximum are clamped down rather than rejected, and a value below the active default leaves the default in place.\",\"type\":\"number\"},\"offset\":{\"description\":\"Line number to start reading from (1-indexed).\",\"type\":\"number\"},\"path\":{\"description\":\"Path to the file to read (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"read"},"ordinal":7,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Replace the To-do list the user watches. For any task with three or more steps or more than one file, write the list before you start, keep exactly one item in_progress, and mark items done as you finish them. Skip it only for a single quick answer or edit."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"properties\":{\"todos\":{\"description\":\"The complete list of To-do items. This replaces the existing list.\",\"items\":{\"properties\":{\"content\":{\"description\":\"The task description\",\"type\":\"string\"},\"status\":{\"description\":\"Task status\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"required\":[\"todos\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"todo_write"},"ordinal":8,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":0,"omitted":0,"rendered":[]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Update the runtime goal completion gate by calling this tool; a prose status in your answer does not change the goal or stop continuation. Critical verification may seal one immutable completion contract. Advisory review is append-only context and never completes, blocks, or pauses the goal. Mark blocked when progress requires user input. Mark yield when you finished a stage and the next step needs the user's decision: the goal stays unfinished, the turn ends, and their next message resumes it."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":true,"value":"{\"additionalProperties\":false,\"properties\":{\"advisory\":{\"description\":\"Required when status is advisory. Appended separately from the judged completion contract.\",\"type\":\"string\"},\"blocker\":{\"description\":\"Required when status is blocked. Explain the condition preventing progress.\",\"type\":\"string\"},\"evidence\":{\"description\":\"Required when status is complete. Briefly cite the proof that the goal is done.\",\"type\":\"string\"},\"progress\":{\"additionalProperties\":false,\"description\":\"Optional with not_achieved, advisory or yield: your current best estimate of overall completion, shown to the user as reported progress. Keep percent honest — it is an estimate, never a verified fraction.\",\"properties\":{\"next\":{\"description\":\"One short line: what comes next.\",\"type\":\"string\"},\"now\":{\"description\":\"One short line: what is being worked on right now.\",\"type\":\"string\"},\"percent\":{\"description\":\"Estimated percent complete, 0-100.\",\"maximum\":100,\"minimum\":0,\"type\":\"integer\"}},\"required\":[\"percent\"],\"type\":\"object\"},\"status\":{\"description\":\"Use complete only when a critical verifier proves the goal; not_achieved to record verifier gaps; blocked when meaningful progress cannot continue; yield when a stage is finished and the next step is the user's call; advisory to append best-effort context without changing lifecycle state.\",\"enum\":[\"complete\",\"blocked\",\"not_achieved\",\"advisory\",\"yield\"],\"type\":\"string\"},\"verification\":{\"additionalProperties\":false,\"description\":\"Required when status is complete or not_achieved. A verifier-as-judge receipt from a concrete check, such as Run action=\\\"verifiers\\\" or an equivalent project-specific gate.\",\"properties\":{\"check\":{\"description\":\"The verifier/check that passed.\",\"type\":\"string\"},\"gaps\":{\"description\":\"Concrete remaining gaps. Required for critical not_achieved reviews; order and duplicate wording do not affect the stall fingerprint.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"role\":{\"description\":\"Critical reviews may satisfy the judged completion contract. Advisory reviews are fail-ope"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"update_goal"},"ordinal":9,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Suggest"}},"cache_control_type":{"status":"known","value":{"truncated":false,"value":"ephemeral"}},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Write content to a file. Creates the file if it does not exist, overwrites it if it does, and creates parent directories automatically."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Content to write to the file.\",\"type\":\"string\"},\"path\":{\"description\":\"Path to the file to write (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"content\",\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"write"},"ordinal":10,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"reason":"tool is not in the registry","status":"unknown"},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"reason":"tool is not in the registry","status":"unknown"},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Run a JavaScript program that composes tool calls with `await tools.call(name, args)` and returns a bounded JSON result. Prefer it whenever you would make several dependent or repetitive calls, MCP and plugin tools included: intermediate results stay in the program and only what you return reaches the conversation. Every nested call passes the same permission checks as a direct call; a call that needs approval pauses the program until the user decides, and a denied or refused call throws inside the program "},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"type\":\"object\",\"properties\":{\"code\":{\"type\":\"string\",\"description\":\"JavaScript program. The return value (or thrown error) becomes the result; use tools.call(name, argsObject) for tool calls.\"}},\"required\":[\"code\"]}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"reason":"tool is not in the registry","status":"unknown"},"name":{"truncated":false,"value":"execute_tools"},"ordinal":11,"provenance":{"status":"known","value":"synthetic"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"status":"known","value":{"truncated":false,"value":"execute_tools_20260918"}},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"reason":"tool is not in the registry","status":"unknown"},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"reason":"tool is not in the registry","status":"unknown"},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Search deferred tool definitions and return matching tool references."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\",\"description\":\"Search query for tool discovery.\"},\"match\":{\"type\":\"string\",\"enum\":[\"bm25\",\"regex\"],\"default\":\"bm25\",\"description\":\"Matching algorithm: bm25 for natural-language matching, regex for a regular expression over tool names/descriptions/schema.\"},\"max_results\":{\"type\":\"integer\",\"minimum\":1,\"maximum\":8,\"default\":8,\"description\":\"Maximum number of matching tool references to return.\"}},\"required\":[\"query\"]}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"reason":"tool is not in the registry","status":"unknown"},"name":{"truncated":false,"value":"tool_search"},"ordinal":12,"provenance":{"status":"known","value":"synthetic"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"status":"known","value":{"truncated":false,"value":"tool_search_20251119"}},"visibility":"active"}],"tools_field_present":true,"turn_id":{"truncated":false,"value":""},"unavailable_for_this_request":["provider_wire_payload"]}} {"event":"message_started","index":0} {"delta":"It says: conformance fixture.","event":"response_delta","index":0} {"duration_ms":"","event":"turn_usage","first_token_ms":"","request_ms":"","usage":{"input_tokens":200,"output_tokens":6}} {"event":"message_complete","index":0} {"engine_session_id":"conformance-session","event":"session_updated","messages":[{"content":[{"text":"What does README.md say?","type":"text"},{"text":"\nCurrent local date: \nCurrent workspace: \nCurrent permission posture: Ask\nCurrent sandbox posture: workspace-write (writes inside the workspace; network blocked) (local OS sandbox applied)\n## Repo Working Set\nKey files: README.md\nActive paths (prioritize these):\n- README.md (file)\nWhen in doubt, use tools to verify and keep changes focused on the working set.\n","type":"text"}],"role":"user"},{"content":[{"execution_id":"","id":"call_read_readme","input":{"action":"read","path":"README.md"},"name":"File","type":"tool_use"}],"role":"assistant"},{"content":[{"content":"content_hash=\"sha256:41f49bffb38de5ba88945f09121ffc590f9d7f6ff80fdb9ea8ad10224fecea61\"\nconformance fixture","execution_id":"","tool_use_id":"call_read_readme","type":"tool_result"}],"role":"user"},{"content":[{"text":"It says: conformance fixture.","type":"text"}],"role":"assistant"}],"model":"deepseek-v4-pro","system_prompt":"","workspace":""} -{"event":"tool_request_snapshot","snapshot":{"active_tool_catalog_sha256":"18c6d652f11c0c195e7b586852ab46b47987939aaa316b39ef8b692255a7e077","capture_source":"prepared model-client request","delivery_status":"unknown (capture does not prove provider delivery)","omitted_tool_count":0,"payload_json_bytes":14890,"payload_measurement_status":"exact (within 1048576-byte measurement bound)","provider":{"model":"deepseek-v4-pro","provider":"Deepseek","status":"available"},"registry_facts_present":true,"registry_only_tools":{"status":"known","value":{"count":40,"omitted":8,"rendered":[{"truncated":false,"value":"Git"},{"truncated":false,"value":"Run"},{"truncated":false,"value":"Web"},{"truncated":false,"value":"apply_patch"},{"truncated":false,"value":"automation"},{"truncated":false,"value":"diagnostics"},{"truncated":false,"value":"finance"},{"truncated":false,"value":"github"},{"truncated":false,"value":"grep_files"},{"truncated":false,"value":"handle_read"},{"truncated":false,"value":"harness"},{"truncated":false,"value":"image_ocr"},{"truncated":false,"value":"list_dir"},{"truncated":false,"value":"lsp"},{"truncated":false,"value":"note"},{"truncated":false,"value":"notify"},{"truncated":false,"value":"pandoc_convert"},{"truncated":false,"value":"project_map"},{"truncated":false,"value":"registry_sync"},{"truncated":false,"value":"request_plugin_install"},{"truncated":false,"value":"request_user_input"},{"truncated":false,"value":"retrieve_tool_result"},{"truncated":false,"value":"revert_turn"},{"truncated":false,"value":"review"},{"truncated":false,"value":"send_later"},{"truncated":false,"value":"session_get"},{"truncated":false,"value":"session_search"},{"truncated":false,"value":"start_mcp_server"},{"truncated":false,"value":"start_registry_mcp_server"},{"truncated":false,"value":"task_shell_wait"},{"truncated":false,"value":"tasks"},{"truncated":false,"value":"terminal/cancel"}]}},"registry_tool_count":{"status":"known","value":62},"rendered_tool_count":12,"schema_version":1,"step":1,"terminal":{"automatic_compaction_attempts":0,"effective_max_steps":null,"emergency_compaction_attempts":0,"empty_stop_retries":0,"final_report_requested":false,"last_prepared_output_limit_tokens":65536,"last_provider_finish_reason":{"truncated":false,"value":"end_turn"},"last_reported_input_tokens":200,"last_response_tool_calls":0,"last_response_tool_calls_suppressed":0,"model_requests_started":2,"model_step_index":1,"permission_denial_rounds_without_progress":0,"permission_strategy_switches":0,"reason":"provider_no_tool_call","reasoning_only_reprompts":0,"route_context_window_tokens":1000000,"soft_landing_sent":false,"status":"completed","step_budget_source":"max_steps","stream_resumes":0,"transparent_stream_retries":0,"transport_retries":0},"tool_count":12,"tools":[{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Required"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"ExecutesCode"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Execute a shell command in the workspace and return stdout and stderr. Output keeps the last 2000 lines or 50KB. An optional timeout is the seconds to wait in the foreground (default 30 seconds); a command still running then is never killed for time: it moves to the background by itself and you get its output so far, how long ago it last printed, its build phase, and a task_id, so long builds and tests need no special timeout. Find task_shell_wait with tool_search to read more or wait for it. In Ask, after "},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The command to execute. Actual execution shell: `/bin/bash`. Use Bash syntax: pipelines, redirections, $(command), and && / || are supported. Quote paths and variable expansions, such as \\\"$path\\\"; use single quotes for literal text. For literal multiline input, use a quoted heredoc delimiter (<<'EOF') with its closing delimiter on a separate line. Use only installed programs; do not assume GNU-specific flags on macOS/BSD. Example: printf '%s\\\\n' 'sample'.\",\"type\":\"string\"},\"justification\":{\"description\":\"Required with sandbox_permissions: one sentence explaining why this exact command needs wider access.\",\"type\":\"string\"},\"read_only\":{\"description\":\"Set true to run analysis code (including Python/SQLite) with mandatory native filesystem read-only isolation and no network. Available during peer writes. Refused when native enforcement is unavailable; no background, stdin, external backend, or sandbox escalation.\",\"type\":\"boolean\"},\"sandbox_permissions\":{\"description\":\"The wider sandbox mode this exact command needs. Use only as a one-shot retry after a sandbox denial; requires justification and user approval in Ask.\",\"enum\":[\"workspace-write\",\"danger-full-access\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional seconds to wait in the foreground (default 30 seconds). A command still running then is not killed: it moves to the background by itself and you get its output so far, how long ago it last printed, its build phase, and a task_id. Find task_shell_wait with tool_search to read more or wait for it.\",\"type\":\"number\"}},\"required\":[\"command\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"bash"},"ordinal":1,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":0,"omitted":0,"rendered":[]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Create the session's one persistent goal: a completion objective Codewhale keeps working toward across turns until it is verified complete, blocked, or the user stops it. A goal is for a durable objective that outlasts one turn: a multi-step outcome the user wants continued and verified. A question, a greeting, or a one-shot edit completes as an ordinary turn. When the user explicitly asks to use `/goal` or to make something the goal, `create_goal` is what records it; acknowledging it in prose records nothi"},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"objective\":{\"description\":\"The full objective to pursue. Keep the complete user goal, not a shortened one-turn version.\",\"type\":\"string\"},\"token_budget\":{\"description\":\"Optional soft token budget for the goal.\",\"minimum\":0,\"type\":\"integer\"}},\"required\":[\"objective\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"create_goal"},"ordinal":2,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Suggest"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Edit one file with targeted text replacements. Every edits[].oldText must identify a unique, non-overlapping region of the original file. Merge nearby or overlapping changes into one edit."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"edits\":{\"description\":\"One or more disjoint replacements, all matched against the original file.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"newText\":{\"description\":\"Replacement text; may be empty to delete the matched span.\",\"type\":\"string\"},\"oldText\":{\"description\":\"Text identifying one unique region to replace.\",\"type\":\"string\"}},\"required\":[\"newText\",\"oldText\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to the file to edit (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"edits\",\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"edit"},"ordinal":3,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":2,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"},{"truncated":false,"value":"Sandboxable"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Find workspace files by name, path, or glob (e.g. src/**/*.rs). Space-separated terms must all match. Literal matches rank above fuzzy matches; results are capped. Respects ignore files and skips build/dependency directories by default. Use extensions to filter suffixes."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"properties\":{\"exclude\":{\"description\":\"Optional replacement exclusion globs (e.g. [\\\"**/target/**\\\", \\\"*.lock\\\"]); [] disables default exclusions.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"extensions\":{\"description\":\"Optional list of file extensions to include (e.g. [\\\"rs\\\", \\\"md\\\"]).\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"limit\":{\"description\":\"Maximum number of ranked results to return (default: 20, maximum: 200).\",\"type\":\"integer\"},\"path\":{\"description\":\"Optional base path to search (relative to workspace).\",\"type\":\"string\"},\"query\":{\"description\":\"File name, path fragment, space-separated terms, or glob (e.g. **/engine*.rs).\",\"type\":\"string\"}},\"required\":[\"query\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"file_search"},"ordinal":4,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Inspect the current runtime goal state, including objective, status, token budget, elapsed time, evidence, and blocker."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{},\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"get_goal"},"ordinal":5,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Load a named skill's SKILL.md body and companion file list into this turn. Use when the user names a skill, or when an entry in the system prompt's `## Skills` index matches the task -- load it before starting the work, not after. Pass query=\"...\" to search names and descriptions, or name=\"list\" for the whole catalogue. Resolves global and plugin skills that `read` cannot reach."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"name\":{\"description\":\"Skill id to load. Omit or pass \\\"list\\\" to see all available skills.\",\"type\":\"string\"},\"query\":{\"description\":\"Search term matched against skill names and descriptions. Use when the index was truncated or no name is known.\",\"type\":\"string\"}},\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"load_skill"},"ordinal":6,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":2,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"},{"truncated":false,"value":"Sandboxable"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Read a file. A text file comes back whole in one call when it fits this call's output budget — 100000 bytes by default, raisable to 500000 with max_bytes. There is no line cap. Use offset and limit for an exact line range; when output is budget-limited the footer names the exact offset to continue from. Every text response reports the file's byte size, line count, and whether output was truncated. A PNG, JPEG, GIF or WebP image comes back as image content you can see (large images are downscaled), including"},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of lines to read.\",\"type\":\"number\"},\"max_bytes\":{\"description\":\"Output budget in bytes for this one call. Defaults to 100000; values above the 500000 maximum are clamped down rather than rejected, and a value below the active default leaves the default in place.\",\"type\":\"number\"},\"offset\":{\"description\":\"Line number to start reading from (1-indexed).\",\"type\":\"number\"},\"path\":{\"description\":\"Path to the file to read (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"read"},"ordinal":7,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Replace the To-do list the user watches. For any task with three or more steps or more than one file, write the list before you start, keep exactly one item in_progress, and mark items done as you finish them. Skip it only for a single quick answer or edit."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"properties\":{\"todos\":{\"description\":\"The complete list of To-do items. This replaces the existing list.\",\"items\":{\"properties\":{\"content\":{\"description\":\"The task description\",\"type\":\"string\"},\"status\":{\"description\":\"Task status\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"required\":[\"todos\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"todo_write"},"ordinal":8,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":0,"omitted":0,"rendered":[]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Update the runtime goal completion gate by calling this tool; a prose status in your answer does not change the goal or stop continuation. Critical verification may seal one immutable completion contract. Advisory review is append-only context and never completes, blocks, or pauses the goal. Mark blocked when progress requires user input. Mark yield when you finished a stage and the next step needs the user's decision: the goal stays unfinished, the turn ends, and their next message resumes it."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":true,"value":"{\"additionalProperties\":false,\"properties\":{\"advisory\":{\"description\":\"Required when status is advisory. Appended separately from the judged completion contract.\",\"type\":\"string\"},\"blocker\":{\"description\":\"Required when status is blocked. Explain the condition preventing progress.\",\"type\":\"string\"},\"evidence\":{\"description\":\"Required when status is complete. Briefly cite the proof that the goal is done.\",\"type\":\"string\"},\"progress\":{\"additionalProperties\":false,\"description\":\"Optional with not_achieved or advisory: your current best estimate of overall completion, shown to the user as reported progress. Keep percent honest — it is an estimate, never a verified fraction.\",\"properties\":{\"next\":{\"description\":\"One short line: what comes next.\",\"type\":\"string\"},\"now\":{\"description\":\"One short line: what is being worked on right now.\",\"type\":\"string\"},\"percent\":{\"description\":\"Estimated percent complete, 0-100.\",\"maximum\":100,\"minimum\":0,\"type\":\"integer\"}},\"required\":[\"percent\"],\"type\":\"object\"},\"status\":{\"description\":\"Use complete only when a critical verifier proves the goal; not_achieved to record verifier gaps; blocked when meaningful progress cannot continue; yield when a stage is finished and the next step is the user's call; advisory to append best-effort context without changing lifecycle state.\",\"enum\":[\"complete\",\"blocked\",\"not_achieved\",\"advisory\",\"yield\"],\"type\":\"string\"},\"verification\":{\"additionalProperties\":false,\"description\":\"Required when status is complete or not_achieved. A verifier-as-judge receipt from a concrete check, such as Run action=\\\"verifiers\\\" or an equivalent project-specific gate.\",\"properties\":{\"check\":{\"description\":\"The verifier/check that passed.\",\"type\":\"string\"},\"gaps\":{\"description\":\"Concrete remaining gaps. Required for critical not_achieved reviews; order and duplicate wording do not affect the stall fingerprint.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"role\":{\"description\":\"Critical reviews may satisfy the judged completion contract. Advisory reviews are fail-open and c"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"update_goal"},"ordinal":9,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Suggest"}},"cache_control_type":{"status":"known","value":{"truncated":false,"value":"ephemeral"}},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Write content to a file. Creates the file if it does not exist, overwrites it if it does, and creates parent directories automatically."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Content to write to the file.\",\"type\":\"string\"},\"path\":{\"description\":\"Path to the file to write (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"content\",\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"write"},"ordinal":10,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"reason":"tool is not in the registry","status":"unknown"},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"reason":"tool is not in the registry","status":"unknown"},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Run a JavaScript program that composes tool calls with `await tools.call(name, args)` and returns a bounded JSON result. Prefer it whenever you would make several dependent or repetitive calls, MCP and plugin tools included: intermediate results stay in the program and only what you return reaches the conversation. Every nested call passes the same permission checks as a direct call; a call that needs approval pauses the program until the user decides, and a denied or refused call throws inside the program "},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"type\":\"object\",\"properties\":{\"code\":{\"type\":\"string\",\"description\":\"JavaScript program. The return value (or thrown error) becomes the result; use tools.call(name, argsObject) for tool calls.\"}},\"required\":[\"code\"]}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"reason":"tool is not in the registry","status":"unknown"},"name":{"truncated":false,"value":"execute_tools"},"ordinal":11,"provenance":{"status":"known","value":"synthetic"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"status":"known","value":{"truncated":false,"value":"execute_tools_20260918"}},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"reason":"tool is not in the registry","status":"unknown"},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"reason":"tool is not in the registry","status":"unknown"},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Search deferred tool definitions and return matching tool references."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\",\"description\":\"Search query for tool discovery.\"},\"match\":{\"type\":\"string\",\"enum\":[\"bm25\",\"regex\"],\"default\":\"bm25\",\"description\":\"Matching algorithm: bm25 for natural-language matching, regex for a regular expression over tool names/descriptions/schema.\"},\"max_results\":{\"type\":\"integer\",\"minimum\":1,\"maximum\":8,\"default\":8,\"description\":\"Maximum number of matching tool references to return.\"}},\"required\":[\"query\"]}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"reason":"tool is not in the registry","status":"unknown"},"name":{"truncated":false,"value":"tool_search"},"ordinal":12,"provenance":{"status":"known","value":"synthetic"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"status":"known","value":{"truncated":false,"value":"tool_search_20251119"}},"visibility":"active"}],"tools_field_present":true,"turn_id":{"truncated":false,"value":""},"unavailable_for_this_request":["provider_wire_payload"]}} +{"event":"tool_request_snapshot","snapshot":{"active_tool_catalog_sha256":"ba34408b0c03b810ba1ef6190c41d215f58e88822148991099b7d70d444992fc","capture_source":"prepared model-client request","delivery_status":"unknown (capture does not prove provider delivery)","omitted_tool_count":0,"payload_json_bytes":14897,"payload_measurement_status":"exact (within 1048576-byte measurement bound)","provider":{"model":"deepseek-v4-pro","provider":"Deepseek","status":"available"},"registry_facts_present":true,"registry_only_tools":{"status":"known","value":{"count":40,"omitted":8,"rendered":[{"truncated":false,"value":"Git"},{"truncated":false,"value":"Run"},{"truncated":false,"value":"Web"},{"truncated":false,"value":"apply_patch"},{"truncated":false,"value":"automation"},{"truncated":false,"value":"diagnostics"},{"truncated":false,"value":"finance"},{"truncated":false,"value":"github"},{"truncated":false,"value":"grep_files"},{"truncated":false,"value":"handle_read"},{"truncated":false,"value":"harness"},{"truncated":false,"value":"image_ocr"},{"truncated":false,"value":"list_dir"},{"truncated":false,"value":"lsp"},{"truncated":false,"value":"note"},{"truncated":false,"value":"notify"},{"truncated":false,"value":"pandoc_convert"},{"truncated":false,"value":"project_map"},{"truncated":false,"value":"registry_sync"},{"truncated":false,"value":"request_plugin_install"},{"truncated":false,"value":"request_user_input"},{"truncated":false,"value":"retrieve_tool_result"},{"truncated":false,"value":"revert_turn"},{"truncated":false,"value":"review"},{"truncated":false,"value":"send_later"},{"truncated":false,"value":"session_get"},{"truncated":false,"value":"session_search"},{"truncated":false,"value":"start_mcp_server"},{"truncated":false,"value":"start_registry_mcp_server"},{"truncated":false,"value":"task_shell_wait"},{"truncated":false,"value":"tasks"},{"truncated":false,"value":"terminal/cancel"}]}},"registry_tool_count":{"status":"known","value":62},"rendered_tool_count":12,"schema_version":1,"step":1,"terminal":{"automatic_compaction_attempts":0,"effective_max_steps":null,"emergency_compaction_attempts":0,"empty_stop_retries":0,"final_report_requested":false,"last_prepared_output_limit_tokens":65536,"last_provider_finish_reason":{"truncated":false,"value":"end_turn"},"last_reported_input_tokens":200,"last_response_tool_calls":0,"last_response_tool_calls_suppressed":0,"model_requests_started":2,"model_step_index":1,"permission_denial_rounds_without_progress":0,"permission_strategy_switches":0,"reason":"provider_no_tool_call","reasoning_only_reprompts":0,"route_context_window_tokens":1000000,"soft_landing_sent":false,"status":"completed","step_budget_source":"max_steps","stream_resumes":0,"transparent_stream_retries":0,"transport_retries":0},"tool_count":12,"tools":[{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Required"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"ExecutesCode"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Execute a shell command in the workspace and return stdout and stderr. Output keeps the last 2000 lines or 50KB. An optional timeout is the seconds to wait in the foreground (default 30 seconds); a command still running then is never killed for time: it moves to the background by itself and you get its output so far, how long ago it last printed, its build phase, and a task_id, so long builds and tests need no special timeout. Find task_shell_wait with tool_search to read more or wait for it. In Ask, after "},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The command to execute. Actual execution shell: `/bin/bash`. Use Bash syntax: pipelines, redirections, $(command), and && / || are supported. Quote paths and variable expansions, such as \\\"$path\\\"; use single quotes for literal text. For literal multiline input, use a quoted heredoc delimiter (<<'EOF') with its closing delimiter on a separate line. Use only installed programs; do not assume GNU-specific flags on macOS/BSD. Example: printf '%s\\\\n' 'sample'.\",\"type\":\"string\"},\"justification\":{\"description\":\"Required with sandbox_permissions: one sentence explaining why this exact command needs wider access.\",\"type\":\"string\"},\"read_only\":{\"description\":\"Set true to run analysis code (including Python/SQLite) with mandatory native filesystem read-only isolation and no network. Available during peer writes. Refused when native enforcement is unavailable; no background, stdin, external backend, or sandbox escalation.\",\"type\":\"boolean\"},\"sandbox_permissions\":{\"description\":\"The wider sandbox mode this exact command needs. Use only as a one-shot retry after a sandbox denial; requires justification and user approval in Ask.\",\"enum\":[\"workspace-write\",\"danger-full-access\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional seconds to wait in the foreground (default 30 seconds). A command still running then is not killed: it moves to the background by itself and you get its output so far, how long ago it last printed, its build phase, and a task_id. Find task_shell_wait with tool_search to read more or wait for it.\",\"type\":\"number\"}},\"required\":[\"command\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"bash"},"ordinal":1,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":0,"omitted":0,"rendered":[]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Create the session's one persistent goal: a completion objective Codewhale keeps working toward across turns until it is verified complete, blocked, or the user stops it. A goal is for a durable objective that outlasts one turn: a multi-step outcome the user wants continued and verified. A question, a greeting, or a one-shot edit completes as an ordinary turn. When the user explicitly asks to use `/goal` or to make something the goal, `create_goal` is what records it; acknowledging it in prose records nothi"},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"objective\":{\"description\":\"The full objective to pursue. Keep the complete user goal, not a shortened one-turn version.\",\"type\":\"string\"},\"token_budget\":{\"description\":\"Optional soft token budget for the goal.\",\"minimum\":0,\"type\":\"integer\"}},\"required\":[\"objective\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"create_goal"},"ordinal":2,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Suggest"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Edit one file with targeted text replacements. Every edits[].oldText must identify a unique, non-overlapping region of the original file. Merge nearby or overlapping changes into one edit."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"edits\":{\"description\":\"One or more disjoint replacements, all matched against the original file.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"newText\":{\"description\":\"Replacement text; may be empty to delete the matched span.\",\"type\":\"string\"},\"oldText\":{\"description\":\"Text identifying one unique region to replace.\",\"type\":\"string\"}},\"required\":[\"newText\",\"oldText\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to the file to edit (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"edits\",\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"edit"},"ordinal":3,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":2,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"},{"truncated":false,"value":"Sandboxable"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Find workspace files by name, path, or glob (e.g. src/**/*.rs). Space-separated terms must all match. Literal matches rank above fuzzy matches; results are capped. Respects ignore files and skips build/dependency directories by default. Use extensions to filter suffixes."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"properties\":{\"exclude\":{\"description\":\"Optional replacement exclusion globs (e.g. [\\\"**/target/**\\\", \\\"*.lock\\\"]); [] disables default exclusions.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"extensions\":{\"description\":\"Optional list of file extensions to include (e.g. [\\\"rs\\\", \\\"md\\\"]).\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"limit\":{\"description\":\"Maximum number of ranked results to return (default: 20, maximum: 200).\",\"type\":\"integer\"},\"path\":{\"description\":\"Optional base path to search (relative to workspace).\",\"type\":\"string\"},\"query\":{\"description\":\"File name, path fragment, space-separated terms, or glob (e.g. **/engine*.rs).\",\"type\":\"string\"}},\"required\":[\"query\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"file_search"},"ordinal":4,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Inspect the current runtime goal state, including objective, status, token budget, elapsed time, evidence, and blocker."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{},\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"get_goal"},"ordinal":5,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Load a named skill's SKILL.md body and companion file list into this turn. Use when the user names a skill, or when an entry in the system prompt's `## Skills` index matches the task -- load it before starting the work, not after. Pass query=\"...\" to search names and descriptions, or name=\"list\" for the whole catalogue. Resolves global and plugin skills that `read` cannot reach."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"name\":{\"description\":\"Skill id to load. Omit or pass \\\"list\\\" to see all available skills.\",\"type\":\"string\"},\"query\":{\"description\":\"Search term matched against skill names and descriptions. Use when the index was truncated or no name is known.\",\"type\":\"string\"}},\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"load_skill"},"ordinal":6,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":2,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"},{"truncated":false,"value":"Sandboxable"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Read a file. A text file comes back whole in one call when it fits this call's output budget — 100000 bytes by default, raisable to 500000 with max_bytes. There is no line cap. Use offset and limit for an exact line range; when output is budget-limited the footer names the exact offset to continue from. Every text response reports the file's byte size, line count, and whether output was truncated. A PNG, JPEG, GIF or WebP image comes back as image content you can see (large images are downscaled), including"},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of lines to read.\",\"type\":\"number\"},\"max_bytes\":{\"description\":\"Output budget in bytes for this one call. Defaults to 100000; values above the 500000 maximum are clamped down rather than rejected, and a value below the active default leaves the default in place.\",\"type\":\"number\"},\"offset\":{\"description\":\"Line number to start reading from (1-indexed).\",\"type\":\"number\"},\"path\":{\"description\":\"Path to the file to read (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"read"},"ordinal":7,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Replace the To-do list the user watches. For any task with three or more steps or more than one file, write the list before you start, keep exactly one item in_progress, and mark items done as you finish them. Skip it only for a single quick answer or edit."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"properties\":{\"todos\":{\"description\":\"The complete list of To-do items. This replaces the existing list.\",\"items\":{\"properties\":{\"content\":{\"description\":\"The task description\",\"type\":\"string\"},\"status\":{\"description\":\"Task status\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"required\":[\"todos\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"todo_write"},"ordinal":8,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":0,"omitted":0,"rendered":[]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Update the runtime goal completion gate by calling this tool; a prose status in your answer does not change the goal or stop continuation. Critical verification may seal one immutable completion contract. Advisory review is append-only context and never completes, blocks, or pauses the goal. Mark blocked when progress requires user input. Mark yield when you finished a stage and the next step needs the user's decision: the goal stays unfinished, the turn ends, and their next message resumes it."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":true,"value":"{\"additionalProperties\":false,\"properties\":{\"advisory\":{\"description\":\"Required when status is advisory. Appended separately from the judged completion contract.\",\"type\":\"string\"},\"blocker\":{\"description\":\"Required when status is blocked. Explain the condition preventing progress.\",\"type\":\"string\"},\"evidence\":{\"description\":\"Required when status is complete. Briefly cite the proof that the goal is done.\",\"type\":\"string\"},\"progress\":{\"additionalProperties\":false,\"description\":\"Optional with not_achieved, advisory or yield: your current best estimate of overall completion, shown to the user as reported progress. Keep percent honest — it is an estimate, never a verified fraction.\",\"properties\":{\"next\":{\"description\":\"One short line: what comes next.\",\"type\":\"string\"},\"now\":{\"description\":\"One short line: what is being worked on right now.\",\"type\":\"string\"},\"percent\":{\"description\":\"Estimated percent complete, 0-100.\",\"maximum\":100,\"minimum\":0,\"type\":\"integer\"}},\"required\":[\"percent\"],\"type\":\"object\"},\"status\":{\"description\":\"Use complete only when a critical verifier proves the goal; not_achieved to record verifier gaps; blocked when meaningful progress cannot continue; yield when a stage is finished and the next step is the user's call; advisory to append best-effort context without changing lifecycle state.\",\"enum\":[\"complete\",\"blocked\",\"not_achieved\",\"advisory\",\"yield\"],\"type\":\"string\"},\"verification\":{\"additionalProperties\":false,\"description\":\"Required when status is complete or not_achieved. A verifier-as-judge receipt from a concrete check, such as Run action=\\\"verifiers\\\" or an equivalent project-specific gate.\",\"properties\":{\"check\":{\"description\":\"The verifier/check that passed.\",\"type\":\"string\"},\"gaps\":{\"description\":\"Concrete remaining gaps. Required for critical not_achieved reviews; order and duplicate wording do not affect the stall fingerprint.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"role\":{\"description\":\"Critical reviews may satisfy the judged completion contract. Advisory reviews are fail-ope"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"update_goal"},"ordinal":9,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Suggest"}},"cache_control_type":{"status":"known","value":{"truncated":false,"value":"ephemeral"}},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Write content to a file. Creates the file if it does not exist, overwrites it if it does, and creates parent directories automatically."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Content to write to the file.\",\"type\":\"string\"},\"path\":{\"description\":\"Path to the file to write (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"content\",\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"write"},"ordinal":10,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"reason":"tool is not in the registry","status":"unknown"},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"reason":"tool is not in the registry","status":"unknown"},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Run a JavaScript program that composes tool calls with `await tools.call(name, args)` and returns a bounded JSON result. Prefer it whenever you would make several dependent or repetitive calls, MCP and plugin tools included: intermediate results stay in the program and only what you return reaches the conversation. Every nested call passes the same permission checks as a direct call; a call that needs approval pauses the program until the user decides, and a denied or refused call throws inside the program "},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"type\":\"object\",\"properties\":{\"code\":{\"type\":\"string\",\"description\":\"JavaScript program. The return value (or thrown error) becomes the result; use tools.call(name, argsObject) for tool calls.\"}},\"required\":[\"code\"]}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"reason":"tool is not in the registry","status":"unknown"},"name":{"truncated":false,"value":"execute_tools"},"ordinal":11,"provenance":{"status":"known","value":"synthetic"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"status":"known","value":{"truncated":false,"value":"execute_tools_20260918"}},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"reason":"tool is not in the registry","status":"unknown"},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"reason":"tool is not in the registry","status":"unknown"},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Search deferred tool definitions and return matching tool references."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\",\"description\":\"Search query for tool discovery.\"},\"match\":{\"type\":\"string\",\"enum\":[\"bm25\",\"regex\"],\"default\":\"bm25\",\"description\":\"Matching algorithm: bm25 for natural-language matching, regex for a regular expression over tool names/descriptions/schema.\"},\"max_results\":{\"type\":\"integer\",\"minimum\":1,\"maximum\":8,\"default\":8,\"description\":\"Maximum number of matching tool references to return.\"}},\"required\":[\"query\"]}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"reason":"tool is not in the registry","status":"unknown"},"name":{"truncated":false,"value":"tool_search"},"ordinal":12,"provenance":{"status":"known","value":"synthetic"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"status":"known","value":{"truncated":false,"value":"tool_search_20251119"}},"visibility":"active"}],"tools_field_present":true,"turn_id":{"truncated":false,"value":""},"unavailable_for_this_request":["provider_wire_payload"]}} {"event":"turn_complete","parent_route_usage":{"input_tokens":350,"output_tokens":26},"routed_usage_dropped_records":0,"status":"completed","tool_catalog":"","usage":{"input_tokens":350,"output_tokens":26}} -{"harness_summary":{"model_requests":2,"non_streaming_requests":0,"workspace_after":{".codewhale/state/subagents.v1.lock":"e3b0c44298fc1c14","NOTES.md":"f957b19529906961","README.md":"41f49bffb38de5ba"}}} +{"harness_summary":{"model_requests":2,"non_streaming_requests":0,"workspace_after":{".codewhale/state/.gitignore":"cdbcae15105d6b78",".codewhale/state/subagents.v1.lock":"e3b0c44298fc1c14","NOTES.md":"f957b19529906961","README.md":"41f49bffb38de5ba"}}} diff --git a/crates/tui/tests/fixtures/conformance/events/parallel_tool_calls.golden.jsonl b/crates/tui/tests/fixtures/conformance/events/parallel_tool_calls.golden.jsonl index ef8df071b5..1ff8cfc455 100644 --- a/crates/tui/tests/fixtures/conformance/events/parallel_tool_calls.golden.jsonl +++ b/crates/tui/tests/fixtures/conformance/events/parallel_tool_calls.golden.jsonl @@ -1,8 +1,8 @@ {"created_at":"","event":"turn_started","route":{"auto_model":false,"base_url":"https://api.deepseek.com/beta","billing_product":{"kind":"unproven"},"model":"deepseek-v4-pro","provider":"deepseek","provider_identity":"deepseek"},"turn_id":""} {"engine_session_id":"conformance-session","event":"session_updated","messages":[{"content":[{"text":"Read README.md and NOTES.md.","type":"text"},{"text":"\nCurrent local date: \nCurrent workspace: \nCurrent permission posture: Ask\nCurrent sandbox posture: workspace-write (writes inside the workspace; network blocked) (local OS sandbox applied)\n## Repo Working Set\nKey files: README.md\nActive paths (prioritize these):\n- NOTES.md (file)\n- README.md (file)\nWhen in doubt, use tools to verify and keep changes focused on the working set.\n","type":"text"}],"role":"user"}],"model":"deepseek-v4-pro","system_prompt":"","workspace":""} {"changed":false,"context_updates":0,"description":"","event":"prefix_cache_change","last_miss_reason":"","pin_reason":"initial","pinned_combined_hash":"","stability_pct":100,"system_prompt_changed":false,"tools_changed":false} -{"changed":false,"context_updates":0,"description":"frozen: 8b8238c51355","event":"prefix_cache_change","last_miss_reason":"","pin_reason":"initial","pinned_combined_hash":"","stability_pct":100,"system_prompt_changed":false,"tools_changed":false} -{"event":"tool_request_snapshot","snapshot":{"active_tool_catalog_sha256":"18c6d652f11c0c195e7b586852ab46b47987939aaa316b39ef8b692255a7e077","capture_source":"prepared model-client request","delivery_status":"unknown (capture does not prove provider delivery)","omitted_tool_count":0,"payload_json_bytes":14890,"payload_measurement_status":"exact (within 1048576-byte measurement bound)","provider":{"model":"deepseek-v4-pro","provider":"Deepseek","status":"available"},"registry_facts_present":true,"registry_only_tools":{"status":"known","value":{"count":40,"omitted":8,"rendered":[{"truncated":false,"value":"Git"},{"truncated":false,"value":"Run"},{"truncated":false,"value":"Web"},{"truncated":false,"value":"apply_patch"},{"truncated":false,"value":"automation"},{"truncated":false,"value":"diagnostics"},{"truncated":false,"value":"finance"},{"truncated":false,"value":"github"},{"truncated":false,"value":"grep_files"},{"truncated":false,"value":"handle_read"},{"truncated":false,"value":"harness"},{"truncated":false,"value":"image_ocr"},{"truncated":false,"value":"list_dir"},{"truncated":false,"value":"lsp"},{"truncated":false,"value":"note"},{"truncated":false,"value":"notify"},{"truncated":false,"value":"pandoc_convert"},{"truncated":false,"value":"project_map"},{"truncated":false,"value":"registry_sync"},{"truncated":false,"value":"request_plugin_install"},{"truncated":false,"value":"request_user_input"},{"truncated":false,"value":"retrieve_tool_result"},{"truncated":false,"value":"revert_turn"},{"truncated":false,"value":"review"},{"truncated":false,"value":"send_later"},{"truncated":false,"value":"session_get"},{"truncated":false,"value":"session_search"},{"truncated":false,"value":"start_mcp_server"},{"truncated":false,"value":"start_registry_mcp_server"},{"truncated":false,"value":"task_shell_wait"},{"truncated":false,"value":"tasks"},{"truncated":false,"value":"terminal/cancel"}]}},"registry_tool_count":{"status":"known","value":62},"rendered_tool_count":12,"schema_version":1,"step":0,"tool_count":12,"tools":[{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Required"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"ExecutesCode"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Execute a shell command in the workspace and return stdout and stderr. Output keeps the last 2000 lines or 50KB. An optional timeout is the seconds to wait in the foreground (default 30 seconds); a command still running then is never killed for time: it moves to the background by itself and you get its output so far, how long ago it last printed, its build phase, and a task_id, so long builds and tests need no special timeout. Find task_shell_wait with tool_search to read more or wait for it. In Ask, after "},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The command to execute. Actual execution shell: `/bin/bash`. Use Bash syntax: pipelines, redirections, $(command), and && / || are supported. Quote paths and variable expansions, such as \\\"$path\\\"; use single quotes for literal text. For literal multiline input, use a quoted heredoc delimiter (<<'EOF') with its closing delimiter on a separate line. Use only installed programs; do not assume GNU-specific flags on macOS/BSD. Example: printf '%s\\\\n' 'sample'.\",\"type\":\"string\"},\"justification\":{\"description\":\"Required with sandbox_permissions: one sentence explaining why this exact command needs wider access.\",\"type\":\"string\"},\"read_only\":{\"description\":\"Set true to run analysis code (including Python/SQLite) with mandatory native filesystem read-only isolation and no network. Available during peer writes. Refused when native enforcement is unavailable; no background, stdin, external backend, or sandbox escalation.\",\"type\":\"boolean\"},\"sandbox_permissions\":{\"description\":\"The wider sandbox mode this exact command needs. Use only as a one-shot retry after a sandbox denial; requires justification and user approval in Ask.\",\"enum\":[\"workspace-write\",\"danger-full-access\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional seconds to wait in the foreground (default 30 seconds). A command still running then is not killed: it moves to the background by itself and you get its output so far, how long ago it last printed, its build phase, and a task_id. Find task_shell_wait with tool_search to read more or wait for it.\",\"type\":\"number\"}},\"required\":[\"command\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"bash"},"ordinal":1,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":0,"omitted":0,"rendered":[]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Create the session's one persistent goal: a completion objective Codewhale keeps working toward across turns until it is verified complete, blocked, or the user stops it. A goal is for a durable objective that outlasts one turn: a multi-step outcome the user wants continued and verified. A question, a greeting, or a one-shot edit completes as an ordinary turn. When the user explicitly asks to use `/goal` or to make something the goal, `create_goal` is what records it; acknowledging it in prose records nothi"},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"objective\":{\"description\":\"The full objective to pursue. Keep the complete user goal, not a shortened one-turn version.\",\"type\":\"string\"},\"token_budget\":{\"description\":\"Optional soft token budget for the goal.\",\"minimum\":0,\"type\":\"integer\"}},\"required\":[\"objective\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"create_goal"},"ordinal":2,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Suggest"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Edit one file with targeted text replacements. Every edits[].oldText must identify a unique, non-overlapping region of the original file. Merge nearby or overlapping changes into one edit."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"edits\":{\"description\":\"One or more disjoint replacements, all matched against the original file.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"newText\":{\"description\":\"Replacement text; may be empty to delete the matched span.\",\"type\":\"string\"},\"oldText\":{\"description\":\"Text identifying one unique region to replace.\",\"type\":\"string\"}},\"required\":[\"newText\",\"oldText\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to the file to edit (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"edits\",\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"edit"},"ordinal":3,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":2,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"},{"truncated":false,"value":"Sandboxable"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Find workspace files by name, path, or glob (e.g. src/**/*.rs). Space-separated terms must all match. Literal matches rank above fuzzy matches; results are capped. Respects ignore files and skips build/dependency directories by default. Use extensions to filter suffixes."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"properties\":{\"exclude\":{\"description\":\"Optional replacement exclusion globs (e.g. [\\\"**/target/**\\\", \\\"*.lock\\\"]); [] disables default exclusions.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"extensions\":{\"description\":\"Optional list of file extensions to include (e.g. [\\\"rs\\\", \\\"md\\\"]).\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"limit\":{\"description\":\"Maximum number of ranked results to return (default: 20, maximum: 200).\",\"type\":\"integer\"},\"path\":{\"description\":\"Optional base path to search (relative to workspace).\",\"type\":\"string\"},\"query\":{\"description\":\"File name, path fragment, space-separated terms, or glob (e.g. **/engine*.rs).\",\"type\":\"string\"}},\"required\":[\"query\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"file_search"},"ordinal":4,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Inspect the current runtime goal state, including objective, status, token budget, elapsed time, evidence, and blocker."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{},\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"get_goal"},"ordinal":5,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Load a named skill's SKILL.md body and companion file list into this turn. Use when the user names a skill, or when an entry in the system prompt's `## Skills` index matches the task -- load it before starting the work, not after. Pass query=\"...\" to search names and descriptions, or name=\"list\" for the whole catalogue. Resolves global and plugin skills that `read` cannot reach."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"name\":{\"description\":\"Skill id to load. Omit or pass \\\"list\\\" to see all available skills.\",\"type\":\"string\"},\"query\":{\"description\":\"Search term matched against skill names and descriptions. Use when the index was truncated or no name is known.\",\"type\":\"string\"}},\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"load_skill"},"ordinal":6,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":2,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"},{"truncated":false,"value":"Sandboxable"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Read a file. A text file comes back whole in one call when it fits this call's output budget — 100000 bytes by default, raisable to 500000 with max_bytes. There is no line cap. Use offset and limit for an exact line range; when output is budget-limited the footer names the exact offset to continue from. Every text response reports the file's byte size, line count, and whether output was truncated. A PNG, JPEG, GIF or WebP image comes back as image content you can see (large images are downscaled), including"},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of lines to read.\",\"type\":\"number\"},\"max_bytes\":{\"description\":\"Output budget in bytes for this one call. Defaults to 100000; values above the 500000 maximum are clamped down rather than rejected, and a value below the active default leaves the default in place.\",\"type\":\"number\"},\"offset\":{\"description\":\"Line number to start reading from (1-indexed).\",\"type\":\"number\"},\"path\":{\"description\":\"Path to the file to read (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"read"},"ordinal":7,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Replace the To-do list the user watches. For any task with three or more steps or more than one file, write the list before you start, keep exactly one item in_progress, and mark items done as you finish them. Skip it only for a single quick answer or edit."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"properties\":{\"todos\":{\"description\":\"The complete list of To-do items. This replaces the existing list.\",\"items\":{\"properties\":{\"content\":{\"description\":\"The task description\",\"type\":\"string\"},\"status\":{\"description\":\"Task status\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"required\":[\"todos\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"todo_write"},"ordinal":8,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":0,"omitted":0,"rendered":[]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Update the runtime goal completion gate by calling this tool; a prose status in your answer does not change the goal or stop continuation. Critical verification may seal one immutable completion contract. Advisory review is append-only context and never completes, blocks, or pauses the goal. Mark blocked when progress requires user input. Mark yield when you finished a stage and the next step needs the user's decision: the goal stays unfinished, the turn ends, and their next message resumes it."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":true,"value":"{\"additionalProperties\":false,\"properties\":{\"advisory\":{\"description\":\"Required when status is advisory. Appended separately from the judged completion contract.\",\"type\":\"string\"},\"blocker\":{\"description\":\"Required when status is blocked. Explain the condition preventing progress.\",\"type\":\"string\"},\"evidence\":{\"description\":\"Required when status is complete. Briefly cite the proof that the goal is done.\",\"type\":\"string\"},\"progress\":{\"additionalProperties\":false,\"description\":\"Optional with not_achieved or advisory: your current best estimate of overall completion, shown to the user as reported progress. Keep percent honest — it is an estimate, never a verified fraction.\",\"properties\":{\"next\":{\"description\":\"One short line: what comes next.\",\"type\":\"string\"},\"now\":{\"description\":\"One short line: what is being worked on right now.\",\"type\":\"string\"},\"percent\":{\"description\":\"Estimated percent complete, 0-100.\",\"maximum\":100,\"minimum\":0,\"type\":\"integer\"}},\"required\":[\"percent\"],\"type\":\"object\"},\"status\":{\"description\":\"Use complete only when a critical verifier proves the goal; not_achieved to record verifier gaps; blocked when meaningful progress cannot continue; yield when a stage is finished and the next step is the user's call; advisory to append best-effort context without changing lifecycle state.\",\"enum\":[\"complete\",\"blocked\",\"not_achieved\",\"advisory\",\"yield\"],\"type\":\"string\"},\"verification\":{\"additionalProperties\":false,\"description\":\"Required when status is complete or not_achieved. A verifier-as-judge receipt from a concrete check, such as Run action=\\\"verifiers\\\" or an equivalent project-specific gate.\",\"properties\":{\"check\":{\"description\":\"The verifier/check that passed.\",\"type\":\"string\"},\"gaps\":{\"description\":\"Concrete remaining gaps. Required for critical not_achieved reviews; order and duplicate wording do not affect the stall fingerprint.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"role\":{\"description\":\"Critical reviews may satisfy the judged completion contract. Advisory reviews are fail-open and c"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"update_goal"},"ordinal":9,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Suggest"}},"cache_control_type":{"status":"known","value":{"truncated":false,"value":"ephemeral"}},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Write content to a file. Creates the file if it does not exist, overwrites it if it does, and creates parent directories automatically."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Content to write to the file.\",\"type\":\"string\"},\"path\":{\"description\":\"Path to the file to write (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"content\",\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"write"},"ordinal":10,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"reason":"tool is not in the registry","status":"unknown"},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"reason":"tool is not in the registry","status":"unknown"},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Run a JavaScript program that composes tool calls with `await tools.call(name, args)` and returns a bounded JSON result. Prefer it whenever you would make several dependent or repetitive calls, MCP and plugin tools included: intermediate results stay in the program and only what you return reaches the conversation. Every nested call passes the same permission checks as a direct call; a call that needs approval pauses the program until the user decides, and a denied or refused call throws inside the program "},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"type\":\"object\",\"properties\":{\"code\":{\"type\":\"string\",\"description\":\"JavaScript program. The return value (or thrown error) becomes the result; use tools.call(name, argsObject) for tool calls.\"}},\"required\":[\"code\"]}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"reason":"tool is not in the registry","status":"unknown"},"name":{"truncated":false,"value":"execute_tools"},"ordinal":11,"provenance":{"status":"known","value":"synthetic"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"status":"known","value":{"truncated":false,"value":"execute_tools_20260918"}},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"reason":"tool is not in the registry","status":"unknown"},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"reason":"tool is not in the registry","status":"unknown"},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Search deferred tool definitions and return matching tool references."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\",\"description\":\"Search query for tool discovery.\"},\"match\":{\"type\":\"string\",\"enum\":[\"bm25\",\"regex\"],\"default\":\"bm25\",\"description\":\"Matching algorithm: bm25 for natural-language matching, regex for a regular expression over tool names/descriptions/schema.\"},\"max_results\":{\"type\":\"integer\",\"minimum\":1,\"maximum\":8,\"default\":8,\"description\":\"Maximum number of matching tool references to return.\"}},\"required\":[\"query\"]}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"reason":"tool is not in the registry","status":"unknown"},"name":{"truncated":false,"value":"tool_search"},"ordinal":12,"provenance":{"status":"known","value":"synthetic"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"status":"known","value":{"truncated":false,"value":"tool_search_20251119"}},"visibility":"active"}],"tools_field_present":true,"turn_id":{"truncated":false,"value":""},"unavailable_for_this_request":["provider_wire_payload"]}} +{"changed":false,"context_updates":0,"description":"frozen: e6c0d3cc59cd","event":"prefix_cache_change","last_miss_reason":"","pin_reason":"initial","pinned_combined_hash":"","stability_pct":100,"system_prompt_changed":false,"tools_changed":false} +{"event":"tool_request_snapshot","snapshot":{"active_tool_catalog_sha256":"ba34408b0c03b810ba1ef6190c41d215f58e88822148991099b7d70d444992fc","capture_source":"prepared model-client request","delivery_status":"unknown (capture does not prove provider delivery)","omitted_tool_count":0,"payload_json_bytes":14897,"payload_measurement_status":"exact (within 1048576-byte measurement bound)","provider":{"model":"deepseek-v4-pro","provider":"Deepseek","status":"available"},"registry_facts_present":true,"registry_only_tools":{"status":"known","value":{"count":40,"omitted":8,"rendered":[{"truncated":false,"value":"Git"},{"truncated":false,"value":"Run"},{"truncated":false,"value":"Web"},{"truncated":false,"value":"apply_patch"},{"truncated":false,"value":"automation"},{"truncated":false,"value":"diagnostics"},{"truncated":false,"value":"finance"},{"truncated":false,"value":"github"},{"truncated":false,"value":"grep_files"},{"truncated":false,"value":"handle_read"},{"truncated":false,"value":"harness"},{"truncated":false,"value":"image_ocr"},{"truncated":false,"value":"list_dir"},{"truncated":false,"value":"lsp"},{"truncated":false,"value":"note"},{"truncated":false,"value":"notify"},{"truncated":false,"value":"pandoc_convert"},{"truncated":false,"value":"project_map"},{"truncated":false,"value":"registry_sync"},{"truncated":false,"value":"request_plugin_install"},{"truncated":false,"value":"request_user_input"},{"truncated":false,"value":"retrieve_tool_result"},{"truncated":false,"value":"revert_turn"},{"truncated":false,"value":"review"},{"truncated":false,"value":"send_later"},{"truncated":false,"value":"session_get"},{"truncated":false,"value":"session_search"},{"truncated":false,"value":"start_mcp_server"},{"truncated":false,"value":"start_registry_mcp_server"},{"truncated":false,"value":"task_shell_wait"},{"truncated":false,"value":"tasks"},{"truncated":false,"value":"terminal/cancel"}]}},"registry_tool_count":{"status":"known","value":62},"rendered_tool_count":12,"schema_version":1,"step":0,"tool_count":12,"tools":[{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Required"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"ExecutesCode"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Execute a shell command in the workspace and return stdout and stderr. Output keeps the last 2000 lines or 50KB. An optional timeout is the seconds to wait in the foreground (default 30 seconds); a command still running then is never killed for time: it moves to the background by itself and you get its output so far, how long ago it last printed, its build phase, and a task_id, so long builds and tests need no special timeout. Find task_shell_wait with tool_search to read more or wait for it. In Ask, after "},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The command to execute. Actual execution shell: `/bin/bash`. Use Bash syntax: pipelines, redirections, $(command), and && / || are supported. Quote paths and variable expansions, such as \\\"$path\\\"; use single quotes for literal text. For literal multiline input, use a quoted heredoc delimiter (<<'EOF') with its closing delimiter on a separate line. Use only installed programs; do not assume GNU-specific flags on macOS/BSD. Example: printf '%s\\\\n' 'sample'.\",\"type\":\"string\"},\"justification\":{\"description\":\"Required with sandbox_permissions: one sentence explaining why this exact command needs wider access.\",\"type\":\"string\"},\"read_only\":{\"description\":\"Set true to run analysis code (including Python/SQLite) with mandatory native filesystem read-only isolation and no network. Available during peer writes. Refused when native enforcement is unavailable; no background, stdin, external backend, or sandbox escalation.\",\"type\":\"boolean\"},\"sandbox_permissions\":{\"description\":\"The wider sandbox mode this exact command needs. Use only as a one-shot retry after a sandbox denial; requires justification and user approval in Ask.\",\"enum\":[\"workspace-write\",\"danger-full-access\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional seconds to wait in the foreground (default 30 seconds). A command still running then is not killed: it moves to the background by itself and you get its output so far, how long ago it last printed, its build phase, and a task_id. Find task_shell_wait with tool_search to read more or wait for it.\",\"type\":\"number\"}},\"required\":[\"command\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"bash"},"ordinal":1,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":0,"omitted":0,"rendered":[]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Create the session's one persistent goal: a completion objective Codewhale keeps working toward across turns until it is verified complete, blocked, or the user stops it. A goal is for a durable objective that outlasts one turn: a multi-step outcome the user wants continued and verified. A question, a greeting, or a one-shot edit completes as an ordinary turn. When the user explicitly asks to use `/goal` or to make something the goal, `create_goal` is what records it; acknowledging it in prose records nothi"},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"objective\":{\"description\":\"The full objective to pursue. Keep the complete user goal, not a shortened one-turn version.\",\"type\":\"string\"},\"token_budget\":{\"description\":\"Optional soft token budget for the goal.\",\"minimum\":0,\"type\":\"integer\"}},\"required\":[\"objective\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"create_goal"},"ordinal":2,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Suggest"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Edit one file with targeted text replacements. Every edits[].oldText must identify a unique, non-overlapping region of the original file. Merge nearby or overlapping changes into one edit."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"edits\":{\"description\":\"One or more disjoint replacements, all matched against the original file.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"newText\":{\"description\":\"Replacement text; may be empty to delete the matched span.\",\"type\":\"string\"},\"oldText\":{\"description\":\"Text identifying one unique region to replace.\",\"type\":\"string\"}},\"required\":[\"newText\",\"oldText\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to the file to edit (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"edits\",\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"edit"},"ordinal":3,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":2,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"},{"truncated":false,"value":"Sandboxable"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Find workspace files by name, path, or glob (e.g. src/**/*.rs). Space-separated terms must all match. Literal matches rank above fuzzy matches; results are capped. Respects ignore files and skips build/dependency directories by default. Use extensions to filter suffixes."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"properties\":{\"exclude\":{\"description\":\"Optional replacement exclusion globs (e.g. [\\\"**/target/**\\\", \\\"*.lock\\\"]); [] disables default exclusions.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"extensions\":{\"description\":\"Optional list of file extensions to include (e.g. [\\\"rs\\\", \\\"md\\\"]).\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"limit\":{\"description\":\"Maximum number of ranked results to return (default: 20, maximum: 200).\",\"type\":\"integer\"},\"path\":{\"description\":\"Optional base path to search (relative to workspace).\",\"type\":\"string\"},\"query\":{\"description\":\"File name, path fragment, space-separated terms, or glob (e.g. **/engine*.rs).\",\"type\":\"string\"}},\"required\":[\"query\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"file_search"},"ordinal":4,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Inspect the current runtime goal state, including objective, status, token budget, elapsed time, evidence, and blocker."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{},\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"get_goal"},"ordinal":5,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Load a named skill's SKILL.md body and companion file list into this turn. Use when the user names a skill, or when an entry in the system prompt's `## Skills` index matches the task -- load it before starting the work, not after. Pass query=\"...\" to search names and descriptions, or name=\"list\" for the whole catalogue. Resolves global and plugin skills that `read` cannot reach."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"name\":{\"description\":\"Skill id to load. Omit or pass \\\"list\\\" to see all available skills.\",\"type\":\"string\"},\"query\":{\"description\":\"Search term matched against skill names and descriptions. Use when the index was truncated or no name is known.\",\"type\":\"string\"}},\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"load_skill"},"ordinal":6,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":2,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"},{"truncated":false,"value":"Sandboxable"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Read a file. A text file comes back whole in one call when it fits this call's output budget — 100000 bytes by default, raisable to 500000 with max_bytes. There is no line cap. Use offset and limit for an exact line range; when output is budget-limited the footer names the exact offset to continue from. Every text response reports the file's byte size, line count, and whether output was truncated. A PNG, JPEG, GIF or WebP image comes back as image content you can see (large images are downscaled), including"},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of lines to read.\",\"type\":\"number\"},\"max_bytes\":{\"description\":\"Output budget in bytes for this one call. Defaults to 100000; values above the 500000 maximum are clamped down rather than rejected, and a value below the active default leaves the default in place.\",\"type\":\"number\"},\"offset\":{\"description\":\"Line number to start reading from (1-indexed).\",\"type\":\"number\"},\"path\":{\"description\":\"Path to the file to read (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"read"},"ordinal":7,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Replace the To-do list the user watches. For any task with three or more steps or more than one file, write the list before you start, keep exactly one item in_progress, and mark items done as you finish them. Skip it only for a single quick answer or edit."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"properties\":{\"todos\":{\"description\":\"The complete list of To-do items. This replaces the existing list.\",\"items\":{\"properties\":{\"content\":{\"description\":\"The task description\",\"type\":\"string\"},\"status\":{\"description\":\"Task status\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"required\":[\"todos\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"todo_write"},"ordinal":8,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":0,"omitted":0,"rendered":[]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Update the runtime goal completion gate by calling this tool; a prose status in your answer does not change the goal or stop continuation. Critical verification may seal one immutable completion contract. Advisory review is append-only context and never completes, blocks, or pauses the goal. Mark blocked when progress requires user input. Mark yield when you finished a stage and the next step needs the user's decision: the goal stays unfinished, the turn ends, and their next message resumes it."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":true,"value":"{\"additionalProperties\":false,\"properties\":{\"advisory\":{\"description\":\"Required when status is advisory. Appended separately from the judged completion contract.\",\"type\":\"string\"},\"blocker\":{\"description\":\"Required when status is blocked. Explain the condition preventing progress.\",\"type\":\"string\"},\"evidence\":{\"description\":\"Required when status is complete. Briefly cite the proof that the goal is done.\",\"type\":\"string\"},\"progress\":{\"additionalProperties\":false,\"description\":\"Optional with not_achieved, advisory or yield: your current best estimate of overall completion, shown to the user as reported progress. Keep percent honest — it is an estimate, never a verified fraction.\",\"properties\":{\"next\":{\"description\":\"One short line: what comes next.\",\"type\":\"string\"},\"now\":{\"description\":\"One short line: what is being worked on right now.\",\"type\":\"string\"},\"percent\":{\"description\":\"Estimated percent complete, 0-100.\",\"maximum\":100,\"minimum\":0,\"type\":\"integer\"}},\"required\":[\"percent\"],\"type\":\"object\"},\"status\":{\"description\":\"Use complete only when a critical verifier proves the goal; not_achieved to record verifier gaps; blocked when meaningful progress cannot continue; yield when a stage is finished and the next step is the user's call; advisory to append best-effort context without changing lifecycle state.\",\"enum\":[\"complete\",\"blocked\",\"not_achieved\",\"advisory\",\"yield\"],\"type\":\"string\"},\"verification\":{\"additionalProperties\":false,\"description\":\"Required when status is complete or not_achieved. A verifier-as-judge receipt from a concrete check, such as Run action=\\\"verifiers\\\" or an equivalent project-specific gate.\",\"properties\":{\"check\":{\"description\":\"The verifier/check that passed.\",\"type\":\"string\"},\"gaps\":{\"description\":\"Concrete remaining gaps. Required for critical not_achieved reviews; order and duplicate wording do not affect the stall fingerprint.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"role\":{\"description\":\"Critical reviews may satisfy the judged completion contract. Advisory reviews are fail-ope"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"update_goal"},"ordinal":9,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Suggest"}},"cache_control_type":{"status":"known","value":{"truncated":false,"value":"ephemeral"}},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Write content to a file. Creates the file if it does not exist, overwrites it if it does, and creates parent directories automatically."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Content to write to the file.\",\"type\":\"string\"},\"path\":{\"description\":\"Path to the file to write (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"content\",\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"write"},"ordinal":10,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"reason":"tool is not in the registry","status":"unknown"},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"reason":"tool is not in the registry","status":"unknown"},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Run a JavaScript program that composes tool calls with `await tools.call(name, args)` and returns a bounded JSON result. Prefer it whenever you would make several dependent or repetitive calls, MCP and plugin tools included: intermediate results stay in the program and only what you return reaches the conversation. Every nested call passes the same permission checks as a direct call; a call that needs approval pauses the program until the user decides, and a denied or refused call throws inside the program "},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"type\":\"object\",\"properties\":{\"code\":{\"type\":\"string\",\"description\":\"JavaScript program. The return value (or thrown error) becomes the result; use tools.call(name, argsObject) for tool calls.\"}},\"required\":[\"code\"]}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"reason":"tool is not in the registry","status":"unknown"},"name":{"truncated":false,"value":"execute_tools"},"ordinal":11,"provenance":{"status":"known","value":"synthetic"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"status":"known","value":{"truncated":false,"value":"execute_tools_20260918"}},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"reason":"tool is not in the registry","status":"unknown"},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"reason":"tool is not in the registry","status":"unknown"},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Search deferred tool definitions and return matching tool references."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\",\"description\":\"Search query for tool discovery.\"},\"match\":{\"type\":\"string\",\"enum\":[\"bm25\",\"regex\"],\"default\":\"bm25\",\"description\":\"Matching algorithm: bm25 for natural-language matching, regex for a regular expression over tool names/descriptions/schema.\"},\"max_results\":{\"type\":\"integer\",\"minimum\":1,\"maximum\":8,\"default\":8,\"description\":\"Maximum number of matching tool references to return.\"}},\"required\":[\"query\"]}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"reason":"tool is not in the registry","status":"unknown"},"name":{"truncated":false,"value":"tool_search"},"ordinal":12,"provenance":{"status":"known","value":"synthetic"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"status":"known","value":{"truncated":false,"value":"tool_search_20251119"}},"visibility":"active"}],"tools_field_present":true,"turn_id":{"truncated":false,"value":""},"unavailable_for_this_request":["provider_wire_payload"]}} {"event":"route_dispatched","route":{"auto_model":false,"base_url":"https://api.deepseek.com/beta","billing":{"billing_mode":"metered","dispatched_at":""},"billing_product":{"kind":"unproven"},"model":"deepseek-v4-pro","provider":"deepseek","provider_identity":"deepseek"},"turn_id":""} {"duration_ms":"","event":"turn_usage","first_token_ms":"","request_ms":"","usage":{"input_tokens":160,"output_tokens":40}} {"event":"tool_call_started","input":{"action":"read","path":"README.md"},"tool_call_id":"","tool_name":"File"} @@ -19,12 +19,12 @@ {"engine_session_id":"conformance-session","event":"session_updated","messages":[{"content":[{"text":"Read README.md and NOTES.md.","type":"text"},{"text":"\nCurrent local date: \nCurrent workspace: \nCurrent permission posture: Ask\nCurrent sandbox posture: workspace-write (writes inside the workspace; network blocked) (local OS sandbox applied)\n## Repo Working Set\nKey files: README.md\nActive paths (prioritize these):\n- NOTES.md (file)\n- README.md (file)\nWhen in doubt, use tools to verify and keep changes focused on the working set.\n","type":"text"}],"role":"user"},{"content":[{"execution_id":"","id":"call_read_a","input":{"action":"read","path":"README.md"},"name":"File","type":"tool_use"},{"execution_id":"","id":"call_read_b","input":{"action":"read","path":"NOTES.md"},"name":"File","type":"tool_use"}],"role":"assistant"},{"content":[{"content":"content_hash=\"sha256:41f49bffb38de5ba88945f09121ffc590f9d7f6ff80fdb9ea8ad10224fecea61\"\nconformance fixture","execution_id":"","tool_use_id":"call_read_a","type":"tool_result"}],"role":"user"},{"content":[{"content":"content_hash=\"sha256:f957b19529906961933c5c30f8713c500a9bb5d9d0695c40d48c97a26a3594ec\"\nsecond file","execution_id":"","tool_use_id":"call_read_b","type":"tool_result"}],"role":"user"}],"model":"deepseek-v4-pro","system_prompt":"","workspace":""} {"event":"status","message":"Continuing — tool results"} {"changed":false,"context_updates":0,"description":"","event":"prefix_cache_change","last_miss_reason":"","pin_reason":"initial","pinned_combined_hash":"","stability_pct":100,"system_prompt_changed":false,"tools_changed":false} -{"event":"tool_request_snapshot","snapshot":{"active_tool_catalog_sha256":"18c6d652f11c0c195e7b586852ab46b47987939aaa316b39ef8b692255a7e077","capture_source":"prepared model-client request","delivery_status":"unknown (capture does not prove provider delivery)","omitted_tool_count":0,"payload_json_bytes":14890,"payload_measurement_status":"exact (within 1048576-byte measurement bound)","provider":{"model":"deepseek-v4-pro","provider":"Deepseek","status":"available"},"registry_facts_present":true,"registry_only_tools":{"status":"known","value":{"count":40,"omitted":8,"rendered":[{"truncated":false,"value":"Git"},{"truncated":false,"value":"Run"},{"truncated":false,"value":"Web"},{"truncated":false,"value":"apply_patch"},{"truncated":false,"value":"automation"},{"truncated":false,"value":"diagnostics"},{"truncated":false,"value":"finance"},{"truncated":false,"value":"github"},{"truncated":false,"value":"grep_files"},{"truncated":false,"value":"handle_read"},{"truncated":false,"value":"harness"},{"truncated":false,"value":"image_ocr"},{"truncated":false,"value":"list_dir"},{"truncated":false,"value":"lsp"},{"truncated":false,"value":"note"},{"truncated":false,"value":"notify"},{"truncated":false,"value":"pandoc_convert"},{"truncated":false,"value":"project_map"},{"truncated":false,"value":"registry_sync"},{"truncated":false,"value":"request_plugin_install"},{"truncated":false,"value":"request_user_input"},{"truncated":false,"value":"retrieve_tool_result"},{"truncated":false,"value":"revert_turn"},{"truncated":false,"value":"review"},{"truncated":false,"value":"send_later"},{"truncated":false,"value":"session_get"},{"truncated":false,"value":"session_search"},{"truncated":false,"value":"start_mcp_server"},{"truncated":false,"value":"start_registry_mcp_server"},{"truncated":false,"value":"task_shell_wait"},{"truncated":false,"value":"tasks"},{"truncated":false,"value":"terminal/cancel"}]}},"registry_tool_count":{"status":"known","value":62},"rendered_tool_count":12,"schema_version":1,"step":1,"tool_count":12,"tools":[{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Required"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"ExecutesCode"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Execute a shell command in the workspace and return stdout and stderr. Output keeps the last 2000 lines or 50KB. An optional timeout is the seconds to wait in the foreground (default 30 seconds); a command still running then is never killed for time: it moves to the background by itself and you get its output so far, how long ago it last printed, its build phase, and a task_id, so long builds and tests need no special timeout. Find task_shell_wait with tool_search to read more or wait for it. In Ask, after "},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The command to execute. Actual execution shell: `/bin/bash`. Use Bash syntax: pipelines, redirections, $(command), and && / || are supported. Quote paths and variable expansions, such as \\\"$path\\\"; use single quotes for literal text. For literal multiline input, use a quoted heredoc delimiter (<<'EOF') with its closing delimiter on a separate line. Use only installed programs; do not assume GNU-specific flags on macOS/BSD. Example: printf '%s\\\\n' 'sample'.\",\"type\":\"string\"},\"justification\":{\"description\":\"Required with sandbox_permissions: one sentence explaining why this exact command needs wider access.\",\"type\":\"string\"},\"read_only\":{\"description\":\"Set true to run analysis code (including Python/SQLite) with mandatory native filesystem read-only isolation and no network. Available during peer writes. Refused when native enforcement is unavailable; no background, stdin, external backend, or sandbox escalation.\",\"type\":\"boolean\"},\"sandbox_permissions\":{\"description\":\"The wider sandbox mode this exact command needs. Use only as a one-shot retry after a sandbox denial; requires justification and user approval in Ask.\",\"enum\":[\"workspace-write\",\"danger-full-access\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional seconds to wait in the foreground (default 30 seconds). A command still running then is not killed: it moves to the background by itself and you get its output so far, how long ago it last printed, its build phase, and a task_id. Find task_shell_wait with tool_search to read more or wait for it.\",\"type\":\"number\"}},\"required\":[\"command\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"bash"},"ordinal":1,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":0,"omitted":0,"rendered":[]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Create the session's one persistent goal: a completion objective Codewhale keeps working toward across turns until it is verified complete, blocked, or the user stops it. A goal is for a durable objective that outlasts one turn: a multi-step outcome the user wants continued and verified. A question, a greeting, or a one-shot edit completes as an ordinary turn. When the user explicitly asks to use `/goal` or to make something the goal, `create_goal` is what records it; acknowledging it in prose records nothi"},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"objective\":{\"description\":\"The full objective to pursue. Keep the complete user goal, not a shortened one-turn version.\",\"type\":\"string\"},\"token_budget\":{\"description\":\"Optional soft token budget for the goal.\",\"minimum\":0,\"type\":\"integer\"}},\"required\":[\"objective\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"create_goal"},"ordinal":2,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Suggest"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Edit one file with targeted text replacements. Every edits[].oldText must identify a unique, non-overlapping region of the original file. Merge nearby or overlapping changes into one edit."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"edits\":{\"description\":\"One or more disjoint replacements, all matched against the original file.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"newText\":{\"description\":\"Replacement text; may be empty to delete the matched span.\",\"type\":\"string\"},\"oldText\":{\"description\":\"Text identifying one unique region to replace.\",\"type\":\"string\"}},\"required\":[\"newText\",\"oldText\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to the file to edit (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"edits\",\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"edit"},"ordinal":3,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":2,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"},{"truncated":false,"value":"Sandboxable"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Find workspace files by name, path, or glob (e.g. src/**/*.rs). Space-separated terms must all match. Literal matches rank above fuzzy matches; results are capped. Respects ignore files and skips build/dependency directories by default. Use extensions to filter suffixes."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"properties\":{\"exclude\":{\"description\":\"Optional replacement exclusion globs (e.g. [\\\"**/target/**\\\", \\\"*.lock\\\"]); [] disables default exclusions.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"extensions\":{\"description\":\"Optional list of file extensions to include (e.g. [\\\"rs\\\", \\\"md\\\"]).\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"limit\":{\"description\":\"Maximum number of ranked results to return (default: 20, maximum: 200).\",\"type\":\"integer\"},\"path\":{\"description\":\"Optional base path to search (relative to workspace).\",\"type\":\"string\"},\"query\":{\"description\":\"File name, path fragment, space-separated terms, or glob (e.g. **/engine*.rs).\",\"type\":\"string\"}},\"required\":[\"query\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"file_search"},"ordinal":4,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Inspect the current runtime goal state, including objective, status, token budget, elapsed time, evidence, and blocker."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{},\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"get_goal"},"ordinal":5,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Load a named skill's SKILL.md body and companion file list into this turn. Use when the user names a skill, or when an entry in the system prompt's `## Skills` index matches the task -- load it before starting the work, not after. Pass query=\"...\" to search names and descriptions, or name=\"list\" for the whole catalogue. Resolves global and plugin skills that `read` cannot reach."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"name\":{\"description\":\"Skill id to load. Omit or pass \\\"list\\\" to see all available skills.\",\"type\":\"string\"},\"query\":{\"description\":\"Search term matched against skill names and descriptions. Use when the index was truncated or no name is known.\",\"type\":\"string\"}},\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"load_skill"},"ordinal":6,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":2,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"},{"truncated":false,"value":"Sandboxable"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Read a file. A text file comes back whole in one call when it fits this call's output budget — 100000 bytes by default, raisable to 500000 with max_bytes. There is no line cap. Use offset and limit for an exact line range; when output is budget-limited the footer names the exact offset to continue from. Every text response reports the file's byte size, line count, and whether output was truncated. A PNG, JPEG, GIF or WebP image comes back as image content you can see (large images are downscaled), including"},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of lines to read.\",\"type\":\"number\"},\"max_bytes\":{\"description\":\"Output budget in bytes for this one call. Defaults to 100000; values above the 500000 maximum are clamped down rather than rejected, and a value below the active default leaves the default in place.\",\"type\":\"number\"},\"offset\":{\"description\":\"Line number to start reading from (1-indexed).\",\"type\":\"number\"},\"path\":{\"description\":\"Path to the file to read (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"read"},"ordinal":7,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Replace the To-do list the user watches. For any task with three or more steps or more than one file, write the list before you start, keep exactly one item in_progress, and mark items done as you finish them. Skip it only for a single quick answer or edit."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"properties\":{\"todos\":{\"description\":\"The complete list of To-do items. This replaces the existing list.\",\"items\":{\"properties\":{\"content\":{\"description\":\"The task description\",\"type\":\"string\"},\"status\":{\"description\":\"Task status\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"required\":[\"todos\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"todo_write"},"ordinal":8,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":0,"omitted":0,"rendered":[]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Update the runtime goal completion gate by calling this tool; a prose status in your answer does not change the goal or stop continuation. Critical verification may seal one immutable completion contract. Advisory review is append-only context and never completes, blocks, or pauses the goal. Mark blocked when progress requires user input. Mark yield when you finished a stage and the next step needs the user's decision: the goal stays unfinished, the turn ends, and their next message resumes it."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":true,"value":"{\"additionalProperties\":false,\"properties\":{\"advisory\":{\"description\":\"Required when status is advisory. Appended separately from the judged completion contract.\",\"type\":\"string\"},\"blocker\":{\"description\":\"Required when status is blocked. Explain the condition preventing progress.\",\"type\":\"string\"},\"evidence\":{\"description\":\"Required when status is complete. Briefly cite the proof that the goal is done.\",\"type\":\"string\"},\"progress\":{\"additionalProperties\":false,\"description\":\"Optional with not_achieved or advisory: your current best estimate of overall completion, shown to the user as reported progress. Keep percent honest — it is an estimate, never a verified fraction.\",\"properties\":{\"next\":{\"description\":\"One short line: what comes next.\",\"type\":\"string\"},\"now\":{\"description\":\"One short line: what is being worked on right now.\",\"type\":\"string\"},\"percent\":{\"description\":\"Estimated percent complete, 0-100.\",\"maximum\":100,\"minimum\":0,\"type\":\"integer\"}},\"required\":[\"percent\"],\"type\":\"object\"},\"status\":{\"description\":\"Use complete only when a critical verifier proves the goal; not_achieved to record verifier gaps; blocked when meaningful progress cannot continue; yield when a stage is finished and the next step is the user's call; advisory to append best-effort context without changing lifecycle state.\",\"enum\":[\"complete\",\"blocked\",\"not_achieved\",\"advisory\",\"yield\"],\"type\":\"string\"},\"verification\":{\"additionalProperties\":false,\"description\":\"Required when status is complete or not_achieved. A verifier-as-judge receipt from a concrete check, such as Run action=\\\"verifiers\\\" or an equivalent project-specific gate.\",\"properties\":{\"check\":{\"description\":\"The verifier/check that passed.\",\"type\":\"string\"},\"gaps\":{\"description\":\"Concrete remaining gaps. Required for critical not_achieved reviews; order and duplicate wording do not affect the stall fingerprint.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"role\":{\"description\":\"Critical reviews may satisfy the judged completion contract. Advisory reviews are fail-open and c"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"update_goal"},"ordinal":9,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Suggest"}},"cache_control_type":{"status":"known","value":{"truncated":false,"value":"ephemeral"}},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Write content to a file. Creates the file if it does not exist, overwrites it if it does, and creates parent directories automatically."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Content to write to the file.\",\"type\":\"string\"},\"path\":{\"description\":\"Path to the file to write (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"content\",\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"write"},"ordinal":10,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"reason":"tool is not in the registry","status":"unknown"},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"reason":"tool is not in the registry","status":"unknown"},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Run a JavaScript program that composes tool calls with `await tools.call(name, args)` and returns a bounded JSON result. Prefer it whenever you would make several dependent or repetitive calls, MCP and plugin tools included: intermediate results stay in the program and only what you return reaches the conversation. Every nested call passes the same permission checks as a direct call; a call that needs approval pauses the program until the user decides, and a denied or refused call throws inside the program "},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"type\":\"object\",\"properties\":{\"code\":{\"type\":\"string\",\"description\":\"JavaScript program. The return value (or thrown error) becomes the result; use tools.call(name, argsObject) for tool calls.\"}},\"required\":[\"code\"]}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"reason":"tool is not in the registry","status":"unknown"},"name":{"truncated":false,"value":"execute_tools"},"ordinal":11,"provenance":{"status":"known","value":"synthetic"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"status":"known","value":{"truncated":false,"value":"execute_tools_20260918"}},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"reason":"tool is not in the registry","status":"unknown"},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"reason":"tool is not in the registry","status":"unknown"},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Search deferred tool definitions and return matching tool references."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\",\"description\":\"Search query for tool discovery.\"},\"match\":{\"type\":\"string\",\"enum\":[\"bm25\",\"regex\"],\"default\":\"bm25\",\"description\":\"Matching algorithm: bm25 for natural-language matching, regex for a regular expression over tool names/descriptions/schema.\"},\"max_results\":{\"type\":\"integer\",\"minimum\":1,\"maximum\":8,\"default\":8,\"description\":\"Maximum number of matching tool references to return.\"}},\"required\":[\"query\"]}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"reason":"tool is not in the registry","status":"unknown"},"name":{"truncated":false,"value":"tool_search"},"ordinal":12,"provenance":{"status":"known","value":"synthetic"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"status":"known","value":{"truncated":false,"value":"tool_search_20251119"}},"visibility":"active"}],"tools_field_present":true,"turn_id":{"truncated":false,"value":""},"unavailable_for_this_request":["provider_wire_payload"]}} +{"event":"tool_request_snapshot","snapshot":{"active_tool_catalog_sha256":"ba34408b0c03b810ba1ef6190c41d215f58e88822148991099b7d70d444992fc","capture_source":"prepared model-client request","delivery_status":"unknown (capture does not prove provider delivery)","omitted_tool_count":0,"payload_json_bytes":14897,"payload_measurement_status":"exact (within 1048576-byte measurement bound)","provider":{"model":"deepseek-v4-pro","provider":"Deepseek","status":"available"},"registry_facts_present":true,"registry_only_tools":{"status":"known","value":{"count":40,"omitted":8,"rendered":[{"truncated":false,"value":"Git"},{"truncated":false,"value":"Run"},{"truncated":false,"value":"Web"},{"truncated":false,"value":"apply_patch"},{"truncated":false,"value":"automation"},{"truncated":false,"value":"diagnostics"},{"truncated":false,"value":"finance"},{"truncated":false,"value":"github"},{"truncated":false,"value":"grep_files"},{"truncated":false,"value":"handle_read"},{"truncated":false,"value":"harness"},{"truncated":false,"value":"image_ocr"},{"truncated":false,"value":"list_dir"},{"truncated":false,"value":"lsp"},{"truncated":false,"value":"note"},{"truncated":false,"value":"notify"},{"truncated":false,"value":"pandoc_convert"},{"truncated":false,"value":"project_map"},{"truncated":false,"value":"registry_sync"},{"truncated":false,"value":"request_plugin_install"},{"truncated":false,"value":"request_user_input"},{"truncated":false,"value":"retrieve_tool_result"},{"truncated":false,"value":"revert_turn"},{"truncated":false,"value":"review"},{"truncated":false,"value":"send_later"},{"truncated":false,"value":"session_get"},{"truncated":false,"value":"session_search"},{"truncated":false,"value":"start_mcp_server"},{"truncated":false,"value":"start_registry_mcp_server"},{"truncated":false,"value":"task_shell_wait"},{"truncated":false,"value":"tasks"},{"truncated":false,"value":"terminal/cancel"}]}},"registry_tool_count":{"status":"known","value":62},"rendered_tool_count":12,"schema_version":1,"step":1,"tool_count":12,"tools":[{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Required"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"ExecutesCode"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Execute a shell command in the workspace and return stdout and stderr. Output keeps the last 2000 lines or 50KB. An optional timeout is the seconds to wait in the foreground (default 30 seconds); a command still running then is never killed for time: it moves to the background by itself and you get its output so far, how long ago it last printed, its build phase, and a task_id, so long builds and tests need no special timeout. Find task_shell_wait with tool_search to read more or wait for it. In Ask, after "},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The command to execute. Actual execution shell: `/bin/bash`. Use Bash syntax: pipelines, redirections, $(command), and && / || are supported. Quote paths and variable expansions, such as \\\"$path\\\"; use single quotes for literal text. For literal multiline input, use a quoted heredoc delimiter (<<'EOF') with its closing delimiter on a separate line. Use only installed programs; do not assume GNU-specific flags on macOS/BSD. Example: printf '%s\\\\n' 'sample'.\",\"type\":\"string\"},\"justification\":{\"description\":\"Required with sandbox_permissions: one sentence explaining why this exact command needs wider access.\",\"type\":\"string\"},\"read_only\":{\"description\":\"Set true to run analysis code (including Python/SQLite) with mandatory native filesystem read-only isolation and no network. Available during peer writes. Refused when native enforcement is unavailable; no background, stdin, external backend, or sandbox escalation.\",\"type\":\"boolean\"},\"sandbox_permissions\":{\"description\":\"The wider sandbox mode this exact command needs. Use only as a one-shot retry after a sandbox denial; requires justification and user approval in Ask.\",\"enum\":[\"workspace-write\",\"danger-full-access\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional seconds to wait in the foreground (default 30 seconds). A command still running then is not killed: it moves to the background by itself and you get its output so far, how long ago it last printed, its build phase, and a task_id. Find task_shell_wait with tool_search to read more or wait for it.\",\"type\":\"number\"}},\"required\":[\"command\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"bash"},"ordinal":1,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":0,"omitted":0,"rendered":[]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Create the session's one persistent goal: a completion objective Codewhale keeps working toward across turns until it is verified complete, blocked, or the user stops it. A goal is for a durable objective that outlasts one turn: a multi-step outcome the user wants continued and verified. A question, a greeting, or a one-shot edit completes as an ordinary turn. When the user explicitly asks to use `/goal` or to make something the goal, `create_goal` is what records it; acknowledging it in prose records nothi"},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"objective\":{\"description\":\"The full objective to pursue. Keep the complete user goal, not a shortened one-turn version.\",\"type\":\"string\"},\"token_budget\":{\"description\":\"Optional soft token budget for the goal.\",\"minimum\":0,\"type\":\"integer\"}},\"required\":[\"objective\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"create_goal"},"ordinal":2,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Suggest"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Edit one file with targeted text replacements. Every edits[].oldText must identify a unique, non-overlapping region of the original file. Merge nearby or overlapping changes into one edit."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"edits\":{\"description\":\"One or more disjoint replacements, all matched against the original file.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"newText\":{\"description\":\"Replacement text; may be empty to delete the matched span.\",\"type\":\"string\"},\"oldText\":{\"description\":\"Text identifying one unique region to replace.\",\"type\":\"string\"}},\"required\":[\"newText\",\"oldText\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to the file to edit (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"edits\",\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"edit"},"ordinal":3,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":2,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"},{"truncated":false,"value":"Sandboxable"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Find workspace files by name, path, or glob (e.g. src/**/*.rs). Space-separated terms must all match. Literal matches rank above fuzzy matches; results are capped. Respects ignore files and skips build/dependency directories by default. Use extensions to filter suffixes."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"properties\":{\"exclude\":{\"description\":\"Optional replacement exclusion globs (e.g. [\\\"**/target/**\\\", \\\"*.lock\\\"]); [] disables default exclusions.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"extensions\":{\"description\":\"Optional list of file extensions to include (e.g. [\\\"rs\\\", \\\"md\\\"]).\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"limit\":{\"description\":\"Maximum number of ranked results to return (default: 20, maximum: 200).\",\"type\":\"integer\"},\"path\":{\"description\":\"Optional base path to search (relative to workspace).\",\"type\":\"string\"},\"query\":{\"description\":\"File name, path fragment, space-separated terms, or glob (e.g. **/engine*.rs).\",\"type\":\"string\"}},\"required\":[\"query\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"file_search"},"ordinal":4,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Inspect the current runtime goal state, including objective, status, token budget, elapsed time, evidence, and blocker."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{},\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"get_goal"},"ordinal":5,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Load a named skill's SKILL.md body and companion file list into this turn. Use when the user names a skill, or when an entry in the system prompt's `## Skills` index matches the task -- load it before starting the work, not after. Pass query=\"...\" to search names and descriptions, or name=\"list\" for the whole catalogue. Resolves global and plugin skills that `read` cannot reach."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"name\":{\"description\":\"Skill id to load. Omit or pass \\\"list\\\" to see all available skills.\",\"type\":\"string\"},\"query\":{\"description\":\"Search term matched against skill names and descriptions. Use when the index was truncated or no name is known.\",\"type\":\"string\"}},\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"load_skill"},"ordinal":6,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":2,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"},{"truncated":false,"value":"Sandboxable"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Read a file. A text file comes back whole in one call when it fits this call's output budget — 100000 bytes by default, raisable to 500000 with max_bytes. There is no line cap. Use offset and limit for an exact line range; when output is budget-limited the footer names the exact offset to continue from. Every text response reports the file's byte size, line count, and whether output was truncated. A PNG, JPEG, GIF or WebP image comes back as image content you can see (large images are downscaled), including"},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of lines to read.\",\"type\":\"number\"},\"max_bytes\":{\"description\":\"Output budget in bytes for this one call. Defaults to 100000; values above the 500000 maximum are clamped down rather than rejected, and a value below the active default leaves the default in place.\",\"type\":\"number\"},\"offset\":{\"description\":\"Line number to start reading from (1-indexed).\",\"type\":\"number\"},\"path\":{\"description\":\"Path to the file to read (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"read"},"ordinal":7,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Replace the To-do list the user watches. For any task with three or more steps or more than one file, write the list before you start, keep exactly one item in_progress, and mark items done as you finish them. Skip it only for a single quick answer or edit."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"properties\":{\"todos\":{\"description\":\"The complete list of To-do items. This replaces the existing list.\",\"items\":{\"properties\":{\"content\":{\"description\":\"The task description\",\"type\":\"string\"},\"status\":{\"description\":\"Task status\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"required\":[\"todos\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"todo_write"},"ordinal":8,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":0,"omitted":0,"rendered":[]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Update the runtime goal completion gate by calling this tool; a prose status in your answer does not change the goal or stop continuation. Critical verification may seal one immutable completion contract. Advisory review is append-only context and never completes, blocks, or pauses the goal. Mark blocked when progress requires user input. Mark yield when you finished a stage and the next step needs the user's decision: the goal stays unfinished, the turn ends, and their next message resumes it."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":true,"value":"{\"additionalProperties\":false,\"properties\":{\"advisory\":{\"description\":\"Required when status is advisory. Appended separately from the judged completion contract.\",\"type\":\"string\"},\"blocker\":{\"description\":\"Required when status is blocked. Explain the condition preventing progress.\",\"type\":\"string\"},\"evidence\":{\"description\":\"Required when status is complete. Briefly cite the proof that the goal is done.\",\"type\":\"string\"},\"progress\":{\"additionalProperties\":false,\"description\":\"Optional with not_achieved, advisory or yield: your current best estimate of overall completion, shown to the user as reported progress. Keep percent honest — it is an estimate, never a verified fraction.\",\"properties\":{\"next\":{\"description\":\"One short line: what comes next.\",\"type\":\"string\"},\"now\":{\"description\":\"One short line: what is being worked on right now.\",\"type\":\"string\"},\"percent\":{\"description\":\"Estimated percent complete, 0-100.\",\"maximum\":100,\"minimum\":0,\"type\":\"integer\"}},\"required\":[\"percent\"],\"type\":\"object\"},\"status\":{\"description\":\"Use complete only when a critical verifier proves the goal; not_achieved to record verifier gaps; blocked when meaningful progress cannot continue; yield when a stage is finished and the next step is the user's call; advisory to append best-effort context without changing lifecycle state.\",\"enum\":[\"complete\",\"blocked\",\"not_achieved\",\"advisory\",\"yield\"],\"type\":\"string\"},\"verification\":{\"additionalProperties\":false,\"description\":\"Required when status is complete or not_achieved. A verifier-as-judge receipt from a concrete check, such as Run action=\\\"verifiers\\\" or an equivalent project-specific gate.\",\"properties\":{\"check\":{\"description\":\"The verifier/check that passed.\",\"type\":\"string\"},\"gaps\":{\"description\":\"Concrete remaining gaps. Required for critical not_achieved reviews; order and duplicate wording do not affect the stall fingerprint.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"role\":{\"description\":\"Critical reviews may satisfy the judged completion contract. Advisory reviews are fail-ope"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"update_goal"},"ordinal":9,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Suggest"}},"cache_control_type":{"status":"known","value":{"truncated":false,"value":"ephemeral"}},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Write content to a file. Creates the file if it does not exist, overwrites it if it does, and creates parent directories automatically."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Content to write to the file.\",\"type\":\"string\"},\"path\":{\"description\":\"Path to the file to write (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"content\",\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"write"},"ordinal":10,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"reason":"tool is not in the registry","status":"unknown"},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"reason":"tool is not in the registry","status":"unknown"},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Run a JavaScript program that composes tool calls with `await tools.call(name, args)` and returns a bounded JSON result. Prefer it whenever you would make several dependent or repetitive calls, MCP and plugin tools included: intermediate results stay in the program and only what you return reaches the conversation. Every nested call passes the same permission checks as a direct call; a call that needs approval pauses the program until the user decides, and a denied or refused call throws inside the program "},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"type\":\"object\",\"properties\":{\"code\":{\"type\":\"string\",\"description\":\"JavaScript program. The return value (or thrown error) becomes the result; use tools.call(name, argsObject) for tool calls.\"}},\"required\":[\"code\"]}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"reason":"tool is not in the registry","status":"unknown"},"name":{"truncated":false,"value":"execute_tools"},"ordinal":11,"provenance":{"status":"known","value":"synthetic"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"status":"known","value":{"truncated":false,"value":"execute_tools_20260918"}},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"reason":"tool is not in the registry","status":"unknown"},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"reason":"tool is not in the registry","status":"unknown"},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Search deferred tool definitions and return matching tool references."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\",\"description\":\"Search query for tool discovery.\"},\"match\":{\"type\":\"string\",\"enum\":[\"bm25\",\"regex\"],\"default\":\"bm25\",\"description\":\"Matching algorithm: bm25 for natural-language matching, regex for a regular expression over tool names/descriptions/schema.\"},\"max_results\":{\"type\":\"integer\",\"minimum\":1,\"maximum\":8,\"default\":8,\"description\":\"Maximum number of matching tool references to return.\"}},\"required\":[\"query\"]}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"reason":"tool is not in the registry","status":"unknown"},"name":{"truncated":false,"value":"tool_search"},"ordinal":12,"provenance":{"status":"known","value":"synthetic"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"status":"known","value":{"truncated":false,"value":"tool_search_20251119"}},"visibility":"active"}],"tools_field_present":true,"turn_id":{"truncated":false,"value":""},"unavailable_for_this_request":["provider_wire_payload"]}} {"event":"message_started","index":0} {"delta":"Both files read.","event":"response_delta","index":0} {"duration_ms":"","event":"turn_usage","first_token_ms":"","request_ms":"","usage":{"input_tokens":200,"output_tokens":6}} {"event":"message_complete","index":0} {"engine_session_id":"conformance-session","event":"session_updated","messages":[{"content":[{"text":"Read README.md and NOTES.md.","type":"text"},{"text":"\nCurrent local date: \nCurrent workspace: \nCurrent permission posture: Ask\nCurrent sandbox posture: workspace-write (writes inside the workspace; network blocked) (local OS sandbox applied)\n## Repo Working Set\nKey files: README.md\nActive paths (prioritize these):\n- NOTES.md (file)\n- README.md (file)\nWhen in doubt, use tools to verify and keep changes focused on the working set.\n","type":"text"}],"role":"user"},{"content":[{"execution_id":"","id":"call_read_a","input":{"action":"read","path":"README.md"},"name":"File","type":"tool_use"},{"execution_id":"","id":"call_read_b","input":{"action":"read","path":"NOTES.md"},"name":"File","type":"tool_use"}],"role":"assistant"},{"content":[{"content":"content_hash=\"sha256:41f49bffb38de5ba88945f09121ffc590f9d7f6ff80fdb9ea8ad10224fecea61\"\nconformance fixture","execution_id":"","tool_use_id":"call_read_a","type":"tool_result"}],"role":"user"},{"content":[{"content":"content_hash=\"sha256:f957b19529906961933c5c30f8713c500a9bb5d9d0695c40d48c97a26a3594ec\"\nsecond file","execution_id":"","tool_use_id":"call_read_b","type":"tool_result"}],"role":"user"},{"content":[{"text":"Both files read.","type":"text"}],"role":"assistant"}],"model":"deepseek-v4-pro","system_prompt":"","workspace":""} -{"event":"tool_request_snapshot","snapshot":{"active_tool_catalog_sha256":"18c6d652f11c0c195e7b586852ab46b47987939aaa316b39ef8b692255a7e077","capture_source":"prepared model-client request","delivery_status":"unknown (capture does not prove provider delivery)","omitted_tool_count":0,"payload_json_bytes":14890,"payload_measurement_status":"exact (within 1048576-byte measurement bound)","provider":{"model":"deepseek-v4-pro","provider":"Deepseek","status":"available"},"registry_facts_present":true,"registry_only_tools":{"status":"known","value":{"count":40,"omitted":8,"rendered":[{"truncated":false,"value":"Git"},{"truncated":false,"value":"Run"},{"truncated":false,"value":"Web"},{"truncated":false,"value":"apply_patch"},{"truncated":false,"value":"automation"},{"truncated":false,"value":"diagnostics"},{"truncated":false,"value":"finance"},{"truncated":false,"value":"github"},{"truncated":false,"value":"grep_files"},{"truncated":false,"value":"handle_read"},{"truncated":false,"value":"harness"},{"truncated":false,"value":"image_ocr"},{"truncated":false,"value":"list_dir"},{"truncated":false,"value":"lsp"},{"truncated":false,"value":"note"},{"truncated":false,"value":"notify"},{"truncated":false,"value":"pandoc_convert"},{"truncated":false,"value":"project_map"},{"truncated":false,"value":"registry_sync"},{"truncated":false,"value":"request_plugin_install"},{"truncated":false,"value":"request_user_input"},{"truncated":false,"value":"retrieve_tool_result"},{"truncated":false,"value":"revert_turn"},{"truncated":false,"value":"review"},{"truncated":false,"value":"send_later"},{"truncated":false,"value":"session_get"},{"truncated":false,"value":"session_search"},{"truncated":false,"value":"start_mcp_server"},{"truncated":false,"value":"start_registry_mcp_server"},{"truncated":false,"value":"task_shell_wait"},{"truncated":false,"value":"tasks"},{"truncated":false,"value":"terminal/cancel"}]}},"registry_tool_count":{"status":"known","value":62},"rendered_tool_count":12,"schema_version":1,"step":1,"terminal":{"automatic_compaction_attempts":0,"effective_max_steps":null,"emergency_compaction_attempts":0,"empty_stop_retries":0,"final_report_requested":false,"last_prepared_output_limit_tokens":65536,"last_provider_finish_reason":{"truncated":false,"value":"end_turn"},"last_reported_input_tokens":200,"last_response_tool_calls":0,"last_response_tool_calls_suppressed":0,"model_requests_started":2,"model_step_index":1,"permission_denial_rounds_without_progress":0,"permission_strategy_switches":0,"reason":"provider_no_tool_call","reasoning_only_reprompts":0,"route_context_window_tokens":1000000,"soft_landing_sent":false,"status":"completed","step_budget_source":"max_steps","stream_resumes":0,"transparent_stream_retries":0,"transport_retries":0},"tool_count":12,"tools":[{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Required"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"ExecutesCode"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Execute a shell command in the workspace and return stdout and stderr. Output keeps the last 2000 lines or 50KB. An optional timeout is the seconds to wait in the foreground (default 30 seconds); a command still running then is never killed for time: it moves to the background by itself and you get its output so far, how long ago it last printed, its build phase, and a task_id, so long builds and tests need no special timeout. Find task_shell_wait with tool_search to read more or wait for it. In Ask, after "},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The command to execute. Actual execution shell: `/bin/bash`. Use Bash syntax: pipelines, redirections, $(command), and && / || are supported. Quote paths and variable expansions, such as \\\"$path\\\"; use single quotes for literal text. For literal multiline input, use a quoted heredoc delimiter (<<'EOF') with its closing delimiter on a separate line. Use only installed programs; do not assume GNU-specific flags on macOS/BSD. Example: printf '%s\\\\n' 'sample'.\",\"type\":\"string\"},\"justification\":{\"description\":\"Required with sandbox_permissions: one sentence explaining why this exact command needs wider access.\",\"type\":\"string\"},\"read_only\":{\"description\":\"Set true to run analysis code (including Python/SQLite) with mandatory native filesystem read-only isolation and no network. Available during peer writes. Refused when native enforcement is unavailable; no background, stdin, external backend, or sandbox escalation.\",\"type\":\"boolean\"},\"sandbox_permissions\":{\"description\":\"The wider sandbox mode this exact command needs. Use only as a one-shot retry after a sandbox denial; requires justification and user approval in Ask.\",\"enum\":[\"workspace-write\",\"danger-full-access\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional seconds to wait in the foreground (default 30 seconds). A command still running then is not killed: it moves to the background by itself and you get its output so far, how long ago it last printed, its build phase, and a task_id. Find task_shell_wait with tool_search to read more or wait for it.\",\"type\":\"number\"}},\"required\":[\"command\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"bash"},"ordinal":1,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":0,"omitted":0,"rendered":[]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Create the session's one persistent goal: a completion objective Codewhale keeps working toward across turns until it is verified complete, blocked, or the user stops it. A goal is for a durable objective that outlasts one turn: a multi-step outcome the user wants continued and verified. A question, a greeting, or a one-shot edit completes as an ordinary turn. When the user explicitly asks to use `/goal` or to make something the goal, `create_goal` is what records it; acknowledging it in prose records nothi"},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"objective\":{\"description\":\"The full objective to pursue. Keep the complete user goal, not a shortened one-turn version.\",\"type\":\"string\"},\"token_budget\":{\"description\":\"Optional soft token budget for the goal.\",\"minimum\":0,\"type\":\"integer\"}},\"required\":[\"objective\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"create_goal"},"ordinal":2,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Suggest"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Edit one file with targeted text replacements. Every edits[].oldText must identify a unique, non-overlapping region of the original file. Merge nearby or overlapping changes into one edit."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"edits\":{\"description\":\"One or more disjoint replacements, all matched against the original file.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"newText\":{\"description\":\"Replacement text; may be empty to delete the matched span.\",\"type\":\"string\"},\"oldText\":{\"description\":\"Text identifying one unique region to replace.\",\"type\":\"string\"}},\"required\":[\"newText\",\"oldText\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to the file to edit (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"edits\",\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"edit"},"ordinal":3,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":2,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"},{"truncated":false,"value":"Sandboxable"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Find workspace files by name, path, or glob (e.g. src/**/*.rs). Space-separated terms must all match. Literal matches rank above fuzzy matches; results are capped. Respects ignore files and skips build/dependency directories by default. Use extensions to filter suffixes."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"properties\":{\"exclude\":{\"description\":\"Optional replacement exclusion globs (e.g. [\\\"**/target/**\\\", \\\"*.lock\\\"]); [] disables default exclusions.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"extensions\":{\"description\":\"Optional list of file extensions to include (e.g. [\\\"rs\\\", \\\"md\\\"]).\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"limit\":{\"description\":\"Maximum number of ranked results to return (default: 20, maximum: 200).\",\"type\":\"integer\"},\"path\":{\"description\":\"Optional base path to search (relative to workspace).\",\"type\":\"string\"},\"query\":{\"description\":\"File name, path fragment, space-separated terms, or glob (e.g. **/engine*.rs).\",\"type\":\"string\"}},\"required\":[\"query\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"file_search"},"ordinal":4,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Inspect the current runtime goal state, including objective, status, token budget, elapsed time, evidence, and blocker."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{},\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"get_goal"},"ordinal":5,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Load a named skill's SKILL.md body and companion file list into this turn. Use when the user names a skill, or when an entry in the system prompt's `## Skills` index matches the task -- load it before starting the work, not after. Pass query=\"...\" to search names and descriptions, or name=\"list\" for the whole catalogue. Resolves global and plugin skills that `read` cannot reach."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"name\":{\"description\":\"Skill id to load. Omit or pass \\\"list\\\" to see all available skills.\",\"type\":\"string\"},\"query\":{\"description\":\"Search term matched against skill names and descriptions. Use when the index was truncated or no name is known.\",\"type\":\"string\"}},\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"load_skill"},"ordinal":6,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":2,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"},{"truncated":false,"value":"Sandboxable"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Read a file. A text file comes back whole in one call when it fits this call's output budget — 100000 bytes by default, raisable to 500000 with max_bytes. There is no line cap. Use offset and limit for an exact line range; when output is budget-limited the footer names the exact offset to continue from. Every text response reports the file's byte size, line count, and whether output was truncated. A PNG, JPEG, GIF or WebP image comes back as image content you can see (large images are downscaled), including"},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of lines to read.\",\"type\":\"number\"},\"max_bytes\":{\"description\":\"Output budget in bytes for this one call. Defaults to 100000; values above the 500000 maximum are clamped down rather than rejected, and a value below the active default leaves the default in place.\",\"type\":\"number\"},\"offset\":{\"description\":\"Line number to start reading from (1-indexed).\",\"type\":\"number\"},\"path\":{\"description\":\"Path to the file to read (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"read"},"ordinal":7,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Replace the To-do list the user watches. For any task with three or more steps or more than one file, write the list before you start, keep exactly one item in_progress, and mark items done as you finish them. Skip it only for a single quick answer or edit."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"properties\":{\"todos\":{\"description\":\"The complete list of To-do items. This replaces the existing list.\",\"items\":{\"properties\":{\"content\":{\"description\":\"The task description\",\"type\":\"string\"},\"status\":{\"description\":\"Task status\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"required\":[\"todos\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"todo_write"},"ordinal":8,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":0,"omitted":0,"rendered":[]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Update the runtime goal completion gate by calling this tool; a prose status in your answer does not change the goal or stop continuation. Critical verification may seal one immutable completion contract. Advisory review is append-only context and never completes, blocks, or pauses the goal. Mark blocked when progress requires user input. Mark yield when you finished a stage and the next step needs the user's decision: the goal stays unfinished, the turn ends, and their next message resumes it."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":true,"value":"{\"additionalProperties\":false,\"properties\":{\"advisory\":{\"description\":\"Required when status is advisory. Appended separately from the judged completion contract.\",\"type\":\"string\"},\"blocker\":{\"description\":\"Required when status is blocked. Explain the condition preventing progress.\",\"type\":\"string\"},\"evidence\":{\"description\":\"Required when status is complete. Briefly cite the proof that the goal is done.\",\"type\":\"string\"},\"progress\":{\"additionalProperties\":false,\"description\":\"Optional with not_achieved or advisory: your current best estimate of overall completion, shown to the user as reported progress. Keep percent honest — it is an estimate, never a verified fraction.\",\"properties\":{\"next\":{\"description\":\"One short line: what comes next.\",\"type\":\"string\"},\"now\":{\"description\":\"One short line: what is being worked on right now.\",\"type\":\"string\"},\"percent\":{\"description\":\"Estimated percent complete, 0-100.\",\"maximum\":100,\"minimum\":0,\"type\":\"integer\"}},\"required\":[\"percent\"],\"type\":\"object\"},\"status\":{\"description\":\"Use complete only when a critical verifier proves the goal; not_achieved to record verifier gaps; blocked when meaningful progress cannot continue; yield when a stage is finished and the next step is the user's call; advisory to append best-effort context without changing lifecycle state.\",\"enum\":[\"complete\",\"blocked\",\"not_achieved\",\"advisory\",\"yield\"],\"type\":\"string\"},\"verification\":{\"additionalProperties\":false,\"description\":\"Required when status is complete or not_achieved. A verifier-as-judge receipt from a concrete check, such as Run action=\\\"verifiers\\\" or an equivalent project-specific gate.\",\"properties\":{\"check\":{\"description\":\"The verifier/check that passed.\",\"type\":\"string\"},\"gaps\":{\"description\":\"Concrete remaining gaps. Required for critical not_achieved reviews; order and duplicate wording do not affect the stall fingerprint.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"role\":{\"description\":\"Critical reviews may satisfy the judged completion contract. Advisory reviews are fail-open and c"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"update_goal"},"ordinal":9,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Suggest"}},"cache_control_type":{"status":"known","value":{"truncated":false,"value":"ephemeral"}},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Write content to a file. Creates the file if it does not exist, overwrites it if it does, and creates parent directories automatically."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Content to write to the file.\",\"type\":\"string\"},\"path\":{\"description\":\"Path to the file to write (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"content\",\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"write"},"ordinal":10,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"reason":"tool is not in the registry","status":"unknown"},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"reason":"tool is not in the registry","status":"unknown"},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Run a JavaScript program that composes tool calls with `await tools.call(name, args)` and returns a bounded JSON result. Prefer it whenever you would make several dependent or repetitive calls, MCP and plugin tools included: intermediate results stay in the program and only what you return reaches the conversation. Every nested call passes the same permission checks as a direct call; a call that needs approval pauses the program until the user decides, and a denied or refused call throws inside the program "},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"type\":\"object\",\"properties\":{\"code\":{\"type\":\"string\",\"description\":\"JavaScript program. The return value (or thrown error) becomes the result; use tools.call(name, argsObject) for tool calls.\"}},\"required\":[\"code\"]}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"reason":"tool is not in the registry","status":"unknown"},"name":{"truncated":false,"value":"execute_tools"},"ordinal":11,"provenance":{"status":"known","value":"synthetic"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"status":"known","value":{"truncated":false,"value":"execute_tools_20260918"}},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"reason":"tool is not in the registry","status":"unknown"},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"reason":"tool is not in the registry","status":"unknown"},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Search deferred tool definitions and return matching tool references."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\",\"description\":\"Search query for tool discovery.\"},\"match\":{\"type\":\"string\",\"enum\":[\"bm25\",\"regex\"],\"default\":\"bm25\",\"description\":\"Matching algorithm: bm25 for natural-language matching, regex for a regular expression over tool names/descriptions/schema.\"},\"max_results\":{\"type\":\"integer\",\"minimum\":1,\"maximum\":8,\"default\":8,\"description\":\"Maximum number of matching tool references to return.\"}},\"required\":[\"query\"]}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"reason":"tool is not in the registry","status":"unknown"},"name":{"truncated":false,"value":"tool_search"},"ordinal":12,"provenance":{"status":"known","value":"synthetic"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"status":"known","value":{"truncated":false,"value":"tool_search_20251119"}},"visibility":"active"}],"tools_field_present":true,"turn_id":{"truncated":false,"value":""},"unavailable_for_this_request":["provider_wire_payload"]}} +{"event":"tool_request_snapshot","snapshot":{"active_tool_catalog_sha256":"ba34408b0c03b810ba1ef6190c41d215f58e88822148991099b7d70d444992fc","capture_source":"prepared model-client request","delivery_status":"unknown (capture does not prove provider delivery)","omitted_tool_count":0,"payload_json_bytes":14897,"payload_measurement_status":"exact (within 1048576-byte measurement bound)","provider":{"model":"deepseek-v4-pro","provider":"Deepseek","status":"available"},"registry_facts_present":true,"registry_only_tools":{"status":"known","value":{"count":40,"omitted":8,"rendered":[{"truncated":false,"value":"Git"},{"truncated":false,"value":"Run"},{"truncated":false,"value":"Web"},{"truncated":false,"value":"apply_patch"},{"truncated":false,"value":"automation"},{"truncated":false,"value":"diagnostics"},{"truncated":false,"value":"finance"},{"truncated":false,"value":"github"},{"truncated":false,"value":"grep_files"},{"truncated":false,"value":"handle_read"},{"truncated":false,"value":"harness"},{"truncated":false,"value":"image_ocr"},{"truncated":false,"value":"list_dir"},{"truncated":false,"value":"lsp"},{"truncated":false,"value":"note"},{"truncated":false,"value":"notify"},{"truncated":false,"value":"pandoc_convert"},{"truncated":false,"value":"project_map"},{"truncated":false,"value":"registry_sync"},{"truncated":false,"value":"request_plugin_install"},{"truncated":false,"value":"request_user_input"},{"truncated":false,"value":"retrieve_tool_result"},{"truncated":false,"value":"revert_turn"},{"truncated":false,"value":"review"},{"truncated":false,"value":"send_later"},{"truncated":false,"value":"session_get"},{"truncated":false,"value":"session_search"},{"truncated":false,"value":"start_mcp_server"},{"truncated":false,"value":"start_registry_mcp_server"},{"truncated":false,"value":"task_shell_wait"},{"truncated":false,"value":"tasks"},{"truncated":false,"value":"terminal/cancel"}]}},"registry_tool_count":{"status":"known","value":62},"rendered_tool_count":12,"schema_version":1,"step":1,"terminal":{"automatic_compaction_attempts":0,"effective_max_steps":null,"emergency_compaction_attempts":0,"empty_stop_retries":0,"final_report_requested":false,"last_prepared_output_limit_tokens":65536,"last_provider_finish_reason":{"truncated":false,"value":"end_turn"},"last_reported_input_tokens":200,"last_response_tool_calls":0,"last_response_tool_calls_suppressed":0,"model_requests_started":2,"model_step_index":1,"permission_denial_rounds_without_progress":0,"permission_strategy_switches":0,"reason":"provider_no_tool_call","reasoning_only_reprompts":0,"route_context_window_tokens":1000000,"soft_landing_sent":false,"status":"completed","step_budget_source":"max_steps","stream_resumes":0,"transparent_stream_retries":0,"transport_retries":0},"tool_count":12,"tools":[{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Required"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"ExecutesCode"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Execute a shell command in the workspace and return stdout and stderr. Output keeps the last 2000 lines or 50KB. An optional timeout is the seconds to wait in the foreground (default 30 seconds); a command still running then is never killed for time: it moves to the background by itself and you get its output so far, how long ago it last printed, its build phase, and a task_id, so long builds and tests need no special timeout. Find task_shell_wait with tool_search to read more or wait for it. In Ask, after "},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The command to execute. Actual execution shell: `/bin/bash`. Use Bash syntax: pipelines, redirections, $(command), and && / || are supported. Quote paths and variable expansions, such as \\\"$path\\\"; use single quotes for literal text. For literal multiline input, use a quoted heredoc delimiter (<<'EOF') with its closing delimiter on a separate line. Use only installed programs; do not assume GNU-specific flags on macOS/BSD. Example: printf '%s\\\\n' 'sample'.\",\"type\":\"string\"},\"justification\":{\"description\":\"Required with sandbox_permissions: one sentence explaining why this exact command needs wider access.\",\"type\":\"string\"},\"read_only\":{\"description\":\"Set true to run analysis code (including Python/SQLite) with mandatory native filesystem read-only isolation and no network. Available during peer writes. Refused when native enforcement is unavailable; no background, stdin, external backend, or sandbox escalation.\",\"type\":\"boolean\"},\"sandbox_permissions\":{\"description\":\"The wider sandbox mode this exact command needs. Use only as a one-shot retry after a sandbox denial; requires justification and user approval in Ask.\",\"enum\":[\"workspace-write\",\"danger-full-access\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional seconds to wait in the foreground (default 30 seconds). A command still running then is not killed: it moves to the background by itself and you get its output so far, how long ago it last printed, its build phase, and a task_id. Find task_shell_wait with tool_search to read more or wait for it.\",\"type\":\"number\"}},\"required\":[\"command\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"bash"},"ordinal":1,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":0,"omitted":0,"rendered":[]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Create the session's one persistent goal: a completion objective Codewhale keeps working toward across turns until it is verified complete, blocked, or the user stops it. A goal is for a durable objective that outlasts one turn: a multi-step outcome the user wants continued and verified. A question, a greeting, or a one-shot edit completes as an ordinary turn. When the user explicitly asks to use `/goal` or to make something the goal, `create_goal` is what records it; acknowledging it in prose records nothi"},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"objective\":{\"description\":\"The full objective to pursue. Keep the complete user goal, not a shortened one-turn version.\",\"type\":\"string\"},\"token_budget\":{\"description\":\"Optional soft token budget for the goal.\",\"minimum\":0,\"type\":\"integer\"}},\"required\":[\"objective\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"create_goal"},"ordinal":2,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Suggest"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Edit one file with targeted text replacements. Every edits[].oldText must identify a unique, non-overlapping region of the original file. Merge nearby or overlapping changes into one edit."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"edits\":{\"description\":\"One or more disjoint replacements, all matched against the original file.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"newText\":{\"description\":\"Replacement text; may be empty to delete the matched span.\",\"type\":\"string\"},\"oldText\":{\"description\":\"Text identifying one unique region to replace.\",\"type\":\"string\"}},\"required\":[\"newText\",\"oldText\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to the file to edit (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"edits\",\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"edit"},"ordinal":3,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":2,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"},{"truncated":false,"value":"Sandboxable"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Find workspace files by name, path, or glob (e.g. src/**/*.rs). Space-separated terms must all match. Literal matches rank above fuzzy matches; results are capped. Respects ignore files and skips build/dependency directories by default. Use extensions to filter suffixes."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"properties\":{\"exclude\":{\"description\":\"Optional replacement exclusion globs (e.g. [\\\"**/target/**\\\", \\\"*.lock\\\"]); [] disables default exclusions.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"extensions\":{\"description\":\"Optional list of file extensions to include (e.g. [\\\"rs\\\", \\\"md\\\"]).\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"limit\":{\"description\":\"Maximum number of ranked results to return (default: 20, maximum: 200).\",\"type\":\"integer\"},\"path\":{\"description\":\"Optional base path to search (relative to workspace).\",\"type\":\"string\"},\"query\":{\"description\":\"File name, path fragment, space-separated terms, or glob (e.g. **/engine*.rs).\",\"type\":\"string\"}},\"required\":[\"query\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"file_search"},"ordinal":4,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Inspect the current runtime goal state, including objective, status, token budget, elapsed time, evidence, and blocker."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{},\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"get_goal"},"ordinal":5,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Load a named skill's SKILL.md body and companion file list into this turn. Use when the user names a skill, or when an entry in the system prompt's `## Skills` index matches the task -- load it before starting the work, not after. Pass query=\"...\" to search names and descriptions, or name=\"list\" for the whole catalogue. Resolves global and plugin skills that `read` cannot reach."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"name\":{\"description\":\"Skill id to load. Omit or pass \\\"list\\\" to see all available skills.\",\"type\":\"string\"},\"query\":{\"description\":\"Search term matched against skill names and descriptions. Use when the index was truncated or no name is known.\",\"type\":\"string\"}},\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"load_skill"},"ordinal":6,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":2,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"},{"truncated":false,"value":"Sandboxable"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Read a file. A text file comes back whole in one call when it fits this call's output budget — 100000 bytes by default, raisable to 500000 with max_bytes. There is no line cap. Use offset and limit for an exact line range; when output is budget-limited the footer names the exact offset to continue from. Every text response reports the file's byte size, line count, and whether output was truncated. A PNG, JPEG, GIF or WebP image comes back as image content you can see (large images are downscaled), including"},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of lines to read.\",\"type\":\"number\"},\"max_bytes\":{\"description\":\"Output budget in bytes for this one call. Defaults to 100000; values above the 500000 maximum are clamped down rather than rejected, and a value below the active default leaves the default in place.\",\"type\":\"number\"},\"offset\":{\"description\":\"Line number to start reading from (1-indexed).\",\"type\":\"number\"},\"path\":{\"description\":\"Path to the file to read (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"read"},"ordinal":7,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Replace the To-do list the user watches. For any task with three or more steps or more than one file, write the list before you start, keep exactly one item in_progress, and mark items done as you finish them. Skip it only for a single quick answer or edit."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"properties\":{\"todos\":{\"description\":\"The complete list of To-do items. This replaces the existing list.\",\"items\":{\"properties\":{\"content\":{\"description\":\"The task description\",\"type\":\"string\"},\"status\":{\"description\":\"Task status\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"required\":[\"todos\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"todo_write"},"ordinal":8,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":0,"omitted":0,"rendered":[]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Update the runtime goal completion gate by calling this tool; a prose status in your answer does not change the goal or stop continuation. Critical verification may seal one immutable completion contract. Advisory review is append-only context and never completes, blocks, or pauses the goal. Mark blocked when progress requires user input. Mark yield when you finished a stage and the next step needs the user's decision: the goal stays unfinished, the turn ends, and their next message resumes it."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":true,"value":"{\"additionalProperties\":false,\"properties\":{\"advisory\":{\"description\":\"Required when status is advisory. Appended separately from the judged completion contract.\",\"type\":\"string\"},\"blocker\":{\"description\":\"Required when status is blocked. Explain the condition preventing progress.\",\"type\":\"string\"},\"evidence\":{\"description\":\"Required when status is complete. Briefly cite the proof that the goal is done.\",\"type\":\"string\"},\"progress\":{\"additionalProperties\":false,\"description\":\"Optional with not_achieved, advisory or yield: your current best estimate of overall completion, shown to the user as reported progress. Keep percent honest — it is an estimate, never a verified fraction.\",\"properties\":{\"next\":{\"description\":\"One short line: what comes next.\",\"type\":\"string\"},\"now\":{\"description\":\"One short line: what is being worked on right now.\",\"type\":\"string\"},\"percent\":{\"description\":\"Estimated percent complete, 0-100.\",\"maximum\":100,\"minimum\":0,\"type\":\"integer\"}},\"required\":[\"percent\"],\"type\":\"object\"},\"status\":{\"description\":\"Use complete only when a critical verifier proves the goal; not_achieved to record verifier gaps; blocked when meaningful progress cannot continue; yield when a stage is finished and the next step is the user's call; advisory to append best-effort context without changing lifecycle state.\",\"enum\":[\"complete\",\"blocked\",\"not_achieved\",\"advisory\",\"yield\"],\"type\":\"string\"},\"verification\":{\"additionalProperties\":false,\"description\":\"Required when status is complete or not_achieved. A verifier-as-judge receipt from a concrete check, such as Run action=\\\"verifiers\\\" or an equivalent project-specific gate.\",\"properties\":{\"check\":{\"description\":\"The verifier/check that passed.\",\"type\":\"string\"},\"gaps\":{\"description\":\"Concrete remaining gaps. Required for critical not_achieved reviews; order and duplicate wording do not affect the stall fingerprint.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"role\":{\"description\":\"Critical reviews may satisfy the judged completion contract. Advisory reviews are fail-ope"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"update_goal"},"ordinal":9,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Suggest"}},"cache_control_type":{"status":"known","value":{"truncated":false,"value":"ephemeral"}},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Write content to a file. Creates the file if it does not exist, overwrites it if it does, and creates parent directories automatically."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Content to write to the file.\",\"type\":\"string\"},\"path\":{\"description\":\"Path to the file to write (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"content\",\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"write"},"ordinal":10,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"reason":"tool is not in the registry","status":"unknown"},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"reason":"tool is not in the registry","status":"unknown"},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Run a JavaScript program that composes tool calls with `await tools.call(name, args)` and returns a bounded JSON result. Prefer it whenever you would make several dependent or repetitive calls, MCP and plugin tools included: intermediate results stay in the program and only what you return reaches the conversation. Every nested call passes the same permission checks as a direct call; a call that needs approval pauses the program until the user decides, and a denied or refused call throws inside the program "},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"type\":\"object\",\"properties\":{\"code\":{\"type\":\"string\",\"description\":\"JavaScript program. The return value (or thrown error) becomes the result; use tools.call(name, argsObject) for tool calls.\"}},\"required\":[\"code\"]}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"reason":"tool is not in the registry","status":"unknown"},"name":{"truncated":false,"value":"execute_tools"},"ordinal":11,"provenance":{"status":"known","value":"synthetic"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"status":"known","value":{"truncated":false,"value":"execute_tools_20260918"}},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"reason":"tool is not in the registry","status":"unknown"},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"reason":"tool is not in the registry","status":"unknown"},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Search deferred tool definitions and return matching tool references."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\",\"description\":\"Search query for tool discovery.\"},\"match\":{\"type\":\"string\",\"enum\":[\"bm25\",\"regex\"],\"default\":\"bm25\",\"description\":\"Matching algorithm: bm25 for natural-language matching, regex for a regular expression over tool names/descriptions/schema.\"},\"max_results\":{\"type\":\"integer\",\"minimum\":1,\"maximum\":8,\"default\":8,\"description\":\"Maximum number of matching tool references to return.\"}},\"required\":[\"query\"]}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"reason":"tool is not in the registry","status":"unknown"},"name":{"truncated":false,"value":"tool_search"},"ordinal":12,"provenance":{"status":"known","value":"synthetic"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"status":"known","value":{"truncated":false,"value":"tool_search_20251119"}},"visibility":"active"}],"tools_field_present":true,"turn_id":{"truncated":false,"value":""},"unavailable_for_this_request":["provider_wire_payload"]}} {"event":"turn_complete","parent_route_usage":{"input_tokens":360,"output_tokens":46},"routed_usage_dropped_records":0,"status":"completed","tool_catalog":"","usage":{"input_tokens":360,"output_tokens":46}} -{"harness_summary":{"model_requests":2,"non_streaming_requests":0,"workspace_after":{".codewhale/state/subagents.v1.lock":"e3b0c44298fc1c14","NOTES.md":"f957b19529906961","README.md":"41f49bffb38de5ba"}}} +{"harness_summary":{"model_requests":2,"non_streaming_requests":0,"workspace_after":{".codewhale/state/.gitignore":"cdbcae15105d6b78",".codewhale/state/subagents.v1.lock":"e3b0c44298fc1c14","NOTES.md":"f957b19529906961","README.md":"41f49bffb38de5ba"}}} diff --git a/crates/tui/tests/fixtures/conformance/events/plain_answer.golden.jsonl b/crates/tui/tests/fixtures/conformance/events/plain_answer.golden.jsonl index fdae81e851..0da424cd53 100644 --- a/crates/tui/tests/fixtures/conformance/events/plain_answer.golden.jsonl +++ b/crates/tui/tests/fixtures/conformance/events/plain_answer.golden.jsonl @@ -1,8 +1,8 @@ {"created_at":"","event":"turn_started","route":{"auto_model":false,"base_url":"https://api.deepseek.com/beta","billing_product":{"kind":"unproven"},"model":"deepseek-v4-pro","provider":"deepseek","provider_identity":"deepseek"},"turn_id":""} {"engine_session_id":"conformance-session","event":"session_updated","messages":[{"content":[{"text":"Say hello.","type":"text"},{"text":"\nCurrent local date: \nCurrent workspace: \nCurrent permission posture: Ask\nCurrent sandbox posture: workspace-write (writes inside the workspace; network blocked) (local OS sandbox applied)\n## Repo Working Set\nKey files: README.md\nWhen in doubt, use tools to verify and keep changes focused on the working set.\n","type":"text"}],"role":"user"}],"model":"deepseek-v4-pro","system_prompt":"","workspace":""} {"changed":false,"context_updates":0,"description":"","event":"prefix_cache_change","last_miss_reason":"","pin_reason":"initial","pinned_combined_hash":"","stability_pct":100,"system_prompt_changed":false,"tools_changed":false} -{"changed":false,"context_updates":0,"description":"frozen: 8b8238c51355","event":"prefix_cache_change","last_miss_reason":"","pin_reason":"initial","pinned_combined_hash":"","stability_pct":100,"system_prompt_changed":false,"tools_changed":false} -{"event":"tool_request_snapshot","snapshot":{"active_tool_catalog_sha256":"18c6d652f11c0c195e7b586852ab46b47987939aaa316b39ef8b692255a7e077","capture_source":"prepared model-client request","delivery_status":"unknown (capture does not prove provider delivery)","omitted_tool_count":0,"payload_json_bytes":14890,"payload_measurement_status":"exact (within 1048576-byte measurement bound)","provider":{"model":"deepseek-v4-pro","provider":"Deepseek","status":"available"},"registry_facts_present":true,"registry_only_tools":{"status":"known","value":{"count":40,"omitted":8,"rendered":[{"truncated":false,"value":"Git"},{"truncated":false,"value":"Run"},{"truncated":false,"value":"Web"},{"truncated":false,"value":"apply_patch"},{"truncated":false,"value":"automation"},{"truncated":false,"value":"diagnostics"},{"truncated":false,"value":"finance"},{"truncated":false,"value":"github"},{"truncated":false,"value":"grep_files"},{"truncated":false,"value":"handle_read"},{"truncated":false,"value":"harness"},{"truncated":false,"value":"image_ocr"},{"truncated":false,"value":"list_dir"},{"truncated":false,"value":"lsp"},{"truncated":false,"value":"note"},{"truncated":false,"value":"notify"},{"truncated":false,"value":"pandoc_convert"},{"truncated":false,"value":"project_map"},{"truncated":false,"value":"registry_sync"},{"truncated":false,"value":"request_plugin_install"},{"truncated":false,"value":"request_user_input"},{"truncated":false,"value":"retrieve_tool_result"},{"truncated":false,"value":"revert_turn"},{"truncated":false,"value":"review"},{"truncated":false,"value":"send_later"},{"truncated":false,"value":"session_get"},{"truncated":false,"value":"session_search"},{"truncated":false,"value":"start_mcp_server"},{"truncated":false,"value":"start_registry_mcp_server"},{"truncated":false,"value":"task_shell_wait"},{"truncated":false,"value":"tasks"},{"truncated":false,"value":"terminal/cancel"}]}},"registry_tool_count":{"status":"known","value":62},"rendered_tool_count":12,"schema_version":1,"step":0,"tool_count":12,"tools":[{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Required"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"ExecutesCode"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Execute a shell command in the workspace and return stdout and stderr. Output keeps the last 2000 lines or 50KB. An optional timeout is the seconds to wait in the foreground (default 30 seconds); a command still running then is never killed for time: it moves to the background by itself and you get its output so far, how long ago it last printed, its build phase, and a task_id, so long builds and tests need no special timeout. Find task_shell_wait with tool_search to read more or wait for it. In Ask, after "},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The command to execute. Actual execution shell: `/bin/bash`. Use Bash syntax: pipelines, redirections, $(command), and && / || are supported. Quote paths and variable expansions, such as \\\"$path\\\"; use single quotes for literal text. For literal multiline input, use a quoted heredoc delimiter (<<'EOF') with its closing delimiter on a separate line. Use only installed programs; do not assume GNU-specific flags on macOS/BSD. Example: printf '%s\\\\n' 'sample'.\",\"type\":\"string\"},\"justification\":{\"description\":\"Required with sandbox_permissions: one sentence explaining why this exact command needs wider access.\",\"type\":\"string\"},\"read_only\":{\"description\":\"Set true to run analysis code (including Python/SQLite) with mandatory native filesystem read-only isolation and no network. Available during peer writes. Refused when native enforcement is unavailable; no background, stdin, external backend, or sandbox escalation.\",\"type\":\"boolean\"},\"sandbox_permissions\":{\"description\":\"The wider sandbox mode this exact command needs. Use only as a one-shot retry after a sandbox denial; requires justification and user approval in Ask.\",\"enum\":[\"workspace-write\",\"danger-full-access\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional seconds to wait in the foreground (default 30 seconds). A command still running then is not killed: it moves to the background by itself and you get its output so far, how long ago it last printed, its build phase, and a task_id. Find task_shell_wait with tool_search to read more or wait for it.\",\"type\":\"number\"}},\"required\":[\"command\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"bash"},"ordinal":1,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":0,"omitted":0,"rendered":[]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Create the session's one persistent goal: a completion objective Codewhale keeps working toward across turns until it is verified complete, blocked, or the user stops it. A goal is for a durable objective that outlasts one turn: a multi-step outcome the user wants continued and verified. A question, a greeting, or a one-shot edit completes as an ordinary turn. When the user explicitly asks to use `/goal` or to make something the goal, `create_goal` is what records it; acknowledging it in prose records nothi"},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"objective\":{\"description\":\"The full objective to pursue. Keep the complete user goal, not a shortened one-turn version.\",\"type\":\"string\"},\"token_budget\":{\"description\":\"Optional soft token budget for the goal.\",\"minimum\":0,\"type\":\"integer\"}},\"required\":[\"objective\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"create_goal"},"ordinal":2,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Suggest"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Edit one file with targeted text replacements. Every edits[].oldText must identify a unique, non-overlapping region of the original file. Merge nearby or overlapping changes into one edit."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"edits\":{\"description\":\"One or more disjoint replacements, all matched against the original file.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"newText\":{\"description\":\"Replacement text; may be empty to delete the matched span.\",\"type\":\"string\"},\"oldText\":{\"description\":\"Text identifying one unique region to replace.\",\"type\":\"string\"}},\"required\":[\"newText\",\"oldText\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to the file to edit (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"edits\",\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"edit"},"ordinal":3,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":2,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"},{"truncated":false,"value":"Sandboxable"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Find workspace files by name, path, or glob (e.g. src/**/*.rs). Space-separated terms must all match. Literal matches rank above fuzzy matches; results are capped. Respects ignore files and skips build/dependency directories by default. Use extensions to filter suffixes."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"properties\":{\"exclude\":{\"description\":\"Optional replacement exclusion globs (e.g. [\\\"**/target/**\\\", \\\"*.lock\\\"]); [] disables default exclusions.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"extensions\":{\"description\":\"Optional list of file extensions to include (e.g. [\\\"rs\\\", \\\"md\\\"]).\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"limit\":{\"description\":\"Maximum number of ranked results to return (default: 20, maximum: 200).\",\"type\":\"integer\"},\"path\":{\"description\":\"Optional base path to search (relative to workspace).\",\"type\":\"string\"},\"query\":{\"description\":\"File name, path fragment, space-separated terms, or glob (e.g. **/engine*.rs).\",\"type\":\"string\"}},\"required\":[\"query\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"file_search"},"ordinal":4,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Inspect the current runtime goal state, including objective, status, token budget, elapsed time, evidence, and blocker."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{},\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"get_goal"},"ordinal":5,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Load a named skill's SKILL.md body and companion file list into this turn. Use when the user names a skill, or when an entry in the system prompt's `## Skills` index matches the task -- load it before starting the work, not after. Pass query=\"...\" to search names and descriptions, or name=\"list\" for the whole catalogue. Resolves global and plugin skills that `read` cannot reach."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"name\":{\"description\":\"Skill id to load. Omit or pass \\\"list\\\" to see all available skills.\",\"type\":\"string\"},\"query\":{\"description\":\"Search term matched against skill names and descriptions. Use when the index was truncated or no name is known.\",\"type\":\"string\"}},\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"load_skill"},"ordinal":6,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":2,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"},{"truncated":false,"value":"Sandboxable"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Read a file. A text file comes back whole in one call when it fits this call's output budget — 100000 bytes by default, raisable to 500000 with max_bytes. There is no line cap. Use offset and limit for an exact line range; when output is budget-limited the footer names the exact offset to continue from. Every text response reports the file's byte size, line count, and whether output was truncated. A PNG, JPEG, GIF or WebP image comes back as image content you can see (large images are downscaled), including"},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of lines to read.\",\"type\":\"number\"},\"max_bytes\":{\"description\":\"Output budget in bytes for this one call. Defaults to 100000; values above the 500000 maximum are clamped down rather than rejected, and a value below the active default leaves the default in place.\",\"type\":\"number\"},\"offset\":{\"description\":\"Line number to start reading from (1-indexed).\",\"type\":\"number\"},\"path\":{\"description\":\"Path to the file to read (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"read"},"ordinal":7,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Replace the To-do list the user watches. For any task with three or more steps or more than one file, write the list before you start, keep exactly one item in_progress, and mark items done as you finish them. Skip it only for a single quick answer or edit."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"properties\":{\"todos\":{\"description\":\"The complete list of To-do items. This replaces the existing list.\",\"items\":{\"properties\":{\"content\":{\"description\":\"The task description\",\"type\":\"string\"},\"status\":{\"description\":\"Task status\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"required\":[\"todos\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"todo_write"},"ordinal":8,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":0,"omitted":0,"rendered":[]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Update the runtime goal completion gate by calling this tool; a prose status in your answer does not change the goal or stop continuation. Critical verification may seal one immutable completion contract. Advisory review is append-only context and never completes, blocks, or pauses the goal. Mark blocked when progress requires user input. Mark yield when you finished a stage and the next step needs the user's decision: the goal stays unfinished, the turn ends, and their next message resumes it."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":true,"value":"{\"additionalProperties\":false,\"properties\":{\"advisory\":{\"description\":\"Required when status is advisory. Appended separately from the judged completion contract.\",\"type\":\"string\"},\"blocker\":{\"description\":\"Required when status is blocked. Explain the condition preventing progress.\",\"type\":\"string\"},\"evidence\":{\"description\":\"Required when status is complete. Briefly cite the proof that the goal is done.\",\"type\":\"string\"},\"progress\":{\"additionalProperties\":false,\"description\":\"Optional with not_achieved or advisory: your current best estimate of overall completion, shown to the user as reported progress. Keep percent honest — it is an estimate, never a verified fraction.\",\"properties\":{\"next\":{\"description\":\"One short line: what comes next.\",\"type\":\"string\"},\"now\":{\"description\":\"One short line: what is being worked on right now.\",\"type\":\"string\"},\"percent\":{\"description\":\"Estimated percent complete, 0-100.\",\"maximum\":100,\"minimum\":0,\"type\":\"integer\"}},\"required\":[\"percent\"],\"type\":\"object\"},\"status\":{\"description\":\"Use complete only when a critical verifier proves the goal; not_achieved to record verifier gaps; blocked when meaningful progress cannot continue; yield when a stage is finished and the next step is the user's call; advisory to append best-effort context without changing lifecycle state.\",\"enum\":[\"complete\",\"blocked\",\"not_achieved\",\"advisory\",\"yield\"],\"type\":\"string\"},\"verification\":{\"additionalProperties\":false,\"description\":\"Required when status is complete or not_achieved. A verifier-as-judge receipt from a concrete check, such as Run action=\\\"verifiers\\\" or an equivalent project-specific gate.\",\"properties\":{\"check\":{\"description\":\"The verifier/check that passed.\",\"type\":\"string\"},\"gaps\":{\"description\":\"Concrete remaining gaps. Required for critical not_achieved reviews; order and duplicate wording do not affect the stall fingerprint.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"role\":{\"description\":\"Critical reviews may satisfy the judged completion contract. Advisory reviews are fail-open and c"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"update_goal"},"ordinal":9,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Suggest"}},"cache_control_type":{"status":"known","value":{"truncated":false,"value":"ephemeral"}},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Write content to a file. Creates the file if it does not exist, overwrites it if it does, and creates parent directories automatically."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Content to write to the file.\",\"type\":\"string\"},\"path\":{\"description\":\"Path to the file to write (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"content\",\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"write"},"ordinal":10,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"reason":"tool is not in the registry","status":"unknown"},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"reason":"tool is not in the registry","status":"unknown"},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Run a JavaScript program that composes tool calls with `await tools.call(name, args)` and returns a bounded JSON result. Prefer it whenever you would make several dependent or repetitive calls, MCP and plugin tools included: intermediate results stay in the program and only what you return reaches the conversation. Every nested call passes the same permission checks as a direct call; a call that needs approval pauses the program until the user decides, and a denied or refused call throws inside the program "},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"type\":\"object\",\"properties\":{\"code\":{\"type\":\"string\",\"description\":\"JavaScript program. The return value (or thrown error) becomes the result; use tools.call(name, argsObject) for tool calls.\"}},\"required\":[\"code\"]}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"reason":"tool is not in the registry","status":"unknown"},"name":{"truncated":false,"value":"execute_tools"},"ordinal":11,"provenance":{"status":"known","value":"synthetic"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"status":"known","value":{"truncated":false,"value":"execute_tools_20260918"}},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"reason":"tool is not in the registry","status":"unknown"},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"reason":"tool is not in the registry","status":"unknown"},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Search deferred tool definitions and return matching tool references."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\",\"description\":\"Search query for tool discovery.\"},\"match\":{\"type\":\"string\",\"enum\":[\"bm25\",\"regex\"],\"default\":\"bm25\",\"description\":\"Matching algorithm: bm25 for natural-language matching, regex for a regular expression over tool names/descriptions/schema.\"},\"max_results\":{\"type\":\"integer\",\"minimum\":1,\"maximum\":8,\"default\":8,\"description\":\"Maximum number of matching tool references to return.\"}},\"required\":[\"query\"]}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"reason":"tool is not in the registry","status":"unknown"},"name":{"truncated":false,"value":"tool_search"},"ordinal":12,"provenance":{"status":"known","value":"synthetic"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"status":"known","value":{"truncated":false,"value":"tool_search_20251119"}},"visibility":"active"}],"tools_field_present":true,"turn_id":{"truncated":false,"value":""},"unavailable_for_this_request":["provider_wire_payload"]}} +{"changed":false,"context_updates":0,"description":"frozen: e6c0d3cc59cd","event":"prefix_cache_change","last_miss_reason":"","pin_reason":"initial","pinned_combined_hash":"","stability_pct":100,"system_prompt_changed":false,"tools_changed":false} +{"event":"tool_request_snapshot","snapshot":{"active_tool_catalog_sha256":"ba34408b0c03b810ba1ef6190c41d215f58e88822148991099b7d70d444992fc","capture_source":"prepared model-client request","delivery_status":"unknown (capture does not prove provider delivery)","omitted_tool_count":0,"payload_json_bytes":14897,"payload_measurement_status":"exact (within 1048576-byte measurement bound)","provider":{"model":"deepseek-v4-pro","provider":"Deepseek","status":"available"},"registry_facts_present":true,"registry_only_tools":{"status":"known","value":{"count":40,"omitted":8,"rendered":[{"truncated":false,"value":"Git"},{"truncated":false,"value":"Run"},{"truncated":false,"value":"Web"},{"truncated":false,"value":"apply_patch"},{"truncated":false,"value":"automation"},{"truncated":false,"value":"diagnostics"},{"truncated":false,"value":"finance"},{"truncated":false,"value":"github"},{"truncated":false,"value":"grep_files"},{"truncated":false,"value":"handle_read"},{"truncated":false,"value":"harness"},{"truncated":false,"value":"image_ocr"},{"truncated":false,"value":"list_dir"},{"truncated":false,"value":"lsp"},{"truncated":false,"value":"note"},{"truncated":false,"value":"notify"},{"truncated":false,"value":"pandoc_convert"},{"truncated":false,"value":"project_map"},{"truncated":false,"value":"registry_sync"},{"truncated":false,"value":"request_plugin_install"},{"truncated":false,"value":"request_user_input"},{"truncated":false,"value":"retrieve_tool_result"},{"truncated":false,"value":"revert_turn"},{"truncated":false,"value":"review"},{"truncated":false,"value":"send_later"},{"truncated":false,"value":"session_get"},{"truncated":false,"value":"session_search"},{"truncated":false,"value":"start_mcp_server"},{"truncated":false,"value":"start_registry_mcp_server"},{"truncated":false,"value":"task_shell_wait"},{"truncated":false,"value":"tasks"},{"truncated":false,"value":"terminal/cancel"}]}},"registry_tool_count":{"status":"known","value":62},"rendered_tool_count":12,"schema_version":1,"step":0,"tool_count":12,"tools":[{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Required"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"ExecutesCode"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Execute a shell command in the workspace and return stdout and stderr. Output keeps the last 2000 lines or 50KB. An optional timeout is the seconds to wait in the foreground (default 30 seconds); a command still running then is never killed for time: it moves to the background by itself and you get its output so far, how long ago it last printed, its build phase, and a task_id, so long builds and tests need no special timeout. Find task_shell_wait with tool_search to read more or wait for it. In Ask, after "},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The command to execute. Actual execution shell: `/bin/bash`. Use Bash syntax: pipelines, redirections, $(command), and && / || are supported. Quote paths and variable expansions, such as \\\"$path\\\"; use single quotes for literal text. For literal multiline input, use a quoted heredoc delimiter (<<'EOF') with its closing delimiter on a separate line. Use only installed programs; do not assume GNU-specific flags on macOS/BSD. Example: printf '%s\\\\n' 'sample'.\",\"type\":\"string\"},\"justification\":{\"description\":\"Required with sandbox_permissions: one sentence explaining why this exact command needs wider access.\",\"type\":\"string\"},\"read_only\":{\"description\":\"Set true to run analysis code (including Python/SQLite) with mandatory native filesystem read-only isolation and no network. Available during peer writes. Refused when native enforcement is unavailable; no background, stdin, external backend, or sandbox escalation.\",\"type\":\"boolean\"},\"sandbox_permissions\":{\"description\":\"The wider sandbox mode this exact command needs. Use only as a one-shot retry after a sandbox denial; requires justification and user approval in Ask.\",\"enum\":[\"workspace-write\",\"danger-full-access\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional seconds to wait in the foreground (default 30 seconds). A command still running then is not killed: it moves to the background by itself and you get its output so far, how long ago it last printed, its build phase, and a task_id. Find task_shell_wait with tool_search to read more or wait for it.\",\"type\":\"number\"}},\"required\":[\"command\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"bash"},"ordinal":1,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":0,"omitted":0,"rendered":[]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Create the session's one persistent goal: a completion objective Codewhale keeps working toward across turns until it is verified complete, blocked, or the user stops it. A goal is for a durable objective that outlasts one turn: a multi-step outcome the user wants continued and verified. A question, a greeting, or a one-shot edit completes as an ordinary turn. When the user explicitly asks to use `/goal` or to make something the goal, `create_goal` is what records it; acknowledging it in prose records nothi"},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"objective\":{\"description\":\"The full objective to pursue. Keep the complete user goal, not a shortened one-turn version.\",\"type\":\"string\"},\"token_budget\":{\"description\":\"Optional soft token budget for the goal.\",\"minimum\":0,\"type\":\"integer\"}},\"required\":[\"objective\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"create_goal"},"ordinal":2,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Suggest"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Edit one file with targeted text replacements. Every edits[].oldText must identify a unique, non-overlapping region of the original file. Merge nearby or overlapping changes into one edit."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"edits\":{\"description\":\"One or more disjoint replacements, all matched against the original file.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"newText\":{\"description\":\"Replacement text; may be empty to delete the matched span.\",\"type\":\"string\"},\"oldText\":{\"description\":\"Text identifying one unique region to replace.\",\"type\":\"string\"}},\"required\":[\"newText\",\"oldText\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to the file to edit (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"edits\",\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"edit"},"ordinal":3,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":2,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"},{"truncated":false,"value":"Sandboxable"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Find workspace files by name, path, or glob (e.g. src/**/*.rs). Space-separated terms must all match. Literal matches rank above fuzzy matches; results are capped. Respects ignore files and skips build/dependency directories by default. Use extensions to filter suffixes."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"properties\":{\"exclude\":{\"description\":\"Optional replacement exclusion globs (e.g. [\\\"**/target/**\\\", \\\"*.lock\\\"]); [] disables default exclusions.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"extensions\":{\"description\":\"Optional list of file extensions to include (e.g. [\\\"rs\\\", \\\"md\\\"]).\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"limit\":{\"description\":\"Maximum number of ranked results to return (default: 20, maximum: 200).\",\"type\":\"integer\"},\"path\":{\"description\":\"Optional base path to search (relative to workspace).\",\"type\":\"string\"},\"query\":{\"description\":\"File name, path fragment, space-separated terms, or glob (e.g. **/engine*.rs).\",\"type\":\"string\"}},\"required\":[\"query\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"file_search"},"ordinal":4,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Inspect the current runtime goal state, including objective, status, token budget, elapsed time, evidence, and blocker."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{},\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"get_goal"},"ordinal":5,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Load a named skill's SKILL.md body and companion file list into this turn. Use when the user names a skill, or when an entry in the system prompt's `## Skills` index matches the task -- load it before starting the work, not after. Pass query=\"...\" to search names and descriptions, or name=\"list\" for the whole catalogue. Resolves global and plugin skills that `read` cannot reach."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"name\":{\"description\":\"Skill id to load. Omit or pass \\\"list\\\" to see all available skills.\",\"type\":\"string\"},\"query\":{\"description\":\"Search term matched against skill names and descriptions. Use when the index was truncated or no name is known.\",\"type\":\"string\"}},\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"load_skill"},"ordinal":6,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":2,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"},{"truncated":false,"value":"Sandboxable"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Read a file. A text file comes back whole in one call when it fits this call's output budget — 100000 bytes by default, raisable to 500000 with max_bytes. There is no line cap. Use offset and limit for an exact line range; when output is budget-limited the footer names the exact offset to continue from. Every text response reports the file's byte size, line count, and whether output was truncated. A PNG, JPEG, GIF or WebP image comes back as image content you can see (large images are downscaled), including"},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of lines to read.\",\"type\":\"number\"},\"max_bytes\":{\"description\":\"Output budget in bytes for this one call. Defaults to 100000; values above the 500000 maximum are clamped down rather than rejected, and a value below the active default leaves the default in place.\",\"type\":\"number\"},\"offset\":{\"description\":\"Line number to start reading from (1-indexed).\",\"type\":\"number\"},\"path\":{\"description\":\"Path to the file to read (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"read"},"ordinal":7,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Replace the To-do list the user watches. For any task with three or more steps or more than one file, write the list before you start, keep exactly one item in_progress, and mark items done as you finish them. Skip it only for a single quick answer or edit."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"properties\":{\"todos\":{\"description\":\"The complete list of To-do items. This replaces the existing list.\",\"items\":{\"properties\":{\"content\":{\"description\":\"The task description\",\"type\":\"string\"},\"status\":{\"description\":\"Task status\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"required\":[\"todos\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"todo_write"},"ordinal":8,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":0,"omitted":0,"rendered":[]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Update the runtime goal completion gate by calling this tool; a prose status in your answer does not change the goal or stop continuation. Critical verification may seal one immutable completion contract. Advisory review is append-only context and never completes, blocks, or pauses the goal. Mark blocked when progress requires user input. Mark yield when you finished a stage and the next step needs the user's decision: the goal stays unfinished, the turn ends, and their next message resumes it."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":true,"value":"{\"additionalProperties\":false,\"properties\":{\"advisory\":{\"description\":\"Required when status is advisory. Appended separately from the judged completion contract.\",\"type\":\"string\"},\"blocker\":{\"description\":\"Required when status is blocked. Explain the condition preventing progress.\",\"type\":\"string\"},\"evidence\":{\"description\":\"Required when status is complete. Briefly cite the proof that the goal is done.\",\"type\":\"string\"},\"progress\":{\"additionalProperties\":false,\"description\":\"Optional with not_achieved, advisory or yield: your current best estimate of overall completion, shown to the user as reported progress. Keep percent honest — it is an estimate, never a verified fraction.\",\"properties\":{\"next\":{\"description\":\"One short line: what comes next.\",\"type\":\"string\"},\"now\":{\"description\":\"One short line: what is being worked on right now.\",\"type\":\"string\"},\"percent\":{\"description\":\"Estimated percent complete, 0-100.\",\"maximum\":100,\"minimum\":0,\"type\":\"integer\"}},\"required\":[\"percent\"],\"type\":\"object\"},\"status\":{\"description\":\"Use complete only when a critical verifier proves the goal; not_achieved to record verifier gaps; blocked when meaningful progress cannot continue; yield when a stage is finished and the next step is the user's call; advisory to append best-effort context without changing lifecycle state.\",\"enum\":[\"complete\",\"blocked\",\"not_achieved\",\"advisory\",\"yield\"],\"type\":\"string\"},\"verification\":{\"additionalProperties\":false,\"description\":\"Required when status is complete or not_achieved. A verifier-as-judge receipt from a concrete check, such as Run action=\\\"verifiers\\\" or an equivalent project-specific gate.\",\"properties\":{\"check\":{\"description\":\"The verifier/check that passed.\",\"type\":\"string\"},\"gaps\":{\"description\":\"Concrete remaining gaps. Required for critical not_achieved reviews; order and duplicate wording do not affect the stall fingerprint.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"role\":{\"description\":\"Critical reviews may satisfy the judged completion contract. Advisory reviews are fail-ope"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"update_goal"},"ordinal":9,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Suggest"}},"cache_control_type":{"status":"known","value":{"truncated":false,"value":"ephemeral"}},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Write content to a file. Creates the file if it does not exist, overwrites it if it does, and creates parent directories automatically."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Content to write to the file.\",\"type\":\"string\"},\"path\":{\"description\":\"Path to the file to write (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"content\",\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"write"},"ordinal":10,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"reason":"tool is not in the registry","status":"unknown"},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"reason":"tool is not in the registry","status":"unknown"},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Run a JavaScript program that composes tool calls with `await tools.call(name, args)` and returns a bounded JSON result. Prefer it whenever you would make several dependent or repetitive calls, MCP and plugin tools included: intermediate results stay in the program and only what you return reaches the conversation. Every nested call passes the same permission checks as a direct call; a call that needs approval pauses the program until the user decides, and a denied or refused call throws inside the program "},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"type\":\"object\",\"properties\":{\"code\":{\"type\":\"string\",\"description\":\"JavaScript program. The return value (or thrown error) becomes the result; use tools.call(name, argsObject) for tool calls.\"}},\"required\":[\"code\"]}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"reason":"tool is not in the registry","status":"unknown"},"name":{"truncated":false,"value":"execute_tools"},"ordinal":11,"provenance":{"status":"known","value":"synthetic"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"status":"known","value":{"truncated":false,"value":"execute_tools_20260918"}},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"reason":"tool is not in the registry","status":"unknown"},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"reason":"tool is not in the registry","status":"unknown"},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Search deferred tool definitions and return matching tool references."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\",\"description\":\"Search query for tool discovery.\"},\"match\":{\"type\":\"string\",\"enum\":[\"bm25\",\"regex\"],\"default\":\"bm25\",\"description\":\"Matching algorithm: bm25 for natural-language matching, regex for a regular expression over tool names/descriptions/schema.\"},\"max_results\":{\"type\":\"integer\",\"minimum\":1,\"maximum\":8,\"default\":8,\"description\":\"Maximum number of matching tool references to return.\"}},\"required\":[\"query\"]}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"reason":"tool is not in the registry","status":"unknown"},"name":{"truncated":false,"value":"tool_search"},"ordinal":12,"provenance":{"status":"known","value":"synthetic"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"status":"known","value":{"truncated":false,"value":"tool_search_20251119"}},"visibility":"active"}],"tools_field_present":true,"turn_id":{"truncated":false,"value":""},"unavailable_for_this_request":["provider_wire_payload"]}} {"event":"route_dispatched","route":{"auto_model":false,"base_url":"https://api.deepseek.com/beta","billing":{"billing_mode":"metered","dispatched_at":""},"billing_product":{"kind":"unproven"},"model":"deepseek-v4-pro","provider":"deepseek","provider_identity":"deepseek"},"turn_id":""} {"event":"message_started","index":0} {"delta":"Hello","event":"response_delta","index":0} @@ -10,6 +10,6 @@ {"duration_ms":"","event":"turn_usage","first_token_ms":"","request_ms":"","usage":{"input_tokens":120,"output_tokens":4}} {"event":"message_complete","index":0} {"engine_session_id":"conformance-session","event":"session_updated","messages":[{"content":[{"text":"Say hello.","type":"text"},{"text":"\nCurrent local date: \nCurrent workspace: \nCurrent permission posture: Ask\nCurrent sandbox posture: workspace-write (writes inside the workspace; network blocked) (local OS sandbox applied)\n## Repo Working Set\nKey files: README.md\nWhen in doubt, use tools to verify and keep changes focused on the working set.\n","type":"text"}],"role":"user"},{"content":[{"text":"Hello there.","type":"text"}],"role":"assistant"}],"model":"deepseek-v4-pro","system_prompt":"","workspace":""} -{"event":"tool_request_snapshot","snapshot":{"active_tool_catalog_sha256":"18c6d652f11c0c195e7b586852ab46b47987939aaa316b39ef8b692255a7e077","capture_source":"prepared model-client request","delivery_status":"unknown (capture does not prove provider delivery)","omitted_tool_count":0,"payload_json_bytes":14890,"payload_measurement_status":"exact (within 1048576-byte measurement bound)","provider":{"model":"deepseek-v4-pro","provider":"Deepseek","status":"available"},"registry_facts_present":true,"registry_only_tools":{"status":"known","value":{"count":40,"omitted":8,"rendered":[{"truncated":false,"value":"Git"},{"truncated":false,"value":"Run"},{"truncated":false,"value":"Web"},{"truncated":false,"value":"apply_patch"},{"truncated":false,"value":"automation"},{"truncated":false,"value":"diagnostics"},{"truncated":false,"value":"finance"},{"truncated":false,"value":"github"},{"truncated":false,"value":"grep_files"},{"truncated":false,"value":"handle_read"},{"truncated":false,"value":"harness"},{"truncated":false,"value":"image_ocr"},{"truncated":false,"value":"list_dir"},{"truncated":false,"value":"lsp"},{"truncated":false,"value":"note"},{"truncated":false,"value":"notify"},{"truncated":false,"value":"pandoc_convert"},{"truncated":false,"value":"project_map"},{"truncated":false,"value":"registry_sync"},{"truncated":false,"value":"request_plugin_install"},{"truncated":false,"value":"request_user_input"},{"truncated":false,"value":"retrieve_tool_result"},{"truncated":false,"value":"revert_turn"},{"truncated":false,"value":"review"},{"truncated":false,"value":"send_later"},{"truncated":false,"value":"session_get"},{"truncated":false,"value":"session_search"},{"truncated":false,"value":"start_mcp_server"},{"truncated":false,"value":"start_registry_mcp_server"},{"truncated":false,"value":"task_shell_wait"},{"truncated":false,"value":"tasks"},{"truncated":false,"value":"terminal/cancel"}]}},"registry_tool_count":{"status":"known","value":62},"rendered_tool_count":12,"schema_version":1,"step":0,"terminal":{"automatic_compaction_attempts":0,"effective_max_steps":null,"emergency_compaction_attempts":0,"empty_stop_retries":0,"final_report_requested":false,"last_prepared_output_limit_tokens":65536,"last_provider_finish_reason":{"truncated":false,"value":"end_turn"},"last_reported_input_tokens":120,"last_response_tool_calls":0,"last_response_tool_calls_suppressed":0,"model_requests_started":1,"model_step_index":0,"permission_denial_rounds_without_progress":0,"permission_strategy_switches":0,"reason":"provider_no_tool_call","reasoning_only_reprompts":0,"route_context_window_tokens":1000000,"soft_landing_sent":false,"status":"completed","step_budget_source":"max_steps","stream_resumes":0,"transparent_stream_retries":0,"transport_retries":0},"tool_count":12,"tools":[{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Required"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"ExecutesCode"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Execute a shell command in the workspace and return stdout and stderr. Output keeps the last 2000 lines or 50KB. An optional timeout is the seconds to wait in the foreground (default 30 seconds); a command still running then is never killed for time: it moves to the background by itself and you get its output so far, how long ago it last printed, its build phase, and a task_id, so long builds and tests need no special timeout. Find task_shell_wait with tool_search to read more or wait for it. In Ask, after "},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The command to execute. Actual execution shell: `/bin/bash`. Use Bash syntax: pipelines, redirections, $(command), and && / || are supported. Quote paths and variable expansions, such as \\\"$path\\\"; use single quotes for literal text. For literal multiline input, use a quoted heredoc delimiter (<<'EOF') with its closing delimiter on a separate line. Use only installed programs; do not assume GNU-specific flags on macOS/BSD. Example: printf '%s\\\\n' 'sample'.\",\"type\":\"string\"},\"justification\":{\"description\":\"Required with sandbox_permissions: one sentence explaining why this exact command needs wider access.\",\"type\":\"string\"},\"read_only\":{\"description\":\"Set true to run analysis code (including Python/SQLite) with mandatory native filesystem read-only isolation and no network. Available during peer writes. Refused when native enforcement is unavailable; no background, stdin, external backend, or sandbox escalation.\",\"type\":\"boolean\"},\"sandbox_permissions\":{\"description\":\"The wider sandbox mode this exact command needs. Use only as a one-shot retry after a sandbox denial; requires justification and user approval in Ask.\",\"enum\":[\"workspace-write\",\"danger-full-access\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional seconds to wait in the foreground (default 30 seconds). A command still running then is not killed: it moves to the background by itself and you get its output so far, how long ago it last printed, its build phase, and a task_id. Find task_shell_wait with tool_search to read more or wait for it.\",\"type\":\"number\"}},\"required\":[\"command\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"bash"},"ordinal":1,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":0,"omitted":0,"rendered":[]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Create the session's one persistent goal: a completion objective Codewhale keeps working toward across turns until it is verified complete, blocked, or the user stops it. A goal is for a durable objective that outlasts one turn: a multi-step outcome the user wants continued and verified. A question, a greeting, or a one-shot edit completes as an ordinary turn. When the user explicitly asks to use `/goal` or to make something the goal, `create_goal` is what records it; acknowledging it in prose records nothi"},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"objective\":{\"description\":\"The full objective to pursue. Keep the complete user goal, not a shortened one-turn version.\",\"type\":\"string\"},\"token_budget\":{\"description\":\"Optional soft token budget for the goal.\",\"minimum\":0,\"type\":\"integer\"}},\"required\":[\"objective\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"create_goal"},"ordinal":2,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Suggest"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Edit one file with targeted text replacements. Every edits[].oldText must identify a unique, non-overlapping region of the original file. Merge nearby or overlapping changes into one edit."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"edits\":{\"description\":\"One or more disjoint replacements, all matched against the original file.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"newText\":{\"description\":\"Replacement text; may be empty to delete the matched span.\",\"type\":\"string\"},\"oldText\":{\"description\":\"Text identifying one unique region to replace.\",\"type\":\"string\"}},\"required\":[\"newText\",\"oldText\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to the file to edit (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"edits\",\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"edit"},"ordinal":3,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":2,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"},{"truncated":false,"value":"Sandboxable"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Find workspace files by name, path, or glob (e.g. src/**/*.rs). Space-separated terms must all match. Literal matches rank above fuzzy matches; results are capped. Respects ignore files and skips build/dependency directories by default. Use extensions to filter suffixes."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"properties\":{\"exclude\":{\"description\":\"Optional replacement exclusion globs (e.g. [\\\"**/target/**\\\", \\\"*.lock\\\"]); [] disables default exclusions.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"extensions\":{\"description\":\"Optional list of file extensions to include (e.g. [\\\"rs\\\", \\\"md\\\"]).\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"limit\":{\"description\":\"Maximum number of ranked results to return (default: 20, maximum: 200).\",\"type\":\"integer\"},\"path\":{\"description\":\"Optional base path to search (relative to workspace).\",\"type\":\"string\"},\"query\":{\"description\":\"File name, path fragment, space-separated terms, or glob (e.g. **/engine*.rs).\",\"type\":\"string\"}},\"required\":[\"query\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"file_search"},"ordinal":4,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Inspect the current runtime goal state, including objective, status, token budget, elapsed time, evidence, and blocker."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{},\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"get_goal"},"ordinal":5,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Load a named skill's SKILL.md body and companion file list into this turn. Use when the user names a skill, or when an entry in the system prompt's `## Skills` index matches the task -- load it before starting the work, not after. Pass query=\"...\" to search names and descriptions, or name=\"list\" for the whole catalogue. Resolves global and plugin skills that `read` cannot reach."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"name\":{\"description\":\"Skill id to load. Omit or pass \\\"list\\\" to see all available skills.\",\"type\":\"string\"},\"query\":{\"description\":\"Search term matched against skill names and descriptions. Use when the index was truncated or no name is known.\",\"type\":\"string\"}},\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"load_skill"},"ordinal":6,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":2,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"},{"truncated":false,"value":"Sandboxable"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Read a file. A text file comes back whole in one call when it fits this call's output budget — 100000 bytes by default, raisable to 500000 with max_bytes. There is no line cap. Use offset and limit for an exact line range; when output is budget-limited the footer names the exact offset to continue from. Every text response reports the file's byte size, line count, and whether output was truncated. A PNG, JPEG, GIF or WebP image comes back as image content you can see (large images are downscaled), including"},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of lines to read.\",\"type\":\"number\"},\"max_bytes\":{\"description\":\"Output budget in bytes for this one call. Defaults to 100000; values above the 500000 maximum are clamped down rather than rejected, and a value below the active default leaves the default in place.\",\"type\":\"number\"},\"offset\":{\"description\":\"Line number to start reading from (1-indexed).\",\"type\":\"number\"},\"path\":{\"description\":\"Path to the file to read (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"read"},"ordinal":7,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Replace the To-do list the user watches. For any task with three or more steps or more than one file, write the list before you start, keep exactly one item in_progress, and mark items done as you finish them. Skip it only for a single quick answer or edit."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"properties\":{\"todos\":{\"description\":\"The complete list of To-do items. This replaces the existing list.\",\"items\":{\"properties\":{\"content\":{\"description\":\"The task description\",\"type\":\"string\"},\"status\":{\"description\":\"Task status\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"required\":[\"todos\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"todo_write"},"ordinal":8,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":0,"omitted":0,"rendered":[]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Update the runtime goal completion gate by calling this tool; a prose status in your answer does not change the goal or stop continuation. Critical verification may seal one immutable completion contract. Advisory review is append-only context and never completes, blocks, or pauses the goal. Mark blocked when progress requires user input. Mark yield when you finished a stage and the next step needs the user's decision: the goal stays unfinished, the turn ends, and their next message resumes it."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":true,"value":"{\"additionalProperties\":false,\"properties\":{\"advisory\":{\"description\":\"Required when status is advisory. Appended separately from the judged completion contract.\",\"type\":\"string\"},\"blocker\":{\"description\":\"Required when status is blocked. Explain the condition preventing progress.\",\"type\":\"string\"},\"evidence\":{\"description\":\"Required when status is complete. Briefly cite the proof that the goal is done.\",\"type\":\"string\"},\"progress\":{\"additionalProperties\":false,\"description\":\"Optional with not_achieved or advisory: your current best estimate of overall completion, shown to the user as reported progress. Keep percent honest — it is an estimate, never a verified fraction.\",\"properties\":{\"next\":{\"description\":\"One short line: what comes next.\",\"type\":\"string\"},\"now\":{\"description\":\"One short line: what is being worked on right now.\",\"type\":\"string\"},\"percent\":{\"description\":\"Estimated percent complete, 0-100.\",\"maximum\":100,\"minimum\":0,\"type\":\"integer\"}},\"required\":[\"percent\"],\"type\":\"object\"},\"status\":{\"description\":\"Use complete only when a critical verifier proves the goal; not_achieved to record verifier gaps; blocked when meaningful progress cannot continue; yield when a stage is finished and the next step is the user's call; advisory to append best-effort context without changing lifecycle state.\",\"enum\":[\"complete\",\"blocked\",\"not_achieved\",\"advisory\",\"yield\"],\"type\":\"string\"},\"verification\":{\"additionalProperties\":false,\"description\":\"Required when status is complete or not_achieved. A verifier-as-judge receipt from a concrete check, such as Run action=\\\"verifiers\\\" or an equivalent project-specific gate.\",\"properties\":{\"check\":{\"description\":\"The verifier/check that passed.\",\"type\":\"string\"},\"gaps\":{\"description\":\"Concrete remaining gaps. Required for critical not_achieved reviews; order and duplicate wording do not affect the stall fingerprint.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"role\":{\"description\":\"Critical reviews may satisfy the judged completion contract. Advisory reviews are fail-open and c"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"update_goal"},"ordinal":9,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Suggest"}},"cache_control_type":{"status":"known","value":{"truncated":false,"value":"ephemeral"}},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Write content to a file. Creates the file if it does not exist, overwrites it if it does, and creates parent directories automatically."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Content to write to the file.\",\"type\":\"string\"},\"path\":{\"description\":\"Path to the file to write (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"content\",\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"write"},"ordinal":10,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"reason":"tool is not in the registry","status":"unknown"},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"reason":"tool is not in the registry","status":"unknown"},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Run a JavaScript program that composes tool calls with `await tools.call(name, args)` and returns a bounded JSON result. Prefer it whenever you would make several dependent or repetitive calls, MCP and plugin tools included: intermediate results stay in the program and only what you return reaches the conversation. Every nested call passes the same permission checks as a direct call; a call that needs approval pauses the program until the user decides, and a denied or refused call throws inside the program "},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"type\":\"object\",\"properties\":{\"code\":{\"type\":\"string\",\"description\":\"JavaScript program. The return value (or thrown error) becomes the result; use tools.call(name, argsObject) for tool calls.\"}},\"required\":[\"code\"]}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"reason":"tool is not in the registry","status":"unknown"},"name":{"truncated":false,"value":"execute_tools"},"ordinal":11,"provenance":{"status":"known","value":"synthetic"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"status":"known","value":{"truncated":false,"value":"execute_tools_20260918"}},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"reason":"tool is not in the registry","status":"unknown"},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"reason":"tool is not in the registry","status":"unknown"},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Search deferred tool definitions and return matching tool references."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\",\"description\":\"Search query for tool discovery.\"},\"match\":{\"type\":\"string\",\"enum\":[\"bm25\",\"regex\"],\"default\":\"bm25\",\"description\":\"Matching algorithm: bm25 for natural-language matching, regex for a regular expression over tool names/descriptions/schema.\"},\"max_results\":{\"type\":\"integer\",\"minimum\":1,\"maximum\":8,\"default\":8,\"description\":\"Maximum number of matching tool references to return.\"}},\"required\":[\"query\"]}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"reason":"tool is not in the registry","status":"unknown"},"name":{"truncated":false,"value":"tool_search"},"ordinal":12,"provenance":{"status":"known","value":"synthetic"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"status":"known","value":{"truncated":false,"value":"tool_search_20251119"}},"visibility":"active"}],"tools_field_present":true,"turn_id":{"truncated":false,"value":""},"unavailable_for_this_request":["provider_wire_payload"]}} +{"event":"tool_request_snapshot","snapshot":{"active_tool_catalog_sha256":"ba34408b0c03b810ba1ef6190c41d215f58e88822148991099b7d70d444992fc","capture_source":"prepared model-client request","delivery_status":"unknown (capture does not prove provider delivery)","omitted_tool_count":0,"payload_json_bytes":14897,"payload_measurement_status":"exact (within 1048576-byte measurement bound)","provider":{"model":"deepseek-v4-pro","provider":"Deepseek","status":"available"},"registry_facts_present":true,"registry_only_tools":{"status":"known","value":{"count":40,"omitted":8,"rendered":[{"truncated":false,"value":"Git"},{"truncated":false,"value":"Run"},{"truncated":false,"value":"Web"},{"truncated":false,"value":"apply_patch"},{"truncated":false,"value":"automation"},{"truncated":false,"value":"diagnostics"},{"truncated":false,"value":"finance"},{"truncated":false,"value":"github"},{"truncated":false,"value":"grep_files"},{"truncated":false,"value":"handle_read"},{"truncated":false,"value":"harness"},{"truncated":false,"value":"image_ocr"},{"truncated":false,"value":"list_dir"},{"truncated":false,"value":"lsp"},{"truncated":false,"value":"note"},{"truncated":false,"value":"notify"},{"truncated":false,"value":"pandoc_convert"},{"truncated":false,"value":"project_map"},{"truncated":false,"value":"registry_sync"},{"truncated":false,"value":"request_plugin_install"},{"truncated":false,"value":"request_user_input"},{"truncated":false,"value":"retrieve_tool_result"},{"truncated":false,"value":"revert_turn"},{"truncated":false,"value":"review"},{"truncated":false,"value":"send_later"},{"truncated":false,"value":"session_get"},{"truncated":false,"value":"session_search"},{"truncated":false,"value":"start_mcp_server"},{"truncated":false,"value":"start_registry_mcp_server"},{"truncated":false,"value":"task_shell_wait"},{"truncated":false,"value":"tasks"},{"truncated":false,"value":"terminal/cancel"}]}},"registry_tool_count":{"status":"known","value":62},"rendered_tool_count":12,"schema_version":1,"step":0,"terminal":{"automatic_compaction_attempts":0,"effective_max_steps":null,"emergency_compaction_attempts":0,"empty_stop_retries":0,"final_report_requested":false,"last_prepared_output_limit_tokens":65536,"last_provider_finish_reason":{"truncated":false,"value":"end_turn"},"last_reported_input_tokens":120,"last_response_tool_calls":0,"last_response_tool_calls_suppressed":0,"model_requests_started":1,"model_step_index":0,"permission_denial_rounds_without_progress":0,"permission_strategy_switches":0,"reason":"provider_no_tool_call","reasoning_only_reprompts":0,"route_context_window_tokens":1000000,"soft_landing_sent":false,"status":"completed","step_budget_source":"max_steps","stream_resumes":0,"transparent_stream_retries":0,"transport_retries":0},"tool_count":12,"tools":[{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Required"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"ExecutesCode"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Execute a shell command in the workspace and return stdout and stderr. Output keeps the last 2000 lines or 50KB. An optional timeout is the seconds to wait in the foreground (default 30 seconds); a command still running then is never killed for time: it moves to the background by itself and you get its output so far, how long ago it last printed, its build phase, and a task_id, so long builds and tests need no special timeout. Find task_shell_wait with tool_search to read more or wait for it. In Ask, after "},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The command to execute. Actual execution shell: `/bin/bash`. Use Bash syntax: pipelines, redirections, $(command), and && / || are supported. Quote paths and variable expansions, such as \\\"$path\\\"; use single quotes for literal text. For literal multiline input, use a quoted heredoc delimiter (<<'EOF') with its closing delimiter on a separate line. Use only installed programs; do not assume GNU-specific flags on macOS/BSD. Example: printf '%s\\\\n' 'sample'.\",\"type\":\"string\"},\"justification\":{\"description\":\"Required with sandbox_permissions: one sentence explaining why this exact command needs wider access.\",\"type\":\"string\"},\"read_only\":{\"description\":\"Set true to run analysis code (including Python/SQLite) with mandatory native filesystem read-only isolation and no network. Available during peer writes. Refused when native enforcement is unavailable; no background, stdin, external backend, or sandbox escalation.\",\"type\":\"boolean\"},\"sandbox_permissions\":{\"description\":\"The wider sandbox mode this exact command needs. Use only as a one-shot retry after a sandbox denial; requires justification and user approval in Ask.\",\"enum\":[\"workspace-write\",\"danger-full-access\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional seconds to wait in the foreground (default 30 seconds). A command still running then is not killed: it moves to the background by itself and you get its output so far, how long ago it last printed, its build phase, and a task_id. Find task_shell_wait with tool_search to read more or wait for it.\",\"type\":\"number\"}},\"required\":[\"command\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"bash"},"ordinal":1,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":0,"omitted":0,"rendered":[]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Create the session's one persistent goal: a completion objective Codewhale keeps working toward across turns until it is verified complete, blocked, or the user stops it. A goal is for a durable objective that outlasts one turn: a multi-step outcome the user wants continued and verified. A question, a greeting, or a one-shot edit completes as an ordinary turn. When the user explicitly asks to use `/goal` or to make something the goal, `create_goal` is what records it; acknowledging it in prose records nothi"},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"objective\":{\"description\":\"The full objective to pursue. Keep the complete user goal, not a shortened one-turn version.\",\"type\":\"string\"},\"token_budget\":{\"description\":\"Optional soft token budget for the goal.\",\"minimum\":0,\"type\":\"integer\"}},\"required\":[\"objective\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"create_goal"},"ordinal":2,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Suggest"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Edit one file with targeted text replacements. Every edits[].oldText must identify a unique, non-overlapping region of the original file. Merge nearby or overlapping changes into one edit."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"edits\":{\"description\":\"One or more disjoint replacements, all matched against the original file.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"newText\":{\"description\":\"Replacement text; may be empty to delete the matched span.\",\"type\":\"string\"},\"oldText\":{\"description\":\"Text identifying one unique region to replace.\",\"type\":\"string\"}},\"required\":[\"newText\",\"oldText\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to the file to edit (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"edits\",\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"edit"},"ordinal":3,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":2,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"},{"truncated":false,"value":"Sandboxable"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Find workspace files by name, path, or glob (e.g. src/**/*.rs). Space-separated terms must all match. Literal matches rank above fuzzy matches; results are capped. Respects ignore files and skips build/dependency directories by default. Use extensions to filter suffixes."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"properties\":{\"exclude\":{\"description\":\"Optional replacement exclusion globs (e.g. [\\\"**/target/**\\\", \\\"*.lock\\\"]); [] disables default exclusions.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"extensions\":{\"description\":\"Optional list of file extensions to include (e.g. [\\\"rs\\\", \\\"md\\\"]).\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"limit\":{\"description\":\"Maximum number of ranked results to return (default: 20, maximum: 200).\",\"type\":\"integer\"},\"path\":{\"description\":\"Optional base path to search (relative to workspace).\",\"type\":\"string\"},\"query\":{\"description\":\"File name, path fragment, space-separated terms, or glob (e.g. **/engine*.rs).\",\"type\":\"string\"}},\"required\":[\"query\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"file_search"},"ordinal":4,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Inspect the current runtime goal state, including objective, status, token budget, elapsed time, evidence, and blocker."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{},\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"get_goal"},"ordinal":5,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Load a named skill's SKILL.md body and companion file list into this turn. Use when the user names a skill, or when an entry in the system prompt's `## Skills` index matches the task -- load it before starting the work, not after. Pass query=\"...\" to search names and descriptions, or name=\"list\" for the whole catalogue. Resolves global and plugin skills that `read` cannot reach."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"name\":{\"description\":\"Skill id to load. Omit or pass \\\"list\\\" to see all available skills.\",\"type\":\"string\"},\"query\":{\"description\":\"Search term matched against skill names and descriptions. Use when the index was truncated or no name is known.\",\"type\":\"string\"}},\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"load_skill"},"ordinal":6,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":2,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"},{"truncated":false,"value":"Sandboxable"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Read a file. A text file comes back whole in one call when it fits this call's output budget — 100000 bytes by default, raisable to 500000 with max_bytes. There is no line cap. Use offset and limit for an exact line range; when output is budget-limited the footer names the exact offset to continue from. Every text response reports the file's byte size, line count, and whether output was truncated. A PNG, JPEG, GIF or WebP image comes back as image content you can see (large images are downscaled), including"},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of lines to read.\",\"type\":\"number\"},\"max_bytes\":{\"description\":\"Output budget in bytes for this one call. Defaults to 100000; values above the 500000 maximum are clamped down rather than rejected, and a value below the active default leaves the default in place.\",\"type\":\"number\"},\"offset\":{\"description\":\"Line number to start reading from (1-indexed).\",\"type\":\"number\"},\"path\":{\"description\":\"Path to the file to read (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"read"},"ordinal":7,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Replace the To-do list the user watches. For any task with three or more steps or more than one file, write the list before you start, keep exactly one item in_progress, and mark items done as you finish them. Skip it only for a single quick answer or edit."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"properties\":{\"todos\":{\"description\":\"The complete list of To-do items. This replaces the existing list.\",\"items\":{\"properties\":{\"content\":{\"description\":\"The task description\",\"type\":\"string\"},\"status\":{\"description\":\"Task status\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"required\":[\"todos\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"todo_write"},"ordinal":8,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":0,"omitted":0,"rendered":[]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Update the runtime goal completion gate by calling this tool; a prose status in your answer does not change the goal or stop continuation. Critical verification may seal one immutable completion contract. Advisory review is append-only context and never completes, blocks, or pauses the goal. Mark blocked when progress requires user input. Mark yield when you finished a stage and the next step needs the user's decision: the goal stays unfinished, the turn ends, and their next message resumes it."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":true,"value":"{\"additionalProperties\":false,\"properties\":{\"advisory\":{\"description\":\"Required when status is advisory. Appended separately from the judged completion contract.\",\"type\":\"string\"},\"blocker\":{\"description\":\"Required when status is blocked. Explain the condition preventing progress.\",\"type\":\"string\"},\"evidence\":{\"description\":\"Required when status is complete. Briefly cite the proof that the goal is done.\",\"type\":\"string\"},\"progress\":{\"additionalProperties\":false,\"description\":\"Optional with not_achieved, advisory or yield: your current best estimate of overall completion, shown to the user as reported progress. Keep percent honest — it is an estimate, never a verified fraction.\",\"properties\":{\"next\":{\"description\":\"One short line: what comes next.\",\"type\":\"string\"},\"now\":{\"description\":\"One short line: what is being worked on right now.\",\"type\":\"string\"},\"percent\":{\"description\":\"Estimated percent complete, 0-100.\",\"maximum\":100,\"minimum\":0,\"type\":\"integer\"}},\"required\":[\"percent\"],\"type\":\"object\"},\"status\":{\"description\":\"Use complete only when a critical verifier proves the goal; not_achieved to record verifier gaps; blocked when meaningful progress cannot continue; yield when a stage is finished and the next step is the user's call; advisory to append best-effort context without changing lifecycle state.\",\"enum\":[\"complete\",\"blocked\",\"not_achieved\",\"advisory\",\"yield\"],\"type\":\"string\"},\"verification\":{\"additionalProperties\":false,\"description\":\"Required when status is complete or not_achieved. A verifier-as-judge receipt from a concrete check, such as Run action=\\\"verifiers\\\" or an equivalent project-specific gate.\",\"properties\":{\"check\":{\"description\":\"The verifier/check that passed.\",\"type\":\"string\"},\"gaps\":{\"description\":\"Concrete remaining gaps. Required for critical not_achieved reviews; order and duplicate wording do not affect the stall fingerprint.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"role\":{\"description\":\"Critical reviews may satisfy the judged completion contract. Advisory reviews are fail-ope"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"update_goal"},"ordinal":9,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Suggest"}},"cache_control_type":{"status":"known","value":{"truncated":false,"value":"ephemeral"}},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Write content to a file. Creates the file if it does not exist, overwrites it if it does, and creates parent directories automatically."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Content to write to the file.\",\"type\":\"string\"},\"path\":{\"description\":\"Path to the file to write (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"content\",\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"write"},"ordinal":10,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"reason":"tool is not in the registry","status":"unknown"},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"reason":"tool is not in the registry","status":"unknown"},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Run a JavaScript program that composes tool calls with `await tools.call(name, args)` and returns a bounded JSON result. Prefer it whenever you would make several dependent or repetitive calls, MCP and plugin tools included: intermediate results stay in the program and only what you return reaches the conversation. Every nested call passes the same permission checks as a direct call; a call that needs approval pauses the program until the user decides, and a denied or refused call throws inside the program "},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"type\":\"object\",\"properties\":{\"code\":{\"type\":\"string\",\"description\":\"JavaScript program. The return value (or thrown error) becomes the result; use tools.call(name, argsObject) for tool calls.\"}},\"required\":[\"code\"]}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"reason":"tool is not in the registry","status":"unknown"},"name":{"truncated":false,"value":"execute_tools"},"ordinal":11,"provenance":{"status":"known","value":"synthetic"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"status":"known","value":{"truncated":false,"value":"execute_tools_20260918"}},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"reason":"tool is not in the registry","status":"unknown"},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"reason":"tool is not in the registry","status":"unknown"},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Search deferred tool definitions and return matching tool references."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\",\"description\":\"Search query for tool discovery.\"},\"match\":{\"type\":\"string\",\"enum\":[\"bm25\",\"regex\"],\"default\":\"bm25\",\"description\":\"Matching algorithm: bm25 for natural-language matching, regex for a regular expression over tool names/descriptions/schema.\"},\"max_results\":{\"type\":\"integer\",\"minimum\":1,\"maximum\":8,\"default\":8,\"description\":\"Maximum number of matching tool references to return.\"}},\"required\":[\"query\"]}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"reason":"tool is not in the registry","status":"unknown"},"name":{"truncated":false,"value":"tool_search"},"ordinal":12,"provenance":{"status":"known","value":"synthetic"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"status":"known","value":{"truncated":false,"value":"tool_search_20251119"}},"visibility":"active"}],"tools_field_present":true,"turn_id":{"truncated":false,"value":""},"unavailable_for_this_request":["provider_wire_payload"]}} {"event":"turn_complete","parent_route_usage":{"input_tokens":120,"output_tokens":4},"routed_usage_dropped_records":0,"status":"completed","tool_catalog":"","usage":{"input_tokens":120,"output_tokens":4}} -{"harness_summary":{"model_requests":1,"non_streaming_requests":0,"workspace_after":{".codewhale/state/subagents.v1.lock":"e3b0c44298fc1c14","NOTES.md":"f957b19529906961","README.md":"41f49bffb38de5ba"}}} +{"harness_summary":{"model_requests":1,"non_streaming_requests":0,"workspace_after":{".codewhale/state/.gitignore":"cdbcae15105d6b78",".codewhale/state/subagents.v1.lock":"e3b0c44298fc1c14","NOTES.md":"f957b19529906961","README.md":"41f49bffb38de5ba"}}} diff --git a/crates/tui/tests/fixtures/conformance/events/provider_error_after_tool_call.golden.jsonl b/crates/tui/tests/fixtures/conformance/events/provider_error_after_tool_call.golden.jsonl index affd26f34c..b3f8a6585b 100644 --- a/crates/tui/tests/fixtures/conformance/events/provider_error_after_tool_call.golden.jsonl +++ b/crates/tui/tests/fixtures/conformance/events/provider_error_after_tool_call.golden.jsonl @@ -1,13 +1,13 @@ {"created_at":"","event":"turn_started","route":{"auto_model":false,"base_url":"https://api.deepseek.com/beta","billing_product":{"kind":"unproven"},"model":"deepseek-v4-pro","provider":"deepseek","provider_identity":"deepseek"},"turn_id":""} {"engine_session_id":"conformance-session","event":"session_updated","messages":[{"content":[{"text":"Write c0205.txt.","type":"text"},{"text":"\nCurrent local date: \nCurrent workspace: \nCurrent permission posture: Full Access\nCurrent sandbox posture: full access (sandbox disabled)\n## Repo Working Set\nKey files: README.md\nWhen in doubt, use tools to verify and keep changes focused on the working set.\n","type":"text"}],"role":"user"}],"model":"deepseek-v4-pro","system_prompt":"","workspace":""} {"changed":false,"context_updates":0,"description":"","event":"prefix_cache_change","last_miss_reason":"","pin_reason":"initial","pinned_combined_hash":"","stability_pct":100,"system_prompt_changed":false,"tools_changed":false} -{"changed":false,"context_updates":0,"description":"frozen: 8b8238c51355","event":"prefix_cache_change","last_miss_reason":"","pin_reason":"initial","pinned_combined_hash":"","stability_pct":100,"system_prompt_changed":false,"tools_changed":false} -{"event":"tool_request_snapshot","snapshot":{"active_tool_catalog_sha256":"18c6d652f11c0c195e7b586852ab46b47987939aaa316b39ef8b692255a7e077","capture_source":"prepared model-client request","delivery_status":"unknown (capture does not prove provider delivery)","omitted_tool_count":0,"payload_json_bytes":14890,"payload_measurement_status":"exact (within 1048576-byte measurement bound)","provider":{"model":"deepseek-v4-pro","provider":"Deepseek","status":"available"},"registry_facts_present":true,"registry_only_tools":{"status":"known","value":{"count":40,"omitted":8,"rendered":[{"truncated":false,"value":"Git"},{"truncated":false,"value":"Run"},{"truncated":false,"value":"Web"},{"truncated":false,"value":"apply_patch"},{"truncated":false,"value":"automation"},{"truncated":false,"value":"diagnostics"},{"truncated":false,"value":"finance"},{"truncated":false,"value":"github"},{"truncated":false,"value":"grep_files"},{"truncated":false,"value":"handle_read"},{"truncated":false,"value":"harness"},{"truncated":false,"value":"image_ocr"},{"truncated":false,"value":"list_dir"},{"truncated":false,"value":"lsp"},{"truncated":false,"value":"note"},{"truncated":false,"value":"notify"},{"truncated":false,"value":"pandoc_convert"},{"truncated":false,"value":"project_map"},{"truncated":false,"value":"registry_sync"},{"truncated":false,"value":"request_plugin_install"},{"truncated":false,"value":"request_user_input"},{"truncated":false,"value":"retrieve_tool_result"},{"truncated":false,"value":"revert_turn"},{"truncated":false,"value":"review"},{"truncated":false,"value":"send_later"},{"truncated":false,"value":"session_get"},{"truncated":false,"value":"session_search"},{"truncated":false,"value":"start_mcp_server"},{"truncated":false,"value":"start_registry_mcp_server"},{"truncated":false,"value":"task_shell_wait"},{"truncated":false,"value":"tasks"},{"truncated":false,"value":"terminal/cancel"}]}},"registry_tool_count":{"status":"known","value":62},"rendered_tool_count":12,"schema_version":1,"step":0,"tool_count":12,"tools":[{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Required"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"ExecutesCode"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Execute a shell command in the workspace and return stdout and stderr. Output keeps the last 2000 lines or 50KB. An optional timeout is the seconds to wait in the foreground (default 30 seconds); a command still running then is never killed for time: it moves to the background by itself and you get its output so far, how long ago it last printed, its build phase, and a task_id, so long builds and tests need no special timeout. Find task_shell_wait with tool_search to read more or wait for it. In Ask, after "},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The command to execute. Actual execution shell: `/bin/bash`. Use Bash syntax: pipelines, redirections, $(command), and && / || are supported. Quote paths and variable expansions, such as \\\"$path\\\"; use single quotes for literal text. For literal multiline input, use a quoted heredoc delimiter (<<'EOF') with its closing delimiter on a separate line. Use only installed programs; do not assume GNU-specific flags on macOS/BSD. Example: printf '%s\\\\n' 'sample'.\",\"type\":\"string\"},\"justification\":{\"description\":\"Required with sandbox_permissions: one sentence explaining why this exact command needs wider access.\",\"type\":\"string\"},\"read_only\":{\"description\":\"Set true to run analysis code (including Python/SQLite) with mandatory native filesystem read-only isolation and no network. Available during peer writes. Refused when native enforcement is unavailable; no background, stdin, external backend, or sandbox escalation.\",\"type\":\"boolean\"},\"sandbox_permissions\":{\"description\":\"The wider sandbox mode this exact command needs. Use only as a one-shot retry after a sandbox denial; requires justification and user approval in Ask.\",\"enum\":[\"workspace-write\",\"danger-full-access\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional seconds to wait in the foreground (default 30 seconds). A command still running then is not killed: it moves to the background by itself and you get its output so far, how long ago it last printed, its build phase, and a task_id. Find task_shell_wait with tool_search to read more or wait for it.\",\"type\":\"number\"}},\"required\":[\"command\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"bash"},"ordinal":1,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":0,"omitted":0,"rendered":[]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Create the session's one persistent goal: a completion objective Codewhale keeps working toward across turns until it is verified complete, blocked, or the user stops it. A goal is for a durable objective that outlasts one turn: a multi-step outcome the user wants continued and verified. A question, a greeting, or a one-shot edit completes as an ordinary turn. When the user explicitly asks to use `/goal` or to make something the goal, `create_goal` is what records it; acknowledging it in prose records nothi"},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"objective\":{\"description\":\"The full objective to pursue. Keep the complete user goal, not a shortened one-turn version.\",\"type\":\"string\"},\"token_budget\":{\"description\":\"Optional soft token budget for the goal.\",\"minimum\":0,\"type\":\"integer\"}},\"required\":[\"objective\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"create_goal"},"ordinal":2,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Suggest"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Edit one file with targeted text replacements. Every edits[].oldText must identify a unique, non-overlapping region of the original file. Merge nearby or overlapping changes into one edit."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"edits\":{\"description\":\"One or more disjoint replacements, all matched against the original file.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"newText\":{\"description\":\"Replacement text; may be empty to delete the matched span.\",\"type\":\"string\"},\"oldText\":{\"description\":\"Text identifying one unique region to replace.\",\"type\":\"string\"}},\"required\":[\"newText\",\"oldText\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to the file to edit (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"edits\",\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"edit"},"ordinal":3,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":2,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"},{"truncated":false,"value":"Sandboxable"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Find workspace files by name, path, or glob (e.g. src/**/*.rs). Space-separated terms must all match. Literal matches rank above fuzzy matches; results are capped. Respects ignore files and skips build/dependency directories by default. Use extensions to filter suffixes."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"properties\":{\"exclude\":{\"description\":\"Optional replacement exclusion globs (e.g. [\\\"**/target/**\\\", \\\"*.lock\\\"]); [] disables default exclusions.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"extensions\":{\"description\":\"Optional list of file extensions to include (e.g. [\\\"rs\\\", \\\"md\\\"]).\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"limit\":{\"description\":\"Maximum number of ranked results to return (default: 20, maximum: 200).\",\"type\":\"integer\"},\"path\":{\"description\":\"Optional base path to search (relative to workspace).\",\"type\":\"string\"},\"query\":{\"description\":\"File name, path fragment, space-separated terms, or glob (e.g. **/engine*.rs).\",\"type\":\"string\"}},\"required\":[\"query\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"file_search"},"ordinal":4,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Inspect the current runtime goal state, including objective, status, token budget, elapsed time, evidence, and blocker."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{},\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"get_goal"},"ordinal":5,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Load a named skill's SKILL.md body and companion file list into this turn. Use when the user names a skill, or when an entry in the system prompt's `## Skills` index matches the task -- load it before starting the work, not after. Pass query=\"...\" to search names and descriptions, or name=\"list\" for the whole catalogue. Resolves global and plugin skills that `read` cannot reach."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"name\":{\"description\":\"Skill id to load. Omit or pass \\\"list\\\" to see all available skills.\",\"type\":\"string\"},\"query\":{\"description\":\"Search term matched against skill names and descriptions. Use when the index was truncated or no name is known.\",\"type\":\"string\"}},\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"load_skill"},"ordinal":6,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":2,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"},{"truncated":false,"value":"Sandboxable"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Read a file. A text file comes back whole in one call when it fits this call's output budget — 100000 bytes by default, raisable to 500000 with max_bytes. There is no line cap. Use offset and limit for an exact line range; when output is budget-limited the footer names the exact offset to continue from. Every text response reports the file's byte size, line count, and whether output was truncated. A PNG, JPEG, GIF or WebP image comes back as image content you can see (large images are downscaled), including"},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of lines to read.\",\"type\":\"number\"},\"max_bytes\":{\"description\":\"Output budget in bytes for this one call. Defaults to 100000; values above the 500000 maximum are clamped down rather than rejected, and a value below the active default leaves the default in place.\",\"type\":\"number\"},\"offset\":{\"description\":\"Line number to start reading from (1-indexed).\",\"type\":\"number\"},\"path\":{\"description\":\"Path to the file to read (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"read"},"ordinal":7,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Replace the To-do list the user watches. For any task with three or more steps or more than one file, write the list before you start, keep exactly one item in_progress, and mark items done as you finish them. Skip it only for a single quick answer or edit."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"properties\":{\"todos\":{\"description\":\"The complete list of To-do items. This replaces the existing list.\",\"items\":{\"properties\":{\"content\":{\"description\":\"The task description\",\"type\":\"string\"},\"status\":{\"description\":\"Task status\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"required\":[\"todos\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"todo_write"},"ordinal":8,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":0,"omitted":0,"rendered":[]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Update the runtime goal completion gate by calling this tool; a prose status in your answer does not change the goal or stop continuation. Critical verification may seal one immutable completion contract. Advisory review is append-only context and never completes, blocks, or pauses the goal. Mark blocked when progress requires user input. Mark yield when you finished a stage and the next step needs the user's decision: the goal stays unfinished, the turn ends, and their next message resumes it."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":true,"value":"{\"additionalProperties\":false,\"properties\":{\"advisory\":{\"description\":\"Required when status is advisory. Appended separately from the judged completion contract.\",\"type\":\"string\"},\"blocker\":{\"description\":\"Required when status is blocked. Explain the condition preventing progress.\",\"type\":\"string\"},\"evidence\":{\"description\":\"Required when status is complete. Briefly cite the proof that the goal is done.\",\"type\":\"string\"},\"progress\":{\"additionalProperties\":false,\"description\":\"Optional with not_achieved or advisory: your current best estimate of overall completion, shown to the user as reported progress. Keep percent honest — it is an estimate, never a verified fraction.\",\"properties\":{\"next\":{\"description\":\"One short line: what comes next.\",\"type\":\"string\"},\"now\":{\"description\":\"One short line: what is being worked on right now.\",\"type\":\"string\"},\"percent\":{\"description\":\"Estimated percent complete, 0-100.\",\"maximum\":100,\"minimum\":0,\"type\":\"integer\"}},\"required\":[\"percent\"],\"type\":\"object\"},\"status\":{\"description\":\"Use complete only when a critical verifier proves the goal; not_achieved to record verifier gaps; blocked when meaningful progress cannot continue; yield when a stage is finished and the next step is the user's call; advisory to append best-effort context without changing lifecycle state.\",\"enum\":[\"complete\",\"blocked\",\"not_achieved\",\"advisory\",\"yield\"],\"type\":\"string\"},\"verification\":{\"additionalProperties\":false,\"description\":\"Required when status is complete or not_achieved. A verifier-as-judge receipt from a concrete check, such as Run action=\\\"verifiers\\\" or an equivalent project-specific gate.\",\"properties\":{\"check\":{\"description\":\"The verifier/check that passed.\",\"type\":\"string\"},\"gaps\":{\"description\":\"Concrete remaining gaps. Required for critical not_achieved reviews; order and duplicate wording do not affect the stall fingerprint.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"role\":{\"description\":\"Critical reviews may satisfy the judged completion contract. Advisory reviews are fail-open and c"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"update_goal"},"ordinal":9,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Suggest"}},"cache_control_type":{"status":"known","value":{"truncated":false,"value":"ephemeral"}},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Write content to a file. Creates the file if it does not exist, overwrites it if it does, and creates parent directories automatically."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Content to write to the file.\",\"type\":\"string\"},\"path\":{\"description\":\"Path to the file to write (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"content\",\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"write"},"ordinal":10,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"reason":"tool is not in the registry","status":"unknown"},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"reason":"tool is not in the registry","status":"unknown"},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Run a JavaScript program that composes tool calls with `await tools.call(name, args)` and returns a bounded JSON result. Prefer it whenever you would make several dependent or repetitive calls, MCP and plugin tools included: intermediate results stay in the program and only what you return reaches the conversation. Every nested call passes the same permission checks as a direct call; a call that needs approval pauses the program until the user decides, and a denied or refused call throws inside the program "},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"type\":\"object\",\"properties\":{\"code\":{\"type\":\"string\",\"description\":\"JavaScript program. The return value (or thrown error) becomes the result; use tools.call(name, argsObject) for tool calls.\"}},\"required\":[\"code\"]}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"reason":"tool is not in the registry","status":"unknown"},"name":{"truncated":false,"value":"execute_tools"},"ordinal":11,"provenance":{"status":"known","value":"synthetic"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"status":"known","value":{"truncated":false,"value":"execute_tools_20260918"}},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"reason":"tool is not in the registry","status":"unknown"},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"reason":"tool is not in the registry","status":"unknown"},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Search deferred tool definitions and return matching tool references."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\",\"description\":\"Search query for tool discovery.\"},\"match\":{\"type\":\"string\",\"enum\":[\"bm25\",\"regex\"],\"default\":\"bm25\",\"description\":\"Matching algorithm: bm25 for natural-language matching, regex for a regular expression over tool names/descriptions/schema.\"},\"max_results\":{\"type\":\"integer\",\"minimum\":1,\"maximum\":8,\"default\":8,\"description\":\"Maximum number of matching tool references to return.\"}},\"required\":[\"query\"]}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"reason":"tool is not in the registry","status":"unknown"},"name":{"truncated":false,"value":"tool_search"},"ordinal":12,"provenance":{"status":"known","value":"synthetic"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"status":"known","value":{"truncated":false,"value":"tool_search_20251119"}},"visibility":"active"}],"tools_field_present":true,"turn_id":{"truncated":false,"value":""},"unavailable_for_this_request":["provider_wire_payload"]}} +{"changed":false,"context_updates":0,"description":"frozen: e6c0d3cc59cd","event":"prefix_cache_change","last_miss_reason":"","pin_reason":"initial","pinned_combined_hash":"","stability_pct":100,"system_prompt_changed":false,"tools_changed":false} +{"event":"tool_request_snapshot","snapshot":{"active_tool_catalog_sha256":"ba34408b0c03b810ba1ef6190c41d215f58e88822148991099b7d70d444992fc","capture_source":"prepared model-client request","delivery_status":"unknown (capture does not prove provider delivery)","omitted_tool_count":0,"payload_json_bytes":14897,"payload_measurement_status":"exact (within 1048576-byte measurement bound)","provider":{"model":"deepseek-v4-pro","provider":"Deepseek","status":"available"},"registry_facts_present":true,"registry_only_tools":{"status":"known","value":{"count":40,"omitted":8,"rendered":[{"truncated":false,"value":"Git"},{"truncated":false,"value":"Run"},{"truncated":false,"value":"Web"},{"truncated":false,"value":"apply_patch"},{"truncated":false,"value":"automation"},{"truncated":false,"value":"diagnostics"},{"truncated":false,"value":"finance"},{"truncated":false,"value":"github"},{"truncated":false,"value":"grep_files"},{"truncated":false,"value":"handle_read"},{"truncated":false,"value":"harness"},{"truncated":false,"value":"image_ocr"},{"truncated":false,"value":"list_dir"},{"truncated":false,"value":"lsp"},{"truncated":false,"value":"note"},{"truncated":false,"value":"notify"},{"truncated":false,"value":"pandoc_convert"},{"truncated":false,"value":"project_map"},{"truncated":false,"value":"registry_sync"},{"truncated":false,"value":"request_plugin_install"},{"truncated":false,"value":"request_user_input"},{"truncated":false,"value":"retrieve_tool_result"},{"truncated":false,"value":"revert_turn"},{"truncated":false,"value":"review"},{"truncated":false,"value":"send_later"},{"truncated":false,"value":"session_get"},{"truncated":false,"value":"session_search"},{"truncated":false,"value":"start_mcp_server"},{"truncated":false,"value":"start_registry_mcp_server"},{"truncated":false,"value":"task_shell_wait"},{"truncated":false,"value":"tasks"},{"truncated":false,"value":"terminal/cancel"}]}},"registry_tool_count":{"status":"known","value":62},"rendered_tool_count":12,"schema_version":1,"step":0,"tool_count":12,"tools":[{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Required"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"ExecutesCode"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Execute a shell command in the workspace and return stdout and stderr. Output keeps the last 2000 lines or 50KB. An optional timeout is the seconds to wait in the foreground (default 30 seconds); a command still running then is never killed for time: it moves to the background by itself and you get its output so far, how long ago it last printed, its build phase, and a task_id, so long builds and tests need no special timeout. Find task_shell_wait with tool_search to read more or wait for it. In Ask, after "},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The command to execute. Actual execution shell: `/bin/bash`. Use Bash syntax: pipelines, redirections, $(command), and && / || are supported. Quote paths and variable expansions, such as \\\"$path\\\"; use single quotes for literal text. For literal multiline input, use a quoted heredoc delimiter (<<'EOF') with its closing delimiter on a separate line. Use only installed programs; do not assume GNU-specific flags on macOS/BSD. Example: printf '%s\\\\n' 'sample'.\",\"type\":\"string\"},\"justification\":{\"description\":\"Required with sandbox_permissions: one sentence explaining why this exact command needs wider access.\",\"type\":\"string\"},\"read_only\":{\"description\":\"Set true to run analysis code (including Python/SQLite) with mandatory native filesystem read-only isolation and no network. Available during peer writes. Refused when native enforcement is unavailable; no background, stdin, external backend, or sandbox escalation.\",\"type\":\"boolean\"},\"sandbox_permissions\":{\"description\":\"The wider sandbox mode this exact command needs. Use only as a one-shot retry after a sandbox denial; requires justification and user approval in Ask.\",\"enum\":[\"workspace-write\",\"danger-full-access\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional seconds to wait in the foreground (default 30 seconds). A command still running then is not killed: it moves to the background by itself and you get its output so far, how long ago it last printed, its build phase, and a task_id. Find task_shell_wait with tool_search to read more or wait for it.\",\"type\":\"number\"}},\"required\":[\"command\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"bash"},"ordinal":1,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":0,"omitted":0,"rendered":[]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Create the session's one persistent goal: a completion objective Codewhale keeps working toward across turns until it is verified complete, blocked, or the user stops it. A goal is for a durable objective that outlasts one turn: a multi-step outcome the user wants continued and verified. A question, a greeting, or a one-shot edit completes as an ordinary turn. When the user explicitly asks to use `/goal` or to make something the goal, `create_goal` is what records it; acknowledging it in prose records nothi"},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"objective\":{\"description\":\"The full objective to pursue. Keep the complete user goal, not a shortened one-turn version.\",\"type\":\"string\"},\"token_budget\":{\"description\":\"Optional soft token budget for the goal.\",\"minimum\":0,\"type\":\"integer\"}},\"required\":[\"objective\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"create_goal"},"ordinal":2,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Suggest"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Edit one file with targeted text replacements. Every edits[].oldText must identify a unique, non-overlapping region of the original file. Merge nearby or overlapping changes into one edit."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"edits\":{\"description\":\"One or more disjoint replacements, all matched against the original file.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"newText\":{\"description\":\"Replacement text; may be empty to delete the matched span.\",\"type\":\"string\"},\"oldText\":{\"description\":\"Text identifying one unique region to replace.\",\"type\":\"string\"}},\"required\":[\"newText\",\"oldText\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to the file to edit (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"edits\",\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"edit"},"ordinal":3,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":2,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"},{"truncated":false,"value":"Sandboxable"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Find workspace files by name, path, or glob (e.g. src/**/*.rs). Space-separated terms must all match. Literal matches rank above fuzzy matches; results are capped. Respects ignore files and skips build/dependency directories by default. Use extensions to filter suffixes."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"properties\":{\"exclude\":{\"description\":\"Optional replacement exclusion globs (e.g. [\\\"**/target/**\\\", \\\"*.lock\\\"]); [] disables default exclusions.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"extensions\":{\"description\":\"Optional list of file extensions to include (e.g. [\\\"rs\\\", \\\"md\\\"]).\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"limit\":{\"description\":\"Maximum number of ranked results to return (default: 20, maximum: 200).\",\"type\":\"integer\"},\"path\":{\"description\":\"Optional base path to search (relative to workspace).\",\"type\":\"string\"},\"query\":{\"description\":\"File name, path fragment, space-separated terms, or glob (e.g. **/engine*.rs).\",\"type\":\"string\"}},\"required\":[\"query\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"file_search"},"ordinal":4,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Inspect the current runtime goal state, including objective, status, token budget, elapsed time, evidence, and blocker."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{},\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"get_goal"},"ordinal":5,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Load a named skill's SKILL.md body and companion file list into this turn. Use when the user names a skill, or when an entry in the system prompt's `## Skills` index matches the task -- load it before starting the work, not after. Pass query=\"...\" to search names and descriptions, or name=\"list\" for the whole catalogue. Resolves global and plugin skills that `read` cannot reach."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"name\":{\"description\":\"Skill id to load. Omit or pass \\\"list\\\" to see all available skills.\",\"type\":\"string\"},\"query\":{\"description\":\"Search term matched against skill names and descriptions. Use when the index was truncated or no name is known.\",\"type\":\"string\"}},\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"load_skill"},"ordinal":6,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":2,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"},{"truncated":false,"value":"Sandboxable"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Read a file. A text file comes back whole in one call when it fits this call's output budget — 100000 bytes by default, raisable to 500000 with max_bytes. There is no line cap. Use offset and limit for an exact line range; when output is budget-limited the footer names the exact offset to continue from. Every text response reports the file's byte size, line count, and whether output was truncated. A PNG, JPEG, GIF or WebP image comes back as image content you can see (large images are downscaled), including"},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of lines to read.\",\"type\":\"number\"},\"max_bytes\":{\"description\":\"Output budget in bytes for this one call. Defaults to 100000; values above the 500000 maximum are clamped down rather than rejected, and a value below the active default leaves the default in place.\",\"type\":\"number\"},\"offset\":{\"description\":\"Line number to start reading from (1-indexed).\",\"type\":\"number\"},\"path\":{\"description\":\"Path to the file to read (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"read"},"ordinal":7,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Replace the To-do list the user watches. For any task with three or more steps or more than one file, write the list before you start, keep exactly one item in_progress, and mark items done as you finish them. Skip it only for a single quick answer or edit."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"properties\":{\"todos\":{\"description\":\"The complete list of To-do items. This replaces the existing list.\",\"items\":{\"properties\":{\"content\":{\"description\":\"The task description\",\"type\":\"string\"},\"status\":{\"description\":\"Task status\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"required\":[\"todos\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"todo_write"},"ordinal":8,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":0,"omitted":0,"rendered":[]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Update the runtime goal completion gate by calling this tool; a prose status in your answer does not change the goal or stop continuation. Critical verification may seal one immutable completion contract. Advisory review is append-only context and never completes, blocks, or pauses the goal. Mark blocked when progress requires user input. Mark yield when you finished a stage and the next step needs the user's decision: the goal stays unfinished, the turn ends, and their next message resumes it."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":true,"value":"{\"additionalProperties\":false,\"properties\":{\"advisory\":{\"description\":\"Required when status is advisory. Appended separately from the judged completion contract.\",\"type\":\"string\"},\"blocker\":{\"description\":\"Required when status is blocked. Explain the condition preventing progress.\",\"type\":\"string\"},\"evidence\":{\"description\":\"Required when status is complete. Briefly cite the proof that the goal is done.\",\"type\":\"string\"},\"progress\":{\"additionalProperties\":false,\"description\":\"Optional with not_achieved, advisory or yield: your current best estimate of overall completion, shown to the user as reported progress. Keep percent honest — it is an estimate, never a verified fraction.\",\"properties\":{\"next\":{\"description\":\"One short line: what comes next.\",\"type\":\"string\"},\"now\":{\"description\":\"One short line: what is being worked on right now.\",\"type\":\"string\"},\"percent\":{\"description\":\"Estimated percent complete, 0-100.\",\"maximum\":100,\"minimum\":0,\"type\":\"integer\"}},\"required\":[\"percent\"],\"type\":\"object\"},\"status\":{\"description\":\"Use complete only when a critical verifier proves the goal; not_achieved to record verifier gaps; blocked when meaningful progress cannot continue; yield when a stage is finished and the next step is the user's call; advisory to append best-effort context without changing lifecycle state.\",\"enum\":[\"complete\",\"blocked\",\"not_achieved\",\"advisory\",\"yield\"],\"type\":\"string\"},\"verification\":{\"additionalProperties\":false,\"description\":\"Required when status is complete or not_achieved. A verifier-as-judge receipt from a concrete check, such as Run action=\\\"verifiers\\\" or an equivalent project-specific gate.\",\"properties\":{\"check\":{\"description\":\"The verifier/check that passed.\",\"type\":\"string\"},\"gaps\":{\"description\":\"Concrete remaining gaps. Required for critical not_achieved reviews; order and duplicate wording do not affect the stall fingerprint.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"role\":{\"description\":\"Critical reviews may satisfy the judged completion contract. Advisory reviews are fail-ope"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"update_goal"},"ordinal":9,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Suggest"}},"cache_control_type":{"status":"known","value":{"truncated":false,"value":"ephemeral"}},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Write content to a file. Creates the file if it does not exist, overwrites it if it does, and creates parent directories automatically."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Content to write to the file.\",\"type\":\"string\"},\"path\":{\"description\":\"Path to the file to write (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"content\",\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"write"},"ordinal":10,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"reason":"tool is not in the registry","status":"unknown"},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"reason":"tool is not in the registry","status":"unknown"},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Run a JavaScript program that composes tool calls with `await tools.call(name, args)` and returns a bounded JSON result. Prefer it whenever you would make several dependent or repetitive calls, MCP and plugin tools included: intermediate results stay in the program and only what you return reaches the conversation. Every nested call passes the same permission checks as a direct call; a call that needs approval pauses the program until the user decides, and a denied or refused call throws inside the program "},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"type\":\"object\",\"properties\":{\"code\":{\"type\":\"string\",\"description\":\"JavaScript program. The return value (or thrown error) becomes the result; use tools.call(name, argsObject) for tool calls.\"}},\"required\":[\"code\"]}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"reason":"tool is not in the registry","status":"unknown"},"name":{"truncated":false,"value":"execute_tools"},"ordinal":11,"provenance":{"status":"known","value":"synthetic"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"status":"known","value":{"truncated":false,"value":"execute_tools_20260918"}},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"reason":"tool is not in the registry","status":"unknown"},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"reason":"tool is not in the registry","status":"unknown"},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Search deferred tool definitions and return matching tool references."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\",\"description\":\"Search query for tool discovery.\"},\"match\":{\"type\":\"string\",\"enum\":[\"bm25\",\"regex\"],\"default\":\"bm25\",\"description\":\"Matching algorithm: bm25 for natural-language matching, regex for a regular expression over tool names/descriptions/schema.\"},\"max_results\":{\"type\":\"integer\",\"minimum\":1,\"maximum\":8,\"default\":8,\"description\":\"Maximum number of matching tool references to return.\"}},\"required\":[\"query\"]}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"reason":"tool is not in the registry","status":"unknown"},"name":{"truncated":false,"value":"tool_search"},"ordinal":12,"provenance":{"status":"known","value":"synthetic"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"status":"known","value":{"truncated":false,"value":"tool_search_20251119"}},"visibility":"active"}],"tools_field_present":true,"turn_id":{"truncated":false,"value":""},"unavailable_for_this_request":["provider_wire_payload"]}} {"event":"route_dispatched","route":{"auto_model":false,"base_url":"https://api.deepseek.com/beta","billing":{"billing_mode":"metered","dispatched_at":""},"billing_product":{"kind":"unproven"},"model":"deepseek-v4-pro","provider":"deepseek","provider_identity":"deepseek"},"turn_id":""} {"category":"invalid_input","code":"invalid_input","event":"error","message":"Model not exist.","recoverable":false,"severity":"error"} {"duration_ms":"","event":"turn_usage","first_token_ms":"","request_ms":"","usage":{"input_tokens":120,"output_tokens":0}} {"event":"tool_call_started","input":{"action":"write","content":"side effect\n","path":"c0205.txt"},"tool_call_id":"","tool_name":"File"} {"event":"tool_call_complete","result":{"content":"Not executed: the provider stream failed before the model response completed (Model not exist.).","metadata":{"error_category":"model_stream_failed","model_output_incomplete":true,"side_effect_status":"not_started"},"outcome":"ok","success":false},"tool_call_id":"","tool_name":"File"} -{"event":"tool_request_snapshot","snapshot":{"active_tool_catalog_sha256":"18c6d652f11c0c195e7b586852ab46b47987939aaa316b39ef8b692255a7e077","capture_source":"prepared model-client request","delivery_status":"unknown (capture does not prove provider delivery)","omitted_tool_count":0,"payload_json_bytes":14890,"payload_measurement_status":"exact (within 1048576-byte measurement bound)","provider":{"model":"deepseek-v4-pro","provider":"Deepseek","status":"available"},"registry_facts_present":true,"registry_only_tools":{"status":"known","value":{"count":40,"omitted":8,"rendered":[{"truncated":false,"value":"Git"},{"truncated":false,"value":"Run"},{"truncated":false,"value":"Web"},{"truncated":false,"value":"apply_patch"},{"truncated":false,"value":"automation"},{"truncated":false,"value":"diagnostics"},{"truncated":false,"value":"finance"},{"truncated":false,"value":"github"},{"truncated":false,"value":"grep_files"},{"truncated":false,"value":"handle_read"},{"truncated":false,"value":"harness"},{"truncated":false,"value":"image_ocr"},{"truncated":false,"value":"list_dir"},{"truncated":false,"value":"lsp"},{"truncated":false,"value":"note"},{"truncated":false,"value":"notify"},{"truncated":false,"value":"pandoc_convert"},{"truncated":false,"value":"project_map"},{"truncated":false,"value":"registry_sync"},{"truncated":false,"value":"request_plugin_install"},{"truncated":false,"value":"request_user_input"},{"truncated":false,"value":"retrieve_tool_result"},{"truncated":false,"value":"revert_turn"},{"truncated":false,"value":"review"},{"truncated":false,"value":"send_later"},{"truncated":false,"value":"session_get"},{"truncated":false,"value":"session_search"},{"truncated":false,"value":"start_mcp_server"},{"truncated":false,"value":"start_registry_mcp_server"},{"truncated":false,"value":"task_shell_wait"},{"truncated":false,"value":"tasks"},{"truncated":false,"value":"terminal/cancel"}]}},"registry_tool_count":{"status":"known","value":62},"rendered_tool_count":12,"schema_version":1,"step":0,"terminal":{"automatic_compaction_attempts":0,"effective_max_steps":null,"emergency_compaction_attempts":0,"empty_stop_retries":0,"final_report_requested":false,"last_prepared_output_limit_tokens":65536,"last_provider_finish_reason":null,"last_reported_input_tokens":120,"last_response_tool_calls":1,"last_response_tool_calls_suppressed":1,"model_requests_started":1,"model_step_index":0,"permission_denial_rounds_without_progress":0,"permission_strategy_switches":0,"reason":"failed","reasoning_only_reprompts":0,"route_context_window_tokens":1000000,"soft_landing_sent":false,"status":"failed","step_budget_source":"max_steps","stream_resumes":0,"transparent_stream_retries":0,"transport_retries":0},"tool_count":12,"tools":[{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Required"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"ExecutesCode"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Execute a shell command in the workspace and return stdout and stderr. Output keeps the last 2000 lines or 50KB. An optional timeout is the seconds to wait in the foreground (default 30 seconds); a command still running then is never killed for time: it moves to the background by itself and you get its output so far, how long ago it last printed, its build phase, and a task_id, so long builds and tests need no special timeout. Find task_shell_wait with tool_search to read more or wait for it. In Ask, after "},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The command to execute. Actual execution shell: `/bin/bash`. Use Bash syntax: pipelines, redirections, $(command), and && / || are supported. Quote paths and variable expansions, such as \\\"$path\\\"; use single quotes for literal text. For literal multiline input, use a quoted heredoc delimiter (<<'EOF') with its closing delimiter on a separate line. Use only installed programs; do not assume GNU-specific flags on macOS/BSD. Example: printf '%s\\\\n' 'sample'.\",\"type\":\"string\"},\"justification\":{\"description\":\"Required with sandbox_permissions: one sentence explaining why this exact command needs wider access.\",\"type\":\"string\"},\"read_only\":{\"description\":\"Set true to run analysis code (including Python/SQLite) with mandatory native filesystem read-only isolation and no network. Available during peer writes. Refused when native enforcement is unavailable; no background, stdin, external backend, or sandbox escalation.\",\"type\":\"boolean\"},\"sandbox_permissions\":{\"description\":\"The wider sandbox mode this exact command needs. Use only as a one-shot retry after a sandbox denial; requires justification and user approval in Ask.\",\"enum\":[\"workspace-write\",\"danger-full-access\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional seconds to wait in the foreground (default 30 seconds). A command still running then is not killed: it moves to the background by itself and you get its output so far, how long ago it last printed, its build phase, and a task_id. Find task_shell_wait with tool_search to read more or wait for it.\",\"type\":\"number\"}},\"required\":[\"command\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"bash"},"ordinal":1,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":0,"omitted":0,"rendered":[]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Create the session's one persistent goal: a completion objective Codewhale keeps working toward across turns until it is verified complete, blocked, or the user stops it. A goal is for a durable objective that outlasts one turn: a multi-step outcome the user wants continued and verified. A question, a greeting, or a one-shot edit completes as an ordinary turn. When the user explicitly asks to use `/goal` or to make something the goal, `create_goal` is what records it; acknowledging it in prose records nothi"},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"objective\":{\"description\":\"The full objective to pursue. Keep the complete user goal, not a shortened one-turn version.\",\"type\":\"string\"},\"token_budget\":{\"description\":\"Optional soft token budget for the goal.\",\"minimum\":0,\"type\":\"integer\"}},\"required\":[\"objective\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"create_goal"},"ordinal":2,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Suggest"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Edit one file with targeted text replacements. Every edits[].oldText must identify a unique, non-overlapping region of the original file. Merge nearby or overlapping changes into one edit."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"edits\":{\"description\":\"One or more disjoint replacements, all matched against the original file.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"newText\":{\"description\":\"Replacement text; may be empty to delete the matched span.\",\"type\":\"string\"},\"oldText\":{\"description\":\"Text identifying one unique region to replace.\",\"type\":\"string\"}},\"required\":[\"newText\",\"oldText\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to the file to edit (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"edits\",\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"edit"},"ordinal":3,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":2,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"},{"truncated":false,"value":"Sandboxable"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Find workspace files by name, path, or glob (e.g. src/**/*.rs). Space-separated terms must all match. Literal matches rank above fuzzy matches; results are capped. Respects ignore files and skips build/dependency directories by default. Use extensions to filter suffixes."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"properties\":{\"exclude\":{\"description\":\"Optional replacement exclusion globs (e.g. [\\\"**/target/**\\\", \\\"*.lock\\\"]); [] disables default exclusions.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"extensions\":{\"description\":\"Optional list of file extensions to include (e.g. [\\\"rs\\\", \\\"md\\\"]).\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"limit\":{\"description\":\"Maximum number of ranked results to return (default: 20, maximum: 200).\",\"type\":\"integer\"},\"path\":{\"description\":\"Optional base path to search (relative to workspace).\",\"type\":\"string\"},\"query\":{\"description\":\"File name, path fragment, space-separated terms, or glob (e.g. **/engine*.rs).\",\"type\":\"string\"}},\"required\":[\"query\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"file_search"},"ordinal":4,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Inspect the current runtime goal state, including objective, status, token budget, elapsed time, evidence, and blocker."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{},\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"get_goal"},"ordinal":5,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Load a named skill's SKILL.md body and companion file list into this turn. Use when the user names a skill, or when an entry in the system prompt's `## Skills` index matches the task -- load it before starting the work, not after. Pass query=\"...\" to search names and descriptions, or name=\"list\" for the whole catalogue. Resolves global and plugin skills that `read` cannot reach."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"name\":{\"description\":\"Skill id to load. Omit or pass \\\"list\\\" to see all available skills.\",\"type\":\"string\"},\"query\":{\"description\":\"Search term matched against skill names and descriptions. Use when the index was truncated or no name is known.\",\"type\":\"string\"}},\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"load_skill"},"ordinal":6,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":2,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"},{"truncated":false,"value":"Sandboxable"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Read a file. A text file comes back whole in one call when it fits this call's output budget — 100000 bytes by default, raisable to 500000 with max_bytes. There is no line cap. Use offset and limit for an exact line range; when output is budget-limited the footer names the exact offset to continue from. Every text response reports the file's byte size, line count, and whether output was truncated. A PNG, JPEG, GIF or WebP image comes back as image content you can see (large images are downscaled), including"},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of lines to read.\",\"type\":\"number\"},\"max_bytes\":{\"description\":\"Output budget in bytes for this one call. Defaults to 100000; values above the 500000 maximum are clamped down rather than rejected, and a value below the active default leaves the default in place.\",\"type\":\"number\"},\"offset\":{\"description\":\"Line number to start reading from (1-indexed).\",\"type\":\"number\"},\"path\":{\"description\":\"Path to the file to read (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"read"},"ordinal":7,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Replace the To-do list the user watches. For any task with three or more steps or more than one file, write the list before you start, keep exactly one item in_progress, and mark items done as you finish them. Skip it only for a single quick answer or edit."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"properties\":{\"todos\":{\"description\":\"The complete list of To-do items. This replaces the existing list.\",\"items\":{\"properties\":{\"content\":{\"description\":\"The task description\",\"type\":\"string\"},\"status\":{\"description\":\"Task status\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"required\":[\"todos\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"todo_write"},"ordinal":8,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":0,"omitted":0,"rendered":[]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Update the runtime goal completion gate by calling this tool; a prose status in your answer does not change the goal or stop continuation. Critical verification may seal one immutable completion contract. Advisory review is append-only context and never completes, blocks, or pauses the goal. Mark blocked when progress requires user input. Mark yield when you finished a stage and the next step needs the user's decision: the goal stays unfinished, the turn ends, and their next message resumes it."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":true,"value":"{\"additionalProperties\":false,\"properties\":{\"advisory\":{\"description\":\"Required when status is advisory. Appended separately from the judged completion contract.\",\"type\":\"string\"},\"blocker\":{\"description\":\"Required when status is blocked. Explain the condition preventing progress.\",\"type\":\"string\"},\"evidence\":{\"description\":\"Required when status is complete. Briefly cite the proof that the goal is done.\",\"type\":\"string\"},\"progress\":{\"additionalProperties\":false,\"description\":\"Optional with not_achieved or advisory: your current best estimate of overall completion, shown to the user as reported progress. Keep percent honest — it is an estimate, never a verified fraction.\",\"properties\":{\"next\":{\"description\":\"One short line: what comes next.\",\"type\":\"string\"},\"now\":{\"description\":\"One short line: what is being worked on right now.\",\"type\":\"string\"},\"percent\":{\"description\":\"Estimated percent complete, 0-100.\",\"maximum\":100,\"minimum\":0,\"type\":\"integer\"}},\"required\":[\"percent\"],\"type\":\"object\"},\"status\":{\"description\":\"Use complete only when a critical verifier proves the goal; not_achieved to record verifier gaps; blocked when meaningful progress cannot continue; yield when a stage is finished and the next step is the user's call; advisory to append best-effort context without changing lifecycle state.\",\"enum\":[\"complete\",\"blocked\",\"not_achieved\",\"advisory\",\"yield\"],\"type\":\"string\"},\"verification\":{\"additionalProperties\":false,\"description\":\"Required when status is complete or not_achieved. A verifier-as-judge receipt from a concrete check, such as Run action=\\\"verifiers\\\" or an equivalent project-specific gate.\",\"properties\":{\"check\":{\"description\":\"The verifier/check that passed.\",\"type\":\"string\"},\"gaps\":{\"description\":\"Concrete remaining gaps. Required for critical not_achieved reviews; order and duplicate wording do not affect the stall fingerprint.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"role\":{\"description\":\"Critical reviews may satisfy the judged completion contract. Advisory reviews are fail-open and c"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"update_goal"},"ordinal":9,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Suggest"}},"cache_control_type":{"status":"known","value":{"truncated":false,"value":"ephemeral"}},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Write content to a file. Creates the file if it does not exist, overwrites it if it does, and creates parent directories automatically."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Content to write to the file.\",\"type\":\"string\"},\"path\":{\"description\":\"Path to the file to write (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"content\",\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"write"},"ordinal":10,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"reason":"tool is not in the registry","status":"unknown"},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"reason":"tool is not in the registry","status":"unknown"},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Run a JavaScript program that composes tool calls with `await tools.call(name, args)` and returns a bounded JSON result. Prefer it whenever you would make several dependent or repetitive calls, MCP and plugin tools included: intermediate results stay in the program and only what you return reaches the conversation. Every nested call passes the same permission checks as a direct call; a call that needs approval pauses the program until the user decides, and a denied or refused call throws inside the program "},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"type\":\"object\",\"properties\":{\"code\":{\"type\":\"string\",\"description\":\"JavaScript program. The return value (or thrown error) becomes the result; use tools.call(name, argsObject) for tool calls.\"}},\"required\":[\"code\"]}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"reason":"tool is not in the registry","status":"unknown"},"name":{"truncated":false,"value":"execute_tools"},"ordinal":11,"provenance":{"status":"known","value":"synthetic"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"status":"known","value":{"truncated":false,"value":"execute_tools_20260918"}},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"reason":"tool is not in the registry","status":"unknown"},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"reason":"tool is not in the registry","status":"unknown"},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Search deferred tool definitions and return matching tool references."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\",\"description\":\"Search query for tool discovery.\"},\"match\":{\"type\":\"string\",\"enum\":[\"bm25\",\"regex\"],\"default\":\"bm25\",\"description\":\"Matching algorithm: bm25 for natural-language matching, regex for a regular expression over tool names/descriptions/schema.\"},\"max_results\":{\"type\":\"integer\",\"minimum\":1,\"maximum\":8,\"default\":8,\"description\":\"Maximum number of matching tool references to return.\"}},\"required\":[\"query\"]}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"reason":"tool is not in the registry","status":"unknown"},"name":{"truncated":false,"value":"tool_search"},"ordinal":12,"provenance":{"status":"known","value":"synthetic"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"status":"known","value":{"truncated":false,"value":"tool_search_20251119"}},"visibility":"active"}],"tools_field_present":true,"turn_id":{"truncated":false,"value":""},"unavailable_for_this_request":["provider_wire_payload"]}} +{"event":"tool_request_snapshot","snapshot":{"active_tool_catalog_sha256":"ba34408b0c03b810ba1ef6190c41d215f58e88822148991099b7d70d444992fc","capture_source":"prepared model-client request","delivery_status":"unknown (capture does not prove provider delivery)","omitted_tool_count":0,"payload_json_bytes":14897,"payload_measurement_status":"exact (within 1048576-byte measurement bound)","provider":{"model":"deepseek-v4-pro","provider":"Deepseek","status":"available"},"registry_facts_present":true,"registry_only_tools":{"status":"known","value":{"count":40,"omitted":8,"rendered":[{"truncated":false,"value":"Git"},{"truncated":false,"value":"Run"},{"truncated":false,"value":"Web"},{"truncated":false,"value":"apply_patch"},{"truncated":false,"value":"automation"},{"truncated":false,"value":"diagnostics"},{"truncated":false,"value":"finance"},{"truncated":false,"value":"github"},{"truncated":false,"value":"grep_files"},{"truncated":false,"value":"handle_read"},{"truncated":false,"value":"harness"},{"truncated":false,"value":"image_ocr"},{"truncated":false,"value":"list_dir"},{"truncated":false,"value":"lsp"},{"truncated":false,"value":"note"},{"truncated":false,"value":"notify"},{"truncated":false,"value":"pandoc_convert"},{"truncated":false,"value":"project_map"},{"truncated":false,"value":"registry_sync"},{"truncated":false,"value":"request_plugin_install"},{"truncated":false,"value":"request_user_input"},{"truncated":false,"value":"retrieve_tool_result"},{"truncated":false,"value":"revert_turn"},{"truncated":false,"value":"review"},{"truncated":false,"value":"send_later"},{"truncated":false,"value":"session_get"},{"truncated":false,"value":"session_search"},{"truncated":false,"value":"start_mcp_server"},{"truncated":false,"value":"start_registry_mcp_server"},{"truncated":false,"value":"task_shell_wait"},{"truncated":false,"value":"tasks"},{"truncated":false,"value":"terminal/cancel"}]}},"registry_tool_count":{"status":"known","value":62},"rendered_tool_count":12,"schema_version":1,"step":0,"terminal":{"automatic_compaction_attempts":0,"effective_max_steps":null,"emergency_compaction_attempts":0,"empty_stop_retries":0,"final_report_requested":false,"last_prepared_output_limit_tokens":65536,"last_provider_finish_reason":null,"last_reported_input_tokens":120,"last_response_tool_calls":1,"last_response_tool_calls_suppressed":1,"model_requests_started":1,"model_step_index":0,"permission_denial_rounds_without_progress":0,"permission_strategy_switches":0,"reason":"failed","reasoning_only_reprompts":0,"route_context_window_tokens":1000000,"soft_landing_sent":false,"status":"failed","step_budget_source":"max_steps","stream_resumes":0,"transparent_stream_retries":0,"transport_retries":0},"tool_count":12,"tools":[{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Required"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"ExecutesCode"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Execute a shell command in the workspace and return stdout and stderr. Output keeps the last 2000 lines or 50KB. An optional timeout is the seconds to wait in the foreground (default 30 seconds); a command still running then is never killed for time: it moves to the background by itself and you get its output so far, how long ago it last printed, its build phase, and a task_id, so long builds and tests need no special timeout. Find task_shell_wait with tool_search to read more or wait for it. In Ask, after "},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The command to execute. Actual execution shell: `/bin/bash`. Use Bash syntax: pipelines, redirections, $(command), and && / || are supported. Quote paths and variable expansions, such as \\\"$path\\\"; use single quotes for literal text. For literal multiline input, use a quoted heredoc delimiter (<<'EOF') with its closing delimiter on a separate line. Use only installed programs; do not assume GNU-specific flags on macOS/BSD. Example: printf '%s\\\\n' 'sample'.\",\"type\":\"string\"},\"justification\":{\"description\":\"Required with sandbox_permissions: one sentence explaining why this exact command needs wider access.\",\"type\":\"string\"},\"read_only\":{\"description\":\"Set true to run analysis code (including Python/SQLite) with mandatory native filesystem read-only isolation and no network. Available during peer writes. Refused when native enforcement is unavailable; no background, stdin, external backend, or sandbox escalation.\",\"type\":\"boolean\"},\"sandbox_permissions\":{\"description\":\"The wider sandbox mode this exact command needs. Use only as a one-shot retry after a sandbox denial; requires justification and user approval in Ask.\",\"enum\":[\"workspace-write\",\"danger-full-access\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional seconds to wait in the foreground (default 30 seconds). A command still running then is not killed: it moves to the background by itself and you get its output so far, how long ago it last printed, its build phase, and a task_id. Find task_shell_wait with tool_search to read more or wait for it.\",\"type\":\"number\"}},\"required\":[\"command\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"bash"},"ordinal":1,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":0,"omitted":0,"rendered":[]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Create the session's one persistent goal: a completion objective Codewhale keeps working toward across turns until it is verified complete, blocked, or the user stops it. A goal is for a durable objective that outlasts one turn: a multi-step outcome the user wants continued and verified. A question, a greeting, or a one-shot edit completes as an ordinary turn. When the user explicitly asks to use `/goal` or to make something the goal, `create_goal` is what records it; acknowledging it in prose records nothi"},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"objective\":{\"description\":\"The full objective to pursue. Keep the complete user goal, not a shortened one-turn version.\",\"type\":\"string\"},\"token_budget\":{\"description\":\"Optional soft token budget for the goal.\",\"minimum\":0,\"type\":\"integer\"}},\"required\":[\"objective\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"create_goal"},"ordinal":2,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Suggest"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Edit one file with targeted text replacements. Every edits[].oldText must identify a unique, non-overlapping region of the original file. Merge nearby or overlapping changes into one edit."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"edits\":{\"description\":\"One or more disjoint replacements, all matched against the original file.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"newText\":{\"description\":\"Replacement text; may be empty to delete the matched span.\",\"type\":\"string\"},\"oldText\":{\"description\":\"Text identifying one unique region to replace.\",\"type\":\"string\"}},\"required\":[\"newText\",\"oldText\"],\"type\":\"object\"},\"type\":\"array\"},\"path\":{\"description\":\"Path to the file to edit (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"edits\",\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"edit"},"ordinal":3,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":2,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"},{"truncated":false,"value":"Sandboxable"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Find workspace files by name, path, or glob (e.g. src/**/*.rs). Space-separated terms must all match. Literal matches rank above fuzzy matches; results are capped. Respects ignore files and skips build/dependency directories by default. Use extensions to filter suffixes."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"properties\":{\"exclude\":{\"description\":\"Optional replacement exclusion globs (e.g. [\\\"**/target/**\\\", \\\"*.lock\\\"]); [] disables default exclusions.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"extensions\":{\"description\":\"Optional list of file extensions to include (e.g. [\\\"rs\\\", \\\"md\\\"]).\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"limit\":{\"description\":\"Maximum number of ranked results to return (default: 20, maximum: 200).\",\"type\":\"integer\"},\"path\":{\"description\":\"Optional base path to search (relative to workspace).\",\"type\":\"string\"},\"query\":{\"description\":\"File name, path fragment, space-separated terms, or glob (e.g. **/engine*.rs).\",\"type\":\"string\"}},\"required\":[\"query\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"file_search"},"ordinal":4,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Inspect the current runtime goal state, including objective, status, token budget, elapsed time, evidence, and blocker."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{},\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"get_goal"},"ordinal":5,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Load a named skill's SKILL.md body and companion file list into this turn. Use when the user names a skill, or when an entry in the system prompt's `## Skills` index matches the task -- load it before starting the work, not after. Pass query=\"...\" to search names and descriptions, or name=\"list\" for the whole catalogue. Resolves global and plugin skills that `read` cannot reach."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"name\":{\"description\":\"Skill id to load. Omit or pass \\\"list\\\" to see all available skills.\",\"type\":\"string\"},\"query\":{\"description\":\"Search term matched against skill names and descriptions. Use when the index was truncated or no name is known.\",\"type\":\"string\"}},\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"load_skill"},"ordinal":6,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":2,"omitted":0,"rendered":[{"truncated":false,"value":"ReadOnly"},{"truncated":false,"value":"Sandboxable"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Read a file. A text file comes back whole in one call when it fits this call's output budget — 100000 bytes by default, raisable to 500000 with max_bytes. There is no line cap. Use offset and limit for an exact line range; when output is budget-limited the footer names the exact offset to continue from. Every text response reports the file's byte size, line count, and whether output was truncated. A PNG, JPEG, GIF or WebP image comes back as image content you can see (large images are downscaled), including"},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of lines to read.\",\"type\":\"number\"},\"max_bytes\":{\"description\":\"Output budget in bytes for this one call. Defaults to 100000; values above the 500000 maximum are clamped down rather than rejected, and a value below the active default leaves the default in place.\",\"type\":\"number\"},\"offset\":{\"description\":\"Line number to start reading from (1-indexed).\",\"type\":\"number\"},\"path\":{\"description\":\"Path to the file to read (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"read"},"ordinal":7,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Replace the To-do list the user watches. For any task with three or more steps or more than one file, write the list before you start, keep exactly one item in_progress, and mark items done as you finish them. Skip it only for a single quick answer or edit."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"properties\":{\"todos\":{\"description\":\"The complete list of To-do items. This replaces the existing list.\",\"items\":{\"properties\":{\"content\":{\"description\":\"The task description\",\"type\":\"string\"},\"status\":{\"description\":\"Task status\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"required\":[\"todos\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"todo_write"},"ordinal":8,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Auto"}},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"status":"known","value":{"count":0,"omitted":0,"rendered":[]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Update the runtime goal completion gate by calling this tool; a prose status in your answer does not change the goal or stop continuation. Critical verification may seal one immutable completion contract. Advisory review is append-only context and never completes, blocks, or pauses the goal. Mark blocked when progress requires user input. Mark yield when you finished a stage and the next step needs the user's decision: the goal stays unfinished, the turn ends, and their next message resumes it."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":true,"value":"{\"additionalProperties\":false,\"properties\":{\"advisory\":{\"description\":\"Required when status is advisory. Appended separately from the judged completion contract.\",\"type\":\"string\"},\"blocker\":{\"description\":\"Required when status is blocked. Explain the condition preventing progress.\",\"type\":\"string\"},\"evidence\":{\"description\":\"Required when status is complete. Briefly cite the proof that the goal is done.\",\"type\":\"string\"},\"progress\":{\"additionalProperties\":false,\"description\":\"Optional with not_achieved, advisory or yield: your current best estimate of overall completion, shown to the user as reported progress. Keep percent honest — it is an estimate, never a verified fraction.\",\"properties\":{\"next\":{\"description\":\"One short line: what comes next.\",\"type\":\"string\"},\"now\":{\"description\":\"One short line: what is being worked on right now.\",\"type\":\"string\"},\"percent\":{\"description\":\"Estimated percent complete, 0-100.\",\"maximum\":100,\"minimum\":0,\"type\":\"integer\"}},\"required\":[\"percent\"],\"type\":\"object\"},\"status\":{\"description\":\"Use complete only when a critical verifier proves the goal; not_achieved to record verifier gaps; blocked when meaningful progress cannot continue; yield when a stage is finished and the next step is the user's call; advisory to append best-effort context without changing lifecycle state.\",\"enum\":[\"complete\",\"blocked\",\"not_achieved\",\"advisory\",\"yield\"],\"type\":\"string\"},\"verification\":{\"additionalProperties\":false,\"description\":\"Required when status is complete or not_achieved. A verifier-as-judge receipt from a concrete check, such as Run action=\\\"verifiers\\\" or an equivalent project-specific gate.\",\"properties\":{\"check\":{\"description\":\"The verifier/check that passed.\",\"type\":\"string\"},\"gaps\":{\"description\":\"Concrete remaining gaps. Required for critical not_achieved reviews; order and duplicate wording do not affect the stall fingerprint.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"role\":{\"description\":\"Critical reviews may satisfy the judged completion contract. Advisory reviews are fail-ope"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"update_goal"},"ordinal":9,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"status":"known","value":{"truncated":false,"value":"Suggest"}},"cache_control_type":{"status":"known","value":{"truncated":false,"value":"ephemeral"}},"capabilities":{"status":"known","value":{"count":3,"omitted":0,"rendered":[{"truncated":false,"value":"WritesFiles"},{"truncated":false,"value":"Sandboxable"},{"truncated":false,"value":"RequiresApproval"}]}},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Write content to a file. Creates the file if it does not exist, overwrites it if it does, and creates parent directories automatically."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Content to write to the file.\",\"type\":\"string\"},\"path\":{\"description\":\"Path to the file to write (relative or absolute).\",\"type\":\"string\"}},\"required\":[\"content\",\"path\"],\"type\":\"object\"}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"status":"known","value":true},"name":{"truncated":false,"value":"write"},"ordinal":10,"provenance":{"status":"known","value":"builtin"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"reason":"request field absent","status":"unknown"},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"reason":"tool is not in the registry","status":"unknown"},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"reason":"tool is not in the registry","status":"unknown"},"defer_loading":{"status":"known","value":false},"description":{"truncated":true,"value":"Run a JavaScript program that composes tool calls with `await tools.call(name, args)` and returns a bounded JSON result. Prefer it whenever you would make several dependent or repetitive calls, MCP and plugin tools included: intermediate results stay in the program and only what you return reaches the conversation. Every nested call passes the same permission checks as a direct call; a call that needs approval pauses the program until the user decides, and a denied or refused call throws inside the program "},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"type\":\"object\",\"properties\":{\"code\":{\"type\":\"string\",\"description\":\"JavaScript program. The return value (or thrown error) becomes the result; use tools.call(name, argsObject) for tool calls.\"}},\"required\":[\"code\"]}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"reason":"tool is not in the registry","status":"unknown"},"name":{"truncated":false,"value":"execute_tools"},"ordinal":11,"provenance":{"status":"known","value":"synthetic"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"status":"known","value":{"truncated":false,"value":"execute_tools_20260918"}},"visibility":"active"},{"allowed_callers":{"status":"known","value":{"count":1,"omitted":0,"rendered":[{"truncated":false,"value":"direct"}]}},"approval":{"reason":"tool is not in the registry","status":"unknown"},"cache_control_type":{"reason":"request field absent","status":"unknown"},"capabilities":{"reason":"tool is not in the registry","status":"unknown"},"defer_loading":{"status":"known","value":false},"description":{"truncated":false,"value":"Search deferred tool definitions and return matching tool references."},"input_examples":{"reason":"request field absent","status":"unknown"},"input_schema_json":{"truncated":false,"value":"{\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\",\"description\":\"Search query for tool discovery.\"},\"match\":{\"type\":\"string\",\"enum\":[\"bm25\",\"regex\"],\"default\":\"bm25\",\"description\":\"Matching algorithm: bm25 for natural-language matching, regex for a regular expression over tool names/descriptions/schema.\"},\"max_results\":{\"type\":\"integer\",\"minimum\":1,\"maximum\":8,\"default\":8,\"description\":\"Maximum number of matching tool references to return.\"}},\"required\":[\"query\"]}"},"mcp_server":{"reason":"the MCP pool did not attribute this tool name","status":"unknown"},"model_visible":{"reason":"tool is not in the registry","status":"unknown"},"name":{"truncated":false,"value":"tool_search"},"ordinal":12,"provenance":{"status":"known","value":"synthetic"},"strict":{"reason":"request field absent","status":"unknown"},"tool_type":{"status":"known","value":{"truncated":false,"value":"tool_search_20251119"}},"visibility":"active"}],"tools_field_present":true,"turn_id":{"truncated":false,"value":""},"unavailable_for_this_request":["provider_wire_payload"]}} {"error":"Model not exist.","event":"turn_complete","parent_route_usage":{"input_tokens":120,"output_tokens":0},"routed_usage_dropped_records":0,"status":"failed","tool_catalog":"","usage":{"input_tokens":120,"output_tokens":0}} -{"harness_summary":{"model_requests":1,"non_streaming_requests":0,"workspace_after":{".codewhale/state/subagents.v1.lock":"e3b0c44298fc1c14","NOTES.md":"f957b19529906961","README.md":"41f49bffb38de5ba"}}} +{"harness_summary":{"model_requests":1,"non_streaming_requests":0,"workspace_after":{".codewhale/state/.gitignore":"cdbcae15105d6b78",".codewhale/state/subagents.v1.lock":"e3b0c44298fc1c14","NOTES.md":"f957b19529906961","README.md":"41f49bffb38de5ba"}}} diff --git a/crates/tui/tests/fixtures/conformance/prompt/agent_suggest.golden.json b/crates/tui/tests/fixtures/conformance/prompt/agent_suggest.golden.json index 5eeee4f62f..324642d80e 100644 --- a/crates/tui/tests/fixtures/conformance/prompt/agent_suggest.golden.json +++ b/crates/tui/tests/fixtures/conformance/prompt/agent_suggest.golden.json @@ -2,8 +2,8 @@ "system_sha256": "8ff744b8ed90564a8c4754ac79cdbaf05df69ed024cc2ec154abfe581ed72d7f", "system_bytes": 9183, "system_blocks": 5, - "tools_sha256": "6f1ab6f7a2f04bcf68a6e8626e97c816b560ae103e94d7642f4cd5bd9a65ab5e", - "tools_bytes": 14890, + "tools_sha256": "157b468336034c773a179ceb07859a3bf5a93a7829dc5e66ccc0e4902622d9bb", + "tools_bytes": 14897, "tool_count": 12, "tool_names": [ "bash", @@ -19,7 +19,7 @@ "execute_tools", "tool_search" ], - "prefix_sha256": "387edcedb4400d61f81c294eef4e4ed2d3d3ccec07224da128c3547873356260", + "prefix_sha256": "3236f11da18b12c2961510681f0991a95ab4e7e33e6df6fb2785f8685d60ae42", "masks": [ "", "", diff --git a/crates/tui/tests/fixtures/conformance/prompt/agent_suggest.tools.golden.json b/crates/tui/tests/fixtures/conformance/prompt/agent_suggest.tools.golden.json index 44f9d559ae..4b314d1368 100644 --- a/crates/tui/tests/fixtures/conformance/prompt/agent_suggest.tools.golden.json +++ b/crates/tui/tests/fixtures/conformance/prompt/agent_suggest.tools.golden.json @@ -283,7 +283,7 @@ }, "progress": { "additionalProperties": false, - "description": "Optional with not_achieved or advisory: your current best estimate of overall completion, shown to the user as reported progress. Keep percent honest — it is an estimate, never a verified fraction.", + "description": "Optional with not_achieved, advisory or yield: your current best estimate of overall completion, shown to the user as reported progress. Keep percent honest — it is an estimate, never a verified fraction.", "properties": { "next": { "description": "One short line: what comes next.", diff --git a/crates/tui/tests/fixtures/conformance/prompt/plan_mode.golden.json b/crates/tui/tests/fixtures/conformance/prompt/plan_mode.golden.json index a7b4ea0722..3bb4378ad4 100644 --- a/crates/tui/tests/fixtures/conformance/prompt/plan_mode.golden.json +++ b/crates/tui/tests/fixtures/conformance/prompt/plan_mode.golden.json @@ -2,8 +2,8 @@ "system_sha256": "8ff744b8ed90564a8c4754ac79cdbaf05df69ed024cc2ec154abfe581ed72d7f", "system_bytes": 9183, "system_blocks": 5, - "tools_sha256": "50c90f2a5678c770190f12690cd9793f7c999f0b2a8013e71a43e61f03191fd0", - "tools_bytes": 13360, + "tools_sha256": "91d0b0a9b35665dc0d9e7811d5ed139e7dc761bb49f17acaff757675e27e372c", + "tools_bytes": 13367, "tool_count": 11, "tool_names": [ "bash", @@ -18,7 +18,7 @@ "write", "tool_search" ], - "prefix_sha256": "e4c70ccf568cd9720d7bab82cd0d9c9e446474c2a23c2db370d113551a4fad34", + "prefix_sha256": "6673c1a81b34efa2d5c2a8541c7f0590d7e0dc158d11ad06fcc6b9e848b621c0", "masks": [ "", "", diff --git a/crates/tui/tests/fixtures/conformance/prompt/plan_mode.tools.golden.json b/crates/tui/tests/fixtures/conformance/prompt/plan_mode.tools.golden.json index 58eb635973..76d7830911 100644 --- a/crates/tui/tests/fixtures/conformance/prompt/plan_mode.tools.golden.json +++ b/crates/tui/tests/fixtures/conformance/prompt/plan_mode.tools.golden.json @@ -283,7 +283,7 @@ }, "progress": { "additionalProperties": false, - "description": "Optional with not_achieved or advisory: your current best estimate of overall completion, shown to the user as reported progress. Keep percent honest — it is an estimate, never a verified fraction.", + "description": "Optional with not_achieved, advisory or yield: your current best estimate of overall completion, shown to the user as reported progress. Keep percent honest — it is an estimate, never a verified fraction.", "properties": { "next": { "description": "One short line: what comes next.", diff --git a/crates/tui/tests/fixtures/conformance/prompt/trusted_project_instructions.golden.json b/crates/tui/tests/fixtures/conformance/prompt/trusted_project_instructions.golden.json index d7262c4944..90d405174e 100644 --- a/crates/tui/tests/fixtures/conformance/prompt/trusted_project_instructions.golden.json +++ b/crates/tui/tests/fixtures/conformance/prompt/trusted_project_instructions.golden.json @@ -2,8 +2,8 @@ "system_sha256": "cd367daa552060c0b30a60263a509fdd25a0d92c89f24aa9f69c174d52076f3d", "system_bytes": 8757, "system_blocks": 5, - "tools_sha256": "6f1ab6f7a2f04bcf68a6e8626e97c816b560ae103e94d7642f4cd5bd9a65ab5e", - "tools_bytes": 14890, + "tools_sha256": "157b468336034c773a179ceb07859a3bf5a93a7829dc5e66ccc0e4902622d9bb", + "tools_bytes": 14897, "tool_count": 12, "tool_names": [ "bash", @@ -19,7 +19,7 @@ "execute_tools", "tool_search" ], - "prefix_sha256": "57bd2508a57eea1011da97e0102b21a549ef7b49267f800400d18a2a7c304745", + "prefix_sha256": "54e30b4850ca16509f2e8fb4b5cbc5eccca33d4802970f1314a6e9d716ea9aa6", "masks": [ "", "", diff --git a/crates/tui/tests/fixtures/conformance/prompt/trusted_project_instructions.tools.golden.json b/crates/tui/tests/fixtures/conformance/prompt/trusted_project_instructions.tools.golden.json index 44f9d559ae..4b314d1368 100644 --- a/crates/tui/tests/fixtures/conformance/prompt/trusted_project_instructions.tools.golden.json +++ b/crates/tui/tests/fixtures/conformance/prompt/trusted_project_instructions.tools.golden.json @@ -283,7 +283,7 @@ }, "progress": { "additionalProperties": false, - "description": "Optional with not_achieved or advisory: your current best estimate of overall completion, shown to the user as reported progress. Keep percent honest — it is an estimate, never a verified fraction.", + "description": "Optional with not_achieved, advisory or yield: your current best estimate of overall completion, shown to the user as reported progress. Keep percent honest — it is an estimate, never a verified fraction.", "properties": { "next": { "description": "One short line: what comes next.", From 82bef73726d2223fb6704189a8f1db83fa5647df Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 14:37:42 +0000 Subject: [PATCH 18/46] chore(budget): record update_goal's +7 B progress description Refs #6957 Lint. 407cb8ebb3 made update_goal accept a progress estimate with yield and named yield in the progress description. The runtime-contract ratchet measured that on hosted Linux (run 38144890574, Lint job 114486327862) as +7 bytes on every Plan, Act and Operate surface: - full and active, Plan: 55163 -> 55170 and 36173 -> 36180; - full and active, Act/Operate: 84384 -> 84391 and 37703 -> 37710; - +1 to +2 estimated tokens on each. The ratchet confirmed base b1e39f4758 passes, so this commit adds the debt; it is intended. No tool enters or leaves a surface, and every identity is unchanged. The twelve ceilings take the exact CI values, with a dated _codex_20261011_goal_yield_remeasure note, as d54a098c1b did. scripts/test_check_runtime_contract_budget.py: Ran 22 tests, OK. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01BsXVvReYca2UxzimQtj7Mj --- scripts/runtime-contract-budget.json | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/scripts/runtime-contract-budget.json b/scripts/runtime-contract-budget.json index 48799b8684..99914e11b8 100644 --- a/scripts/runtime-contract-budget.json +++ b/scripts/runtime-contract-budget.json @@ -3,6 +3,7 @@ "_codex_20261005_main_remeasure": "2026-10-05: pair already-merged tool description disclosures with exact hosted Linux measurement on main 3684e2a776fd6ef4f07e8d75e8429d4bed52858b, CI 37359068458 Lint job 111929025135. Only Act/Operate full catalog ceilings failed: 84865 bytes / 21217 estimated tokens versus 84151 / 21038 (+714 B / +179). Accept only that measured increase; tool identities, active/Plan surfaces, prompt and other ceilings unchanged. Estimate only, not provider usage. Wave composition has its own measurement.", "_codex_20261005_wave_remeasure": "2026-10-05: exact provider-free measurement of wave 7903b9645: Act/Operate full 85359 B / 21340 estimated tokens, +644 B / +161 over stale 84715 / 21179. The merged tool-documentation disclosures add 714 B (hosted main measurement); factual description rewrites reduce the common full surface by 70 B (Plan 54336 -> 54266), yielding +644. Only these four numeric ceilings increase. All identity checks and other existing ceilings pass. Receipt CW/artifacts/codex-engine-takeover-20261005/wave-runtime-contract.json. Correction: a53b58b058 incorrectly said this budget check passed; it failed on exactly these four fields. No hosted/native qualification or provider usage implied.", "_codex_20261006_contributor_remeasure": "2026-10-06: provider-free composed release measurement. Existing bounded agent-wait timeout disclosure adds 172 schema bytes to active surfaces; contributor locale descriptions add 189 Plan full / 259 Act and Operate full bytes beyond that shared disclosure. Accept exactly measured tool schema ceilings: Plan full 54697 B / 13675 estimated tokens, active 34882 / 8721; Act and Operate full 85790 / 21448, active 36412 / 9103. All tool identities/counts, prompt identities and skill-discovery contracts remain unchanged. Other measured decreases are locked. Receipt CW/artifacts/takeover-0101-20261005/runtime-contract-final-receipt.json. bytes/4 estimates are not provider usage or billing.", + "_codex_20261011_goal_yield_remeasure": "2026-10-11: update_goal's progress description names yield, which update_goal now accepts with a progress estimate (407cb8ebb3). Hosted Linux measurement, CI run 38144890574 Lint job 114486327862: +7 B on every Plan/Act/Operate full and active surface (+1 to +2 estimated tokens). No tool enters or leaves a surface; identities unchanged.", "_comment": "One-way numeric ceilings and exact structural identities for the provider-free runtime contract. Decreases pass; increases or identity changes fail. Lock in decreases with: python3 scripts/check-runtime-contract-budget.py --update The v0.9.8 child-receipt restore grew every production tool surface by 1496 schema bytes / 374 estimated tokens (agent tool). The v0.9.8 workshop read/tool-result byte fields then grew every production tool surface by 371 schema bytes / 93 estimated tokens. Both raises are explicit maintainer decisions; identities stay on the pre-raise digests only if the name set is unchanged \u2014 re-measure on Linux CI if Lint reports identity drift. The v0.9.8 pinned session prefix added the sentence to the base prompt (5848 -> 6084 bytes, every representative stage re-hashed), and the host-side Workflow/Goal verbs plus honest child posture grew the tool catalog (active 16531 -> 16602 bytes, full 71473 -> 72371); both are explicit v0.9.8 maintainer decisions measured from the release train. The v0.9.9 configured-skills change hides only custom configured-root paths, preserves discoverable default-root paths, normalizes Windows prompt separators, and trims 50 redundant skills-prompt bytes. The skill/memory/goal/handoff identities were re-measured without raising any ceiling. Explicit maintainer decision for #5473/#5492. The v0.9.10 full surfaces intentionally add the safe read_media tool; their measured schemas remain below the prior byte/token ceilings. Representative prompt byte metrics now use the same host-independent normalized text as their identities; the normalized base is 6089 bytes. The v0.9.11 model-visible sub-agent surface intentionally retires six legacy agents/* tools in favor of the canonical agent tool; all affected schema and prompt metrics decrease. The v0.9.12 plugin prompt-match slice intentionally adds the request_plugin_install tool to the full tool surfaces (plan full: +518 schema bytes / +130 estimated tokens / 29 -> 30 tools) so a strong prompt match can surface the human review CTA; explicit maintainer decision for #5663/#5579. The v0.9.13 profile pins a non-executed bare bash shell so interpreter guidance is reproducible across hosts. The duplicate tts catalog entry is intentionally hidden; speech remains canonical and the alias remains available for saved-transcript dispatch. Explicit v0.9.13 maintainer decision (2026-09-08): after removing 1426 repeated guidance bytes and pinning the bash-v2 fixture, accept only the measured tool byte/token ceilings from all-features macOS source e27735bb63c897f88061c71567701fd971f5d396, verified libtest SHA-256 5e8cbe213f32c4ecdec63494c4de5e31857b4a40134edf7b21a55bca926b1b38: active 13274/3319 in every mode, Plan full 39885/9972, Act/Operate full 67603/16901, with no margin. Against the prior budget, active +390 bytes is agent -41 plus retained bash command syntax +431. Plan full also retains Git commit_plan +253, update_goal progress +583, github bounded local-report guidance +127, review complete-input refusal +35, and send_later dispatching status +14. Act/Operate full instead has github +2151 and additionally speech +230, hidden tts -2120, and tasks/automation exact model-route fields +274 each. The older budget predates v0.9.12: that tag had already removed 361 agent bytes and added the two 274-byte route fields; the retained initial increase versus the tag is 751 source-attributed bytes (agent +320, bash +431), not the +390 budget delta. Only the seven active definitions form the initial request; full catalogs include deferred tools. Estimated tokens use the existing bytes/4 heuristic, not provider usage or billing. Prompt, representative-context, skill-discovery and tool-name identities/ceilings are unchanged. Explicit v0.9.13 maintainer decision (2026-09-09): source ccc5dadfa2279545bf084d37cff3617e41ceaae2 intentionally exposes create_goal, get_goal and update_goal before continuation, so all three initial surfaces now contain ten tools. Measure exact source 4648d148eea64782be857eda6952af2c539cbfcc with the hosted macOS all-features libtest SHA-256 4485c88c7a8b66b8bb9a135807321e417a1e266457ecb74cffc7dfb92f850fc4: four exact provider-free metric tests pass. Active schemas are exactly 17847 bytes / 4462 estimated tokens (+4573 / +1143 for the three eager goal definitions); Plan full is 40597 / 10150 and Act/Operate full is 68315 / 17079, with no margin. The +712 full-catalog bytes are request_user_input guidance +358, update_goal state-change guidance +98, list_dir home-relative path guidance +56, explicit review max_passes schema +197, and three defer_loading true-to-false values +3. The three active name sets/digests, their counts, and measured active/full byte/token ceilings change; full name identities and all prompt, representative-context and skill-discovery measurements remain unchanged. This updates the earlier seven-tool initial-request receipt; bytes/4 remains an estimate, not measured provider usage or billing. 2026-10-05: the shell handoff grew every surface by 564 schema bytes / 141 estimated tokens. The bash and action=run descriptions now say a command still running after the foreground wait moves to the background and is not killed, and name task_shell_wait. No tool entered or left a surface. Measured on macOS aarch64; the Linux Lint run reported the same deltas. The one-way numeric and exact identity gates remain enforced. Explicit v0.9.13 maintainer decision (2026-09-10): the +765 active/full tool-schema bytes since source 4648d148ee are exactly source-attributed \u2014 the agent tool's followup/parked-child continuation guidance (b6fad79373: +144 action description, +43 message parameter, +169 resume_from parameter) and the read tool's real output budget (e7f7c71e2c: +164 description, +245 for the new max_bytes parameter). No tool enters or leaves any surface: every name-set identity, count, and all prompt, representative-context and skill-discovery measurements are unchanged; only the measured byte/token ceilings move, to active 18612/4653 in every mode, Plan full 41362/10341, and Act/Operate full 69080/17270, with no margin. bytes/4 remains an estimate, not measured provider usage or billing. The one-way numeric and exact identity gates remain enforced. Explicit v0.9.13 maintainer decision (2026-09-13): source 5bfe88c8e8f45266ea1f9abce87c76b2eed894af intentionally keeps the native workflow tool eager on Plan/Act/Operate first-turn surfaces (DEFAULT_ACTIVE_NATIVE_TOOLS; commit 9e49d0918). Active name sets gain `workflow` (10\u219211 tools); active schema ceilings move to 29402/7351 with margin pending exact Linux --update lock-in. Full catalogs already advertised workflow; only a small defer_loading true\u2192false spelling bump is reserved (+32 bytes / +8 tokens). Prompt, representative-context, and skill-discovery measurements are unchanged. Do not remove workflow from Plan. bytes/4 remains an estimate, not measured provider usage or billing. The one-way numeric and exact identity gates remain enforced. Explicit v0.9.13 maintainer decision (2026-09-13, CI lock-in after b4d48e9a4): Linux Lint run 34766373771 measured the intentional eager `workflow` surface at active 31438/7860 in every mode, Plan full 50801/12701, and Act/Operate full 78513/19629. Prior ceilings (29402/7351 active, Plan full 41394/10349, Act/Operate full 69112/17278) under-counted the workflow schema body plus defer_loading true\u2192false on full catalogs; name-set identities are unchanged and `workflow` stays on Plan. Lock ceilings to those measured values with no margin. bytes/4 remains an estimate, not measured provider usage or billing. The one-way numeric and exact identity gates remain enforced. Explicit v0.9.14 maintainer decision (2026-09-16): #5715 intentionally adds the two bounded read-only recall tools `session_get` and `session_search` to the Act/Operate full catalogs (50 -> 52 tools), so both full name-set identities and their digests move to 1203d192385fd2b02227ef9e4212e5379bc5cbb813a373f12539406b5958aef1. Plan full is unchanged: the session tools are not offered there. Measured on macOS aarch64 all-features from source 55a9e1b778fa; per the 2026-09-13 precedent the exact byte/token ceilings must be re-locked from a Linux Lint run if CI reports drift. bytes/4 remains an estimate, not measured provider usage or billing. The one-way numeric and exact identity gates remain enforced. Explicit v0.9.14 maintainer decision (2026-09-17, #6319): re-measure from source b0866943b4 on macOS aarch64 all-features; per the 2026-09-13 precedent the exact byte/token ceilings must be re-locked from a Linux Lint run if CI reports drift. No tool enters or leaves any surface (52 tools; every name-set identity unchanged). Representative stages and system prompt grow exactly +742 bytes per stage/mode (base 6089 -> 6831, prompt 6084 -> 6826) for the deliberate dc32272f15 Bearing article plus mandate-first scope law; every stage re-hashed. Tool growth is exactly source-attributed per tool (measured per-tool at 55a9e1b778fa vs HEAD): agent +670 (2ca54ea8da #6282 output-token-cap parameter, 3c9c62571a spawn-requirement docs, 85d8dc7501 #6278 exact_files sentence, less 3dcb41f5d2 #6272 release-clause trim and the a7a8bdb338 token-allowance description removal), workflow +424 (a035914336 #6232 plan-child cwd property, serialized twice via phases/items and top-level children/items), Git +867 (b89349286f #6298 merge_tree verify surface), Run +158 (233da9fb76 #6296 bounded cwd), read +91 (f6fb5f42d1 #6283 size/truncated/line_count response fields), create_goal +76 (4de9e9e281 model-decides-goals description rewrite). Active 31453 -> 32714 (+1261 = agent +670, workflow +424, read +91, create_goal +76); Plan full 50816 -> 52944 (+2128 = active +1261 plus Git +867); Act/Operate full 79531 -> 81817 (+2286 = Plan full +2128 plus Run +158). bytes/4 remains an estimate, not measured provider usage or billing. The one-way numeric and exact identity gates remain enforced. Explicit v0.9.14 maintainer decision (2026-09-19 overnight): Code Mode Phase-1 advertises `execute_tools` on Act/Operate full catalogs (52 -> 53 tools; Plan full unchanged). Default Direct mode keeps it deferred (`defer_loading: true`), so active surfaces are unchanged. Name-set identity digests move with the sorted name list; measured tool JSON is 884 bytes (+885 including the catalog comma) so Act/Operate full ceilings lock to 82702/20676. bytes/4 remains an estimate, not measured provider usage or billing. The one-way numeric and exact identity gates remain enforced. Explicit v0.10.0 source reconciliation (2026-09-19): retain the existing agent cwd parameter (+256 serialized bytes in every surface) and tasks create name parameter (+121 bytes in Act/Operate full only), both already present before the preceding execute_tools lock-in. Linux CI run 35490467762 measured active 32970/8243, Plan full 53200/13300, and Act/Operate full 83079/20770. Each increase is attributed to exactly one schema property; no margin, tool identity, prompt or representative-context change. These are schema byte/4 estimates, not billed tokens. Explicit v0.10.0 maintainer decision (2026-09-22): 91b5898a2 intentionally makes `load_skill` eager so the `## Skills` instruction in the base prompt is actually callable \u2014 it was deferred behind tool_search, so the prompt told the model to call a tool that was not in the array it was printed beside. load_skill is read-only, so it joins the active sets of Plan, Act and Operate (11 -> 12 tools; +816 schema bytes / +204 estimated tokens in every mode, active ceilings lock to 33786/8447). Full catalogs move only by the defer_loading true->false spelling plus that schema body (+245 bytes / +61-62 tokens). Representative stages re-hash and each move +149 bytes with the total +37 tokens: dc32272f15 landed the Bearing article and mandate-first scope law, and 91b5898a2 rewrote the Skills paragraph every stage renders. No other name-set identity, count, prompt or skill-discovery measurement changes. Measured from 6bafe6e72 on macOS aarch64 all-features; per the 2026-09-13 precedent the exact byte/token ceilings must be re-locked from a Linux Lint run if CI reports drift. bytes/4 remains an estimate, not measured provider usage or billing. The one-way numeric and exact identity gates remain enforced. Explicit maintainer decision (#6562, PR #6583, 2026-09-25): code mode for MCP is on by default ([features] code_mode = true), so `execute_tools` is eager on the Act/Operate first-turn surfaces (12 -> 13 tools; active 33876/8469 -> 35406/8852, +1530 bytes / +383 estimated tokens) and its definition text now documents nested MCP/plugin calls through the direct-call gate (Act/Operate full 83384/20846 -> 84029/21008, +645 bytes). Plan is unchanged: execute_tools is not offered there. `code_mode = false` restores the prior deferred surface. Measured on macOS aarch64 all-features; re-lock from a Linux Lint run if CI reports drift. Re-measured after merging main 2026-09-25: active 35392/8848 (-14, main's 4b0f54edb fork_context trim), Act/Operate full 83737/20935 (-292: the same -14 plus -278 not attributed per tool in this merge); decreases locked with --update, identities unchanged. bytes/4 remains an estimate, not measured provider usage or billing. Explicit maintainer decision (PR #6589, 2026-09-25): the agent tool declares `runtime` (local|cloud; cloud only proposes a /dispatch job the person confirms) and `remote` (github|cnb|gitee) as schema properties so the model can request a cloud proposal without an undeclared field. Both descriptions were trimmed (-303 bytes) before locking, which also keeps the parent prompt+catalog surface under PARENT_SURFACE_BYTE_CEILING; the remaining +284 bytes / +71 estimated tokens land on every surface that carries agent (Plan/Act/Operate active and full: active 35676/8919 Act/Operate, 34146/8537 Plan; full 84021/21006 Act/Operate, 53785/13447 Plan). No tool enters or leaves any surface; identities unchanged. Measured on macOS aarch64 all-features; re-lock from a Linux Lint run if CI reports drift. bytes/4 remains an estimate, not measured provider usage or billing.", "document_kind": "codewhale.runtime_contract_budget", "representative_context": { @@ -90,9 +91,9 @@ "modes": { "act": { "active": { - "bytes": 37703, + "bytes": 37710, "identity_sha256": "e2d6dfb7168d48ee8d2a415feac7ff5b8864e18528c233a697055370144eb138", - "tokens_est": 9426, + "tokens_est": 9428, "tool_names": [ "agent", "bash", @@ -112,9 +113,9 @@ "tools": 14 }, "full": { - "bytes": 84384, + "bytes": 84391, "identity_sha256": "2a2ee97e7c482c625bc50c77db7f0e08d6bc67e2b4e73e7351385418700252bb", - "tokens_est": 21096, + "tokens_est": 21098, "tool_names": [ "Git", "Run", @@ -173,9 +174,9 @@ }, "operate": { "active": { - "bytes": 37703, + "bytes": 37710, "identity_sha256": "e2d6dfb7168d48ee8d2a415feac7ff5b8864e18528c233a697055370144eb138", - "tokens_est": 9426, + "tokens_est": 9428, "tool_names": [ "agent", "bash", @@ -195,9 +196,9 @@ "tools": 14 }, "full": { - "bytes": 84384, + "bytes": 84391, "identity_sha256": "2a2ee97e7c482c625bc50c77db7f0e08d6bc67e2b4e73e7351385418700252bb", - "tokens_est": 21096, + "tokens_est": 21098, "tool_names": [ "Git", "Run", @@ -256,9 +257,9 @@ }, "plan": { "active": { - "bytes": 36173, + "bytes": 36180, "identity_sha256": "6c432c75bf776d95b367b17308b171c3eaba8e70aa0c2f76bac453b3c3368a21", - "tokens_est": 9044, + "tokens_est": 9045, "tool_names": [ "agent", "bash", @@ -277,9 +278,9 @@ "tools": 13 }, "full": { - "bytes": 55163, + "bytes": 55170, "identity_sha256": "ac8af1f4988199825be7b00b054c258724a44074b1d4e6de6c92ade7c1cffe63", - "tokens_est": 13791, + "tokens_est": 13793, "tool_names": [ "Git", "Web", From f7264f7d72fb232f327a223e98d91e06dcac6869 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 14:38:15 +0000 Subject: [PATCH 19/46] fix(tui): the shell inspector follows a running job; Alt+V keeps the clicked card MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refs #6931. Acceptance 4 (follow mode; refresh without the model's cursor) and acceptance 1 (a new card never takes over the target). Follow mode: - When the Bash Job pager opens on a running job, it records the job id. - On its 2.5 s tick, the event loop re-reads that job through `inspect_job_for_session`. This is a bounded read, under try_lock as `refresh_shell_exec_live_output` does, and never touches the model's output cursor. The event loop then swaps the text into the open pager. - A view at the bottom stays pinned to the newest row. Scrolling up, Home, `gg` or a search jump stops following; End, `G` or scrolling back to the bottom resumes it. - When the job ends or leaves the session, refresh stops and Stop is removed, because it no longer applies (acceptance 5: inapplicable controls are disabled). - The Status line reads "running · no output for 2m 05s" once a live job has been quiet for QUIET_NO_OUTPUT_AFTER. It uses the label the exec card already uses. - `ViewStack::top_pager_mut` is the downcast, next to `refresh_extensions`. Alt+V target: - 674dcc364d sent Alt+V with no selection to the newest running shell on screen before anything else. That bypassed `detail_cell_index_for_viewport`, which the footer hint also uses. So a running card that scrolled into view took over the completed card the user was reading, and the hint and the key disagreed. Alt+V now resolves only through `detail_target_cell_index`. `newest_visible_running_shell_cell` is deleted. A running shell stays reachable by click, by the work dock and by /jobs. - A selection stored only its rendered line. When a run of successful tool cards collapsed above it, or cells were appended while an active cell was open, that line pointed at another card. A click now also records its owner cell as the existing `TranscriptActionOwner` (cell index plus transcript identity epoch). Space-fold already uses it. `rebase_active_cell_bindings` shifts the owner with the other bindings. Any other identity change (folds, rollback) invalidates it, and the target falls back to the line. Alt+V, the detail target and Ctrl+O prefer the owner. - CHANGELOG lines that advertised the old Alt+V rule are corrected. Known limits, stated in shell_job_routing.rs: - new output reaches the pager up to 2.5 s late; - the work dock's inspector is still a snapshot; - delegated agents' shells appear in /jobs and the dock with their Owner, not in the agent's detail; - named PTYs live in the terminal dock (#6912). Focused tests: the issue asks for them in acceptance 7. - pager: follow, unfollow and resume, and dropping Stop when the job ends; - shell_job_routing: a silent running job reads quiet, not stale, with "(no output yet)"; - the existing stale-list test now shares the snapshot fixture. Evidence: cargo fmt --all clean. clippy -p codewhale-tui --lib --tests (-D warnings) is running locally on this exact tree; the focused tests run in hosted CI on codewhale-hq/Codewhale#6957. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01BsXVvReYca2UxzimQtj7Mj --- crates/tui/src/tui/app.rs | 50 +++++++++-- crates/tui/src/tui/mouse_ui.rs | 2 + crates/tui/src/tui/pager.rs | 105 +++++++++++++++++++++- crates/tui/src/tui/selection.rs | 2 + crates/tui/src/tui/shell_job_routing.rs | 107 ++++++++++++++++------- crates/tui/src/tui/ui/activity_detail.rs | 20 ++--- crates/tui/src/tui/ui/event_loop.rs | 4 + crates/tui/src/tui/views/mod.rs | 7 ++ 8 files changed, 248 insertions(+), 49 deletions(-) diff --git a/crates/tui/src/tui/app.rs b/crates/tui/src/tui/app.rs index c4c21a6ae6..fe5a0f5d7d 100644 --- a/crates/tui/src/tui/app.rs +++ b/crates/tui/src/tui/app.rs @@ -5630,6 +5630,34 @@ impl App { /// Pick the detail target for the current viewport. The footer hint and /// Alt+V both resolve through this so they agree on the target. #[must_use] + pub(crate) fn transcript_owner_at_line( + &self, + line_index: usize, + ) -> Option { + let (rendered, _) = self + .viewport + .transcript_cache + .line_meta() + .get(line_index)? + .cell_line()?; + Some(TranscriptActionOwner { + cell_index: self.original_cell_index_for_rendered(rendered), + identity_epoch: self.transcript_identity_epoch, + }) + } + + pub(crate) fn selection_owner_cell(&self) -> Option { + let selection = &self.viewport.transcript_selection; + selection + .owner + .filter(|owner| { + selection.is_active() + && owner.identity_epoch == self.transcript_identity_epoch + && self.cell_at_virtual_index(owner.cell_index).is_some() + }) + .map(|owner| owner.cell_index) + } + pub fn detail_cell_index_for_viewport( &self, top: usize, @@ -5641,11 +5669,14 @@ impl App { .map(|(idx, _)| self.original_cell_index_for_rendered(idx)) }; let selected = self - .viewport - .transcript_selection - .ordered_endpoints() - .and_then(|(start, _)| line_meta.get(start.line_index)) - .and_then(original) + .selection_owner_cell() + .or_else(|| { + self.viewport + .transcript_selection + .ordered_endpoints() + .and_then(|(start, _)| line_meta.get(start.line_index)) + .and_then(original) + }) .filter(|&idx| self.cell_has_detail_target(idx)); let start = top.min(line_meta.len().saturating_sub(1)); let end = start.saturating_add(visible).min(line_meta.len()); @@ -5764,7 +5795,16 @@ impl App { } // A pre-insertion action still names the old virtual index. Reject // it until the renderer establishes the owner's new position. + let previous_epoch = self.transcript_identity_epoch; self.transcript_identity_epoch = self.transcript_identity_epoch.wrapping_add(1); + if let Some(owner) = self.viewport.transcript_selection.owner.as_mut() + && owner.identity_epoch == previous_epoch + { + if owner.cell_index >= boundary { + owner.cell_index = owner.cell_index.saturating_add(added); + } + owner.identity_epoch = self.transcript_identity_epoch; + } for index in self.tool_cells.values_mut() { if *index >= boundary { *index = index.saturating_add(added); diff --git a/crates/tui/src/tui/mouse_ui.rs b/crates/tui/src/tui/mouse_ui.rs index 4c8ea199bb..8f0594274f 100644 --- a/crates/tui/src/tui/mouse_ui.rs +++ b/crates/tui/src/tui/mouse_ui.rs @@ -749,6 +749,8 @@ pub(crate) fn handle_mouse_event(app: &mut App, mouse: MouseEvent) -> Vec, action_area: Cell>, + live_shell_job: Option, + follow: bool, } impl PagerView { @@ -270,9 +272,46 @@ impl PagerView { last_visible_height: Cell::new(0), destructive_action: None, action_area: Cell::new(None), + live_shell_job: None, + follow: false, } } + pub(crate) fn with_live_shell_job(mut self, job_id: impl Into) -> Self { + self.live_shell_job = Some(job_id.into()); + self + } + + pub(crate) fn live_shell_job(&self) -> Option<&str> { + self.live_shell_job.as_deref() + } + + pub(crate) fn replace_live_text(&mut self, text: &str) -> bool { + let page = self.current_page(); + if page.copy_text.as_deref() == Some(text) { + return false; + } + if self.scroll.get() >= self.max_scroll() { + self.follow = true; + } + let mut sanitized = String::with_capacity(text.len()); + crate::tui::osc8::strip_ansi_into(text, &mut sanitized); + let page = self.current_page_mut(); + page.source = PagerSource::Text(sanitized); + page.copy_text = Some(text.to_string()); + *page.rows.get_mut() = PagerRows::default(); + if !self.search_input.trim().is_empty() { + self.update_search_matches(); + } + true + } + + pub(crate) fn end_live_shell_job(&mut self) { + self.live_shell_job = None; + self.destructive_action = None; + self.action_area.set(None); + } + /// Attach a compact Markdown export (e.g. the #4108 turn handoff) that the /// `e` key copies to the clipboard. Only the Turn Inspector pager sets this; /// other pagers leave `e` inert. @@ -379,23 +418,28 @@ impl PagerView { self.search_index.set(0); self.search_mode = false; self.pending_g = false; + self.follow = false; } fn scroll_up(&mut self, amount: usize) { self.scroll.set(self.scroll.get().saturating_sub(amount)); + self.follow = false; } fn scroll_down(&mut self, amount: usize, max_scroll: usize) { self.scroll .set(self.scroll.get().saturating_add(amount).min(max_scroll)); + self.follow = self.live_shell_job.is_some() && self.scroll.get() >= max_scroll; } fn scroll_to_top(&mut self) { self.scroll.set(0); + self.follow = false; } fn scroll_to_bottom(&mut self, max_scroll: usize) { self.scroll.set(max_scroll); + self.follow = self.live_shell_job.is_some(); } /// Plain-text rendered body of the pager joined with `\n`. This reflects @@ -492,6 +536,7 @@ impl PagerView { fn jump_to_match(&mut self) { if let Some(&row) = self.search_matches.get_mut().get(self.search_index.get()) { self.scroll.set(row); + self.follow = false; } } @@ -867,7 +912,12 @@ impl ModalView for PagerView { let rows = page.rows.borrow(); let row_count = rows.plain.len(); let max_scroll = row_count.saturating_sub(visible_height); - let scroll = self.scroll.get().min(max_scroll); + let scroll = if self.follow { + self.scroll.set(max_scroll); + max_scroll + } else { + self.scroll.get().min(max_scroll) + }; let end = (scroll + visible_height).min(row_count); let mut visible_lines: Vec> = if row_count == 0 { vec![Line::from("")] @@ -1289,6 +1339,59 @@ mod tests { assert_eq!(p.scroll.get(), 0); } + #[test] + fn live_shell_job_pager_follows_new_output_until_scrolled_back() { + let body = |rows: usize| { + (0..rows) + .map(|row| format!("out-{row:03}")) + .collect::>() + .join("\n") + }; + let mut p = PagerView::from_text("Bash Job shell_1", &body(5), 40) + .with_copy_text(body(5)) + .with_live_shell_job("shell_1"); + prime_layout(&mut p, 12); + + assert!(p.replace_live_text(&body(60))); + prime_layout(&mut p, 12); + assert!(p.max_scroll() > 0); + assert_eq!(p.scroll.get(), p.max_scroll()); + + let _ = p.handle_key(key(KeyCode::Char('k'))); + let held = p.scroll.get(); + assert!(p.replace_live_text(&body(80))); + prime_layout(&mut p, 12); + assert_eq!(p.scroll.get(), held); + + let _ = p.handle_key(key(KeyCode::Char('G'))); + assert!(p.replace_live_text(&body(90))); + prime_layout(&mut p, 12); + assert_eq!(p.scroll.get(), p.max_scroll()); + assert!(!p.replace_live_text(&body(90))); + } + + #[test] + fn ending_a_live_shell_job_drops_stop() { + let mut p = make_pager(2) + .with_live_shell_job("shell_1") + .with_destructive_action( + 's', + "stop", + "confirm stop · Esc cancels", + ViewEvent::SidebarAgentCancel { + agent_id: "agent_1".to_string(), + }, + ); + p.end_live_shell_job(); + assert!(p.live_shell_job().is_none()); + for _ in 0..2 { + assert!(matches!( + p.handle_key(key(KeyCode::Char('s'))), + ViewAction::None + )); + } + } + #[test] fn shift_g_jumps_to_bottom() { let mut p = make_pager(50); diff --git a/crates/tui/src/tui/selection.rs b/crates/tui/src/tui/selection.rs index dea1c6707b..069a425486 100644 --- a/crates/tui/src/tui/selection.rs +++ b/crates/tui/src/tui/selection.rs @@ -17,6 +17,7 @@ pub struct TranscriptSelection { pub anchor: Option, pub head: Option, pub dragging: bool, + pub(crate) owner: Option, } /// Drag-past-edge auto-scroll state. While the user holds the left button @@ -39,6 +40,7 @@ impl TranscriptSelection { self.anchor = None; self.head = None; self.dragging = false; + self.owner = None; } /// Whether a full selection is active. diff --git a/crates/tui/src/tui/shell_job_routing.rs b/crates/tui/src/tui/shell_job_routing.rs index 946ac3ca7a..61f9f05ae0 100644 --- a/crates/tui/src/tui/shell_job_routing.rs +++ b/crates/tui/src/tui/shell_job_routing.rs @@ -1,13 +1,16 @@ //! Background shell job-center helpers for slash commands and pagers. //! -//! Known limitations: the inspector is a snapshot (`/jobs poll` re-reads it, -//! without consuming the model's output cursor); there is no follow mode. -//! Named PTY sessions (`tools/terminal_session.rs`) are not listed here or in +//! Known limitations: the Bash Job pager follows a running job by re-reading +//! it on the event loop's 2.5 s refresh (a bounded read that never consumes +//! the model's output cursor), so new output lands up to 2.5 s late; the work +//! dock's inspector is still a snapshot. Shells run by delegated agents show +//! in `/jobs` and the dock with their Owner, not in the agent's detail. Named +//! PTY sessions (`tools/terminal_session.rs`) are not listed here or in //! `/jobs`; they live in the terminal dock (#6912). use crate::tools::shell::{ShellJobDetail, ShellJobSnapshot, ShellResult, ShellStatus}; use crate::tui::app::App; -use crate::tui::history::{HistoryCell, ToolCell, ToolStatus}; +use crate::tui::history::{HistoryCell, ToolCell}; use crate::tui::pager::PagerView; use crate::tui::views::{CommandPaletteAction, ViewEvent}; use codewhale_localization::MessageId; @@ -144,25 +147,6 @@ pub(crate) fn shell_job_for_cell(app: &App, cell_index: usize) -> Option Option { - let meta = app.viewport.transcript_cache.line_meta(); - let start = app.viewport.last_transcript_top.min(meta.len()); - let end = start - .saturating_add(app.viewport.last_transcript_visible.max(1)) - .min(meta.len()); - meta[start..end] - .iter() - .rev() - .filter_map(|line| line.cell_line()) - .map(|(rendered, _)| app.original_cell_index_for_rendered(rendered)) - .find(|&index| { - matches!( - app.cell_at_virtual_index(index), - Some(HistoryCell::Tool(ToolCell::Exec(exec))) if exec.status == ToolStatus::Running - ) && shell_job_for_cell(app, index).is_some() - }) -} - pub(crate) fn open_shell_job_pager(app: &mut App, detail: &ShellJobDetail) { let width = app .viewport @@ -179,6 +163,7 @@ pub(crate) fn open_shell_job_pager(app: &mut App, detail: &ShellJobDetail) { ) .with_copy_text(body); if live { + pager = pager.with_live_shell_job(detail.snapshot.id.clone()); pager = pager.with_destructive_action( 's', app.tr(MessageId::SidebarStopControl), @@ -193,11 +178,55 @@ pub(crate) fn open_shell_job_pager(app: &mut App, detail: &ShellJobDetail) { app.view_stack.push(pager); } +pub(crate) fn refresh_live_shell_job_pager(app: &mut App) -> bool { + let Some(job_id) = app + .view_stack + .top_pager_mut() + .and_then(|pager| pager.live_shell_job().map(str::to_owned)) + else { + return false; + }; + let Some(shell_manager) = app.runtime_services.shell_manager.clone() else { + return false; + }; + let session = app.current_session_id.clone().unwrap_or_default(); + let detail = { + let Ok(mut manager) = shell_manager.try_lock() else { + return false; + }; + manager.inspect_job_for_session(&session, &job_id).ok() + }; + let Some(pager) = app.view_stack.top_pager_mut() else { + return false; + }; + let Some(detail) = detail else { + pager.end_live_shell_job(); + return true; + }; + let changed = pager.replace_live_text(&format_shell_job_detail(&detail)); + if detail.snapshot.status != ShellStatus::Running { + pager.end_live_shell_job(); + return true; + } + changed +} + +fn detail_status_label(job: &ShellJobSnapshot) -> String { + let quiet = + u64::try_from(crate::tools::shell::QUIET_NO_OUTPUT_AFTER.as_millis()).unwrap_or(u64::MAX); + match job.elapsed_since_output_ms { + Some(ms) if job.status == ShellStatus::Running && !job.stale && ms >= quiet => { + crate::tui::history::stale_shell_status_label(ms) + } + _ => status_label(&job.status, job.stale).to_string(), + } +} + pub(crate) fn format_shell_job_detail(detail: &ShellJobDetail) -> String { let job = &detail.snapshot; let mut lines = vec![ format!("Job: {}", job.id), - format!("Status: {}", status_label(&job.status, job.stale)), + format!("Status: {}", detail_status_label(job)), format!("Command: {}", job.command), format!("Cwd: {}", crate::utils::display_path(&job.cwd)), ]; @@ -260,14 +289,13 @@ mod tests { use super::*; use std::path::PathBuf; - #[test] - fn list_shows_controls_and_stale_state() { - let jobs = vec![ShellJobSnapshot { + fn snapshot(status: ShellStatus, stale: bool) -> ShellJobSnapshot { + ShellJobSnapshot { id: "shell_dead".to_string(), job_id: "shell_dead".to_string(), command: "cargo test".to_string(), cwd: PathBuf::from("/tmp/repo"), - status: ShellStatus::Killed, + status, exit_code: None, elapsed_ms: 0, stdout_tail: String::new(), @@ -275,7 +303,7 @@ mod tests { stdout_len: 0, stderr_len: 8, stdin_available: false, - stale: true, + stale, elapsed_since_output_ms: None, linked_task_id: Some("task_1".to_string()), owner_agent_id: None, @@ -285,7 +313,26 @@ mod tests { owner_session_id: "session-test".to_string(), background: true, finished_at: Some(chrono::Utc::now()), - }]; + } + } + + #[test] + fn silent_running_job_detail_reads_quiet_not_stale() { + let mut job = snapshot(ShellStatus::Running, false); + job.elapsed_since_output_ms = Some(125_000); + let body = format_shell_job_detail(&ShellJobDetail { + snapshot: job, + stdout: String::new(), + stderr: String::new(), + }); + assert!(body.contains("Status: running · no output for"), "{body}"); + assert!(body.contains("(no output yet)"), "{body}"); + assert!(!body.contains("stale"), "{body}"); + } + + #[test] + fn list_shows_controls_and_stale_state() { + let jobs = vec![snapshot(ShellStatus::Killed, true)]; let formatted = format_shell_job_list(&jobs); assert!(formatted.contains("Bash jobs (1)")); assert!(!formatted.contains("Background commands")); diff --git a/crates/tui/src/tui/ui/activity_detail.rs b/crates/tui/src/tui/ui/activity_detail.rs index f330e92fa8..da0d069314 100644 --- a/crates/tui/src/tui/ui/activity_detail.rs +++ b/crates/tui/src/tui/ui/activity_detail.rs @@ -18,6 +18,9 @@ use crate::tui::ui_text::{ use codewhale_localization::{MessageId, tr}; fn selected_transcript_cell_index(app: &App) -> Option { + if let Some(owner) = app.selection_owner_cell() { + return Some(owner); + } app.viewport .transcript_selection .ordered_endpoints() @@ -275,19 +278,7 @@ const RAW_DETAIL_PAGER_INTRO: &str = "Raw detail for the selected item — press Ctrl+Alt+O for the whole-turn overview."; pub(super) fn open_tool_details_pager(app: &mut App) -> bool { - let target_cell = if app - .viewport - .transcript_selection - .ordered_endpoints() - .is_some() - { - detail_target_cell_index(app) - } else { - crate::tui::shell_job_routing::newest_visible_running_shell_cell(app) - .or_else(|| detail_target_cell_index(app)) - }; - - let Some(cell_index) = target_cell else { + let Some(cell_index) = detail_target_cell_index(app) else { app.status_message = Some(NO_RAW_DETAIL_HINT.to_string()); return false; }; @@ -594,6 +585,9 @@ fn turn_answer_payload(app: &App, start: usize, end: usize, width: u16) -> Optio } pub(super) fn detail_target_cell_index(app: &App) -> Option { + if let Some(owner) = app.selection_owner_cell() { + return Some(owner); + } if let Some((start, _)) = app.viewport.transcript_selection.ordered_endpoints() { return app .viewport diff --git a/crates/tui/src/tui/ui/event_loop.rs b/crates/tui/src/tui/ui/event_loop.rs index 89023504be..37b950b637 100644 --- a/crates/tui/src/tui/ui/event_loop.rs +++ b/crates/tui/src/tui/ui/event_loop.rs @@ -466,6 +466,7 @@ pub(super) fn handle_transcript_space(app: &mut App) -> bool { app.viewport.transcript_selection.clear(); app.viewport.transcript_selection.anchor = Some(point); app.viewport.transcript_selection.head = Some(point); + app.viewport.transcript_selection.owner = Some(owner); } app.mark_history_updated(); true @@ -2228,6 +2229,9 @@ pub(crate) async fn run_event_loop( if refresh_shell_exec_live_output(app) { app.needs_redraw = true; } + if crate::tui::shell_job_routing::refresh_live_shell_job_pager(app) { + app.needs_redraw = true; + } if app .runtime_services .work diff --git a/crates/tui/src/tui/views/mod.rs b/crates/tui/src/tui/views/mod.rs index e4267abe1a..8f12325b22 100644 --- a/crates/tui/src/tui/views/mod.rs +++ b/crates/tui/src/tui/views/mod.rs @@ -1593,6 +1593,13 @@ impl ViewStack { panel.refresh_snapshot(snapshot); } } + + pub(crate) fn top_pager_mut(&mut self) -> Option<&mut crate::tui::pager::PagerView> { + self.views + .last_mut()? + .as_any_mut() + .downcast_mut::() + } } impl fmt::Debug for ViewStack { From 02fd46315082865acc2a3c84f3f3b1c3db21a676 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 14:38:26 +0000 Subject: [PATCH 20/46] fix(shell): wait results carry progress; a person's kill says what survived MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refs #6944, acceptance 1 and 2 for what the model and the person read. - `ShellManager::running_progress` polls a live job and reads its bounded 1,200-byte tails (`job_snapshot`). It returns "Progress: last output Ns ago; output phase: …" using the existing `output_phase_hint`. - A running single-task `wait` puts that line ahead of the delta result. - A multi-task `wait` adds it to each running id's line. - Silence plus phase separates "still compiling" from a stall without another poll. - Metadata and `build_shell_delta_tool_result` are unchanged. - The /jobs cancel receipt (`format_shell_poll`) and the shell completion handoff the model receives after a person's cancel now name whether compile artifacts survive. They use the `kill_outcome_note` the model's own cancel path already shows. The handoff adds a `kill_note` key on Killed events only. It is an append-only user-role message that is already in the session log, so the KV-cache prefix is unchanged. - `tools::shell_output` becomes pub(crate) so the TUI receipt can reuse the note. - Two comments that 34bf6568f2 made false no longer claim that Full Access cannot bypass the background floor (turn_loop.rs, auto_review.rs). Known limit: cargo prints no "crate N of M" or "k/N tests" to a non-TTY pipe, so the phase stays coarse (compiling / build finished / running / unrecognized). Not here: - (a) unpolled mid-run progress events; - (b) deleting `Run(action=tests)`; - (c) the measured side-by-side. These are founder questions. Evidence: cargo fmt --all clean; clippy pending locally on this tree. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01BsXVvReYca2UxzimQtj7Mj --- crates/tui/src/core/authority/auto_review.rs | 11 ++--- crates/tui/src/core/engine/turn_loop.rs | 3 +- crates/tui/src/runtime_handoff.rs | 13 ++++-- crates/tui/src/tools/mod.rs | 2 +- crates/tui/src/tools/shell.rs | 45 ++++++++++++++++++-- crates/tui/src/tui/shell_job_routing.rs | 10 +++++ 6 files changed, 69 insertions(+), 15 deletions(-) diff --git a/crates/tui/src/core/authority/auto_review.rs b/crates/tui/src/core/authority/auto_review.rs index 1ec98fef85..49fd28d06c 100644 --- a/crates/tui/src/core/authority/auto_review.rs +++ b/crates/tui/src/core/authority/auto_review.rs @@ -1578,11 +1578,12 @@ fn shell_params_are_destructive_like(params: &Value, workspace: Option<&std::pat }) } -/// The non-bypassable floor must hold genuinely catastrophic writes even when -/// `command_safety` (tuned to avoid over-blocking build/test chains) rates -/// them merely `RequiresApproval`: `dd`/`shred`/`wipefs` onto a device, -/// `mkfs`, and a forced recursive delete of an absolute path that is not -/// provably inside a safe workspace (#3883 follow-up). +/// The background/headless floor, which Full Access skips, must hold +/// genuinely catastrophic writes even when `command_safety` (tuned to avoid +/// over-blocking build/test chains) rates them merely `RequiresApproval`: +/// `dd`/`shred`/`wipefs` onto a device, `mkfs`, and a forced recursive delete +/// of an absolute path that is not provably inside a safe workspace (#3883 +/// follow-up). fn argv_is_destroyer(argv: &[String], workspace: Option<&std::path::Path>) -> bool { let Some((command, args)) = argv.split_first() else { return false; diff --git a/crates/tui/src/core/engine/turn_loop.rs b/crates/tui/src/core/engine/turn_loop.rs index 0c6990831d..295a0f2240 100644 --- a/crates/tui/src/core/engine/turn_loop.rs +++ b/crates/tui/src/core/engine/turn_loop.rs @@ -2161,8 +2161,7 @@ impl Engine { !child.authority.runtime.has_foreground_ownership() }) { // A detached child's caller remains background even - // for a synchronous tool. Full Access cannot bypass - // the existing catastrophic-background safety floor. + // for a synchronous tool. RunOrigin::Background } else { auto_review_run_origin_for_plan(detached_start) diff --git a/crates/tui/src/runtime_handoff.rs b/crates/tui/src/runtime_handoff.rs index 822d83dd29..ea26ea960b 100644 --- a/crates/tui/src/runtime_handoff.rs +++ b/crates/tui/src/runtime_handoff.rs @@ -583,7 +583,7 @@ pub(crate) fn shell_completion_runtime_message( let payload = events .iter() .map(|event| { - serde_json::json!({ + let mut entry = serde_json::json!({ "task_id": event.task_id, "command": event.command, "status": format!("{:?}", event.status), @@ -599,8 +599,15 @@ pub(crate) fn shell_completion_runtime_message( "owner_agent_name": event.owner_agent_name, "origin_tool_call_id": event.origin_tool_call_id, "origin_turn_id": event.origin_turn_id, - }) - .to_string() + }); + if event.status == crate::tools::shell::ShellStatus::Killed { + entry["kill_note"] = + serde_json::json!(crate::tools::shell_output::kill_outcome_note(&format!( + "{}{}", + event.stdout_tail, event.stderr_tail + ))); + } + entry.to_string() }) .collect::>() .join("\n"); diff --git a/crates/tui/src/tools/mod.rs b/crates/tui/src/tools/mod.rs index b7877b9e53..e276dbcd6c 100644 --- a/crates/tui/src/tools/mod.rs +++ b/crates/tui/src/tools/mod.rs @@ -68,7 +68,7 @@ pub mod search; pub mod send_later; pub mod session; pub mod shell; -mod shell_output; +pub(crate) mod shell_output; pub mod skill; pub mod spec; pub mod speech; diff --git a/crates/tui/src/tools/shell.rs b/crates/tui/src/tools/shell.rs index 9062f68d04..2fadaea283 100644 --- a/crates/tui/src/tools/shell.rs +++ b/crates/tui/src/tools/shell.rs @@ -3179,6 +3179,20 @@ impl ShellManager { Some(u64::try_from(shell.last_output_at.elapsed().as_millis()).unwrap_or(u64::MAX)) } + pub fn running_progress(&mut self, task_id: &str) -> Option { + let shell = self.processes.get_mut(task_id)?; + shell.poll(); + if shell.status != ShellStatus::Running { + return None; + } + let job = shell.job_snapshot(); + let quiet_secs = shell.last_output_at.elapsed().as_secs(); + Some(format!( + "Progress: last output {quiet_secs}s ago; {}.", + output_phase_hint(&format!("{}\n{}", job.stdout_tail, job.stderr_tail)) + )) + } + /// Kill a running background process pub fn kill(&mut self, task_id: &str) -> Result { let shell = self @@ -6709,6 +6723,15 @@ impl BashTool { { object.insert("wait_timeout_ms".to_string(), json!(timeout_ms)); } + if matches!(status, ShellStatus::Running) + && let Some(progress) = context + .shell_manager + .lock() + .map_err(|_| ToolError::execution_failed("shell manager lock poisoned"))? + .running_progress(task_id) + { + result.content = format!("{progress}\n{}", result.content); + } if let Some(interruption) = interruption { let (action, key) = match interruption { ShellWaitInterruption::Canceled => ("canceled", "wait_canceled"), @@ -6837,10 +6860,24 @@ impl BashTool { .filter(|(_, status)| *status == ShellStatus::Running) .count(); let settled = statuses.len().saturating_sub(running_after); - let lines: Vec = statuses - .iter() - .map(|(id, status)| format!("{id}: {status:?}")) - .collect(); + let lines: Vec = { + let mut manager = context + .shell_manager + .lock() + .map_err(|_| ToolError::execution_failed("shell manager lock poisoned"))?; + statuses + .iter() + .map(|(id, status)| { + let progress = (*status == ShellStatus::Running) + .then(|| manager.running_progress(id)) + .flatten(); + match progress { + Some(progress) => format!("{id}: {status:?} ({progress})"), + None => format!("{id}: {status:?}"), + } + }) + .collect() + }; let still_running: Vec<&str> = statuses .iter() .filter(|(_, status)| *status == ShellStatus::Running) diff --git a/crates/tui/src/tui/shell_job_routing.rs b/crates/tui/src/tui/shell_job_routing.rs index 61f9f05ae0..86816d3959 100644 --- a/crates/tui/src/tui/shell_job_routing.rs +++ b/crates/tui/src/tui/shell_job_routing.rs @@ -90,6 +90,16 @@ pub(super) fn format_shell_poll(result: &ShellResult) -> String { ), String::new(), ]; + if result.status == ShellStatus::Killed { + lines.push( + crate::tools::shell_output::kill_outcome_note(&format!( + "{}{}", + result.stdout, result.stderr + )) + .to_string(), + ); + lines.push(String::new()); + } if result.stdout.is_empty() && result.stderr.is_empty() { lines.push("(no new output)".to_string()); } else { From 41c44ee1bbb0eb357a81db813e9ee2026bc19afd Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 14:38:26 +0000 Subject: [PATCH 21/46] refactor(oauth): drop the never-set `originator` authorize parameter Refs #6925. Since the sign-in repair, every `OAuthProviderParams` sets `originator: None`. The authorize-URL branch that appended it could not run, and its doc comment described a ChatGPT requirement that no longer holds. - The field, its five `None` initializers and the dead branch are removed. - `OwnedAuthEntry.originator` stays, so credential files written by older builds still parse. A new ChatGPT login stores `None` there. - `authorize_url_uses_documented_chatgpt_parameters_and_pkce` already asserts that the URL has no `originator=`. - The `oauth` module is private to the crate, so no published API changes. Evidence: cargo fmt --all clean; clippy pending locally on this tree. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01BsXVvReYca2UxzimQtj7Mj --- crates/tui/src/oauth.rs | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/crates/tui/src/oauth.rs b/crates/tui/src/oauth.rs index a0975a6963..d4588ba8f7 100644 --- a/crates/tui/src/oauth.rs +++ b/crates/tui/src/oauth.rs @@ -282,9 +282,6 @@ pub struct OAuthProviderParams<'a> { /// account instead of reusing the browser's session. Sent unless /// [`OAuthEnvOverrides::no_account_prompt_var`] is set. pub account_choice_extras: &'a [(&'a str, &'a str)], - /// Honest client identity for issuers that require one (ChatGPT's - /// `originator`). Never impersonate another CLI. - pub originator: Option<&'a str>, /// Remote revoke path under the issuer, pinned rather than discovered: /// revoke must still clear local credentials when the issuer is /// unreachable, so a discovery fetch would only add a failure mode to a @@ -325,7 +322,6 @@ pub const XAI_OAUTH_PARAMS: OAuthProviderParams<'static> = OAuthProviderParams { device_poll_floor_secs: 30, authorize_extras: &[], account_choice_extras: &[], - originator: None, revoke_path: None, callback_path: "", loopback_ports: &[], @@ -353,7 +349,6 @@ pub const CLAUDE_OAUTH_PARAMS: OAuthProviderParams<'static> = OAuthProviderParam device_poll_floor_secs: 0, authorize_extras: &[], account_choice_extras: &[], - originator: None, revoke_path: None, callback_path: "/oauth/code/callback", loopback_ports: &[], @@ -368,7 +363,6 @@ pub const CHATGPT_OAUTH_PARAMS: OAuthProviderParams<'static> = OAuthProviderPara default_issuer: CHATGPT_OAUTH_ISSUER, default_client_id: CHATGPT_OAUTH_CLIENT_ID, default_scopes: CHATGPT_OAUTH_SCOPE, - originator: None, env: OAuthEnvOverrides { issuer_vars: &["CODEWHALE_CHATGPT_OAUTH_ISSUER"], client_id_vars: &["CODEWHALE_CHATGPT_OAUTH_CLIENT_ID"], @@ -461,7 +455,6 @@ pub const ORCAROUTER_OAUTH_PARAMS: OAuthProviderParams<'static> = OAuthProviderP device_poll_floor_secs: 30, authorize_extras: &[], account_choice_extras: &[], - originator: None, revoke_path: None, callback_path: "/callback", // OrcaRouter validates `callback_url` per request and accepts any @@ -1854,9 +1847,6 @@ pub fn build_authorize_url( .append_pair("code_challenge", &pkce.challenge) .append_pair("code_challenge_method", "S256") .append_pair("state", state); - if let Some(originator) = params.originator { - url.query_pairs_mut().append_pair("originator", originator); - } for (key, value) in params.authorize_extras { url.query_pairs_mut().append_pair(key, value); } @@ -3182,9 +3172,7 @@ fn apply_token_response( entry.oidc_issuer = Some(issuer.to_string()); entry.oidc_client_id = Some(client_id.to_string()); entry.auth_mode = Some("oidc".to_string()); - entry.originator = oauth_provider_params(provider) - .originator - .map(ToOwned::to_owned); + entry.originator = None; if let Some(id_token) = token.id_token.clone() { if provider != OAuthProvider::Chatgpt && let Some(account_id) = account_id_from_id_token(&id_token) @@ -4528,7 +4516,6 @@ impl PluginOAuthConfig { device_poll_floor_secs: 0, authorize_extras: &[], account_choice_extras: &[], - originator: None, revoke_path: None, callback_path: &self.callback_path, loopback_ports: &[], From 0cbdb9b83bd595fca8aa081cda082a41bc60e292 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 14:38:26 +0000 Subject: [PATCH 22/46] test(fleet): move the last fleet -> lib.rs test edge to the CLI tests Refs #6939 (RS-12). The test `default_max_turns_does_not_add_a_hidden_worker_cap` parsed the worker argv with `crate::Cli` from inside fleet/executor.rs. That was the remaining `test fleet|lib.rs 3` edge. - The test moves unchanged in substance into lib/terminal_mode_test_cases_03.rs. That is where 0f48161ad5 put its sibling. - The task is built from JSON, and the launch spec comes from the public `fleet_task_to_worker_spec_with_profiles`. No visibility changes. - The runtime-boundary baseline drops from test 35 to test 32 (`module_graph.py --update`, then --check PASS 11/32/1/1 over 117 closure modules). - DECONSTRUCTION_ROADMAP.md's gates and the 0.10.5 edge list follow the new baseline. - TUI_DECONSTRUCTION.md records RS-12 as landed. Evidence: python3 scripts/split/module_graph.py --check: [runtime-boundary] PASS: {prod 11, test 32, late 1, uilib 1, doc 0} (117 closure modules). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01BsXVvReYca2UxzimQtj7Mj --- crates/tui/src/fleet/executor.rs | 40 ----------- .../src/lib/terminal_mode_test_cases_03.rs | 69 +++++++++++++++++++ docs/design/DECONSTRUCTION_ROADMAP.md | 16 ++--- docs/design/TUI_DECONSTRUCTION.md | 2 +- scripts/runtime-boundary-baseline.json | 3 +- 5 files changed, 79 insertions(+), 51 deletions(-) diff --git a/crates/tui/src/fleet/executor.rs b/crates/tui/src/fleet/executor.rs index b9eed4d9d1..f521446376 100644 --- a/crates/tui/src/fleet/executor.rs +++ b/crates/tui/src/fleet/executor.rs @@ -1698,46 +1698,6 @@ mod tests { assert!(!cmd.args.join(" ").contains("--max-tool-calls")); } - #[test] - fn default_max_turns_does_not_add_a_hidden_worker_cap() { - use clap::Parser; - - let exec = FleetExecConfig::default(); - let workspace = TempDir::new().unwrap(); - for requested_steps in [None, Some(0), Some(13)] { - let mut task = task("x"); - task.budget = requested_steps.map(|max_steps| FleetTaskBudget { - max_steps: Some(max_steps), - ..FleetTaskBudget::default() - }); - let spec = launch_spec(&task, workspace.path()); - let cmd = build_worker_exec_command_with_launch_spec( - "codewhale", - &task, - &spec, - &exec, - None, - &[], - ) - .expect("production worker command"); - let cli = crate::Cli::try_parse_from( - std::iter::once("codewhale").chain(cmd.args.iter().map(String::as_str)), - ) - .expect("production worker args must parse"); - let Some(crate::Commands::Exec(args)) = cli.command else { - panic!("expected worker exec command"); - }; - let expected = requested_steps.filter(|steps| *steps > 0); - assert_eq!(args.max_turns, expected); - assert_eq!(args.max_tool_calls, None); - // Follow omission beyond argv through the real CLI resolver. This - // previously installed 200 despite correct unbounded launch args. - let turn = crate::core::turn::TurnContext::new(crate::exec_max_steps(args.max_turns)); - assert_eq!(turn.step_limit(), expected); - assert_eq!(turn.stop_diagnostics.effective_max_steps, expected); - } - } - #[test] fn stream_line_maps_tool_use_to_running_tool() { let line = r#"{"type":"tool_use","name":"read_file","id":"call-7","input":{}}"#; diff --git a/crates/tui/src/lib/terminal_mode_test_cases_03.rs b/crates/tui/src/lib/terminal_mode_test_cases_03.rs index e984ba602c..61685c0c0c 100644 --- a/crates/tui/src/lib/terminal_mode_test_cases_03.rs +++ b/crates/tui/src/lib/terminal_mode_test_cases_03.rs @@ -1363,3 +1363,72 @@ } } } + + #[test] + fn default_max_turns_does_not_add_a_hidden_worker_cap() { + use crate::fleet::executor::build_worker_exec_command_with_launch_spec; + use crate::fleet::worker_runtime::fleet_task_to_worker_spec_with_profiles; + use codewhale_config::FleetExecConfig; + use codewhale_protocol::fleet::{FleetHostSpec, FleetTaskSpec, FleetWorkerSpec}; + + let exec = FleetExecConfig::default(); + let workspace = tempfile::TempDir::new().unwrap(); + let worker = FleetWorkerSpec { + id: "worker-1".to_string(), + name: "Worker 1".to_string(), + host: FleetHostSpec::Local, + trust_level: None, + labels: std::collections::BTreeMap::new(), + capabilities: Vec::new(), + max_concurrent_tasks: Some(1), + }; + for requested_steps in [None, Some(0), Some(13)] { + let mut task = serde_json::json!({ + "id": "t1", + "name": "Smoke", + "objective": "prove it runs", + "instructions": "x", + "worker": { "role": "reviewer", "tool_profile": "read-only" } + }); + if let Some(max_steps) = requested_steps { + task["budget"] = serde_json::json!({ "max_steps": max_steps }); + } + let task: FleetTaskSpec = serde_json::from_value(task).unwrap(); + let spec = fleet_task_to_worker_spec_with_profiles( + "worker-1", + "run-1", + &task, + &worker, + "auto", + workspace.path(), + workspace.path(), + &[], + None, + ) + .unwrap(); + let cmd = build_worker_exec_command_with_launch_spec( + "codewhale", + &task, + &spec, + &exec, + None, + &[], + ) + .expect("production worker command"); + let cli = Cli::try_parse_from( + std::iter::once("codewhale").chain(cmd.args.iter().map(String::as_str)), + ) + .expect("production worker args must parse"); + let Some(Commands::Exec(args)) = cli.command else { + panic!("expected worker exec command"); + }; + let expected = requested_steps.filter(|steps| *steps > 0); + assert_eq!(args.max_turns, expected); + assert_eq!(args.max_tool_calls, None); + // Follow omission beyond argv through the real CLI resolver. This + // previously installed 200 despite correct unbounded launch args. + let turn = crate::core::turn::TurnContext::new(exec_max_steps(args.max_turns)); + assert_eq!(turn.step_limit(), expected); + assert_eq!(turn.stop_diagnostics.effective_max_steps, expected); + } + } diff --git a/docs/design/DECONSTRUCTION_ROADMAP.md b/docs/design/DECONSTRUCTION_ROADMAP.md index a52f3bdfc5..b210db32c8 100644 --- a/docs/design/DECONSTRUCTION_ROADMAP.md +++ b/docs/design/DECONSTRUCTION_ROADMAP.md @@ -33,9 +33,9 @@ Slices: - Non-move rider: write the real per-crate tarball sizes (from the dry-run's check-crate-sizes.py output) into the release notes table. This is the baseline column for every later release. - Non-move rider: record the codewhale-ratatui 0.1.2 publication receipt in the release notes (out-of-band crate, not in crates.sh). -- **Entry gate:** Founder-owned release process per docs/RELEASE_RUNBOOK.md. `python3 scripts/split/module_graph.py --check` PASS at 11/35/1/1. codewhale-ratatui 0.1.2 is on crates.io (human gate). The dirty full-access and settings work is either in the candidate or excluded by its owner. +- **Entry gate:** Founder-owned release process per docs/RELEASE_RUNBOOK.md. `python3 scripts/split/module_graph.py --check` PASS at 11/32/1/1. codewhale-ratatui 0.1.2 is on crates.io (human gate). The dirty full-access and settings work is either in the candidate or excluded by its owner. - **Exit gate:** `./scripts/release/publish-crates.sh dry-run` passes for all 29 crates, including check-crate-sizes.py. validate-crate-publish-order.py passes. Hosted CI green on the exact SHA (macOS and Windows jobs). Source, local, CI, packaged-artifact and release-state evidence are reported separately. -- **Ratchet target:** unchanged: prod 11, test 35, late 1, uilib 1. +- **Ratchet target:** prod 11, test 32 (RS-12 cleared fleet|lib.rs 3), late 1, uilib 1. - **Size budget:** All 29 crates: no growth versus the recorded table. codewhale-tui about 9.47 MiB real (about 9.50 by proxy). Not a gate on structure. - **Risks:** Carrying a structural move into the candidate would collide with the dirty tree. Nothing to roll back structurally. - **Rollback:** Normal release rollback per the runbook. No structural change to revert. @@ -47,9 +47,9 @@ Slices: - Design-doc correction the sequencer dropped: install the real catalog process-wide for tests. A thread-local guard cannot reach the Runtime API test server thread. - Riders (no graph change): cut `runtime_api|commands` test edges (2) in the same files; move `late` (test_cases_04.rs:1197) and `uilib` (roster_routes.rs:798) test edges only if the owning test files are not dirty. -- **Entry gate:** module_graph.py --check PASS at 11/35/1/1. The files this slice edits are clean in `git status`: runtime_api.rs (13,649 lines, not dirty now), tools/tui_help.rs, commands/catalog.rs, extension_host/command.rs. The sequencer's 'clean in all of commands/ and runtime_api/' would block this slice today, since runtime_api/tests.rs, runtime_api/thread_history.rs and commands/groups/config/config.rs are dirty. Require only the exact files edited, and coordinate on runtime_api/tests.rs if the test guard install touches it. One named build owner. +- **Entry gate:** module_graph.py --check PASS at 11/32/1/1. The files this slice edits are clean in `git status`: runtime_api.rs (13,649 lines, not dirty now), tools/tui_help.rs, commands/catalog.rs, extension_host/command.rs. The sequencer's 'clean in all of commands/ and runtime_api/' would block this slice today, since runtime_api/tests.rs, runtime_api/thread_history.rs and commands/groups/config/config.rs are dirty. Require only the exact files edited, and coordinate on runtime_api/tests.rs if the test guard install touches it. One named build owner. - **Exit gate:** Slice gate: ratchet --check at prod 0 with the baseline JSON updated in the same commit. cargo fmt --check. clippy with CI's flags. `scripts/dev-test.sh` runtime_api and commands filters with N > 0 passed, including a slash-command-list API test and the /help test. Journey on the built binary: slash palette lists commands, /help, GET of the runtime API command list. Release gate at cut: publish-crates.sh dry-run and green hosted CI on the SHA. -- **Ratchet target:** prod 11 -> 0, test 35 -> 33 (runtime_api|commands 2 cleared if achieved), late 1, uilib 1. Design doc predicts the cycle 100 -> 83 modules; re-run partition_report.py and quote the real number. +- **Ratchet target:** prod 11 -> 0, test 32 -> 30 (runtime_api|commands 2 cleared if achieved), late 1, uilib 1. Design doc predicts the cycle 100 -> 83 modules; re-run partition_report.py and quote the real number. - **Size budget:** No crate grows. A new leaf module in tui adds a few KiB. - **Risks:** Highest churn path: runtime_api is 286 commits in 30 days. The port could be built as a second registry (guard by deleting callers). The catalog process-wide install can leak between tests. - **Rollback:** Annotated local recovery tag plus a CW/backups note. `git revert` of the slice commit on main. No reset, no force-push. If 0.10.3 is already cut, fix forward before any other move. @@ -71,10 +71,10 @@ Slices: ### 0.10.5: Test-edge rehoming and the A/B decision release. Non-graph cleanup, no crate moves. Slices: -- Rehome the remaining test edges so no #[cfg(test)] code in the runtime half names crate::tui, commands or lib.rs: tools|tui 12, client|tui 3, session_manager|tui 3, fleet|lib.rs 3, config|tui 2, core|tui 2, prompts|tui 1, plus late 1 and uilib 1. Mechanism: move each such test to a tui-side integration module or replace the tui fixture with a runtime-side one. Split across several landings on main, owned per directory. +- Rehome the remaining test edges so no #[cfg(test)] code in the runtime half names crate::tui, commands or lib.rs: tools|tui 12, client|tui 3, session_manager|tui 3, config|tui 2, core|tui 2, prompts|tui 1, plus late 1 and uilib 1. Mechanism: move each such test to a tui-side integration module or replace the tui fixture with a runtime-side one. Split across several landings on main, owned per directory. - Record the founder's A/B decision (open_decisions[0]) before any Track B release starts. -- **Entry gate:** 0.10.4 shipped. Per-landing: the test files edited are not dirty. Mapping of the 35 edges to files regenerated with `module_graph.py --report` at the start of the release and posted in the Linear issue. +- **Entry gate:** 0.10.4 shipped. Per-landing: the test files edited are not dirty. Mapping of the 32 edges to files regenerated with `module_graph.py --report` at the start of the release and posted in the Linear issue. - **Exit gate:** module_graph.py --check at prod 0, test 0, late 0, uilib 0, baseline JSON updated. Existing tests for each touched file run with identical pass counts (quote before and after). No production file changed except visibility needed by a moved fixture. The A/B decision is recorded in docs/design/TUI_DECONSTRUCTION.md. - **Ratchet target:** prod 0, test 33 -> 0, late 1 -> 0, uilib 1 -> 0. - **Size budget:** No change. Test files are excluded from the tarball. @@ -203,7 +203,7 @@ Slices: ## Measurement plan -Record at every release, on one machine, at a named source commit with a dirty-file inventory. Numbers from a tree that changed during the compile are void. (1) Tarball sizes: the real sizes from the release dry-run's check-crate-sizes.py output for all published crates, in a table with the previous release's value and the delta. Between releases use the proxy, run from crates/tui: `git ls-files . | grep -vE '^tests/|/tests\.rs$|/tests/|_tests\.rs$|/test_cases_[^/]*\.rs$|^extension-host/test/|^plugins/computer-use/tests/' | tar -czf /tmp/x.tgz -T -`. It read 9.50 MiB for HEAD against 9.47 real, so it is good to about 0.03 MiB. Anything smaller than 0.03 MiB is not measurable, so no per-slice size budgets below that. Mark each cell proxy or real. (2) `python3 scripts/split/module_graph.py --check` four counts plus the closure-module count, committed as the baseline in the same commit as the move. Today: prod 11, test 35, late 1, uilib 1, 117 modules. (3) Build timing via scripts/dev-cargo.sh after `df -h /Volumes/VIXinSSD`: cold `cargo test -p codewhale-tui --lib --locked --no-run --timings`; warm touch-one-file loops; a loop touching a file inside the moved code; peak RSS via /usr/bin/time -l; three runs, median and spread. Baseline from docs/BUILD_PERFORMANCE.md: cold 94 s for `cargo build -p codewhale-tui`, about 12.5 s incremental build and about 20 s lib-test. No speedup is claimed for any leaf move (each is under 1% of the closure). Only Option A can plausibly change build time, and it is not measured until run. (4) nextest wall time and real 'N passed' per affected crate (N > 0), quoting before and after for every relocated test. (5) Hosted CI job durations for macOS and Windows on the exact SHA. Windows-only cfg code (test-support tempdir, credentials ACLs, fd-lock paths) cannot be proven locally. (6) Churn: `git log --since=30.days` per moved path. Today runtime_api is 286, runtime_threads 169, tools/subagent 226, settings.rs 16, core/events.rs 27. Use it to schedule landings, not to justify moves. (7) Release cadence: days between tags (v0.10.0 to v0.10.1 was 15 days) recorded per release to keep the calendar honest. (8) One table row per release in docs/design/TUI_DECONSTRUCTION.md, each cell marked measured, proxy or not measured. +Record at every release, on one machine, at a named source commit with a dirty-file inventory. Numbers from a tree that changed during the compile are void. (1) Tarball sizes: the real sizes from the release dry-run's check-crate-sizes.py output for all published crates, in a table with the previous release's value and the delta. Between releases use the proxy, run from crates/tui: `git ls-files . | grep -vE '^tests/|/tests\.rs$|/tests/|_tests\.rs$|/test_cases_[^/]*\.rs$|^extension-host/test/|^plugins/computer-use/tests/' | tar -czf /tmp/x.tgz -T -`. It read 9.50 MiB for HEAD against 9.47 real, so it is good to about 0.03 MiB. Anything smaller than 0.03 MiB is not measurable, so no per-slice size budgets below that. Mark each cell proxy or real. (2) `python3 scripts/split/module_graph.py --check` four counts plus the closure-module count, committed as the baseline in the same commit as the move. Today: prod 11, test 32, late 1, uilib 1, 117 modules. (3) Build timing via scripts/dev-cargo.sh after `df -h /Volumes/VIXinSSD`: cold `cargo test -p codewhale-tui --lib --locked --no-run --timings`; warm touch-one-file loops; a loop touching a file inside the moved code; peak RSS via /usr/bin/time -l; three runs, median and spread. Baseline from docs/BUILD_PERFORMANCE.md: cold 94 s for `cargo build -p codewhale-tui`, about 12.5 s incremental build and about 20 s lib-test. No speedup is claimed for any leaf move (each is under 1% of the closure). Only Option A can plausibly change build time, and it is not measured until run. (4) nextest wall time and real 'N passed' per affected crate (N > 0), quoting before and after for every relocated test. (5) Hosted CI job durations for macOS and Windows on the exact SHA. Windows-only cfg code (test-support tempdir, credentials ACLs, fd-lock paths) cannot be proven locally. (6) Churn: `git log --since=30.days` per moved path. Today runtime_api is 286, runtime_threads 169, tools/subagent 226, settings.rs 16, core/events.rs 27. Use it to schedule landings, not to justify moves. (7) Release cadence: days between tags (v0.10.0 to v0.10.1 was 15 days) recorded per release to keep the calendar honest. (8) One table row per release in docs/design/TUI_DECONSTRUCTION.md, each cell marked measured, proxy or not measured. ## Coordination rules (shared checkout) @@ -243,7 +243,7 @@ Record at every release, on one machine, at a named source commit with a dirty-f - **0.10.2**: 0.10.2: ship candidate with no structural move; record per-crate tarball table and ratatui 0.1.2 receipt. No move in the candidate. Add real per-crate tarball sizes to the release notes (baseline for later releases) and the codewhale-ratatui 0.1.2 publication receipt (out of band, not in crates.sh). Gate: publish-crates.sh dry-run, validate-crate-publish-order.py, hosted CI green on the SHA. - **0.10.3**: 0.10.3 X2: command catalog port, finish RS-13 (#6940). runtime_api.rs:4815-4898 and tools/tui_help.rs go through a command_catalog leaf; delete the direct call sites; install the real catalog process-wide for tests. Ratchet prod 11 -> 0. Claim exact files only (runtime_api.rs, tools/tui_help.rs, commands/catalog.rs, extension_host/command.rs). Linear only, not mirrored. - **0.10.4**: 0.10.4 W0-S2: codewhale-test-support (publish=false), env half. Move test_env_lock.rs (300) and the env half of test_support.rs (657 non-test lines) to crates/test/test-support; version.workspace=true; add to root members; keep tui shim and App fixtures; remove shell_dispatcher cfg(test) mount and the tests/integration #[path] include. Critical path for settings and credentials tests. Gate: equal before/after pass counts, one process-global lock copy, crates.sh stays 29. -- **0.10.5**: 0.10.5 Test-edge rehoming: test 35 -> 0, late 1 -> 0, uilib 1 -> 0. Rehome tests naming crate::tui, commands or lib.rs (tools|tui 12, client|tui 3, session_manager|tui 3, fleet|lib.rs 3, config|tui 2, core|tui 2, prompts|tui 1), plus test_cases_04.rs:1197 (exec_agent) and roster_routes.rs:798 (crossterm). Test-only; equal pass counts before and after per directory. Prerequisite for any new runtime crate that must not depend on tui or crossterm. +- **0.10.5**: 0.10.5 Test-edge rehoming: test 32 -> 0, late 1 -> 0, uilib 1 -> 0. Rehome tests naming crate::tui, commands or lib.rs (tools|tui 12, client|tui 3, session_manager|tui 3, config|tui 2, core|tui 2, prompts|tui 1), plus test_cases_04.rs:1197 (exec_agent) and roster_routes.rs:798 (crossterm). Test-only; equal pass counts before and after per directory. Prerequisite for any new runtime crate that must not depend on tui or crossterm. - **0.10.6**: 0.10.6 X1 [Track B]: fslock into crates/runtime. Move try_lock_file_exclusive plus RuntimeProcessOwnerLock::try_lock_exclusive/is_contention (runtime_threads.rs:6592, 6855) to crates/runtime. Seam-introducing, not pure rename. Blocked until runtime_threads.rs and its tests are committed. Visibility review in the commit message; revert immediately on any lock regression. - **0.10.7**: 0.10.7 X7 [Track B]: tool rules into execpolicy. Reconcile the moved set (design table: tool_denied, tool_matches_any_rule, requires_raw_shell, policy_tool_aliases; AutoReviewPolicy lives in core/authority/auto_review) then move to crates/execpolicy with re-exports. Approval trust boundary: no matching or visibility change. Blocked by dirty core/authority.rs. - **0.10.8**: 0.10.8 X4 [Track B]: schema_sanitize/schema_canonicalize production half into codewhale-models. Move schema_sanitize.rs lines 1-469 and schema_canonicalize.rs to codewhale-models (owns Tool). Tests stay in tui. No new package name. Assert byte-identical sanitized output on existing fixtures (KV-cache prefix). diff --git a/docs/design/TUI_DECONSTRUCTION.md b/docs/design/TUI_DECONSTRUCTION.md index 5f68007344..35d51792b3 100644 --- a/docs/design/TUI_DECONSTRUCTION.md +++ b/docs/design/TUI_DECONSTRUCTION.md @@ -395,7 +395,7 @@ Running them in parallel: tests, `python3 scripts/split/module_graph.py --check` with the counts it should lower, and `python3 scripts/split/partition_report.py` before and after. -Mapping of the open issues: RS-8 (#6935), RS-9 (#6936), RS-12 (#6939, partial) and +Mapping of the open issues: RS-8 (#6935), RS-9 (#6936), RS-12 (#6939) and RS-13 (#6940, first sub-slice) landed; X2 completes RS-13; W0-S1 and W0-S2 replace RS-10 (#6937) and the test-support half of RS-11 (#6938); RS-14 (#6941) is replaced by this table pending the founder's decision. diff --git a/scripts/runtime-boundary-baseline.json b/scripts/runtime-boundary-baseline.json index 9abce5e6e2..30d8c6176e 100644 --- a/scripts/runtime-boundary-baseline.json +++ b/scripts/runtime-boundary-baseline.json @@ -2,7 +2,7 @@ "_comment": "Runtime -> UI boundary ratchet (docs/design/TUI_DECONSTRUCTION.md, runtime split). Counts may only go down. Regenerate with python3 scripts/split/module_graph.py --update after removing references; never raise a count by hand.", "totals": { "prod": 11, - "test": 35, + "test": 32, "late": 1, "uilib": 1, "doc": 0 @@ -16,7 +16,6 @@ "client|tui": 3, "config|tui": 2, "core|tui": 2, - "fleet|lib.rs": 3, "prompts|tui": 1, "runtime_api|commands": 2, "session_manager|tui": 3, From e2ee2d259947946715f9f399f45ab688f94f1092 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 14:38:31 +0000 Subject: [PATCH 23/46] docs(changelog): 0.10.2 notes for the shell inspector, Alt+V and wait progress Refs #6931, #6944. - The inspector bullet says it follows output, drops Stop when the command ends, and that Alt+V keeps the clicked card. - The silent-shell bullet loses the clause that advertised the removed 'running shell first' Alt+V rule. - A new bullet covers wait progress and the kill note. - crates/tui/CHANGELOG.md is regenerated by scripts/sync-changelog.sh. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01BsXVvReYca2UxzimQtj7Mj --- CHANGELOG.md | 13 +++++++++---- crates/tui/CHANGELOG.md | 13 +++++++++---- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d23aa0c166..851a3d4f62 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,8 +27,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 from plugins stay on demand. Finishing an OAuth login reconnects just that server, and `/mcp reload` redials every server that is not `lazy` and reports how many are not connected out of the configured total. -- Running shell commands open an inspector with their output and status, and - `Alt+V` opens the newest running shell (#6931). +- Running shell commands open an inspector with their output and status. It + follows new output until you scroll back, and drops Stop once the command + ends. `Alt+V` opens the card you clicked, even after newer cards arrive or a + tool run collapses around it (#6931). - `/resume`, `/load` and other session changes that cannot run yet now list what is still busy and how to stop it. - Sessions that ended without a clean exit show `interrupted` with their age @@ -234,12 +236,15 @@ launcher guidance. - Goal usage counts the turn that completes, yields or blocks the goal, so `tokens_used` and `time_used_seconds` no longer stay at 0. `update_goal` accepts a progress estimate with `yield`. +- Waiting on a running shell job reports how long ago it last printed and + whether it is still compiling or already running tests or the program. + Stopping a job from `/jobs`, and the completion the model then receives, + say whether build artifacts already on disk survive the kill (#6944). - A shell command that has printed nothing for a minute still reads as running ("no output for 1m 05s"), keeps its Stop action and is reachable through the Runtime API; only a job whose process is gone after a restart is stale. `/jobs poll` and `/jobs stdin` no longer consume the output the - model has not read yet, and Alt+V prefers a running shell only when it is - on screen (#6931). + model has not read yet (#6931). - `/constitution`: `ratify` checks the digest you reviewed in `suggestions`, so a file that changed after your review is refused as documented; `preview` shows the block exactly as the model receives it; messages no longer claim a diff --git a/crates/tui/CHANGELOG.md b/crates/tui/CHANGELOG.md index 694c557e91..411e0aeffc 100644 --- a/crates/tui/CHANGELOG.md +++ b/crates/tui/CHANGELOG.md @@ -27,8 +27,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 from plugins stay on demand. Finishing an OAuth login reconnects just that server, and `/mcp reload` redials every server that is not `lazy` and reports how many are not connected out of the configured total. -- Running shell commands open an inspector with their output and status, and - `Alt+V` opens the newest running shell (#6931). +- Running shell commands open an inspector with their output and status. It + follows new output until you scroll back, and drops Stop once the command + ends. `Alt+V` opens the card you clicked, even after newer cards arrive or a + tool run collapses around it (#6931). - `/resume`, `/load` and other session changes that cannot run yet now list what is still busy and how to stop it. - Sessions that ended without a clean exit show `interrupted` with their age @@ -234,12 +236,15 @@ launcher guidance. - Goal usage counts the turn that completes, yields or blocks the goal, so `tokens_used` and `time_used_seconds` no longer stay at 0. `update_goal` accepts a progress estimate with `yield`. +- Waiting on a running shell job reports how long ago it last printed and + whether it is still compiling or already running tests or the program. + Stopping a job from `/jobs`, and the completion the model then receives, + say whether build artifacts already on disk survive the kill (#6944). - A shell command that has printed nothing for a minute still reads as running ("no output for 1m 05s"), keeps its Stop action and is reachable through the Runtime API; only a job whose process is gone after a restart is stale. `/jobs poll` and `/jobs stdin` no longer consume the output the - model has not read yet, and Alt+V prefers a running shell only when it is - on screen (#6931). + model has not read yet (#6931). - `/constitution`: `ratify` checks the digest you reviewed in `suggestions`, so a file that changed after your review is refused as documented; `preview` shows the block exactly as the model receives it; messages no longer claim a From 8fa735e53a6b835def00a4f7b20dbb183256525a Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 14:48:03 +0000 Subject: [PATCH 24/46] fix(tui): /jobs wait stops freezing the UI; dock rows open the live inspector MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refs #6931 (acceptance 4 and 5, "inspectable and stoppable"). Found by the 0.10.2 UX audit's live-work lens; each finding was checked against the code before this change. - `/jobs wait ` called `wait_job_for_session(…, 5_000)` on the UI event loop. It held the ShellManager std Mutex and slept in 100 ms steps, so the screen and the model's shell tools stalled for up to 5 s, against the #6149 blocking-call rule. `poll` and `wait` now parse to the same action as `show`: the live-following pager from f7264f7d72. The `ShellJobAction::Poll` variant, its handler arm and `wait_job_for_session`, which had no other caller, are deleted. - Work-dock shell rows, live and finished, ran `shell_inspector_body` in every projection. That is several passes per frame, on every dock tab: a `try_lock`, then `inspect_job_for_session`, which polls the child and copies the full retained stdout and stderr (up to 16 MiB each). The pager it opened was a frozen snapshot that said "Open this row to watch live output". Shell rows now run `/jobs show `, and `shell_inspector_body` is deleted, so projection never touches ShellManager. - The right-click menu keeps "Stop work" for a running shell (`/jobs cancel ` behind the same in-menu confirm). The pager has its confirmed `s` Stop. - Durable task rows ran `/jobs show task_…` against the shell manager, which printed "Command lookup failed: Job task_… not found". They now run `/task show `, and RLM pseudo-rows (`rlm-N`) have no action. - Tests updated where this made them wrong: the live shell row's primary action, and the shell-prefixed task rows' `/task show`. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01BsXVvReYca2UxzimQtj7Mj --- .../tui/src/commands/groups/utility/jobs.rs | 11 +-- crates/tui/src/tools/shell.rs | 13 --- crates/tui/src/tui/app/types.rs | 4 - crates/tui/src/tui/mouse_ui.rs | 13 +++ crates/tui/src/tui/ui/handlers.rs | 11 --- crates/tui/src/tui/work_surface/model.rs | 86 ++++++------------- 6 files changed, 40 insertions(+), 98 deletions(-) diff --git a/crates/tui/src/commands/groups/utility/jobs.rs b/crates/tui/src/commands/groups/utility/jobs.rs index 50ed97c07e..c2d5c21651 100644 --- a/crates/tui/src/commands/groups/utility/jobs.rs +++ b/crates/tui/src/commands/groups/utility/jobs.rs @@ -42,18 +42,11 @@ fn jobs(args: Option<&str>) -> CommandResult { match action.as_str() { "list" => CommandResult::action(AppAction::ShellJob(ShellJobAction::List)), - "show" | "inspect" => match id { + "show" | "inspect" | "poll" | "wait" => match id { Some(id) => CommandResult::action(AppAction::ShellJob(ShellJobAction::Show { id: id.to_string(), })), - None => CommandResult::error("Usage: /jobs show "), - }, - "poll" | "wait" => match id { - Some(id) => CommandResult::action(AppAction::ShellJob(ShellJobAction::Poll { - id: id.to_string(), - wait: action == "wait", - })), - None => CommandResult::error("Usage: /jobs poll "), + None => CommandResult::error(format!("Usage: /jobs {action} ")), }, "stdin" | "send" => match id { Some(id) if !rest.is_empty() => { diff --git a/crates/tui/src/tools/shell.rs b/crates/tui/src/tools/shell.rs index 2fadaea283..aef47ada03 100644 --- a/crates/tui/src/tools/shell.rs +++ b/crates/tui/src/tools/shell.rs @@ -3487,19 +3487,6 @@ impl ShellManager { self.inspect_job(task_id) } - pub fn wait_job_for_session( - &mut self, - active_session_id: &str, - task_id: &str, - timeout_ms: u64, - ) -> Result { - self.require_session_owner(task_id, active_session_id)?; - if let Some(shell) = self.processes.get_mut(task_id) { - wait_while_running(shell, true, timeout_ms); - } - self.inspect_job(task_id) - } - /// List all live and known-stale background shell jobs for the TUI. pub fn list_jobs(&mut self) -> Vec { for shell in self.processes.values_mut() { diff --git a/crates/tui/src/tui/app/types.rs b/crates/tui/src/tui/app/types.rs index 802a95b4b3..ba689d5d69 100644 --- a/crates/tui/src/tui/app/types.rs +++ b/crates/tui/src/tui/app/types.rs @@ -856,10 +856,6 @@ pub enum ShellJobAction { Show { id: String, }, - Poll { - id: String, - wait: bool, - }, SendStdin { id: String, input: String, diff --git a/crates/tui/src/tui/mouse_ui.rs b/crates/tui/src/tui/mouse_ui.rs index 8f0594274f..04bbb13e78 100644 --- a/crates/tui/src/tui/mouse_ui.rs +++ b/crates/tui/src/tui/mouse_ui.rs @@ -1608,6 +1608,19 @@ fn push_work_row_entries(app: &App, mouse: MouseEvent, entries: &mut Vec { + if let Some(id) = command.strip_prefix("/jobs show ") + && app.task_panel.iter().any(|entry| { + entry.id == id && crate::tui::background_indicator::is_live_shell_entry(entry) + }) + { + stop = Some( + row( + app.tr(MessageId::CtxMenuStopWork).into_owned(), + SidebarRowAction::Command(format!("/jobs cancel {id}")), + ) + .confirm(confirm()), + ); + } let label = app .tr(MessageId::CtxMenuRunCommand) .replace("{command}", &command); diff --git a/crates/tui/src/tui/ui/handlers.rs b/crates/tui/src/tui/ui/handlers.rs index 0e5e3fdcbc..cc8a1354ae 100644 --- a/crates/tui/src/tui/ui/handlers.rs +++ b/crates/tui/src/tui/ui/handlers.rs @@ -1244,17 +1244,6 @@ pub(crate) fn handle_shell_job_action(app: &mut App, action: crate::tui::app::Sh Err(err) => add_shell_job_message(app, format!("Command lookup failed: {err}")), } } - crate::tui::app::ShellJobAction::Poll { id, wait } => { - let detail = if wait { - manager.wait_job_for_session(&active_session_id, &id, 5_000) - } else { - manager.inspect_job_for_session(&active_session_id, &id) - }; - match detail { - Ok(detail) => open_shell_job_pager(app, &detail), - Err(err) => add_shell_job_message(app, format!("Command poll failed: {err}")), - } - } crate::tui::app::ShellJobAction::SendStdin { id, input, close } => { match manager.write_stdin_for_session(&active_session_id, &id, &input, close) { Ok(()) => match manager.inspect_job_for_session(&active_session_id, &id) { diff --git a/crates/tui/src/tui/work_surface/model.rs b/crates/tui/src/tui/work_surface/model.rs index ef276f40a5..593c3a3753 100644 --- a/crates/tui/src/tui/work_surface/model.rs +++ b/crates/tui/src/tui/work_surface/model.rs @@ -981,16 +981,25 @@ fn durable_task_rows(app: &App) -> Vec { detail: format!("{status} · {}", entry.id), tone: bucket_tone(bucket), selectable: true, - primary_action: Some(SidebarRowAction::Command(format!( - "/jobs show {}", - entry.id - ))), + primary_action: task_show_action(entry), agent: None, } }) .collect() } +fn task_show_action(entry: &TaskPanelEntry) -> Option { + (!entry.id.starts_with("rlm-")) + .then(|| SidebarRowAction::Command(format!("/task show {}", entry.id))) +} + +fn shell_show_action(entry: &TaskPanelEntry) -> Option { + Some(SidebarRowAction::Command(format!( + "/jobs show {}", + entry.id + ))) +} + /// Finished shells and durable tasks, muted, each saying how it ended: /// `exit 0 · 12s`, `failed · exit 2`, `killed`, `timed out`. The shell /// leads with its command; its id is only in the detail and inspector. @@ -1020,11 +1029,7 @@ fn finished_background_rows(app: &App) -> Vec { detail: format!("{outcome} · {}", entry.id), tone, selectable: true, - primary_action: Some(SidebarRowAction::InspectWork { - title: format!("Shell {}", entry.id), - body: shell_inspector_body(app, entry, &command, &outcome), - stop_action: None, - }), + primary_action: shell_show_action(entry), agent: Some(AgentRowFacts { role_label: "shell".to_string(), status: outcome, @@ -1059,10 +1064,7 @@ fn finished_background_rows(app: &App) -> Vec { ), tone, selectable: true, - primary_action: Some(SidebarRowAction::Command(format!( - "/jobs show {}", - entry.id - ))), + primary_action: task_show_action(entry), agent: None, } } @@ -2635,14 +2637,7 @@ fn shell_work_rows(app: &App) -> Vec { detail: format!("{status} · {}", entry.id), tone: WorkTone::Live, selectable: true, - primary_action: Some(SidebarRowAction::InspectWork { - title: format!("Shell {}", entry.id), - body: shell_inspector_body(app, entry, command, status), - stop_action: Some(Box::new(SidebarRowAction::Command(format!( - "/jobs cancel {}", - entry.id - )))), - }), + primary_action: shell_show_action(entry), agent: Some(AgentRowFacts { role_label: "shell".to_string(), status: status.to_string(), @@ -2697,27 +2692,6 @@ fn shells_section_heading(shells: &[WorkRow]) -> WorkRow { } } -fn shell_inspector_body(app: &App, entry: &TaskPanelEntry, command: &str, status: &str) -> String { - let session = app.current_session_id.as_deref().unwrap_or(""); - if let Some(manager) = app.runtime_services.shell_manager.as_ref() - && let Ok(mut manager) = manager.try_lock() - && let Ok(detail) = manager.inspect_job_for_session(session, &entry.id) - { - let live = detail.snapshot.status == crate::tools::shell::ShellStatus::Running - && !detail.snapshot.stale; - let body = crate::tui::shell_job_routing::format_shell_job_detail(&detail); - return if live { - format!("Cancel: /jobs cancel {}\n\n{body}", entry.id) - } else { - body - }; - } - format!( - "Job: {}\nStatus: {status}\nCommand: {command}\n\nCancel: /jobs cancel {}\n\nOpen this row to watch live output.", - entry.id, entry.id - ) -} - fn graph_node_row(snapshot: &WorkGraphSnapshot, node: &WorkNode) -> WorkRow { let (mark, tone) = match node.state { NodeState::Ready => (crate::tui::glyphs::READY, WorkTone::Muted), @@ -4375,24 +4349,14 @@ mod tests { // #6565: the command leads; the id stays in the detail. assert_eq!(shell.label, "cd /workspace/example-project"); assert!(shell.detail.contains("shell_a1b2c3d4"), "{}", shell.detail); - let Some(SidebarRowAction::InspectWork { - title, - body, - stop_action, - }) = shell.primary_action.as_ref() - else { - panic!( - "shell row must open live output, got {:?}", - shell.primary_action - ); - }; - assert!(title.contains("shell_a1b2c3d4"), "{title}"); - assert!(body.contains("Job: shell_a1b2c3d4"), "{body}"); - assert!(body.contains("/jobs cancel shell_a1b2c3d4"), "{body}"); - assert!(matches!( - stop_action.as_deref(), - Some(SidebarRowAction::Command(command)) if command == "/jobs cancel shell_a1b2c3d4" - )); + assert!( + matches!( + shell.primary_action.as_ref(), + Some(SidebarRowAction::Command(command)) if command == "/jobs show shell_a1b2c3d4" + ), + "shell row must open the live inspector, got {:?}", + shell.primary_action + ); let facts = shell .agent .as_ref() @@ -4625,7 +4589,7 @@ mod tests { .find(|row| row.id.0 == format!("task:{id}")) .expect("task row"); assert!( - matches!(&row.primary_action, Some(SidebarRowAction::Command(command)) if command == &format!("/jobs show {id}")) + matches!(&row.primary_action, Some(SidebarRowAction::Command(command)) if command == &format!("/task show {id}")) ); } } From 28ea342400425b30ffd86aca3498027a0c3117d3 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 14:48:03 +0000 Subject: [PATCH 25/46] fix(tui): /setup u/d only act on the agreement step; retired hints skip settings I/O Found by the 0.10.2 UX audit (first-run and chrome lenses); each was checked against the code before this change. - `/setup` mapped `u` (bundled) and `d` (defer) with no step guard. Pressing either on the Provider, Permissions, Remote or Ready step closed the wizard. `prompts.rs` treats Bundled and Deferred as "user constitution disabled", so a ratified working agreement silently stopped applying. Both arms now carry the `selected_step() == SetupStep::Constitution` guard their g/a/f/k neighbours already have. - `note_footer_hint_used` ran a locked `settings.toml` transaction (process mutex, fd lock, read and parse) on the UI thread for every Tab, Shift+Tab, Ctrl+], Esc interrupt and agent arrow, forever. It now returns early once the in-memory count is retired. That count is seeded from settings at startup, so a hint retired in another instance still counts. Its doc comment claimed it never blocks the input path; it now says what really happens. - CHANGELOG: the #6931 bullet adds `/jobs wait`, dock shell rows and task rows; a new bullet covers the `/setup` keys. crates/tui/CHANGELOG.md is regenerated. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01BsXVvReYca2UxzimQtj7Mj --- CHANGELOG.md | 7 ++++++- crates/tui/CHANGELOG.md | 7 ++++++- crates/tui/src/tui/footer_hints.rs | 12 ++++++++---- crates/tui/src/tui/setup/mod.rs | 8 ++++++-- 4 files changed, 26 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 851a3d4f62..66dd9de802 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,7 +30,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Running shell commands open an inspector with their output and status. It follows new output until you scroll back, and drops Stop once the command ends. `Alt+V` opens the card you clicked, even after newer cards arrive or a - tool run collapses around it (#6931). + tool run collapses around it. `/jobs wait` and `/jobs poll` open the same + inspector instead of freezing the screen for up to 5 s, a shell row in the + work dock opens it too, and a task row opens `/task show` instead of failing + with "Job … not found" (#6931). +- In `/setup`, `u` and `d` act only on the working-agreement step. Pressed on + another step they no longer close the wizard and switch your agreement off. - `/resume`, `/load` and other session changes that cannot run yet now list what is still busy and how to stop it. - Sessions that ended without a clean exit show `interrupted` with their age diff --git a/crates/tui/CHANGELOG.md b/crates/tui/CHANGELOG.md index 411e0aeffc..381da94537 100644 --- a/crates/tui/CHANGELOG.md +++ b/crates/tui/CHANGELOG.md @@ -30,7 +30,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Running shell commands open an inspector with their output and status. It follows new output until you scroll back, and drops Stop once the command ends. `Alt+V` opens the card you clicked, even after newer cards arrive or a - tool run collapses around it (#6931). + tool run collapses around it. `/jobs wait` and `/jobs poll` open the same + inspector instead of freezing the screen for up to 5 s, a shell row in the + work dock opens it too, and a task row opens `/task show` instead of failing + with "Job … not found" (#6931). +- In `/setup`, `u` and `d` act only on the working-agreement step. Pressed on + another step they no longer close the wizard and switch your agreement off. - `/resume`, `/load` and other session changes that cannot run yet now list what is still busy and how to stop it. - Sessions that ended without a clean exit show `interrupted` with their age diff --git a/crates/tui/src/tui/footer_hints.rs b/crates/tui/src/tui/footer_hints.rs index 8f6df92888..181b1a703a 100644 --- a/crates/tui/src/tui/footer_hints.rs +++ b/crates/tui/src/tui/footer_hints.rs @@ -39,10 +39,11 @@ pub(crate) fn retired(uses: &BTreeMap, key: &str) -> bool { impl App { /// Record one use of the binding behind footer hint `key`. /// - /// Persistence is best-effort and never blocks the input path: once a - /// hint is retired on disk the transaction is abandoned without a write, - /// so each key costs at most [`USES_TO_RETIRE`] writes per install. A - /// read-only home still retires the hint for the running session. + /// Persistence is best-effort. Once the in-memory count (seeded from + /// settings at startup) retires the hint, a use touches no file at all; + /// before that each of at most [`USES_TO_RETIRE`] uses per key runs one + /// settings transaction on this thread. A read-only home still retires + /// the hint for the running session. pub(crate) fn note_footer_hint_used(&mut self, key: &str) { if cfg!(test) { // Tests never touch the settings file here, so only the @@ -52,6 +53,9 @@ impl App { *count = count.saturating_add(1); return; } + if retired(&self.footer_hint_uses, key) { + return; + } let owned = key.to_string(); let persisted = Settings::transact_opt(|settings| { let count = settings.footer_hint_uses.get(&owned).copied().unwrap_or(0); diff --git a/crates/tui/src/tui/setup/mod.rs b/crates/tui/src/tui/setup/mod.rs index c66fce53d7..dc71d77e6a 100644 --- a/crates/tui/src/tui/setup/mod.rs +++ b/crates/tui/src/tui/setup/mod.rs @@ -3222,8 +3222,12 @@ impl ModalView for SetupWizardView { KeyCode::Char('k') if self.selected_step() == SetupStep::Constitution => { self.commit_keep_existing_constitution() } - KeyCode::Char('u') => self.commit_constitution(SetupCommitKind::BundledConstitution), - KeyCode::Char('d') => self.commit_constitution(SetupCommitKind::DeferredConstitution), + KeyCode::Char('u') if self.selected_step() == SetupStep::Constitution => { + self.commit_constitution(SetupCommitKind::BundledConstitution) + } + KeyCode::Char('d') if self.selected_step() == SetupStep::Constitution => { + self.commit_constitution(SetupCommitKind::DeferredConstitution) + } KeyCode::Enter if self.selected_step() == SetupStep::Constitution => { self.commit_constitution(SetupCommitKind::BundledConstitution) } From e99934afa04b8cc1ac0c8a0c11983e844a7aef90 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 14:56:51 +0000 Subject: [PATCH 26/46] docs(tui): the shell inspector note matches the dock's live pager Refs #6931. 8fa735e53a pointed work-dock shell rows at the live-following Bash Job pager, so the known-limitations note's 'the work dock's inspector is still a snapshot' became false. The note now names the three ways the pager opens; the remaining limits (2.5 s latency, delegated-agent shells outside the agent detail, PTYs in the terminal dock) are unchanged. Comment-only; no code change. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01BsXVvReYca2UxzimQtj7Mj --- crates/tui/src/tui/shell_job_routing.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/crates/tui/src/tui/shell_job_routing.rs b/crates/tui/src/tui/shell_job_routing.rs index 86816d3959..babafcf194 100644 --- a/crates/tui/src/tui/shell_job_routing.rs +++ b/crates/tui/src/tui/shell_job_routing.rs @@ -1,12 +1,12 @@ //! Background shell job-center helpers for slash commands and pagers. //! -//! Known limitations: the Bash Job pager follows a running job by re-reading -//! it on the event loop's 2.5 s refresh (a bounded read that never consumes -//! the model's output cursor), so new output lands up to 2.5 s late; the work -//! dock's inspector is still a snapshot. Shells run by delegated agents show -//! in `/jobs` and the dock with their Owner, not in the agent's detail. Named -//! PTY sessions (`tools/terminal_session.rs`) are not listed here or in -//! `/jobs`; they live in the terminal dock (#6912). +//! Known limitations: the Bash Job pager (opened from `/jobs`, Alt+V or a +//! work-dock shell row) follows a running job by re-reading it on the event +//! loop's 2.5 s refresh (a bounded read that never consumes the model's +//! output cursor), so new output lands up to 2.5 s late. Shells run by +//! delegated agents show in `/jobs` and the dock with their Owner, not in the +//! agent's detail. Named PTY sessions (`tools/terminal_session.rs`) are not +//! listed here or in `/jobs`; they live in the terminal dock (#6912). use crate::tools::shell::{ShellJobDetail, ShellJobSnapshot, ShellResult, ShellStatus}; use crate::tui::app::App; From d40be2b1f03b0b66a6b9caf008fce5652667e51b Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 15:35:14 +0000 Subject: [PATCH 27/46] fix(l10n): three strings that misdescribe what happens MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From the 0.10.2 UX audit. Each was checked against the code first. - ko ChipPermissionNever said '묻지 않음' ("doesn't ask"), which reads as auto-run on the deny-all Never chip. It is now '허용 안 함', the word /status already uses for the same posture. - OnboardLanguageBlurb, in all 15 packs, told a new user to change language with `/settings set locale `, which answers "Usage: /settings [text]". The command is now `/config locale --save`, which persists the locale. The translated prose and placeholder words are unchanged. - en ElevationOptionAbortDesc said "Cancel this run", but the handler denies only the tool call and the turn continues. It now says "Skip only this tool call", as the other packs do. The test-only English mirror in approval/elevation.rs and its assertion follow. All 15 packs still parse, and `rg '/settings set locale' crates/localization` finds nothing. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01BsXVvReYca2UxzimQtj7Mj --- crates/localization/locales/ca.json | 2 +- crates/localization/locales/de.json | 2 +- crates/localization/locales/en.json | 4 ++-- crates/localization/locales/es-419.json | 2 +- crates/localization/locales/fr.json | 2 +- crates/localization/locales/hi.json | 2 +- crates/localization/locales/id.json | 2 +- crates/localization/locales/ja.json | 2 +- crates/localization/locales/ko.json | 4 ++-- crates/localization/locales/pt-BR.json | 2 +- crates/localization/locales/ru.json | 2 +- crates/localization/locales/uk.json | 2 +- crates/localization/locales/vi.json | 2 +- crates/localization/locales/zh-Hans.json | 2 +- crates/localization/locales/zh-Hant.json | 2 +- crates/tui/src/tui/approval/elevation.rs | 2 +- crates/tui/src/tui/approval/tests.rs | 2 +- 17 files changed, 19 insertions(+), 19 deletions(-) diff --git a/crates/localization/locales/ca.json b/crates/localization/locales/ca.json index ad0ec51865..7d0e94fea1 100644 --- a/crates/localization/locales/ca.json +++ b/crates/localization/locales/ca.json @@ -914,7 +914,7 @@ "HomePlanModeChecklistTip": " Presenta un pla i el progrés de tasques, després canvia a Act o Operate", "HomeGoalModeTip": "Seguiment d'objectius - Defineix /goal per perseguir objectius", "OnboardLanguageTitle": "Tria l'idioma", - "OnboardLanguageBlurb": "Tria l'idioma de la interfície. El pots canviar en qualsevol moment amb `/settings set locale `.", + "OnboardLanguageBlurb": "Tria l'idioma de la interfície. El pots canviar en qualsevol moment amb `/config locale --save`.", "OnboardProviderTitle": "Tria el teu proveïdor de models", "OnboardProviderBlurb": "Tria on s'executa el teu model. Els proveïdors allotjats necessiten una clau; els runtimes locals poden continuar sense.", "KimiCodePlanApiKeyHint": "Per als plans de membresia de Kimi Code, crea la clau vinculada al pla a {console}.", diff --git a/crates/localization/locales/de.json b/crates/localization/locales/de.json index ba1cccf748..d4bef1358d 100644 --- a/crates/localization/locales/de.json +++ b/crates/localization/locales/de.json @@ -914,7 +914,7 @@ "HomePlanModeChecklistTip": " Plan und To-do-Fortschritt vorlegen, dann zu Act oder Operate wechseln", "HomeGoalModeTip": "Zielverfolgung – /goal setzen, um Ziele zu verfolgen", "OnboardLanguageTitle": "Sprache wählen", - "OnboardLanguageBlurb": "Wähle die UI-Sprache. Du kannst sie jederzeit mit `/settings set locale ` ändern.", + "OnboardLanguageBlurb": "Wähle die UI-Sprache. Du kannst sie jederzeit mit `/config locale --save` ändern.", "OnboardProviderTitle": "Modellprovider wählen", "OnboardProviderBlurb": "Wähle, wo dein Modell läuft. Gehostete Provider brauchen einen Schlüssel; lokale Laufzeiten kommen ohne aus.", "KimiCodePlanApiKeyHint": "Für Kimi-Code-Mitgliedschaftspläne erstelle den plangebundenen Schlüssel unter {console}.", diff --git a/crates/localization/locales/en.json b/crates/localization/locales/en.json index 884d99a03c..4d73fceee4 100644 --- a/crates/localization/locales/en.json +++ b/crates/localization/locales/en.json @@ -931,7 +931,7 @@ "HomePlanModeChecklistTip": " Present a plan and To-do progress, then switch to Work or Operate", "HomeGoalModeTip": "Goal tracking — /goal pursues one objective", "OnboardLanguageTitle": "Choose your language", - "OnboardLanguageBlurb": "Pick the UI language — change it anytime with `/settings set locale `.", + "OnboardLanguageBlurb": "Pick the UI language — change it anytime with `/config locale --save`.", "OnboardProviderTitle": "Choose your model provider", "OnboardProviderBlurb": "Pick where your model runs. Hosted providers need a key; local ones don't.", "KimiCodePlanApiKeyHint": "For Kimi Code plans, create the linked key at {console}.", @@ -1269,7 +1269,7 @@ "ElevationOptionNetworkDesc": "Retry with outbound network (downloads and HTTP)", "ElevationOptionWriteDesc": "Retry with a wider writable scope", "ElevationOptionFullAccessDesc": "Retry without sandbox limits; grants unrestricted filesystem and network access", - "ElevationOptionAbortDesc": "Cancel this run", + "ElevationOptionAbortDesc": "Skip only this tool call", "ContextAutoCompacting": "Making room in the conversation…", "ContextManualCompacting": "Compacting context…", "ContextCompactionQueued": "Making room is queued — it runs after this turn.", diff --git a/crates/localization/locales/es-419.json b/crates/localization/locales/es-419.json index add65a18c0..f147275172 100644 --- a/crates/localization/locales/es-419.json +++ b/crates/localization/locales/es-419.json @@ -931,7 +931,7 @@ "HomePlanModeChecklistTip": " Presenta el plan y luego cambia a Act u Operate", "HomeGoalModeTip": "Seguimiento de Goal - Usa /goal para seguir un objetivo persistente", "OnboardLanguageTitle": "Elige el idioma", - "OnboardLanguageBlurb": "Elige el idioma de la interfaz. Puedes cambiarlo en cualquier momento con `/settings set locale `.", + "OnboardLanguageBlurb": "Elige el idioma de la interfaz. Puedes cambiarlo en cualquier momento con `/config locale --save`.", "OnboardProviderTitle": "Elige tu proveedor de modelos", "OnboardProviderBlurb": "Elige dónde se ejecutará tu modelo. Los proveedores alojados necesitan una clave; los runtimes locales pueden continuar sin ella.", "KimiCodePlanApiKeyHint": "Para los planes de membresía de Kimi Code, crea la clave vinculada al plan en {console}.", diff --git a/crates/localization/locales/fr.json b/crates/localization/locales/fr.json index c76ec25742..b5f4100bce 100644 --- a/crates/localization/locales/fr.json +++ b/crates/localization/locales/fr.json @@ -914,7 +914,7 @@ "HomePlanModeChecklistTip": " Présentez un plan et l'avancement des tâches, puis passez à Act ou Operate", "HomeGoalModeTip": "Suivi d'objectif — définissez /goal pour poursuivre des objectifs", "OnboardLanguageTitle": "Choisissez votre langue", - "OnboardLanguageBlurb": "Choisissez la langue de l'interface. Vous pouvez la changer à tout moment avec `/settings set locale `.", + "OnboardLanguageBlurb": "Choisissez la langue de l'interface. Vous pouvez la changer à tout moment avec `/config locale --save`.", "OnboardProviderTitle": "Choisissez votre fournisseur de modèles", "OnboardProviderBlurb": "Choisissez où votre modèle s'exécute. Les fournisseurs hébergés demandent une clé ; les runtimes locaux peuvent continuer sans.", "KimiCodePlanApiKeyHint": "Pour les plans d'adhésion Kimi Code, créez la clé liée au plan sur {console}.", diff --git a/crates/localization/locales/hi.json b/crates/localization/locales/hi.json index 0be7d9bb14..e0cb6d1324 100644 --- a/crates/localization/locales/hi.json +++ b/crates/localization/locales/hi.json @@ -914,7 +914,7 @@ "HomePlanModeChecklistTip": " योजना और To-do प्रगति प्रस्तुत करें, फिर Act या Operate पर जाएँ", "HomeGoalModeTip": "गोल ट्रैकिंग - लक्ष्य के लिए /goal सेट करें", "OnboardLanguageTitle": "अपनी भाषा चुनें", - "OnboardLanguageBlurb": "UI भाषा चुनें। इसे कभी भी `/settings set locale ` से बदल सकते हैं।", + "OnboardLanguageBlurb": "UI भाषा चुनें। इसे कभी भी `/config locale --save` से बदल सकते हैं।", "OnboardProviderTitle": "अपना मॉडल प्रोवाइडर चुनें", "OnboardProviderBlurb": "चुनें कि आपका मॉडल कहाँ चलेगा। होस्टेड प्रोवाइडर को कुंजी चाहिए; लोकल रनटाइम बिना कुंजी के चल सकते हैं।", "KimiCodePlanApiKeyHint": "Kimi Code मेंबरशिप प्लान के लिए, {console} पर प्लान-लिंक्ड कुंजी बनाएँ।", diff --git a/crates/localization/locales/id.json b/crates/localization/locales/id.json index a8f77bdc58..45aa88c69f 100644 --- a/crates/localization/locales/id.json +++ b/crates/localization/locales/id.json @@ -914,7 +914,7 @@ "HomePlanModeChecklistTip": " Sajikan rencana dan progres To-do, lalu beralih ke Act atau Operate", "HomeGoalModeTip": "Pelacakan tujuan - Atur /goal untuk mengejar sasaran", "OnboardLanguageTitle": "Pilih bahasa Anda", - "OnboardLanguageBlurb": "Pilih bahasa UI. Anda dapat mengubahnya kapan saja dengan `/settings set locale `.", + "OnboardLanguageBlurb": "Pilih bahasa UI. Anda dapat mengubahnya kapan saja dengan `/config locale --save`.", "OnboardProviderTitle": "Pilih penyedia model Anda", "OnboardProviderBlurb": "Pilih tempat model Anda berjalan. Penyedia hosted memerlukan kunci; runtime lokal dapat lanjut tanpa kunci.", "KimiCodePlanApiKeyHint": "Untuk paket keanggotaan Kimi Code, buat kunci tertaut paket di {console}.", diff --git a/crates/localization/locales/ja.json b/crates/localization/locales/ja.json index 9ba749bde2..d90c78f90e 100644 --- a/crates/localization/locales/ja.json +++ b/crates/localization/locales/ja.json @@ -931,7 +931,7 @@ "HomePlanModeChecklistTip": " 計画を提示してから Act または Operate へ", "HomeGoalModeTip": "Goal 追跡 - /goal <目標> で持続的な目標を追跡", "OnboardLanguageTitle": "言語を選択", - "OnboardLanguageBlurb": "UI 言語を選んでください。`/settings set locale ` でいつでも変更できます。", + "OnboardLanguageBlurb": "UI 言語を選んでください。`/config locale --save` でいつでも変更できます。", "OnboardProviderTitle": "モデルプロバイダーを選択", "OnboardProviderBlurb": "モデルの実行先を選びます。ホステッドプロバイダーにはキーが必要ですが、ローカルランタイムはキーなしで続行できます。", "KimiCodePlanApiKeyHint": "Kimi Code メンバーシッププランでは、{console} でプラン連携キーを作成します。", diff --git a/crates/localization/locales/ko.json b/crates/localization/locales/ko.json index 4494683292..a3335626bb 100644 --- a/crates/localization/locales/ko.json +++ b/crates/localization/locales/ko.json @@ -931,7 +931,7 @@ "HomePlanModeChecklistTip": " 계획과 할 일 진행 상황을 제시한 후 실행 또는 운영 모드로 전환", "HomeGoalModeTip": "목표 추적 - /goal <목표>로 목표를 설정하고 추진", "OnboardLanguageTitle": "언어를 선택하세요", - "OnboardLanguageBlurb": "UI 언어를 선택하세요. 언제든지 `/settings set locale <태그>`로 변경할 수 있습니다.", + "OnboardLanguageBlurb": "UI 언어를 선택하세요. 언제든지 `/config locale <태그> --save`로 변경할 수 있습니다.", "OnboardProviderTitle": "모델 프로바이더를 선택하세요", "OnboardProviderBlurb": "모델이 실행될 위치를 선택하세요. 호스팅 프로바이더는 키가 필요하지만 로컬 런타임은 키 없이 계속할 수 있습니다.", "KimiCodePlanApiKeyHint": "Kimi Code 멤버십 플랜은 {console}에서 플랜 연결 키를 만드세요.", @@ -1561,7 +1561,7 @@ "ChipPermissionAsk": "확인", "ChipPermissionAuto": "자동", "ChipPermissionFullAccess": "Full Access", - "ChipPermissionNever": "묻지 않음", + "ChipPermissionNever": "허용 안 함", "PermissionsChangedReceipt": "권한이 {label}(으)로 변경되었습니다", "FooterHintKeys": "단축키", "FooterHintOutput": "출력", diff --git a/crates/localization/locales/pt-BR.json b/crates/localization/locales/pt-BR.json index 155ca942c4..8c75c5fe29 100644 --- a/crates/localization/locales/pt-BR.json +++ b/crates/localization/locales/pt-BR.json @@ -931,7 +931,7 @@ "HomePlanModeChecklistTip": " Apresente o plano e depois mude para Act ou Operate", "HomeGoalModeTip": "Rastreamento de Goal - Use /goal para rastrear um objetivo persistente", "OnboardLanguageTitle": "Escolha o idioma", - "OnboardLanguageBlurb": "Escolha o idioma da interface. Você pode mudá-lo a qualquer momento com `/settings set locale `.", + "OnboardLanguageBlurb": "Escolha o idioma da interface. Você pode mudá-lo a qualquer momento com `/config locale --save`.", "OnboardProviderTitle": "Escolha seu provedor de modelos", "OnboardProviderBlurb": "Escolha onde o modelo será executado. Provedores hospedados precisam de chave; runtimes locais podem continuar sem uma.", "KimiCodePlanApiKeyHint": "Para planos de assinatura Kimi Code, crie a chave vinculada ao plano em {console}.", diff --git a/crates/localization/locales/ru.json b/crates/localization/locales/ru.json index 83e433bb81..7cdf1d32f0 100644 --- a/crates/localization/locales/ru.json +++ b/crates/localization/locales/ru.json @@ -914,7 +914,7 @@ "HomePlanModeChecklistTip": " Представьте план и прогресс To-do, затем переключитесь в Act или Operate", "HomeGoalModeTip": "Отслеживание цели - задайте /goal для достижения целей", "OnboardLanguageTitle": "Выберите язык", - "OnboardLanguageBlurb": "Выберите язык интерфейса. Его можно сменить в любой момент командой `/settings set locale `.", + "OnboardLanguageBlurb": "Выберите язык интерфейса. Его можно сменить в любой момент командой `/config locale --save`.", "OnboardProviderTitle": "Выберите провайдера модели", "OnboardProviderBlurb": "Выберите, где работает модель. Облачным провайдерам нужен ключ; локальные среды могут работать без него.", "KimiCodePlanApiKeyHint": "Для планов подписки Kimi Code создайте ключ, привязанный к плану, на {console}.", diff --git a/crates/localization/locales/uk.json b/crates/localization/locales/uk.json index ee940bd5c1..a3f210d0bc 100644 --- a/crates/localization/locales/uk.json +++ b/crates/localization/locales/uk.json @@ -914,7 +914,7 @@ "HomePlanModeChecklistTip": " Подайте план і прогрес To-do, потім перейдіть до Act або Operate", "HomeGoalModeTip": "Відстеження цілей - задайте /goal , щоб досягати цілей", "OnboardLanguageTitle": "Оберіть мову", - "OnboardLanguageBlurb": "Оберіть мову інтерфейсу. Її можна змінити будь-коли командою `/settings set locale `.", + "OnboardLanguageBlurb": "Оберіть мову інтерфейсу. Її можна змінити будь-коли командою `/config locale --save`.", "OnboardProviderTitle": "Оберіть провайдера моделі", "OnboardProviderBlurb": "Оберіть, де працюватиме модель. Хмарні провайдери потребують ключ; локальні середовища можуть працювати без нього.", "KimiCodePlanApiKeyHint": "Для планів підписки Kimi Code створіть прив'язаний до плану ключ на {console}.", diff --git a/crates/localization/locales/vi.json b/crates/localization/locales/vi.json index e690314efa..9f0c5eb6f3 100644 --- a/crates/localization/locales/vi.json +++ b/crates/localization/locales/vi.json @@ -931,7 +931,7 @@ "HomePlanModeChecklistTip": " Trình kế hoạch rồi chuyển sang Act hoặc Operate", "HomeGoalModeTip": "Theo dõi mục tiêu - Dùng /goal để đặt mục tiêu làm việc", "OnboardLanguageTitle": "Chọn ngôn ngữ của bạn", - "OnboardLanguageBlurb": "Chọn ngôn ngữ hiển thị. Bạn có thể thay đổi bất kỳ lúc nào bằng lệnh `/settings set locale `.", + "OnboardLanguageBlurb": "Chọn ngôn ngữ hiển thị. Bạn có thể thay đổi bất kỳ lúc nào bằng lệnh `/config locale --save`.", "OnboardProviderTitle": "Chọn nhà cung cấp mô hình", "OnboardProviderBlurb": "Chọn nơi mô hình chạy. Nhà cung cấp được lưu trữ cần khóa; runtime cục bộ có thể tiếp tục mà không cần khóa.", "KimiCodePlanApiKeyHint": "Với gói thành viên Kimi Code, hãy tạo khóa gắn với gói tại {console}.", diff --git a/crates/localization/locales/zh-Hans.json b/crates/localization/locales/zh-Hans.json index 2d12ff30cc..e4a3f8d3c3 100644 --- a/crates/localization/locales/zh-Hans.json +++ b/crates/localization/locales/zh-Hans.json @@ -931,7 +931,7 @@ "HomePlanModeChecklistTip": " 提交计划和待办进度,然后切换到 Work 或 Operate", "HomeGoalModeTip": "Goal 跟踪 - 设置 /goal <目标> 以跟踪持久目标", "OnboardLanguageTitle": "选择语言", - "OnboardLanguageBlurb": "选择界面语言。可随时使用 `/settings set locale ` 修改。", + "OnboardLanguageBlurb": "选择界面语言。可随时使用 `/config locale --save` 修改。", "OnboardProviderTitle": "选择模型提供商", "OnboardProviderBlurb": "选择模型运行的位置。托管提供商需要密钥;本地运行时无需密钥即可继续。", "KimiCodePlanApiKeyHint": "对于 Kimi Code 会员计划,请在 {console} 创建与计划关联的密钥。", diff --git a/crates/localization/locales/zh-Hant.json b/crates/localization/locales/zh-Hant.json index d8fc236ff1..640a931e5e 100644 --- a/crates/localization/locales/zh-Hant.json +++ b/crates/localization/locales/zh-Hant.json @@ -929,7 +929,7 @@ "HomePlanModeChecklistTip": " 提交計畫與待辦進度,然後切換到 Work 或 Operate", "HomeGoalModeTip": "Goal 跟蹤 - 設定 /goal <目標> 以跟蹤持久目標", "OnboardLanguageTitle": "選擇語言", - "OnboardLanguageBlurb": "選擇介面語言。可隨時使用 `/settings set locale ` 修改。", + "OnboardLanguageBlurb": "選擇介面語言。可隨時使用 `/config locale --save` 修改。", "OnboardProviderTitle": "選擇模型供應商", "OnboardProviderBlurb": "選擇模型執行的位置。託管供應商需要金鑰;本地執行階段無需金鑰即可繼續。", "KimiCodePlanApiKeyHint": "對於 Kimi Code 會員計畫,請在 {console} 建立與計畫關聯的金鑰。", diff --git a/crates/tui/src/tui/approval/elevation.rs b/crates/tui/src/tui/approval/elevation.rs index 79dc6a0bbc..c9b892aa28 100644 --- a/crates/tui/src/tui/approval/elevation.rs +++ b/crates/tui/src/tui/approval/elevation.rs @@ -50,7 +50,7 @@ impl ElevationOption { ElevationOption::FullAccess => { "Retry without sandbox limits; grants unrestricted filesystem and network access" } - ElevationOption::Abort => "Cancel this run", + ElevationOption::Abort => "Skip only this tool call", } } diff --git a/crates/tui/src/tui/approval/tests.rs b/crates/tui/src/tui/approval/tests.rs index 2c29d8a9fd..c0130c79f8 100644 --- a/crates/tui/src/tui/approval/tests.rs +++ b/crates/tui/src/tui/approval/tests.rs @@ -2763,7 +2763,7 @@ fn test_elevation_option_descriptions() { .description() .contains("filesystem and network access") ); - assert!(ElevationOption::Abort.description().contains("Cancel")); + assert!(ElevationOption::Abort.description().contains("Skip")); } #[test] From 4b7a62349d8599f9bb0f9e176441b17b056433fb Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 15:35:30 +0000 Subject: [PATCH 28/46] fix(tui): file edits read as patches; tool status words come from one table MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From the 0.10.2 UX audit (transcript-cards lens), checked against the code before the change. - The most common card, a file edit, used the generic "• tool" verb because PatchSummaryCell passes the title "File", which tool_family_for_title did not map. "File" now maps to the Patch family. The card's header carries the receipt's change summary, and the bold first body row that repeated it is gone. With inline diffs off, the header keeps the calm outcome label. The pager, inspect text and exact diff are unchanged. - One transcript said "run Done", "tool done", "run issue" (failed) and "✓ tool tool loaded - retry required". - en ToolReceiptDone is lowercase like every other pack. - tool_status_label says "failed" for Failed (Warning stays "issue") and "loaded, not run" for Hydrated. - A hydrated tool gets the neutral glyph, not the done check. - activity_detail's duplicate table is deleted and its four call sites use tool_status_label. - The exploring card's per-entry prefix also says "failed", so a failed read card no longer says "failed" in its header and "issue" on its row. - Tests whose copy changed are updated. The behavioural asserts are kept: hydrated is still not reported done, and failed is still not reported hydrated. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01BsXVvReYca2UxzimQtj7Mj --- crates/localization/locales/en.json | 2 +- crates/tui/src/tui/history.rs | 21 +++++++++++------ crates/tui/src/tui/history/file_mutation.rs | 26 +++++---------------- crates/tui/src/tui/history/tests.rs | 12 +++++----- crates/tui/src/tui/ui/activity_detail.rs | 23 ++++-------------- crates/tui/src/tui/ui/tests.rs | 18 +++++++------- crates/tui/src/tui/widgets/mod.rs | 4 ++-- crates/tui/src/tui/widgets/tool_card.rs | 2 +- 8 files changed, 44 insertions(+), 64 deletions(-) diff --git a/crates/localization/locales/en.json b/crates/localization/locales/en.json index 4d73fceee4..aa19f7dc4e 100644 --- a/crates/localization/locales/en.json +++ b/crates/localization/locales/en.json @@ -1333,7 +1333,7 @@ "ToolFamilyVerify": "verify", "ToolFamilyThink": "think", "ToolFamilyGeneric": "tool", - "ToolReceiptDone": "Done", + "ToolReceiptDone": "done", "ToolReceiptLinesSingular": "1 line", "ToolReceiptLinesPlural": "{count} lines", "CmdVoiceDescription": "Dictate into the composer", diff --git a/crates/tui/src/tui/history.rs b/crates/tui/src/tui/history.rs index 75165b91a5..52103f48c2 100644 --- a/crates/tui/src/tui/history.rs +++ b/crates/tui/src/tui/history.rs @@ -1376,7 +1376,7 @@ impl ExploringCell { ToolStatus::Success => "done", ToolStatus::Hydrated => "loaded", ToolStatus::Warning => "issue", - ToolStatus::Failed => "issue", + ToolStatus::Failed => "failed", }; lines.extend(render_compact_kv( prefix, @@ -1426,7 +1426,13 @@ impl PatchSummaryCell { let header_summary = self .receipt .as_ref() - .map(FileMutationReceipt::outcome_label) + .map(|receipt| { + if inline_diff_mode == crate::settings::InlineDiffMode::Off { + receipt.outcome_label() + } else { + receipt.semantic_summary() + } + }) .unwrap_or_else(|| self.path.clone()); lines.push(render_tool_header_with_summary( "File", @@ -2584,7 +2590,8 @@ fn status_symbol( ToolStatus::Running => { crate::tui::spinner::braille_spinner_frame(started_at, low_motion).to_string() } - ToolStatus::Success | ToolStatus::Hydrated => TOOL_DONE_SYMBOL.to_string(), + ToolStatus::Success => TOOL_DONE_SYMBOL.to_string(), + ToolStatus::Hydrated => crate::tui::glyphs::NEUTRAL.to_string(), ToolStatus::Warning => "!".to_string(), ToolStatus::Failed => TOOL_FAILED_SYMBOL.to_string(), } @@ -2979,7 +2986,7 @@ fn tool_detail_label_style() -> Style { /// its border colour follows it. In-flight takes the action accent, a settled /// success recedes into muted text so finished work stops competing for the /// eye, and only warning and failure keep a loud colour. `Hydrated` is a -/// stalled "tool loaded — retry required", not live work, so it takes the hint +/// stalled "loaded, not run", not live work, so it takes the hint /// colour instead of borrowing the running accent and reading as in-flight. /// /// The glyph and status word use the same lifecycle ink; the glyph shape @@ -3002,13 +3009,13 @@ fn tool_glyph_color( tool_rail_color(status) } -fn tool_status_label(status: ToolStatus) -> &'static str { +pub(crate) fn tool_status_label(status: ToolStatus) -> &'static str { match status { ToolStatus::Running => "running", ToolStatus::Success => "done", - ToolStatus::Hydrated => "tool loaded - retry required", + ToolStatus::Hydrated => "loaded, not run", ToolStatus::Warning => "issue", - ToolStatus::Failed => "issue", + ToolStatus::Failed => "failed", } } diff --git a/crates/tui/src/tui/history/file_mutation.rs b/crates/tui/src/tui/history/file_mutation.rs index 5671c01d86..7ee9a8081e 100644 --- a/crates/tui/src/tui/history/file_mutation.rs +++ b/crates/tui/src/tui/history/file_mutation.rs @@ -6,8 +6,8 @@ use std::path::{Component, Path}; -use ratatui::style::{Modifier, Style}; -use ratatui::text::{Line, Span}; +use ratatui::style::Style; +use ratatui::text::Line; use serde_json::Value; use crate::settings::InlineDiffMode; @@ -166,23 +166,9 @@ impl FileMutationReceipt { pub fn render_inline(&self, width: u16, mode: InlineDiffMode) -> Vec> { match mode { - InlineDiffMode::Off => vec![exact_evidence_hint()], - InlineDiffMode::Summary => vec![ - Line::from(Span::styled( - self.semantic_summary(), - Style::default() - .fg(palette::TEXT_PRIMARY) - .add_modifier(Modifier::BOLD), - )), - exact_evidence_hint(), - ], + InlineDiffMode::Off | InlineDiffMode::Summary => vec![exact_evidence_hint()], InlineDiffMode::Full => { - let mut lines = vec![Line::from(Span::styled( - self.semantic_summary(), - Style::default() - .fg(palette::TEXT_PRIMARY) - .add_modifier(Modifier::BOLD), - ))]; + let mut lines = Vec::new(); if !self.display_diff.trim().is_empty() { let rendered = diff_render::render_diff_body_bounded( &self.display_diff, @@ -412,8 +398,8 @@ mod tests { assert!(!full_text.contains("summary:"), "{full_text}"); let summary = plain(&receipt.render_inline(80, InlineDiffMode::Summary)); - assert!(summary.contains("Created src/lib.rs"), "{summary}"); - assert!(summary.contains("+30 -0"), "{summary}"); + assert!(!summary.contains("Created src/lib.rs"), "{summary}"); + assert!(!summary.contains("+30 -0"), "{summary}"); assert!(!summary.contains("line 0"), "{summary}"); assert!(summary.contains(":change"), "{summary}"); diff --git a/crates/tui/src/tui/history/tests.rs b/crates/tui/src/tui/history/tests.rs index ef4f7394c5..3e0d273fbc 100644 --- a/crates/tui/src/tui/history/tests.rs +++ b/crates/tui/src/tui/history/tests.rs @@ -999,7 +999,7 @@ fn the_copy_prefix_skips_every_decoration_and_keeps_the_payload() { )); let copied = slice_visible_columns(&body, header.copy_prefix_width, text_visible_width(&body)); assert!( - copied.contains("run Done"), + copied.contains("run done"), "receipt text was clipped away: {copied:?}" ); for glyph in decorations { @@ -1414,11 +1414,11 @@ fn a_card_verb_agrees_with_its_own_label_in_every_locale() { for (label, expected_en, expected_zh, forbidden_en) in [ ( "Searching for `TranscriptScroll`", - "find Done", + "find done", "find 完成", - "read Done", + "read done", ), - ("Reading src/foo.rs", "read Done", "read 完成", "find Done"), + ("Reading src/foo.rs", "read done", "read 完成", "find done"), ] { let cell = super::ExploringCell { entries: vec![super::ExploringEntry { @@ -1466,7 +1466,7 @@ fn receipts_count_only_what_they_actually_counted() { use crate::tui::widgets::tool_card::ToolFamily; use codewhale_localization::Locale; - for (locale, done, unit) in [(Locale::En, "Done", "line"), (Locale::ZhHans, "完成", "行")] { + for (locale, done, unit) in [(Locale::En, "done", "line"), (Locale::ZhHans, "完成", "行")] { let label = |family, status, output| { super::tool_receipt_label(family, status, Some(output), locale) }; @@ -1548,7 +1548,7 @@ fn shell_headers_stay_truthful_through_the_output_formatters() { cell.duration_ms = Some(42); for (locale, done, unit) in [ - (Locale::En, "Done", "lines"), + (Locale::En, "done", "lines"), (Locale::ZhHans, "完成", "行"), ] { let header = line_text(&cell.render_with_locale(80, true, RenderMode::Live, locale)[0]); diff --git a/crates/tui/src/tui/ui/activity_detail.rs b/crates/tui/src/tui/ui/activity_detail.rs index da0d069314..bb7285d6d9 100644 --- a/crates/tui/src/tui/ui/activity_detail.rs +++ b/crates/tui/src/tui/ui/activity_detail.rs @@ -10,7 +10,7 @@ use crate::snapshot::SnapshotRepo; use crate::tui::app::App; use crate::tui::footer_ui::one_line_summary; -use crate::tui::history::{HistoryCell, ToolCell, ToolStatus}; +use crate::tui::history::{HistoryCell, ToolCell, ToolStatus, tool_status_label}; use crate::tui::pager::{PagerPage, PagerView}; use crate::tui::ui_text::{ history_cell_to_clipboard_text, history_cell_to_text, truncate_line_to_width, @@ -253,16 +253,6 @@ fn tool_duration_for_activity(tool: &ToolCell) -> Option { } } -fn activity_status_label(status: ToolStatus) -> &'static str { - match status { - ToolStatus::Running => "running", - ToolStatus::Success => "done", - ToolStatus::Hydrated => "tool loaded - retry required", - ToolStatus::Warning => "issue", - ToolStatus::Failed => "failed", - } -} - /// Empty-state hint shown when the selection has no raw leaf detail to open. /// `v` / `Alt+V` only ever surface the raw detail of the ONE selected /// tool/card/leaf, so when there is nothing leaf-level to show we point the @@ -1190,7 +1180,7 @@ fn turn_timeline_lines(app: &App, start: usize, end: usize) -> Vec { let (kind, summary) = timeline_tool_summary(app, idx, tool); let duration = tool_duration_for_activity(tool) .map(codewhale_command_contract::elapsed::format_elapsed_ms); - let status = tool.status().map(activity_status_label); + let status = tool.status().map(tool_status_label); let actions = timeline_cell_actions(app, idx, cell); rows.push(timeline_row( kind, @@ -1444,7 +1434,7 @@ fn turn_files_changed(app: &App, start: usize, end: usize) -> Vec { lines.push(format!( "• {} — {}", truncate_line_to_width(&patch.path, 60), - activity_status_label(patch.status) + tool_status_label(patch.status) )); } _ => {} @@ -1513,7 +1503,7 @@ fn turn_verifier_lines(app: &App, start: usize, end: usize) -> Vec { lines.push(format!( "• {} — {}", truncate_line_to_width(&exec.command, 56), - activity_status_label(exec.status) + tool_status_label(exec.status) )); } ToolCell::Review(review) => { @@ -1523,10 +1513,7 @@ fn turn_verifier_lines(app: &App, start: usize, end: usize) -> Vec { } else { format!("review {target}") }; - lines.push(format!( - "• {target} — {}", - activity_status_label(review.status) - )); + lines.push(format!("• {target} — {}", tool_status_label(review.status))); } _ => {} } diff --git a/crates/tui/src/tui/ui/tests.rs b/crates/tui/src/tui/ui/tests.rs index 62c9ec475c..077f5db5eb 100644 --- a/crates/tui/src/tui/ui/tests.rs +++ b/crates/tui/src/tui/ui/tests.rs @@ -4442,7 +4442,7 @@ fn selection_to_text_copies_rendered_transcript_block() { !selected.contains("Ctrl+O"), "detail affordance leaked into the selection: {selected:?}" ); - assert!(selected.contains("run Done · cargo check"), "{selected:?}"); + assert!(selected.contains("run done · cargo check"), "{selected:?}"); assert!(selected.contains("copy assistant"), "{selected:?}"); // #1163: tool-card middle lines are rendered with a `│ ` left rail // glyph, but that decoration must not leak into copied text. Assert @@ -22528,8 +22528,8 @@ fn completed_exec_tool_result_still_renders_run_done() { assert_eq!(exec.status, ToolStatus::Success); let text = rendered_text(&exec.lines_with_motion(100, true)); - assert!(text.contains("run Done"), "{text}"); - assert!(!text.contains("tool loaded - retry required"), "{text}"); + assert!(text.contains("run done"), "{text}"); + assert!(!text.contains("loaded, not run"), "{text}"); } /// #6566: the tool output the person reads drops the engine's approval note @@ -22615,8 +22615,8 @@ fn hydrated_exec_tool_result_renders_retry_required_not_run_done() { assert_eq!(exec.status, ToolStatus::Hydrated); let text = rendered_text(&exec.lines_with_motion(120, true)); - assert!(text.contains("run tool loaded - retry required"), "{text}"); - assert!(!text.contains("run Done"), "{text}"); + assert!(text.contains("run loaded, not run"), "{text}"); + assert!(!text.contains("run done"), "{text}"); } #[test] @@ -22653,8 +22653,8 @@ fn hydrated_tool_with_validation_body_still_uses_hydrated_status() { assert_eq!(generic.status, ToolStatus::Hydrated); let text = rendered_text(&HistoryCell::Tool(ToolCell::Generic(generic.clone())).lines(120)); - assert!(text.contains("tool loaded - retry required"), "{text}"); - assert!(!text.contains("tool Done"), "{text}"); + assert!(text.contains("tool loaded, not run"), "{text}"); + assert!(!text.contains("tool done"), "{text}"); } #[test] @@ -22689,8 +22689,8 @@ fn failed_tool_result_with_hydration_metadata_stays_failed() { assert_eq!(generic.status, ToolStatus::Failed); let text = rendered_text(&HistoryCell::Tool(ToolCell::Generic(generic.clone())).lines(120)); - assert!(text.contains("tool issue"), "{text}"); - assert!(!text.contains("tool loaded - retry required"), "{text}"); + assert!(text.contains("tool failed"), "{text}"); + assert!(!text.contains("loaded, not run"), "{text}"); } #[test] diff --git a/crates/tui/src/tui/widgets/mod.rs b/crates/tui/src/tui/widgets/mod.rs index ca6fa4e0cd..ba0a5311b9 100644 --- a/crates/tui/src/tui/widgets/mod.rs +++ b/crates/tui/src/tui/widgets/mod.rs @@ -3844,7 +3844,7 @@ mod tests { !settled.contains("run running"), "flushed terminal state reused the active cache entry:\n{settled}" ); - assert!(settled.contains("run issue"), "{settled}"); + assert!(settled.contains("run failed"), "{settled}"); } fn render_approval_request( @@ -4300,7 +4300,7 @@ mod tests { !settled.contains("run running"), "history cell reused the active slow-path cache entry:\n{settled}" ); - assert!(settled.contains("run issue"), "{settled}"); + assert!(settled.contains("run failed"), "{settled}"); } #[test] diff --git a/crates/tui/src/tui/widgets/tool_card.rs b/crates/tui/src/tui/widgets/tool_card.rs index 45eb23a3a3..8e912913f7 100644 --- a/crates/tui/src/tui/widgets/tool_card.rs +++ b/crates/tui/src/tui/widgets/tool_card.rs @@ -63,7 +63,7 @@ pub enum ToolFamily { pub fn tool_family_for_title(title: &str) -> ToolFamily { match title { "Shell" => ToolFamily::Run, - "Patch" | "Diff" => ToolFamily::Patch, + "Patch" | "Diff" | "File" => ToolFamily::Patch, "Workspace" | "Image" => ToolFamily::Read, "Search" => ToolFamily::Find, "Plan" | "Legacy plan" | "Review" => ToolFamily::Generic, From a4603c3fa8f6b6ae613ef4c264a74fbb319a4461 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 15:35:30 +0000 Subject: [PATCH 29/46] fix(tui): the approval card uses its translated labels in every locale MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From the 0.10.2 UX audit (errors-toasts and localization lenses, merged), checked against the code first. On the most safety-critical surface, 13 of 15 locales showed translated options next to English field labels (Command, Dir, File, Preview…) and an English "About" sentence. localize_detail_label and description_for_locale gated existing ApprovalLabel*/ApprovalDesc* MessageIds to zh-Hans only. The packs already carry them, and the pager already used tr(ApprovalLabelPreview) for every locale. - The MessageId-backed label arms apply to every locale. The hard-coded Chinese arms stay zh-only. - The category description comes from tr(locale, ApprovalDesc*) for every non-English locale. - ApprovalDetail gains a typed `is_preview`, set where the Preview detail is built. The live card and the legacy band read it instead of matching the English label "Preview", so the 3/5-row preview bound holds in every locale. - impacts_for_locale is unchanged: its zh builder carries literal Chinese. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01BsXVvReYca2UxzimQtj7Mj --- crates/tui/src/tui/approval.rs | 27 +++++++--- crates/tui/src/tui/approval/previews.rs | 54 +++++++++---------- .../src/tui/widgets/approval_band_legacy.rs | 2 +- crates/tui/src/tui/widgets/mod.rs | 2 +- 4 files changed, 48 insertions(+), 37 deletions(-) diff --git a/crates/tui/src/tui/approval.rs b/crates/tui/src/tui/approval.rs index c15fd5b09e..32c5b36fdb 100644 --- a/crates/tui/src/tui/approval.rs +++ b/crates/tui/src/tui/approval.rs @@ -151,6 +151,7 @@ pub struct ApprovalDetail { /// Preformatted shell lines for commands that benefit from safe wrapping /// or a compact write-file preview. `value` remains the original command. pub shell_lines: Option>, + pub is_preview: bool, } impl ApprovalRequest { @@ -272,11 +273,11 @@ impl ApprovalRequest { pub fn description_for_locale(&self, locale: Locale) -> String { match locale { - Locale::ZhHans => localized_description_zh_hans(self.category), - _ if self.category == ToolCategory::Shell => { + Locale::En if self.category == ToolCategory::Shell => { "Review the command before it runs.".to_string() } - _ => self.description.clone(), + Locale::En => self.description.clone(), + _ => localized_description(self.category, locale), } } @@ -337,9 +338,10 @@ impl ApprovalRequest { if matches!(detail.label.as_str(), "File" | "Path" | "Dir") { detail.value = workspace_relative(&detail.value, &self.workspace); } - let is_preview = detail.label == "Preview"; detail.label = localize_detail_label(&detail.label, locale).to_string(); - if is_preview && let Some(lines) = detail.shell_lines.as_mut() { + if detail.is_preview + && let Some(lines) = detail.shell_lines.as_mut() + { for line in lines.iter_mut() { *line = localize_preview_shell_line(semantic_tool_name, line, locale) .to_string(); @@ -460,8 +462,7 @@ fn build_impact_summary(tool_name: &str, category: ToolCategory, params: &Value) impacts } -fn localized_description_zh_hans(category: ToolCategory) -> String { - let locale = Locale::ZhHans; +fn localized_description(category: ToolCategory, locale: Locale) -> String { match category { ToolCategory::Safe => tr(locale, MessageId::ApprovalDescSafe).to_string(), ToolCategory::FileWrite => tr(locale, MessageId::ApprovalDescFileWrite).to_string(), @@ -565,6 +566,7 @@ fn build_prominent_details( label, value, shell_lines: None, + is_preview: false, }) .collect(); match category { @@ -574,6 +576,7 @@ fn build_prominent_details( label: "Command".to_string(), shell_lines: Some(format_shell_command_for_approval(&command)), value: command, + is_preview: false, }); } if let Some(workdir) = param_preview(params, &["workdir", "cwd"], 96) { @@ -581,6 +584,7 @@ fn build_prominent_details( label: "Dir".to_string(), value: workdir, shell_lines: None, + is_preview: false, }); } } @@ -590,6 +594,7 @@ fn build_prominent_details( label: "File".to_string(), value: path, shell_lines: None, + is_preview: false, }); } if let Some(preview_lines) = file_write_preview_lines(tool_name, params) { @@ -597,6 +602,7 @@ fn build_prominent_details( label: "Preview".to_string(), value: preview_lines.join("\n"), shell_lines: Some(preview_lines), + is_preview: true, }); } } @@ -606,6 +612,7 @@ fn build_prominent_details( label: "Path".to_string(), value: path, shell_lines: None, + is_preview: false, }); } } @@ -617,6 +624,7 @@ fn build_prominent_details( label: "Target".to_string(), value: target, shell_lines: None, + is_preview: false, }); } } @@ -629,6 +637,7 @@ fn build_prominent_details( label: label.to_string(), value, shell_lines: None, + is_preview: false, }); } } @@ -638,6 +647,7 @@ fn build_prominent_details( label: "Action".to_string(), value: action, shell_lines: None, + is_preview: false, }); } if let Some(kind) = param_preview(params, &["type"], 40) { @@ -645,6 +655,7 @@ fn build_prominent_details( label: "Type".to_string(), value: kind, shell_lines: None, + is_preview: false, }); } if let Some(prompt) = param_preview(params, &["prompt", "task", "message"], 200) { @@ -652,6 +663,7 @@ fn build_prominent_details( label: "Prompt".to_string(), value: prompt, shell_lines: None, + is_preview: false, }); } } @@ -665,6 +677,7 @@ fn build_prominent_details( label: "Input".to_string(), value: input, shell_lines: None, + is_preview: false, }); } } diff --git a/crates/tui/src/tui/approval/previews.rs b/crates/tui/src/tui/approval/previews.rs index 429de7b9e4..9f00efdb51 100644 --- a/crates/tui/src/tui/approval/previews.rs +++ b/crates/tui/src/tui/approval/previews.rs @@ -312,34 +312,32 @@ pub(super) fn param_text(params: &Value, keys: &[&str]) -> Option { } pub(super) fn localize_detail_label(label: &str, locale: Locale) -> Cow<'static, str> { - match locale { - Locale::ZhHans => match label { - "Command" => tr(locale, MessageId::ApprovalLabelCommand), - "Dir" => tr(locale, MessageId::ApprovalLabelDir), - "File" => tr(locale, MessageId::ApprovalLabelFile), - "Preview" => tr(locale, MessageId::ApprovalLabelPreview), - "proposed content" => tr(locale, MessageId::ApprovalLabelProposedContent), - "replace this" => tr(locale, MessageId::ApprovalLabelReplaceThis), - "with this" => tr(locale, MessageId::ApprovalLabelWithThis), - "replacement content" => tr(locale, MessageId::ApprovalLabelReplacementContent), - "Path" => tr(locale, MessageId::ApprovalLabelPath), - "Target" => tr(locale, MessageId::ApprovalLabelTarget), - "Input" => tr(locale, MessageId::ApprovalLabelInput), - "Action" => tr(locale, MessageId::ApprovalLabelAction), - "Type" => tr(locale, MessageId::ApprovalLabelType), - "Prompt" => tr(locale, MessageId::ApprovalLabelPrompt), - "Goal" => "目标".into(), - "Children" => "子任务".into(), - "Writes" => "写入".into(), - "Shell" => "Shell".into(), - "Network" => "网络".into(), - "Budget" => "预算".into(), - "Trust mode" => "信任模式".into(), - "Auto-approve" => "自动批准".into(), - "Mode" => "模式".into(), - "Workspace" => "工作区".into(), - _ => label.to_string().into(), - }, + match label { + "Command" => tr(locale, MessageId::ApprovalLabelCommand), + "Dir" => tr(locale, MessageId::ApprovalLabelDir), + "File" => tr(locale, MessageId::ApprovalLabelFile), + "Preview" => tr(locale, MessageId::ApprovalLabelPreview), + "proposed content" => tr(locale, MessageId::ApprovalLabelProposedContent), + "replace this" => tr(locale, MessageId::ApprovalLabelReplaceThis), + "with this" => tr(locale, MessageId::ApprovalLabelWithThis), + "replacement content" => tr(locale, MessageId::ApprovalLabelReplacementContent), + "Path" => tr(locale, MessageId::ApprovalLabelPath), + "Target" => tr(locale, MessageId::ApprovalLabelTarget), + "Input" => tr(locale, MessageId::ApprovalLabelInput), + "Action" => tr(locale, MessageId::ApprovalLabelAction), + "Type" => tr(locale, MessageId::ApprovalLabelType), + "Prompt" => tr(locale, MessageId::ApprovalLabelPrompt), + _ if locale != Locale::ZhHans => label.to_string().into(), + "Goal" => "目标".into(), + "Children" => "子任务".into(), + "Writes" => "写入".into(), + "Shell" => "Shell".into(), + "Network" => "网络".into(), + "Budget" => "预算".into(), + "Trust mode" => "信任模式".into(), + "Auto-approve" => "自动批准".into(), + "Mode" => "模式".into(), + "Workspace" => "工作区".into(), _ => label.to_string().into(), } } diff --git a/crates/tui/src/tui/widgets/approval_band_legacy.rs b/crates/tui/src/tui/widgets/approval_band_legacy.rs index 5cd390ac5f..653daea8e4 100644 --- a/crates/tui/src/tui/widgets/approval_band_legacy.rs +++ b/crates/tui/src/tui/widgets/approval_band_legacy.rs @@ -176,7 +176,7 @@ impl<'a> ApprovalWidget<'a> { } else { let mut rendered_detail = false; for detail in details.iter().take(4) { - let is_change_preview = matches!(detail.label.as_str(), "Preview" | "预览"); + let is_change_preview = detail.is_preview; if let Some(shell_lines) = detail.shell_lines.as_deref() { let command_width = area.width.saturating_sub(10) as usize; // A short approval band has room for only one detail row diff --git a/crates/tui/src/tui/widgets/mod.rs b/crates/tui/src/tui/widgets/mod.rs index ba0a5311b9..0ec6707ae0 100644 --- a/crates/tui/src/tui/widgets/mod.rs +++ b/crates/tui/src/tui/widgets/mod.rs @@ -1692,7 +1692,7 @@ impl<'a> ApprovalWidget<'a> { } else { let mut rendered_detail = false; for detail in details.iter().take(4) { - let is_change_preview = matches!(detail.label.as_str(), "Preview" | "预览"); + let is_change_preview = detail.is_preview; if let Some(shell_lines) = detail.shell_lines.as_deref() { let command_width = area.width.saturating_sub(10) as usize; // A short approval band has room for only one detail row From cad2c003d6ade249e386544cae03ca819ba137a0 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 15:35:42 +0000 Subject: [PATCH 30/46] fix(tui): first run continues after Ollama; failures read as failures From the 0.10.2 UX audit (first-run and errors-toasts lenses). Each item was checked against the code first. Ollama adoption. When a local Ollama model was adopted during first run, the picker was popped and onboarding set to None. That skipped the trust question and the ready screen, and mark_onboarded never ran, so the next launch restarted onboarding. It now calls advance_onboarding_after_provider. The adoption test expects TrustDirectory or Ready. Retry receipts stay in the transcript. They also wrote status_message, which the legacy classifier turned into an 8-second red Error toast that hid the later "recovered" line. A receipt now adds its System cell and nothing else. A turn that ended in failure reads as Error, live and on resume. - present_turn_failure and the resume replay of saved outcomes used Warning, which goes against the pinned a_failure_that_ended_the_work_is_never_a_warning rule. - The terminal stream-open branch in model_step promotes a Warning envelope to Error before sending it. `recoverable`, `category` and `code` are untouched, so provider fallback is unchanged. - When an earlier Warning error cell (a stream stall) already showed the failure, present_turn_failure upgrades that cell to Error. The live view then matches the record that resume replays. /setup honesty. - The final step no longer says "You're ready." without a working provider. It reuses the Provider step's title and blurb and leads its action bar with P (provider). C (customize) is listed because it was already handled. - The key-entry modal puts the rejection reason first, so the 6-row cap drops guidance rather than the reason. - Its continue/back footers use SetupActionContinue/SetupActionBack. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01BsXVvReYca2UxzimQtj7Mj --- .../src/core/engine/turn_loop/model_step.rs | 3 ++ crates/tui/src/tui/provider_picker.rs | 27 ++++++------ crates/tui/src/tui/setup/mod.rs | 42 +++++++++++++------ crates/tui/src/tui/ui/apply.rs | 2 +- crates/tui/src/tui/ui/event_loop.rs | 30 +++++++++---- crates/tui/src/tui/ui/tests.rs | 10 ++++- 6 files changed, 80 insertions(+), 34 deletions(-) diff --git a/crates/tui/src/core/engine/turn_loop/model_step.rs b/crates/tui/src/core/engine/turn_loop/model_step.rs index 8e64cf7305..5e6642446b 100644 --- a/crates/tui/src/core/engine/turn_loop/model_step.rs +++ b/crates/tui/src/core/engine/turn_loop/model_step.rs @@ -315,6 +315,9 @@ impl Engine { } } progress.turn_error = Some(display_message); + if envelope.severity == crate::error_taxonomy::ErrorSeverity::Warning { + envelope.severity = crate::error_taxonomy::ErrorSeverity::Error; + } let _ = self.send_event(Event::error(envelope)).await; return PhaseResult::Return(( TurnOutcomeStatus::Failed, diff --git a/crates/tui/src/tui/provider_picker.rs b/crates/tui/src/tui/provider_picker.rs index b8c9bb10a0..92648788b8 100644 --- a/crates/tui/src/tui/provider_picker.rs +++ b/crates/tui/src/tui/provider_picker.rs @@ -3660,8 +3660,8 @@ impl ProviderPickerView { inner, buf, &[ - ActionHint::new("Enter", "continue"), - ActionHint::new("Esc", "back"), + ActionHint::new("Enter", self.tr(MessageId::SetupActionContinue)), + ActionHint::new("Esc", self.tr(MessageId::SetupActionBack)), ], ) }; @@ -3760,10 +3760,13 @@ impl ProviderPickerView { }; if let Some(ref error) = self.key_entry_error { - hint_lines.push(Line::from(Span::styled( - error.clone(), - Style::default().fg(palette::STATUS_ERROR), - ))); + hint_lines.insert( + 0, + Line::from(Span::styled( + error.clone(), + Style::default().fg(palette::STATUS_ERROR), + )), + ); } // `Line` count is not rendered row count: long environment-variable @@ -4031,8 +4034,8 @@ impl ProviderPickerView { buf, &[ ActionHint::new("↑↓", "move"), - ActionHint::new("Enter", "continue"), - ActionHint::new("Esc", "back"), + ActionHint::new("Enter", self.tr(MessageId::SetupActionContinue)), + ActionHint::new("Esc", self.tr(MessageId::SetupActionBack)), ], ); @@ -4148,8 +4151,8 @@ impl ProviderPickerView { buf, &[ ActionHint::new("↑↓", "choose"), - ActionHint::new("Enter", "continue"), - ActionHint::new("Esc", "back"), + ActionHint::new("Enter", self.tr(MessageId::SetupActionContinue)), + ActionHint::new("Esc", self.tr(MessageId::SetupActionBack)), ], ); self.render_setup_choices( @@ -4187,8 +4190,8 @@ impl ProviderPickerView { buf, &[ ActionHint::new("↑↓", "choose"), - ActionHint::new("Enter", "continue"), - ActionHint::new("Esc", "back"), + ActionHint::new("Enter", self.tr(MessageId::SetupActionContinue)), + ActionHint::new("Esc", self.tr(MessageId::SetupActionBack)), ], ); // Keep the actual endpoint with each route; choosing only stages diff --git a/crates/tui/src/tui/setup/mod.rs b/crates/tui/src/tui/setup/mod.rs index dc71d77e6a..c461c8d86b 100644 --- a/crates/tui/src/tui/setup/mod.rs +++ b/crates/tui/src/tui/setup/mod.rs @@ -3328,7 +3328,11 @@ impl ModalView for SetupWizardView { let spec = self.selected_spec(); let (title_text, question_text) = if self.progressive_guide { match self.selected_step() { - SetupStep::ProviderModel => ( + SetupStep::Verification if self.facts.provider_ready => ( + tr(self.locale, MessageId::OnboardReadyTitle).into_owned(), + tr(self.locale, MessageId::OnboardReadyLead).into_owned(), + ), + SetupStep::ProviderModel | SetupStep::Verification => ( tr(self.locale, MessageId::OnboardProviderTitle).into_owned(), tr(self.locale, MessageId::OnboardProviderBlurb).into_owned(), ), @@ -3340,10 +3344,6 @@ impl ModalView for SetupWizardView { "/rc".to_string(), tr(self.locale, MessageId::CmdRemoteControlDescription).into_owned(), ), - SetupStep::Verification => ( - tr(self.locale, MessageId::OnboardReadyTitle).into_owned(), - tr(self.locale, MessageId::OnboardReadyLead).into_owned(), - ), _ => ( tr(self.locale, spec.title_id()).into_owned(), tr(self.locale, spec.why_id()).into_owned(), @@ -3449,14 +3449,30 @@ impl SetupWizardView { details(), exit(), ], - SetupStep::Verification => vec![ - ActionHint::new( - "Enter", - tr(self.locale, MessageId::OnboardReadyStart).to_string(), - ), - details(), - exit(), - ], + SetupStep::Verification => { + let mut hints = vec![ + ActionHint::new( + "Enter", + tr(self.locale, MessageId::OnboardReadyStart).to_string(), + ), + ActionHint::new( + "C", + tr(self.locale, MessageId::SetupActionCustomize).to_string(), + ), + details(), + exit(), + ]; + if !self.facts.provider_ready { + hints.insert( + 0, + ActionHint::new( + "P", + tr(self.locale, MessageId::SetupActionProvider).to_string(), + ), + ); + } + hints + } _ => vec![exit()], }; let position = self diff --git a/crates/tui/src/tui/ui/apply.rs b/crates/tui/src/tui/ui/apply.rs index 66f987b81b..8f6b747755 100644 --- a/crates/tui/src/tui/ui/apply.rs +++ b/crates/tui/src/tui/ui/apply.rs @@ -4437,7 +4437,7 @@ pub(crate) fn apply_loaded_session_with_goal( { app.extend_history(std::iter::once(HistoryCell::Error { message: outcome.error.clone(), - severity: crate::error_taxonomy::ErrorSeverity::Warning, + severity: crate::error_taxonomy::ErrorSeverity::Error, })); } }; diff --git a/crates/tui/src/tui/ui/event_loop.rs b/crates/tui/src/tui/ui/event_loop.rs index 37b950b637..3b4bfa4b75 100644 --- a/crates/tui/src/tui/ui/event_loop.rs +++ b/crates/tui/src/tui/ui/event_loop.rs @@ -1550,6 +1550,19 @@ pub(super) fn present_turn_failure( error: Option<&str>, ) { let failed = matches!(status, crate::core::events::TurnOutcomeStatus::Failed); + if failed + && app.turn_error_posted + && let Some(notice) = app.turn_error_notice.clone() + && let Some(index) = (0..app.virtual_cell_count()).rev().find(|&index| { + matches!( + app.cell_at_virtual_index(index), + Some(HistoryCell::Error { message, .. }) if *message == notice + ) + }) + && let Some(HistoryCell::Error { severity, .. }) = app.cell_at_virtual_index_mut(index) + { + *severity = crate::error_taxonomy::ErrorSeverity::Error; + } // What the transcript shows for this turn's failure: the error cell an // earlier `Event::Error` already posted, or the notice added here. let shown = if app.turn_error_posted { @@ -1559,7 +1572,7 @@ pub(super) fn present_turn_failure( if failed { app.add_message(HistoryCell::Error { message: notice.clone(), - severity: crate::error_taxonomy::ErrorSeverity::Warning, + severity: crate::error_taxonomy::ErrorSeverity::Error, }); } app.set_sticky_status(notice.clone(), StatusToastLevel::Error, None); @@ -7423,14 +7436,14 @@ pub(super) async fn adopt_live_local_ollama_catalog( app.onboarding_needs_api_key = false; app.onboarding_missing_key_recovery = false; // A launch with no key opens the provider picker (#6566). The local model - // just answered that question, so close the picker and its onboarding - // step rather than leave a stale "connect a model" screen whose Esc would - // now walk back to the welcome screen. + // just answered that question, so close the picker and advance past its + // onboarding step rather than leave a stale "connect a model" screen whose + // Esc would now walk back to the welcome screen. if app.onboarding == OnboardingState::Provider { if app.view_stack.top_kind() == Some(ModalKind::ProviderPicker) { app.view_stack.pop(); } - app.onboarding = OnboardingState::None; + crate::tui::onboarding::advance_onboarding_after_provider(app); } // Say plainly which model is in use and how to change it, instead of the // endpoint it was discovered from (#6566). @@ -8575,10 +8588,13 @@ pub(super) fn route_key_to_view_stack( /// Keep only the Engine's retry receipts in the existing transcript. Ordinary /// status/footer behavior and internal/model-only status projection stay intact. pub(super) fn apply_engine_status(app: &mut App, message: String) -> bool { + if crate::core::events::is_retry_status_receipt(&message) { + app.add_message(HistoryCell::System { content: message }); + return true; + } // The account-profile fallback notice is sent once per engine; keep it in // the transcript so the next turn's status line cannot erase it. - let retain = crate::core::events::is_retry_status_receipt(&message) - || app.tr(MessageId::ProfileConstitutionUnavailableLocal) == message.as_str(); + let retain = app.tr(MessageId::ProfileConstitutionUnavailableLocal) == message.as_str(); if retain { app.add_message(HistoryCell::System { content: message.clone(), diff --git a/crates/tui/src/tui/ui/tests.rs b/crates/tui/src/tui/ui/tests.rs index 077f5db5eb..f98789c816 100644 --- a/crates/tui/src/tui/ui/tests.rs +++ b/crates/tui/src/tui/ui/tests.rs @@ -10957,7 +10957,15 @@ async fn adopting_a_local_model_closes_the_connect_picker_and_names_it() { .await; assert_eq!(app.api_provider, ProviderKind::Ollama); - assert_eq!(app.onboarding, crate::tui::app::OnboardingState::None); + assert!( + matches!( + app.onboarding, + crate::tui::app::OnboardingState::TrustDirectory + | crate::tui::app::OnboardingState::Ready + ), + "{:?}", + app.onboarding + ); assert_ne!( app.view_stack.top_kind(), Some(crate::tui::views::ModalKind::ProviderPicker) From 34d35fa260535b3c742a24ae5d541127f93cf943 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 15:36:02 +0000 Subject: [PATCH 31/46] fix(tui): mode receipts say Work; /zidong names the Full Access it grants MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From the 0.10.2 UX audit (commands-help and localization lenses, merged), checked against the code first. The picker, chip, hotbar and settings say "Work", but several places still said "Act": - `/mode work` replied "Switched to Act mode."; - /overview printed "Mode: ACT"; - /setup showed "Work intent: Act"; - the startup-default notice spliced in "Act". All four now use AppModeUi::display_name_localized. /zidong and `/mode yolo` switch to Full Access, with shell and trust on, but replied only "Switched to Act mode.". They now reply "Switched to Full Access (Work)." (tr(HotbarActionModeYoloName)). The /mode usage is now "/mode [work|plan|operate|1|2|3]", and the usage error prints MODE_INFO.usage instead of a hand copy that also listed "agent". The English hints that still said "Act" ("Act + Full Access", "applies in Act and Operate", "Act mode will expose shell") now say Work. Unchanged: AppMode::display_name, label() and the hotbar short_label. Session export and the hotbar metadata cross-check depend on them. Tests that pinned "Act" are updated: config yolo, the allow_shell messages, the app permission-cycle toast, and the help filters "mode [act" → "mode [work". Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01BsXVvReYca2UxzimQtj7Mj --- .../tui/src/commands/groups/config/config.rs | 38 +++++++++++++------ crates/tui/src/commands/groups/config/mod.rs | 2 +- crates/tui/src/commands/groups/core/core.rs | 4 +- crates/tui/src/commands/traits.rs | 2 +- crates/tui/src/tui/app.rs | 7 ++-- crates/tui/src/tui/app/tests.rs | 2 +- crates/tui/src/tui/setup/mod.rs | 4 +- crates/tui/src/tui/views/help.rs | 14 +++---- 8 files changed, 45 insertions(+), 28 deletions(-) diff --git a/crates/tui/src/commands/groups/config/config.rs b/crates/tui/src/commands/groups/config/config.rs index 85d6719747..73555a3195 100644 --- a/crates/tui/src/commands/groups/config/config.rs +++ b/crates/tui/src/commands/groups/config/config.rs @@ -18,7 +18,9 @@ use crate::config_persistence::{ }; use crate::reasoning_preference::ReasoningEffort; use crate::settings::Settings; -use crate::tui::app::{App, AppAction, OnboardingState, ScreenMode, SettingSelection, VimMode}; +use crate::tui::app::{ + App, AppAction, AppModeUi, OnboardingState, ScreenMode, SettingSelection, VimMode, +}; use anyhow::Result; use codewhale_config::AppMode; use codewhale_execpolicy::ApprovalMode; @@ -2429,7 +2431,7 @@ pub fn set_config_value(app: &mut App, key: &str, value: &str, persist: bool) -> }; app.set_agent_shell_access(enabled); let mode_hint = if enabled { - " Act mode will expose shell on the next turn with approval gating. Full Access (Shift+Tab) also enables shell and auto-approves." + " Work mode will expose shell on the next turn with approval gating. Full Access (Shift+Tab) also enables shell and auto-approves." } else { " Shell tools will be hidden on the next turn. Re-enable with `/config allow_shell true`." }; @@ -3081,7 +3083,7 @@ fn mode_selection(app: &mut App, arg: &str) -> CommandResult { CommandResult::message(message) } } - None => CommandResult::error("Usage: /mode [act|agent|plan|operate|1|2|3]"), + None => CommandResult::error(format!("Usage: {}", super::MODE_INFO.usage)), } } @@ -3099,7 +3101,13 @@ pub fn switch_mode(app: &mut App, mode: AppMode) -> String { /// written nothing. fn switch_mode_with_status(app: &mut App, mode: AppMode) -> (String, bool) { match app.select_mode(mode) { - SettingSelection::Changed => (format!("Switched to {} mode.", mode.display_name()), true), + SettingSelection::Changed => ( + format!( + "Switched to {} mode.", + mode.display_name_localized(app.ui_locale) + ), + true, + ), SettingSelection::PersistedSame => (app.mode_startup_default_receipt(mode), false), SettingSelection::Refused => ( app.setting_locked_message(MessageId::SettingSubjectMode), @@ -3108,12 +3116,15 @@ fn switch_mode_with_status(app: &mut App, mode: AppMode) -> (String, bool) { } } -/// Status for the legacy YOLO alias: user-facing copy says Act, because the -/// alias is invisible Act + Full Access. +/// Status for the legacy YOLO alias: user-facing copy names Full Access (Work), +/// because the alias is invisible Work + Full Access. fn switch_yolo_compat_with_status(app: &mut App) -> (String, bool) { match app.select_yolo_compat() { SettingSelection::Changed => ( - format!("Switched to {} mode.", AppMode::Agent.display_name()), + format!( + "Switched to {}.", + tr(app.ui_locale, MessageId::HotbarActionModeYoloName) + ), true, ), SettingSelection::PersistedSame => { @@ -4128,8 +4139,13 @@ mod tests { // user settings on the host machine. let _ = mode(&mut app, Some("agent")); let result = mode(&mut app, Some("yolo")); - // YOLO is invisible Act+Bypass shorthand — user-facing copy says Act. - assert!(result.message.unwrap().contains("Switched to Act mode")); + // YOLO is invisible Work+Bypass shorthand — user-facing copy names Full Access (Work). + assert!( + result + .message + .unwrap() + .contains("Switched to Full Access (Work)") + ); assert_eq!(result.action, Some(AppAction::ModeChanged(AppMode::Agent))); assert!(app.allow_shell); assert!(app.trust_mode); @@ -4857,7 +4873,7 @@ mod tests { assert!(msg.contains("allow_shell = true")); assert!(msg.contains("session only")); - assert!(msg.contains("Act mode")); + assert!(msg.contains("Work mode")); assert!(msg.contains("approval gating")); assert!(msg.contains("next turn")); assert!(msg.contains("Full Access (Shift+Tab) also enables shell and auto-approves")); @@ -4880,7 +4896,7 @@ mod tests { assert_eq!( msg, format!( - "allow_shell = true (saved to {}). Act mode will expose shell on the next turn with approval gating. Full Access (Shift+Tab) also enables shell and auto-approves.", + "allow_shell = true (saved to {}). Work mode will expose shell on the next turn with approval gating. Full Access (Shift+Tab) also enables shell and auto-approves.", config_path.display() ) ); diff --git a/crates/tui/src/commands/groups/config/mod.rs b/crates/tui/src/commands/groups/config/mod.rs index 65f41bc896..8d3db263ce 100644 --- a/crates/tui/src/commands/groups/config/mod.rs +++ b/crates/tui/src/commands/groups/config/mod.rs @@ -106,7 +106,7 @@ static STATUSLINE_INFO: CommandInfo = CommandInfo { static MODE_INFO: CommandInfo = CommandInfo { name: "mode", aliases: &["jihua", "zidong"], - usage: "/mode [act|plan|operate|1|2|3]", + usage: "/mode [work|plan|operate|1|2|3]", description_id: MessageId::CmdModeDescription, }; static FULLSCREEN_INFO: CommandInfo = CommandInfo { diff --git a/crates/tui/src/commands/groups/core/core.rs b/crates/tui/src/commands/groups/core/core.rs index e7c862ef70..1751e24e77 100644 --- a/crates/tui/src/commands/groups/core/core.rs +++ b/crates/tui/src/commands/groups/core/core.rs @@ -9,7 +9,7 @@ use crate::config::{ }; #[cfg(test)] use crate::reasoning_preference::ReasoningEffort; -use crate::tui::app::{App, AppAction}; +use crate::tui::app::{App, AppAction, AppModeUi}; use crate::tui::views::{HelpView, ModalKind, SubAgentsView, subagent_view_agents}; use codewhale_config::AppMode; use codewhale_localization::{Locale, MessageId, tr}; @@ -658,7 +658,7 @@ pub fn home_dashboard(app: &mut App) -> CommandResult { stats, "{} {}", tr(locale, MessageId::HomeMode), - app.mode.label() + app.mode.display_name_localized(locale) ); let _ = writeln!( stats, diff --git a/crates/tui/src/commands/traits.rs b/crates/tui/src/commands/traits.rs index 96cfaa87ea..dd66b3849f 100644 --- a/crates/tui/src/commands/traits.rs +++ b/crates/tui/src/commands/traits.rs @@ -216,7 +216,7 @@ fn leading_literal_token(alternative: &str) -> Option<&str> { .unwrap_or(alternative.len()); let token = &alternative[..end]; // A word only counts when it is spelled the way a typed verb is spelled: - // starting lowercase (or a digit, for `/mode [act|plan|1|2|3]`) and made + // starting lowercase (or a digit, for `/mode [work|plan|1|2|3]`) and made // of ASCII word characters. `N`, `entry_id`, `--force` and // `path/to/export.json` are all values, not verbs. let mut chars = token.chars(); diff --git a/crates/tui/src/tui/app.rs b/crates/tui/src/tui/app.rs index fe5a0f5d7d..75d6d84293 100644 --- a/crates/tui/src/tui/app.rs +++ b/crates/tui/src/tui/app.rs @@ -3535,7 +3535,7 @@ impl App { #[must_use] pub fn mode_startup_default_receipt(&self, mode: AppMode) -> String { self.tr(MessageId::ModeAlreadyActiveSavedAsDefault) - .replace("{mode}", mode.display_name()) + .replace("{mode}", &mode.display_name_localized(self.ui_locale)) } /// Surface any startup-default write that failed since the last drain. @@ -3602,7 +3602,8 @@ impl App { Ok(()) }); self.push_status_toast( - "Legacy full-access mode is deprecated — use Act + Full Access (Shift+Tab)".to_string(), + "Legacy full-access mode is deprecated — use Work + Full Access (Shift+Tab)" + .to_string(), StatusToastLevel::Warning, Some(8_000), ); @@ -3969,7 +3970,7 @@ impl App { if self.mode == AppMode::Plan { self.push_status_toast( format!( - "Permissions set to {}. Plan stays Read Only; this applies in Act and Operate.", + "Permissions set to {}. Plan stays Read Only; this applies in Work and Operate.", next.permission_chip_label() ), StatusToastLevel::Info, diff --git a/crates/tui/src/tui/app/tests.rs b/crates/tui/src/tui/app/tests.rs index 326c56c755..3aa842d9a9 100644 --- a/crates/tui/src/tui/app/tests.rs +++ b/crates/tui/src/tui/app/tests.rs @@ -4021,7 +4021,7 @@ fn plan_permission_cycle_moves_the_baseline_and_leaves_plan_read_only() { assert!( app.status_toasts .iter() - .any(|toast| toast.text.contains("applies in Act and Operate")), + .any(|toast| toast.text.contains("applies in Work and Operate")), "the receipt must say when the new posture starts applying" ); diff --git a/crates/tui/src/tui/setup/mod.rs b/crates/tui/src/tui/setup/mod.rs index c461c8d86b..1602ecfd49 100644 --- a/crates/tui/src/tui/setup/mod.rs +++ b/crates/tui/src/tui/setup/mod.rs @@ -23,7 +23,7 @@ use crate::config::{Config, has_api_key}; use crate::prompts::{ BASE_PROMPT_OVERRIDE_OPT_IN_ENV, CONSTITUTION_OVERRIDE_FILE, base_prompt_override_opt_in, }; -use crate::tui::app::App; +use crate::tui::app::{App, AppModeUi}; use crate::tui::onboarding; use crate::tui::views::{ ActionHint, ModalKind, ModalView, ViewAction, ViewEvent, render_modal_footer, @@ -507,7 +507,7 @@ impl SetupRuntimeFacts { provider_ready, provider_status, provider_result, - work_intent: app.mode.display_name().to_string(), + work_intent: app.mode.display_name_localized(app.ui_locale).into_owned(), approval: app .approval_mode .permission_chip_label() diff --git a/crates/tui/src/tui/views/help.rs b/crates/tui/src/tui/views/help.rs index 7ef802340b..08d487f0a4 100644 --- a/crates/tui/src/tui/views/help.rs +++ b/crates/tui/src/tui/views/help.rs @@ -1489,14 +1489,14 @@ mod tests { #[test] fn substring_filter_narrows_to_command() { let mut view = HelpView::new(); - type_filter(&mut view, "mode [act"); + type_filter(&mut view, "mode [work"); assert!(!view.filtered.is_empty()); // Every filtered entry should genuinely contain the query in its // searchable haystack — no false positives slipped past. for idx in &view.filtered { assert!( - view.entries[*idx].haystack.contains("mode [act"), - "entry {:?} leaked through `mode [act` filter", + view.entries[*idx].haystack.contains("mode [work"), + "entry {:?} leaked through `mode [work` filter", view.entries[*idx] ); } @@ -1506,7 +1506,7 @@ mod tests { view.filtered .iter() .any(|idx| view.entries[*idx].label == "/mode"), - "/mode should match the `mode [act` filter" + "/mode should match the `mode [work` filter" ); } @@ -2182,14 +2182,14 @@ mod tests { #[test] fn render_with_filter_shows_only_matching_section_and_status() { let mut view = HelpView::new(); - type_filter(&mut view, "mode [act"); + type_filter(&mut view, "mode [work"); let area = Rect::new(0, 0, 96, 24); let mut buf = Buffer::empty(area); view.render(area, &mut buf); let dump = buffer_text(&buf, area); assert!( - dump.contains("Filter: mode [act"), + dump.contains("Filter: mode [work"), "filter echo missing:\n{dump}" ); assert!( @@ -2202,7 +2202,7 @@ mod tests { ); assert!( !dump.contains("/model"), - "non-matching commands should not render under a `mode [act` filter:\n{dump}" + "non-matching commands should not render under a `mode [work` filter:\n{dump}" ); } From e53972a7d825f4c1e08b6e0703b4c185882ff37e Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 15:36:02 +0000 Subject: [PATCH 32/46] fix(subagent): delegated cards name the running tool, not "Core tool " From the 0.10.2 UX audit (transcript-cards and live-work lenses), checked against the code first. drive_child_actor sent `format!("Core tool {id}")` to the parent mailbox on ToolExecutionStarted, and recorded "Core tool {id} settled" on completion, so every delegated card read "Core tool call_7f3a9c2e running". Event::ToolCallStarted, which carries the name, fell through to `_ => {}`. A local map from call id to the tool's model-facing name is filled on ToolCallStarted. The card now reads "read running" and then "read done" or "read failed", falling back to "tool". It uses the raw name, the one the progress meta's `.with_tool(name)` already records, rather than the retired policy alias (read_file), so both surfaces agree. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01BsXVvReYca2UxzimQtj7Mj --- crates/tui/src/tools/subagent/engine.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/crates/tui/src/tools/subagent/engine.rs b/crates/tui/src/tools/subagent/engine.rs index 3d1c7978f8..cd543a0c1f 100644 --- a/crates/tui/src/tools/subagent/engine.rs +++ b/crates/tui/src/tools/subagent/engine.rs @@ -1673,6 +1673,7 @@ pub(crate) async fn drive_child_actor( let mut input_open = true; let mut pending_input: Option = None; let mut cancellation_deadline = None; + let mut tool_names: HashMap = HashMap::new(); // The launch permit alone is not a started turn. Project only Core's // admitted lifecycle event, once across work, report and route retries. let mut started = false; @@ -1795,14 +1796,19 @@ pub(crate) async fn drive_child_actor( job.hard_deadline.map(Into::into), ).await; } + Event::ToolCallStarted { id, name, .. } => { + tool_names.insert(id.clone(), name.clone()); + } Event::ToolExecutionStarted { id } => { if let Some(mailbox) = runtime.mailbox.as_ref() { - let _ = mailbox.send(MailboxMessage::ToolCallStarted { agent_id: agent_id.clone(), tool_name: format!("Core tool {id}"), step: job.steps() }); + let tool_name = tool_names.get(id).cloned().unwrap_or_else(|| "tool".to_string()); + let _ = mailbox.send(MailboxMessage::ToolCallStarted { agent_id: agent_id.clone(), tool_name, step: job.steps() }); } } Event::ToolCallComplete { id, name, result, .. } => { + let label = tool_names.remove(id).unwrap_or_else(|| name.clone()); record_agent_progress(&runtime, &agent_id, AgentProgressEventMeta::new(AgentWorkerStatus::RunningTool).with_tool(name.clone()), - format!("Core tool {id} {}", if result.is_ok() { "settled" } else { "refused or failed" })); + format!("{label} {}", if result.is_ok() { "done" } else { "failed" })); } Event::Status { message, .. } => record_agent_progress(&runtime, &agent_id, AgentProgressEventMeta::new(AgentWorkerStatus::Running), message.clone()), From 619a6d9556eaadd1da55b04f9ebc0f92f7747e5b Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 15:36:02 +0000 Subject: [PATCH 33/46] fix(tui): markdown headings need '# ' and render inline markup; tables lose per-row rules From the 0.10.2 UX audit (transcript-cards lens), checked against the code before the change. - parse_heading accepted '#' with no following space, so a reply line like '#6235 fixed the race' became an H1 with a 40-column rule. It now follows CommonMark: 1-6 '#' then whitespace or end of line. - Headings rendered through the plain wrapper, so '## Changes to `x`' showed literal backticks and asterisks. They now use the inline path paragraphs use, with the heading style. render_wrapped_line_tagged, and wrap_code_line and wrap_text which only it reached, are deleted with their four direct unit tests. Two comments that named wrap_text are corrected. - Tables pushed a rule after every row, nearly doubling their height. A rule now marks only the header separator, the header row is bold, and ragged rows are padded to the column count so they line up with the borders. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01BsXVvReYca2UxzimQtj7Mj --- crates/tui/src/tui/markdown_render.rs | 265 ++++---------------------- 1 file changed, 41 insertions(+), 224 deletions(-) diff --git a/crates/tui/src/tui/markdown_render.rs b/crates/tui/src/tui/markdown_render.rs index 0dfa3d194c..f005eee13c 100644 --- a/crates/tui/src/tui/markdown_render.rs +++ b/crates/tui/src/tui/markdown_render.rs @@ -745,7 +745,12 @@ pub(crate) fn render_parsed_tagged_with_palette( let style = Style::default() .fg(palette::WHALE_ACTION) .add_modifier(Modifier::BOLD); - out.extend(render_wrapped_line_tagged(text, width, style, false, false)); + out.extend(render_line_with_links_tagged( + text, + width, + style, + link_style(), + )); } Block::HeadingRule => { out.push(RenderedMarkdownLine { @@ -1075,10 +1080,11 @@ fn last_plain_break_pos(text: &str) -> Option { fn parse_heading(line: &str) -> Option<(usize, &str)> { let trimmed = line.trim_start(); let hashes = trimmed.chars().take_while(|c| *c == '#').count(); - if hashes == 0 { + let rest = &trimmed[hashes..]; + if !(1..=6).contains(&hashes) || !(rest.is_empty() || rest.starts_with(char::is_whitespace)) { return None; } - let text = trimmed[hashes..].trim(); + let text = rest.trim(); if text.is_empty() { None } else { @@ -1362,54 +1368,6 @@ fn render_wrapped_code_spans_tagged( .collect() } -fn render_wrapped_line_tagged( - line: &str, - width: usize, - style: Style, - indent_code: bool, - is_code: bool, -) -> Vec { - let prefix = if indent_code { " " } else { "" }; - let prefix_width = prefix.width(); - let available = width.saturating_sub(prefix_width).max(1); - // Code blocks must preserve leading whitespace (indentation is semantic). - // Use hard character-width wrapping instead of word-wrap. - let wrapped = if indent_code { - wrap_code_line(line, available) - } else { - wrap_text(line, available) - }; - let mut out = Vec::new(); - - let last_index = wrapped.len().saturating_sub(1); - for (idx, chunk) in wrapped.into_iter().enumerate() { - let line = if idx == 0 { - Line::from(vec![Span::raw(prefix), Span::styled(chunk, style)]) - } else { - Line::from(vec![ - Span::raw(" ".repeat(prefix_width)), - Span::styled(chunk, style), - ]) - }; - let copy_separator_after = if idx == last_index { - CopyLineSeparator::Newline - } else if is_code { - CopyLineSeparator::None - } else { - CopyLineSeparator::Space - }; - out.push(RenderedMarkdownLine { - line, - links: Vec::new(), - is_code, - copy_prefix_width: if indent_code { prefix_width } else { 0 }, - copy_separator_after, - }); - } - - out -} - fn render_list_line_tagged( bullet: &str, text: &str, @@ -2108,18 +2066,21 @@ fn wrap_cell_text(cell: &str, col_width: usize) -> Vec { lines } -fn render_table_row(cells: &[String], width: usize, base_style: Style) -> Vec> { - if cells.is_empty() { - return vec![Line::from("")]; - } - let col_width = (width.saturating_sub(3 * cells.len() + 1)) / cells.len(); - let col_width = col_width.max(4); +fn render_table_row( + cells: &[String], + num_cols: usize, + width: usize, + base_style: Style, +) -> Vec> { + let col_width = table_col_width(num_cols, width); let sep_style = Style::default().fg(palette::TEXT_DIM); // Wrap each cell into one or more visual segments. The row's visual // height equals the tallest column. Cells that wrap to fewer segments // get blank-padded continuation lines so column separators stay aligned. - let wrapped: Vec> = cells.iter().map(|c| wrap_cell_text(c, col_width)).collect(); + let wrapped: Vec> = (0..num_cols) + .map(|i| wrap_cell_text(cells.get(i).map_or("", String::as_str), col_width)) + .collect(); let row_height = wrapped.iter().map(Vec::len).max().unwrap_or(1).max(1); let mut lines: Vec> = Vec::with_capacity(row_height); @@ -2134,7 +2095,7 @@ fn render_table_row(cells: &[String], width: usize, base_style: Style) -> Vec
  • Vec< ) }; - for i in 0..blocks.len() { - match &blocks[i] { + let header_end = blocks + .iter() + .position(|b| matches!(b, Block::TableSeparator)) + .unwrap_or(0); + + for (i, block) in blocks.iter().enumerate() { + match block { Block::TableRow(cells) => { - lines.extend(render_table_row(cells, width, base_style)); - if i + 1 < blocks.len() && matches!(&blocks[i + 1], Block::TableRow(_)) { - lines.push(mid_border()); - } + let style = if i < header_end { + base_style.add_modifier(Modifier::BOLD) + } else { + base_style + }; + lines.extend(render_table_row(cells, num_cols, width, style)); } Block::TableSeparator => { lines.push(mid_border()); @@ -2274,86 +2242,11 @@ fn markdown_grapheme_width(grapheme: &str, col: usize) -> usize { grapheme.width() } -/// Hard-wrap a code line at `width` display columns, preserving all -/// whitespace (including leading indentation). Unlike [`wrap_text`], this -/// does not split on word boundaries — code indentation is semantic. -fn wrap_code_line(line: &str, width: usize) -> Vec { - if width == 0 || line.is_empty() { - return vec![line.to_string()]; - } - let mut chunks = Vec::new(); - let mut current = String::new(); - let mut current_width = 0usize; - - for grapheme in line.graphemes(true) { - let grapheme_width = markdown_grapheme_width(grapheme, current_width); - if current_width + grapheme_width > width && !current.is_empty() { - chunks.push(current); - current = String::new(); - current_width = 0; - } - current.push_str(grapheme); - current_width += grapheme_width; - } - chunks.push(current); - chunks -} - -fn wrap_text(text: &str, width: usize) -> Vec { - if width == 0 { - return vec![text.to_string()]; - } - let mut lines = Vec::new(); - let mut current = String::new(); - let mut current_width = 0; - - for word in text.split_whitespace() { - let word_width = word.width(); - // If this single word is wider than the entire line, hard-break it - // at grapheme boundaries so wrapping always makes progress - // (#1344, #1351). Without this, long URLs / paths / hashes overflow - // the right edge silently. - if word_width > width { - if !current.is_empty() { - lines.push(std::mem::take(&mut current)); - current_width = 0; - } - push_word_breaking_graphemes(word, width, &mut current, &mut current_width, &mut lines); - continue; - } - let additional = if current.is_empty() { - word_width - } else { - word_width + 1 - }; - if current_width + additional > width && !current.is_empty() { - lines.push(current); - current = word.to_string(); - current_width = word_width; - } else { - if !current.is_empty() { - current.push(' '); - current_width += 1; - } - current.push_str(word); - current_width += word_width; - } - } - - if current.is_empty() { - lines.push(String::new()); - } else { - lines.push(current); - } - - lines -} - /// Push graphemes from `word` into `current`, flushing to `lines` when the /// running display width would exceed `width`. String-level Unicode width /// matches Ratatui for emoji and combining sequences. -/// Used by `wrap_text` and `wrap_cell_text` so a word longer than the -/// allotted width never silently overflows the right edge. +/// Used by `wrap_cell_text` so a word longer than the allotted width never +/// silently overflows the right edge. fn push_word_breaking_graphemes( word: &str, width: usize, @@ -3133,38 +3026,6 @@ mod tests { ); } - #[test] - fn wrap_code_line_preserves_leading_whitespace() { - // A short line must not be modified. - assert_eq!(wrap_code_line(" let x = 1;", 80), vec![" let x = 1;"]); - - // A line that exceeds the width must be hard-wrapped, keeping the - // leading whitespace on the first chunk. - let chunks = wrap_code_line(" abcdefgh", 8); - assert_eq!(chunks[0], " abcd", "first chunk keeps leading spaces"); - assert_eq!(chunks[1], "efgh"); - - // Empty line produces one empty chunk. - assert_eq!(wrap_code_line("", 80), vec![""]); - } - - #[test] - fn wrap_code_line_tab_counts_toward_width() { - // tab (8 cols) + "xy" (2 cols) = 10 ≤ 10 — fits on one line. - let chunks = wrap_code_line("\txy", 10); - assert_eq!(chunks, vec!["\txy"], "tab + 2 chars fits in width 10"); - - // tab (8 cols) + "x" (1 col) = 9 ≤ 9 — "x" fits; "y" overflows. - let chunks = wrap_code_line("\txy", 9); - assert_eq!(chunks[0], "\tx", "tab + first char fits exactly"); - assert_eq!(chunks[1], "y", "second char wraps"); - - // tab alone (8 cols) fits in width 8; the next "x" overflows. - let chunks = wrap_code_line("\tx", 8); - assert_eq!(chunks[0], "\t"); - assert_eq!(chunks[1], "x"); - } - #[test] fn markdown_grapheme_width_uses_tab_stop_and_string_width() { // At column 0 a tab fills to column 8. @@ -3785,12 +3646,12 @@ mod tests { // ─── Paragraph wrap regression suite (#1344, #1351) ──────────────────── // - // The bug: paragraph wrap (render_line_with_links) and code-block wrap - // (wrap_text) are word-based. A single word wider than the available - // width was placed alone on a line and silently overflowed the right - // edge of the transcript. Long URLs / paths / hashes / no-whitespace - // CJK runs all hit this. The fix hard-breaks overlong words at - // grapheme boundaries; these tests pin that across widths 40/60/80/120. + // The bug: paragraph wrap (render_line_with_links) is word-based. A + // single word wider than the available width was placed alone on a line + // and silently overflowed the right edge of the transcript. Long URLs / + // paths / hashes / no-whitespace CJK runs all hit this. The fix + // hard-breaks overlong words at grapheme boundaries; these tests pin + // that across widths 40/60/80/120. fn rendered_widths(rendered: &[Line<'static>]) -> Vec { rendered @@ -3907,24 +3768,6 @@ mod tests { assert_eq!(combined.matches('x').count(), 150); } - #[test] - fn wrap_text_breaks_overlong_word_for_code_blocks() { - // The standalone code-block wrap (wrap_text) had the same overflow - // bug; pin the fix at widths 40 and 80. - for &width in &[40usize, 80] { - let long = "z".repeat(200); - let lines = wrap_text(&long, width); - for line in &lines { - assert!( - line.width() <= width, - "wrap_text line {line:?} exceeds {width}" - ); - } - let combined: String = lines.join(""); - assert_eq!(combined.matches('z').count(), 200); - } - } - #[test] fn wrap_cell_text_already_handled_long_words_remains_correct() { // Regression guard for the v0.8.25 table-cell fix. After consolidating @@ -4022,32 +3865,6 @@ mod tests { } } - #[test] - fn lower_level_wrap_text_keeps_unicode_runs_within_qa_widths() { - let cases = [ - ("cjk", "中".repeat(140)), - ("emoji", "😀".repeat(110)), - ("combining", "e\u{301}".repeat(140)), - ]; - - for (label, input) in cases { - for &width in &[80usize, 100, 120] { - let lines = wrap_text(&input, width); - for line in &lines { - assert!( - line.width() <= width, - "{label} width={width}: wrap_text line {line:?} exceeds budget" - ); - } - let combined: String = lines.join(""); - assert_eq!( - combined, input, - "{label} width={width}: wrap_text changed content" - ); - } - } - } - #[test] fn table_render_keeps_cjk_cells_within_qa_widths() { let cjk = "界".repeat(80); From be6ed76c30c76d201e0f4e18e6c5a59f6f8e3df9 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 15:36:09 +0000 Subject: [PATCH 34/46] perf(tui): the context meter keeps estimates for unchanged history From the 0.10.2 Rust optimization audit (memory lens, judged safe-now). The cost. apply_engine_session_projection cleared the UI's per-message token cache on every engine projection, and the engine sends one per tool result. The next frame's estimated_context_tokens then re-estimated every message on the UI thread. That includes serializing every ToolUse input to a String, so a write_file carrying a whole file was serialized again for each later tool result. The change. - set_api_messages already counted the leading messages that are unchanged and then dropped that number. It now returns it, and the projection truncates the cache to that prefix instead of clearing it. The estimates kept are for messages equal to the ones they were computed from, so every value matches what a full clear produces. - In-place edits go through api_messages_mut, for example replace_matching_assistant_text, and a prefix compare cannot see them. So api_messages_mut now clears the cache, and the edit shows sooner than before. - push_api_message appends through Arc::make_mut directly, so appends keep the cache. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01BsXVvReYca2UxzimQtj7Mj --- crates/tui/src/tui/app.rs | 14 ++++++++------ crates/tui/src/tui/ui/event_loop.rs | 7 +++++-- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/crates/tui/src/tui/app.rs b/crates/tui/src/tui/app.rs index 75d6d84293..1e32c786dd 100644 --- a/crates/tui/src/tui/app.rs +++ b/crates/tui/src/tui/app.rs @@ -5387,6 +5387,7 @@ impl App { /// exclusive `Arc` mutates in place, a shared one detaches first, so an /// outstanding engine snapshot can never observe the mutation. pub fn api_messages_mut(&mut self) -> &mut Vec { + self.context_token_cache.get_mut().clear(); Arc::make_mut(&mut self.api_messages) } @@ -5396,7 +5397,7 @@ impl App { pub fn push_api_message(&mut self, message: Message) { self.api_message_stamps .resize_with(self.api_messages.len(), Utc::now); - self.api_messages_mut().push(message); + Arc::make_mut(&mut self.api_messages).push(message); self.api_message_stamps.push(Utc::now()); } @@ -5406,18 +5407,19 @@ impl App { /// that are new or were rewritten (compaction) are stamped now, which /// lands within a turn-event of the real append. The shared snapshot is /// installed without copying. - pub fn set_api_messages(&mut self, messages: Arc>) { - let keep = self + pub fn set_api_messages(&mut self, messages: Arc>) -> usize { + let unchanged = self .api_messages .iter() .zip(messages.iter()) .take_while(|(old, new)| old == new) - .count() - .min(self.api_message_stamps.len()); - self.api_message_stamps.truncate(keep); + .count(); + self.api_message_stamps + .truncate(unchanged.min(self.api_message_stamps.len())); self.api_message_stamps .resize_with(messages.len(), Utc::now); self.api_messages = messages; + unchanged } /// Install a resumed conversation, reusing the persisted journal's diff --git a/crates/tui/src/tui/ui/event_loop.rs b/crates/tui/src/tui/ui/event_loop.rs index 3b4bfa4b75..1752c80a68 100644 --- a/crates/tui/src/tui/ui/event_loop.rs +++ b/crates/tui/src/tui/ui/event_loop.rs @@ -64,8 +64,11 @@ pub(super) fn apply_engine_session_projection( { surface_goal_persistence_failure(app, &error); } - app.context_token_cache.borrow_mut().clear(); - app.set_api_messages(messages); + let unchanged = app.set_api_messages(messages); + app.context_token_cache + .get_mut() + .message_tokens + .truncate(unchanged); // #6190: the projection is the engine's own record, so it is where a // steer's acceptance becomes observable — and the only place the steer's // real message index is known. Promote before anything else reads the From cf068be0ad0f05061179752767e964b17faa6ce5 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 16:20:37 +0000 Subject: [PATCH 35/46] perf(utils): write_atomic sweeps a directory for stale temps at most once an hour MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From the 0.10.2 Rust optimization audit (blocking lens, judged safe-now). The cost. Every private single write_atomic, and every write_atomic_batch parent, ran is_codewhale_owned_state_dir (up to 4 canonicalize calls) and then a full read_dir of the directory looking for stale temp files. Those temps only become stale after STALE_AFTER (1 h). A fork that clones hundreds of items paid one directory scan per item, measured at 22 ms each. The change. A process-wide map from directory to last sweep, using the same OnceLock> pattern as settings_transaction_mutex, lets sweep_owned_dir_if_due skip both the owned check and the read_dir when the directory was swept less than STALE_AFTER ago. Expired entries are dropped, so the map holds only directories touched in the last hour. The owned check and the sweep themselves are unchanged. A cached entry can only skip a sweep; it can never cause one in a directory that is not owned. Comments the change made false are corrected: the AtomicWriteScope::Single and write_atomic_batch docs, the call site, and the two runtime_threads.rs comments that described a per-item sweep. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01BsXVvReYca2UxzimQtj7Mj --- crates/tui/src/runtime_threads.rs | 16 +++---- crates/tui/src/utils.rs | 77 ++++++++++++++++++++----------- 2 files changed, 59 insertions(+), 34 deletions(-) diff --git a/crates/tui/src/runtime_threads.rs b/crates/tui/src/runtime_threads.rs index 52f8832793..2d1290410c 100644 --- a/crates/tui/src/runtime_threads.rs +++ b/crates/tui/src/runtime_threads.rs @@ -3135,10 +3135,11 @@ impl RuntimeThreadStore { /// Publish many items at once, paying the items directory's costs once. /// - /// Each `save_item` sweeps the whole items directory for stale temp files - /// and fsyncs it — right for one record, ruinous for the hundreds a fork - /// clones (measured: 22 ms of directory scan per item, 34 s for one fork's - /// 771). The per-record checks and the atomic replace are unchanged; see + /// Each `save_item` fsyncs the items directory — right for one record, + /// ruinous for the hundreds a fork clones. The stale-temp sweep is + /// throttled per directory inside `write_atomic`; unthrottled it cost 22 ms + /// of directory scan per item, 34 s for one fork's 771. The per-record + /// checks and the atomic replace are unchanged; see /// [`crate::utils::write_atomic_batch`]. /// /// Ordering stays the caller's: items, then their turns, then the thread @@ -12968,10 +12969,9 @@ impl RuntimeThreadManager { let mut saved_turn_ids = Vec::new(); let mut saved_item_ids = Vec::new(); let persistence = (|| -> Result<()> { - // Every cloned item in one batch. Each `save_item` sweeps the - // whole items directory and fsyncs it, which is right for a single - // record and ruinous for the hundreds a fork clones — the sweep - // gives the same answer every time. Items first, turns next, the + // Every cloned item in one batch. Each `save_item` fsyncs the items + // directory, which is right for a single record and ruinous for + // the hundreds a fork clones. Items first, turns next, the // thread record that makes them reachable last: the commit point is // unchanged, and the ids are recorded before the batch so a partial // write is still cleaned up. diff --git a/crates/tui/src/utils.rs b/crates/tui/src/utils.rs index b6fba0ae53..5b509ccebe 100644 --- a/crates/tui/src/utils.rs +++ b/crates/tui/src/utils.rs @@ -417,8 +417,9 @@ fn write_atomic_with_permissions( /// once for the whole set — see [`write_atomic_batch`]. #[derive(Clone, Copy, PartialEq, Eq)] enum AtomicWriteScope { - /// Sweep this directory for stale temp files and fsync it: what a single - /// write should do, and what every caller of `write_atomic` gets. + /// Sweep this directory for stale temp files (at most once per + /// `STALE_AFTER`) and fsync it: what a single write should do, and what + /// every caller of `write_atomic` gets. Single, /// Leave both to the caller. Used only by [`write_atomic_batch`]. Batch, @@ -426,18 +427,18 @@ enum AtomicWriteScope { /// Write many files, and pay each directory's costs once. /// -/// Every [`write_atomic`] call sweeps its directory for stale temp files and -/// fsyncs that directory, which is exactly right for one record at a time. A -/// caller publishing a thousand records into one directory pays that thousand -/// times, though, and the sweep is the same answer every time: scanning a -/// store directory of tens of thousands of entries per file is minutes of work -/// that buys nothing (measured: 22 ms per item write, 34 s for one fork's -/// clone). +/// Every [`write_atomic`] call fsyncs its directory, which is exactly right +/// for one record at a time. A caller publishing a thousand records into one +/// directory pays that thousand times, though. The stale-temp sweep is +/// already throttled to once per directory per `STALE_AFTER`, because +/// scanning a store directory of tens of thousands of entries per file is +/// minutes of work that buys nothing (measured before the throttle: 22 ms per +/// item write, 34 s for one fork's clone). /// -/// The batch sweeps each directory once, writes every file with the same -/// atomic replace and per-file data sync, and fsyncs each directory once at -/// the end. Per-file durability is unchanged; only the per-file directory work -/// is hoisted out of the loop. +/// The batch goes through the same throttled sweep once per directory, writes +/// every file with the same atomic replace and per-file data sync, and fsyncs +/// each directory once at the end. Per-file durability is unchanged; only the +/// per-file directory work is hoisted out of the loop. /// /// Ordering is still the caller's job: a batch that publishes a graph of /// records must write its commit record last, as the single-write callers do. @@ -451,9 +452,7 @@ pub fn write_atomic_batch(files: &[(PathBuf, Vec)]) -> std::io::Result<()> { } } for parent in &parents { - if is_codewhale_owned_state_dir(parent) { - sweep_stale_atomic_write_temps(parent); - } + sweep_owned_dir_if_due(parent); } for (path, contents) in files { write_atomic_scoped( @@ -571,14 +570,12 @@ fn write_atomic_scoped( builder.permissions(fs::Permissions::from_mode(0o666)); } - // Reclaim our own strays before adding another (see the function docs). + // Reclaim our own strays before adding another, at most once per + // directory per `STALE_AFTER` (see `sweep_stale_atomic_write_temps`). // Private permission policy is also used for user-chosen destinations // such as `/save `; only sweep Codewhale-owned state/config dirs. - if permission_policy == AtomicWritePermissions::Private - && scope == AtomicWriteScope::Single - && is_codewhale_owned_state_dir(parent) - { - sweep_stale_atomic_write_temps(parent); + if permission_policy == AtomicWritePermissions::Private && scope == AtomicWriteScope::Single { + sweep_owned_dir_if_due(parent); } // Validated before any temp exists, so a refused name leaves nothing. @@ -688,6 +685,34 @@ fn is_codewhale_owned_state_dir(dir: &Path) -> bool { }) } +const STALE_AFTER: std::time::Duration = std::time::Duration::from_secs(60 * 60); + +fn sweep_owned_dir_if_due(dir: &Path) { + use std::collections::HashMap; + use std::sync::{Mutex, OnceLock, PoisonError}; + use std::time::Instant; + + static LAST_SWEPT: OnceLock>> = OnceLock::new(); + let now = Instant::now(); + { + let mut last_swept = LAST_SWEPT + .get_or_init(Default::default) + .lock() + .unwrap_or_else(PoisonError::into_inner); + if last_swept + .get(dir) + .is_some_and(|swept| now.saturating_duration_since(*swept) < STALE_AFTER) + { + return; + } + last_swept.retain(|_, swept| now.saturating_duration_since(*swept) < STALE_AFTER); + last_swept.insert(dir.to_path_buf(), now); + } + if is_codewhale_owned_state_dir(dir) { + sweep_stale_atomic_write_temps(dir); + } +} + /// Remove `.tmpXXXXXX` files this writer stranded in `dir` on an earlier run. /// /// `NamedTempFile` deletes itself on drop, so an ordinary failure — or an @@ -712,7 +737,6 @@ fn is_codewhale_owned_state_dir(dir: &Path) -> bool { /// - **Best effort** — every failure is ignored; this must never turn a /// successful write into an error. fn sweep_stale_atomic_write_temps(dir: &Path) { - const STALE_AFTER: std::time::Duration = std::time::Duration::from_secs(60 * 60); let Ok(entries) = fs::read_dir(dir) else { return; }; @@ -1198,9 +1222,10 @@ mod atomic_write_tests { /// A batch writes every file, and pays the directory's hygiene once. /// - /// The per-file path sweeps the directory for stale temp files and fsyncs - /// it on every call; a fork publishing hundreds of cloned items paid that - /// hundreds of times (22 ms of directory scan each, 34 s for one fork). + /// The per-file path fsyncs the directory on every call, and used to sweep + /// it for stale temp files on every call too; a fork publishing hundreds + /// of cloned items paid that hundreds of times (22 ms of directory scan + /// each, 34 s for one fork). /// What must not change: every file lands with its content, a stray temp /// file is still reclaimed, and none of ours is left behind. #[test] From 8fee238b229b2d1d3b23f9aa1ca26fc207f48d7e Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 16:20:37 +0000 Subject: [PATCH 36/46] perf(runtime): /v1/usage reads the turn store once instead of once per thread From the 0.10.2 Rust optimization audit (io lens, judged safe-now). The cost. aggregate_usage called list_turns_for_thread for every thread. Each call is list_all_turns, which reads and parses every turn file, plus a retain. So one GET /v1/usage cost T x N file reads, inline on a Tokio worker. The change follows the pattern thread_list_facts and running_threads already use (#3757). One spawn_blocking on a cloned store lists the threads and reads all turns once, grouped by thread id. The async side accumulates in the same thread order and still validates each thread id. A join error reads 'Runtime usage aggregation task failed', matching aggregate_usage_for_thread. Totals and ordering are unchanged. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01BsXVvReYca2UxzimQtj7Mj --- crates/tui/src/runtime_threads.rs | 40 ++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/crates/tui/src/runtime_threads.rs b/crates/tui/src/runtime_threads.rs index 2d1290410c..8a2f7dda92 100644 --- a/crates/tui/src/runtime_threads.rs +++ b/crates/tui/src/runtime_threads.rs @@ -10603,12 +10603,30 @@ impl RuntimeThreadManager { until: Option>, group_by: UsageGroupBy, ) -> Result { + let store = self.store.clone(); + let (threads, turns_by_thread) = tokio::task::spawn_blocking(move || { + let threads = store.list_threads()?; + let mut turns_by_thread: HashMap> = HashMap::new(); + if let Some(first) = threads.first() { + validated_record_id(&first.id, "thread id")?; + for turn in store.list_all_turns()? { + turns_by_thread + .entry(turn.thread_id.clone()) + .or_default() + .push(turn); + } + } + Ok::<_, anyhow::Error>((threads, turns_by_thread)) + }) + .await + .context("Runtime usage aggregation task failed")??; + let mut buckets: std::collections::BTreeMap = std::collections::BTreeMap::new(); let mut totals = UsageTotals::default(); - for thread in self.store.list_threads()? { - let turns = self.store.list_turns_for_thread(&thread.id)?; - for turn in turns { + for thread in &threads { + validated_record_id(&thread.id, "thread id")?; + for turn in turns_by_thread.get(&thread.id).into_iter().flatten() { let parent_route = turn.effective_route_envelope(); let parent_dispatched_at = parent_route .as_ref() @@ -10622,8 +10640,8 @@ impl RuntimeThreadManager { group_by, parent_route.as_ref(), usage, - &turn, - &thread, + turn, + thread, ); } for receipt in &turn.decision_receipts { @@ -10650,8 +10668,8 @@ impl RuntimeThreadManager { &mut buckets, group_by, child, - &turn, - &thread, + turn, + thread, ); } } @@ -10663,8 +10681,8 @@ impl RuntimeThreadManager { group_by, &drop_record.route, drop_record.reason, - &turn, - &thread, + turn, + thread, ); } } @@ -10678,8 +10696,8 @@ impl RuntimeThreadManager { &mut buckets, group_by, turn.routed_usage_dropped_records, - &turn, - &thread, + turn, + thread, ); } } From f518a5cad98c82b67205e82b10b9d51f3ff90fab Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 16:20:37 +0000 Subject: [PATCH 37/46] perf(engine): tool contexts share the turn-start history instead of copying it From the 0.10.2 Rust optimization audit (memory lens, judged safe-now). The cost. SessionObjectSnapshot.messages was Vec, and build_tool_context_for_turn filled it with self.session.messages.clone().into(). AppendLog::into_inner deep-copies whenever the Arc is shared, and the engine always shares it. ToolContext derives Clone, and live_tool_context clones the registry context per call. So about 8 full copies of the history stayed resident per turn, plus 2-3 per tool call. The change. The field is Arc> and is filled from the existing AppendLog::snapshot(). SessionObjectSnapshot::new takes impl Into>>, so the existing vec![] callers compile unchanged. Every reader only iterates, indexes or borrows as &[Message]. The snapshot is immutable: AppendLog mutates only through Arc::make_mut, so tools still see exactly the turn-start view. Debug output is identical. Nothing model-visible, persisted or serialized changes. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01BsXVvReYca2UxzimQtj7Mj --- crates/tui/src/core/engine.rs | 4 ++-- crates/tui/src/rlm/session.rs | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/tui/src/core/engine.rs b/crates/tui/src/core/engine.rs index 8088475ea0..7bcad4f191 100644 --- a/crates/tui/src/core/engine.rs +++ b/crates/tui/src/core/engine.rs @@ -7273,7 +7273,7 @@ impl Engine { route.model.clone(), self.session.workspace.clone(), self.session.system_prompt.clone(), - self.session.messages.clone().into(), + self.session.messages.snapshot(), )) .with_cancel_token(self.cancel_token.clone()) .with_origin_turn_id(self.turn_counter.to_string()); @@ -7329,7 +7329,7 @@ impl Engine { route.model.clone(), self.session.workspace.clone(), self.session.system_prompt.clone(), - self.session.messages.clone().into(), + self.session.messages.snapshot(), )) .with_cancel_token(self.cancel_token.clone()) .with_shell_policy(authority.shell_policy()) diff --git a/crates/tui/src/rlm/session.rs b/crates/tui/src/rlm/session.rs index 220fbeb8fd..1b14e3cb01 100644 --- a/crates/tui/src/rlm/session.rs +++ b/crates/tui/src/rlm/session.rs @@ -127,7 +127,7 @@ pub struct SessionObjectSnapshot { pub model: String, pub workspace: PathBuf, pub system_prompt: Option, - pub messages: Vec, + pub messages: Arc>, } impl SessionObjectSnapshot { @@ -137,14 +137,14 @@ impl SessionObjectSnapshot { model: String, workspace: PathBuf, system_prompt: Option, - messages: Vec, + messages: impl Into>>, ) -> Self { Self { session_id, model, workspace, system_prompt, - messages, + messages: messages.into(), } } From 85331b58f308e38c91dbe44cf706059fc3ee9222 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 16:20:37 +0000 Subject: [PATCH 38/46] perf(compaction): one pressure walk per model step, and JSON is sized without allocating MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From the 0.10.2 Rust optimization audit (turn-loop lens, judged safe-now). The redundant walk. - run_auto_compaction first computes live = turn.live_input_tokens_for_compaction(messages, system, …), which is already max(E, billed + growth). E is estimate_input_tokens_for_pressure over the same messages and system prompt. - It then gated on compaction_pressure_reached_with_billed(messages, system, config, live). Below the threshold, which is most steps, that recomputed the same E with a second full-history walk before returning max(E, live) >= threshold. - Since live >= E, that equals live >= threshold. The gate now compares live with the threshold directly. - When live is None, E is 0 and both forms are 0 >= threshold. - compaction_pressure_reached_with_billed keeps its other callers, and the sentence in its doc that described the double pass is removed. JSON sizing. estimate_tokens_for_message serialized each ToolUse, ServerToolUse, ToolSearchToolResult and CodeExecutionToolResult value to a String only to take its length. compact_json_len now counts the bytes through serde_json::to_writer into the existing utils::CountingWriter. That gives the same compact-formatter count with no allocation. ToolUse keeps its 100-token fallback. Every estimate and every compaction decision is unchanged. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01BsXVvReYca2UxzimQtj7Mj --- crates/tui/src/compaction.rs | 21 ++++++++++++++------- crates/tui/src/core/engine/compaction.rs | 12 ++++++------ 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/crates/tui/src/compaction.rs b/crates/tui/src/compaction.rs index 29d2f36f4c..3adce23231 100644 --- a/crates/tui/src/compaction.rs +++ b/crates/tui/src/compaction.rs @@ -572,9 +572,9 @@ pub(crate) fn estimate_tokens_for_message(message: &Message, include_thinking: b // assistant messages and across later user turns. ContentBlock::Thinking { thinking, .. } if include_thinking => thinking.len() / 4, ContentBlock::Thinking { .. } => 0, - ContentBlock::ToolUse { input, .. } => serde_json::to_string(input) - .map(|s| s.len() / 4) - .unwrap_or(100), + ContentBlock::ToolUse { input, .. } => { + compact_json_len(input).map_or(100, |len| len / 4) + } ContentBlock::ToolResult { content, content_blocks, @@ -596,15 +596,23 @@ pub(crate) fn estimate_tokens_for_message(message: &Message, include_thinking: b // tiles are typically ~1k tokens); erring high compacts slightly // early rather than overflowing. ContentBlock::ImageUrl { .. } => IMAGE_TOKEN_ESTIMATE, - ContentBlock::ServerToolUse { input, .. } => input.to_string().len() / 4, + ContentBlock::ServerToolUse { input, .. } => { + compact_json_len(input).unwrap_or_default() / 4 + } ContentBlock::ToolSearchToolResult { content, .. } | ContentBlock::CodeExecutionToolResult { content, .. } => { - content.to_string().len() / 4 + compact_json_len(content).unwrap_or_default() / 4 } }) .sum::() } +fn compact_json_len(value: &serde_json::Value) -> Option { + let mut counter = crate::utils::CountingWriter::new(); + serde_json::to_writer(&mut counter, value).ok()?; + Some(counter.count()) +} + /// Conservative flat token estimate for an inline image (`ContentBlock::ImageUrl`). /// Vision models bill images by resized tile count; ~1k tokens is a safe /// mid-range estimate that keeps the compaction trigger from under-reading an @@ -753,8 +761,7 @@ pub fn compaction_pressure_reached_with_billed( // Billing alone proving pressure short-circuits the walk (#perf-r5): // `estimated.max(billed) >= threshold` is unconditionally true when // `billed >= threshold`, so estimating cannot change the answer and the - // O(transcript) pass is skipped. Over-pressure sessions pay this check - // multiple times per step (pressure gate + decision re-check). + // O(transcript) pass is skipped. if billed >= config.token_threshold { return true; } diff --git a/crates/tui/src/core/engine/compaction.rs b/crates/tui/src/core/engine/compaction.rs index 8bbefcb60c..eda542bf08 100644 --- a/crates/tui/src/core/engine/compaction.rs +++ b/crates/tui/src/core/engine/compaction.rs @@ -524,12 +524,12 @@ impl Engine { self.session.latest_parent_input_tokens, ); let prepared = if !*auto_compaction_suppressed - && crate::compaction::compaction_pressure_reached_with_billed( - &self.session.messages, - self.session.system_prompt.as_ref(), - &auto_compaction_config, - billed_input_tokens, - ) { + && auto_compaction_config.enabled + && billed_input_tokens + .and_then(|tokens| usize::try_from(tokens).ok()) + .unwrap_or(0) + >= auto_compaction_config.token_threshold + { let mut prepared = self.prepare_compaction_envelope(auto_compaction_config); prepared.tools = active_tools.map(<[Tool]>::to_vec); Some(prepared) From 1f7ebc9235cf13b3478c492046e8a4eee34c1cd3 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 16:21:22 +0000 Subject: [PATCH 39/46] perf(tui): drop four shell ocean passes that repeat the full-frame pass From the 0.10.2 Rust optimization audit (ui-hot-path lens, judged safe-now). The default theme is underwater, so this runs on every frame. The underwater shell block painted the full frame with the surface_bg ground and then repainted four sub-rects (strip, side work, work chat, preview) with the same ground. Why the four repaints changed nothing: - Nothing is drawn between the full-frame pass and those four. - Each pass uses the same column, theme, ground and ocean semantic-surface slice. - The frame area is the buffer area, so the first pass already visits every cell. - Water depends only on the absolute row: paint_native pins the viewport to y, and paint_matching_native passes no samples. The four passes are removed. The composer, plugin-CTA, footer and workbar passes, which use different grounds, stay. The comment above the full-frame pass no longer implies more surface_bg passes follow. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01BsXVvReYca2UxzimQtj7Mj --- crates/tui/src/tui/ui/frame.rs | 41 +++++----------------------------- 1 file changed, 5 insertions(+), 36 deletions(-) diff --git a/crates/tui/src/tui/ui/frame.rs b/crates/tui/src/tui/ui/frame.rs index 84fb88b44f..c1b9aef433 100644 --- a/crates/tui/src/tui/ui/frame.rs +++ b/crates/tui/src/tui/ui/frame.rs @@ -1922,10 +1922,11 @@ pub(crate) fn render(f: &mut Frame, app: &mut App, _config: &Config) -> Option<( // frame-derived explicit styled-surface mask, including aliased colors. if let Some(column) = shell_ocean { // The working canvas may keep a small responsive gutter, but the water - // does not stop at that content edge. Paint the cleared terminal floor - // first so wide layouts read as one ocean rather than a blue card - // floating between black banks. `paint_matching` leaves every semantic - // widget background untouched. + // does not stop at that content edge. One full-frame `surface_bg` pass + // paints the cleared terminal floor and every ordinary surface band + // so wide layouts read as one ocean rather than a blue card floating + // between black banks. `paint_matching` leaves every semantic widget + // background untouched. column.paint_matching_native( size, f.buffer_mut(), @@ -1933,38 +1934,6 @@ pub(crate) fn render(f: &mut Frame, app: &mut App, _config: &Config) -> Option<( &app.ui_theme, &app.viewport.ocean_semantic_surfaces, ); - if top_work_strip_height > 0 { - column.paint_matching_native( - body_chunks[strip_slot], - f.buffer_mut(), - app.ui_theme.surface_bg, - &app.ui_theme, - &app.viewport.ocean_semantic_surfaces, - ); - } - if let Some(side_area) = side_work_area { - column.paint_matching_native( - side_area, - f.buffer_mut(), - app.ui_theme.surface_bg, - &app.ui_theme, - &app.viewport.ocean_semantic_surfaces, - ); - } - column.paint_matching_native( - work_chat_area, - f.buffer_mut(), - app.ui_theme.surface_bg, - &app.ui_theme, - &app.viewport.ocean_semantic_surfaces, - ); - column.paint_matching_native( - body_chunks[preview_slot], - f.buffer_mut(), - app.ui_theme.surface_bg, - &app.ui_theme, - &app.viewport.ocean_semantic_surfaces, - ); if plugin_cta_height > 0 { column.paint_matching_native( body_chunks[plugin_cta_slot], From cadd72156ec0345ad45060d4f173d8a09a69dfbf Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 16:21:22 +0000 Subject: [PATCH 40/46] perf(subagent): checkpoints project only the bounded tail, not the whole child history twice From the 0.10.2 Rust optimization audit (memory lens, judged safe-now). The cost. - insert_subagent_full_transcript_handle and build_subagent_checkpoint each ran safe_tool_result_message_projection over the whole child history, which is a to_vec copy, only to keep a 256 KiB / 1 MiB tail. - Sizing each message took a serde_json::to_string allocation. - On the Core path, the transcript writer's projected sync found its cursor already advanced by the raw sync, so the copy bought nothing. The change. - bounded_tail_messages walks newest-first and projects one message at a time until the budget, keeping the always-keep-final rule. - approximate_message_bytes counts serialized bytes with CountingWriter. The count is identical and still falls back to 1024. - The writer projects only its unsynced suffix. - Callers pass the raw history. The doc comment says it returns projected messages, because re-projecting is lossy. The persisted checkpoint and transcript bytes are unchanged. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01BsXVvReYca2UxzimQtj7Mj --- crates/tui/src/tools/subagent/mod.rs | 61 ++++++++++++++++------------ 1 file changed, 36 insertions(+), 25 deletions(-) diff --git a/crates/tui/src/tools/subagent/mod.rs b/crates/tui/src/tools/subagent/mod.rs index 81e66a3b4a..a0335d4ce1 100644 --- a/crates/tui/src/tools/subagent/mod.rs +++ b/crates/tui/src/tools/subagent/mod.rs @@ -10274,15 +10274,28 @@ impl SubAgentTranscriptArtifactWriter { }) } - fn sync_messages(&mut self, messages: &[Message], durable: bool) -> Result<()> { - if messages.len() < self.synced_live_messages { - return Err(anyhow!( + fn unsynced_messages<'a>(&self, messages: &'a [Message]) -> Result<&'a [Message]> { + messages.get(self.synced_live_messages..).ok_or_else(|| { + anyhow!( "sub-agent transcript history shrank from {} to {} messages", self.synced_live_messages, messages.len() - )); - } - self.append_messages(&messages[self.synced_live_messages..], durable)?; + ) + }) + } + + fn sync_messages(&mut self, messages: &[Message], durable: bool) -> Result<()> { + let unsynced = self.unsynced_messages(messages)?; + self.append_messages(unsynced, durable)?; + self.synced_live_messages = messages.len(); + Ok(()) + } + + fn sync_projected_messages(&mut self, messages: &[Message], durable: bool) -> Result<()> { + let unsynced = crate::image_attach::safe_tool_result_message_projection( + self.unsynced_messages(messages)?, + ); + self.append_messages(&unsynced, durable)?; self.synced_live_messages = messages.len(); Ok(()) } @@ -14415,11 +14428,8 @@ async fn insert_subagent_full_transcript_handle( // bounded message tail — embedding it verbatim would duplicate that tail // inside one payload. Keep checkpoint metadata, drop its messages, and // record how much of the true history the bounded tail omits. - let projected_messages = crate::image_attach::safe_tool_result_message_projection(messages); - let (bounded_messages, omitted_messages) = bounded_tail_messages( - &projected_messages, - SUBAGENT_TRANSCRIPT_MESSAGE_BUDGET_BYTES, - ); + let (bounded_messages, omitted_messages) = + bounded_tail_messages(messages, SUBAGENT_TRANSCRIPT_MESSAGE_BUDGET_BYTES); let checkpoint_meta = checkpoint.map(|checkpoint| SubAgentCheckpoint { omitted_messages: checkpoint.message_count, messages: Vec::new(), @@ -14427,7 +14437,7 @@ async fn insert_subagent_full_transcript_handle( }); let transcript_artifact = transcript_artifact.map(|writer| { let synced = - match writer.sync_messages(&projected_messages, *status != SubAgentStatus::Running) { + match writer.sync_projected_messages(messages, *status != SubAgentStatus::Running) { Ok(()) => true, Err(err) => { tracing::warn!( @@ -14439,7 +14449,7 @@ async fn insert_subagent_full_transcript_handle( false } }; - writer.metadata(synced && writer.synced_live_messages == projected_messages.len()) + writer.metadata(synced && writer.synced_live_messages == messages.len()) }); let payload = json!({ "kind": "subagent_full_transcript", @@ -14544,23 +14554,27 @@ fn bound_subagent_tool_result( /// budgets. Exact JSON size via serde; unserializable messages (should not /// happen) count as 1 KiB so they still consume budget. fn approximate_message_bytes(message: &Message) -> usize { - serde_json::to_string(message).map_or(1024, |s| s.len()) + let mut counter = crate::utils::CountingWriter::new(); + serde_json::to_writer(&mut counter, message).map_or(1024, |()| counter.count()) } -/// Keep the most recent messages whose combined approximate size fits -/// `budget_bytes`. Always keeps at least the final message (even if it alone -/// exceeds the budget) so a non-empty history stays continuable. Returns the -/// retained tail and how many older messages were omitted. +/// Keep the most recent messages, after `safe_tool_result_message_projection`, +/// whose combined approximate size fits `budget_bytes`. Always keeps at least +/// the final message (even if it alone exceeds the budget) so a non-empty +/// history stays continuable. Returns the retained tail and how many older +/// messages were omitted. fn bounded_tail_messages(messages: &[Message], budget_bytes: usize) -> (Vec, usize) { let mut kept_rev: Vec = Vec::new(); let mut used = 0usize; for message in messages.iter().rev() { - let size = approximate_message_bytes(message); + let projected = + crate::image_attach::safe_tool_result_message_projection(std::slice::from_ref(message)); + let size: usize = projected.iter().map(approximate_message_bytes).sum(); if !kept_rev.is_empty() && used.saturating_add(size) > budget_bytes { break; } used = used.saturating_add(size); - kept_rev.push(message.clone()); + kept_rev.extend(projected); } kept_rev.reverse(); let omitted = messages.len().saturating_sub(kept_rev.len()); @@ -14576,11 +14590,8 @@ fn build_subagent_checkpoint( ) -> SubAgentCheckpoint { let created_at_ms = epoch_millis_now(); let checkpoint_id = format!("{agent_id}:step:{steps_taken}:ts:{created_at_ms}"); - let projected_messages = crate::image_attach::safe_tool_result_message_projection(messages); - let (bounded_messages, omitted_messages) = bounded_tail_messages( - &projected_messages, - SUBAGENT_CHECKPOINT_MESSAGE_BUDGET_BYTES, - ); + let (bounded_messages, omitted_messages) = + bounded_tail_messages(messages, SUBAGENT_CHECKPOINT_MESSAGE_BUDGET_BYTES); SubAgentCheckpoint { checkpoint_id: checkpoint_id.clone(), agent_id: agent_id.to_string(), From ae17d531c1dc8adc3c1c3df18cd856dab32902de Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 16:21:22 +0000 Subject: [PATCH 41/46] perf(session): retention lists records without the late-usage cost overlay From the 0.10.2 Rust optimization audit (io lens, judged safe-now). Every save ends with cleanup_old_sessions, which called list_session_records. That applied apply_late_usage_to_metadata to every saved session: a lock-file open, a blocking fd_lock read, a tombstone read and a ledger parse per record. Retention only orders and archives by metadata it never reads costs from, so all of that was wasted on every save. The overlay moves into list_sessions, the only other caller, which applies it to each record after listing. Retention gets the raw metadata, and the session list a person sees is unchanged. The list_session_records doc says it is list_sessions without the overlay. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01BsXVvReYca2UxzimQtj7Mj --- crates/tui/src/session_manager.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/crates/tui/src/session_manager.rs b/crates/tui/src/session_manager.rs index 449e8bc80d..a613eae541 100644 --- a/crates/tui/src/session_manager.rs +++ b/crates/tui/src/session_manager.rs @@ -3678,11 +3678,15 @@ impl SessionManager { Ok(self .list_session_records()? .into_iter() - .map(|(_, metadata)| metadata) + .map(|(_, mut metadata)| { + self.apply_late_usage_to_metadata(&mut metadata); + metadata + }) .collect()) } - /// [`Self::list_sessions`], keeping the file each record was read from. + /// [`Self::list_sessions`] without the late-usage cost overlay, keeping + /// the file each record was read from. /// A record's file is not always `.json`: early builds wrote /// `session_.json`, and retention must address those by path. fn list_session_records(&self) -> std::io::Result> { @@ -3693,9 +3697,8 @@ impl SessionManager { let path = entry.path(); if path.extension().is_some_and(|ext| ext == "json") - && let Ok(mut session) = Self::load_session_metadata(&path) + && let Ok(session) = Self::load_session_metadata(&path) { - self.apply_late_usage_to_metadata(&mut session); sessions.push((path, session)); } } From 9413b158fab6d09692ea1ac2dd2ef084969cc7fa Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 16:21:22 +0000 Subject: [PATCH 42/46] perf(pet): the pet tick skips its tty probe and view build when no pet runs From the 0.10.2 Rust optimization audit (ui-hot-path lens, judged safe-now). The cost. pet_watch::tick runs on every event-loop iteration. Every 30 ms it called crossterm window_size(), which on unix opens /dev/tty, does an ioctl and closes it. It also read CODEWHALE_PET_GRAPHICS, allocated the locale, ran ShellPhase::from_app and built a live::View. All of that happened even when no pet worker existed, and the view was then dropped unread. The change. last_tick still advances unconditionally inside the 30 ms gate, so timing is unchanged. The phase, probe, env read, locale and View build all move inside 'if let Some(worker)'. The notices drain reads app.pet_watch.worker, which ends the mutable borrow before ShellPhase::from_app. A running pet sees exactly the same view. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01BsXVvReYca2UxzimQtj7Mj --- crates/tui/src/tui/pet_watch/mod.rs | 76 +++++++++++++++-------------- 1 file changed, 39 insertions(+), 37 deletions(-) diff --git a/crates/tui/src/tui/pet_watch/mod.rs b/crates/tui/src/tui/pet_watch/mod.rs index 89634846b1..0c43d57ffb 100644 --- a/crates/tui/src/tui/pet_watch/mod.rs +++ b/crates/tui/src/tui/pet_watch/mod.rs @@ -457,10 +457,6 @@ pub fn tick(app: &mut App, now: Instant) { if visible && app.pet_watch.avatars.refresh(&app.workspace, now) { app.needs_redraw = true; } - let waiting = matches!( - ShellPhase::from_app(app), - ShellPhase::Waiting | ShellPhase::Approval - ); let sound_allowed = visible && app.onboarding == crate::tui::app::OnboardingState::None && !app.notification_settings.quiet @@ -513,41 +509,47 @@ pub fn tick(app: &mut App, now: Instant) { .last_tick .is_none_or(|t| now.saturating_duration_since(t) >= Duration::from_millis(30)) { - let a = state.area.unwrap_or(Rect::new(0, 0, 40, 8)); - let cell = crossterm::terminal::window_size() - .ok() - .filter(|s| s.columns > 0 && s.rows > 0 && s.width > 0 && s.height > 0) - .map(|s| { - ( - f64::from(s.width) / f64::from(s.columns), - f64::from(s.height) / f64::from(s.rows), - ) - }) - .unwrap_or((8.0, 16.0)); - let next = live::View { - locale: app.ui_locale.tag().into(), - width: a.width.clamp(1, 512), - height: a.height.saturating_sub(1).clamp(1, 256), - cell_width: cell.0, - cell_height: cell.1, - motion, - pixels: !full::canonical(state) - && matches!(state.avatars.key.as_str(), "" | "whale") - && crate::tui::mark::kitty_graphics_supported() - && app.synchronized_output_enabled - && std::env::var("CODEWHALE_PET_GRAPHICS").as_deref() != Ok("braille"), - visible, - waiting, - sound: state.sound_requested && sound_allowed, - }; - if let Some(worker) = &state.worker - && let Ok(mut view) = worker.view.lock() - { - *view = next; - } state.last_tick = Some(now); + let state = &app.pet_watch; + if let Some(worker) = &state.worker { + let waiting = matches!( + ShellPhase::from_app(app), + ShellPhase::Waiting | ShellPhase::Approval + ); + let a = state.area.unwrap_or(Rect::new(0, 0, 40, 8)); + let cell = crossterm::terminal::window_size() + .ok() + .filter(|s| s.columns > 0 && s.rows > 0 && s.width > 0 && s.height > 0) + .map(|s| { + ( + f64::from(s.width) / f64::from(s.columns), + f64::from(s.height) / f64::from(s.rows), + ) + }) + .unwrap_or((8.0, 16.0)); + let next = live::View { + locale: app.ui_locale.tag().into(), + width: a.width.clamp(1, 512), + height: a.height.saturating_sub(1).clamp(1, 256), + cell_width: cell.0, + cell_height: cell.1, + motion, + pixels: !full::canonical(state) + && matches!(state.avatars.key.as_str(), "" | "whale") + && crate::tui::mark::kitty_graphics_supported() + && app.synchronized_output_enabled + && std::env::var("CODEWHALE_PET_GRAPHICS").as_deref() != Ok("braille"), + visible, + waiting, + sound: state.sound_requested && sound_allowed, + }; + if let Ok(mut view) = worker.view.lock() { + *view = next; + } + } } - let notices: Vec<_> = state + let notices: Vec<_> = app + .pet_watch .worker .as_ref() .map(|w| w.notices.try_iter().take(16).collect()) From 79735df1ae4018c848556f6d562b69461cf19ad8 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 16:21:22 +0000 Subject: [PATCH 43/46] build(tui): no doctest pass for codewhale-tui; test-only deps move to dev-dependencies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From the 0.10.2 Rust optimization audit (build-size lens, judged safe-now). Doctests. codewhale-tui has no runnable doc example: its fences are 7 ignore, 1 rust,ignore, and text/json/sh blocks. Yet 'cargo test --doc' still built a rustdoc test harness for the crate on every run. Its [lib] now sets doctest = false. BUILD_PERFORMANCE.md says why. The workspace doctest inventory (3 passing / 8 ignored across 21 crates, all 8 ignored in codewhale-tui) is marked as not re-measured. Dependencies. schemars and rusqlite are used only under cfg(test): the schemars derive is cfg_attr(test, …), and rusqlite appears only in test modules. Both move to [dev-dependencies] with the same versions and features. rusqlite stays in the workspace's normal build through codewhale-memory, codewhale-state and codewhale-app-server, so its features are unchanged. Cargo.lock is unchanged, and --locked still resolves. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01BsXVvReYca2UxzimQtj7Mj --- crates/tui/Cargo.toml | 5 +++-- docs/BUILD_PERFORMANCE.md | 10 ++++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/crates/tui/Cargo.toml b/crates/tui/Cargo.toml index 6b44c76106..257da8502e 100644 --- a/crates/tui/Cargo.toml +++ b/crates/tui/Cargo.toml @@ -36,6 +36,7 @@ rusty-alloc = [] [lib] name = "codewhale_tui" path = "src/lib.rs" +doctest = false [dependencies] # Independent terminal kit, pinned and vendored for reproducible binary builds. @@ -87,7 +88,6 @@ jsonwebtoken = { version = "10", default-features = false, features = ["aws_lc_r ratatui = { version = "=0.30.2", features = ["unstable-rendered-line-info"] } regex = "1.11" reqwest = { workspace = true, features = ["blocking", "stream", "form", "http2"] } -rusqlite.workspace = true rquickjs.workspace = true rmcp = { version = "3.2.0", default-features = false, features = ["auth", "client"] } rustls.workspace = true @@ -103,7 +103,6 @@ syntect = { version = "5.2", default-features = false, features = ["default-synt serde.workspace = true serde_json = { workspace = true, features = ["preserve_order", "raw_value"] } yaml-rust2 = { workspace = true } -schemars = { version = "1.2.1", features = ["derive", "preserve_order"] } shellexpand = "3" # Grammar-exact Rust parsing for the post-edit syntax gate (#6204). `syn` is # already in the lockfile as a proc-macro build dependency; this reuses that @@ -172,6 +171,8 @@ wiremock = "0.6" tiny_http = "0.12" pretty_assertions = "1.4" rio-vt = "0.5.1" +rusqlite.workspace = true +schemars = { version = "1.2.1", features = ["derive", "preserve_order"] } tokio-tungstenite = "0.30" [target.'cfg(unix)'.dependencies] diff --git a/docs/BUILD_PERFORMANCE.md b/docs/BUILD_PERFORMANCE.md index 3f0aa739a3..4b1ea29b22 100644 --- a/docs/BUILD_PERFORMANCE.md +++ b/docs/BUILD_PERFORMANCE.md @@ -210,10 +210,12 @@ every workspace crate. ### A2 nextest in CI -`cargo test --workspace --all-features --locked --doc` inventories -**3 passing / 8 ignored doctests across 21 crates**; CI keeps them as a -separate step next to `cargo nextest run --workspace --all-features ---locked --profile ci`. +`cargo test --workspace --all-features --locked --doc` inventoried +**3 passing / 8 ignored doctests across 21 crates**. All 8 ignored fences +were in codewhale-tui, which has no runnable doc example and now sets +`doctest = false`, so its doc pass is skipped (the new inventory is not +re-measured yet). CI keeps the doc run as a separate step next to +`cargo nextest run --workspace --all-features --locked --profile ci`. ### A3/A4 (not adopted, measured) From 2f8299361ad28fcdd47d0bd5436efb3556cbbb1e Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 16:21:38 +0000 Subject: [PATCH 44/46] docs(changelog): 0.10.2 notes for the UX polish and performance slices User-facing bullets for d40be2b1f0 through 79735df1ae: setup honesty, the Ollama first run, failure severity and retry notices, file-edit cards and status words, delegated-agent cards, markdown headings and tables, approval labels in every locale, the Work mode name, the three locale strings, and the UI-thread and memory work. crates/tui/CHANGELOG.md is regenerated by scripts/sync-changelog.sh. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01BsXVvReYca2UxzimQtj7Mj --- CHANGELOG.md | 30 ++++++++++++++++++++++++++++++ crates/tui/CHANGELOG.md | 30 ++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 66dd9de802..8492311b96 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,36 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 with "Job … not found" (#6931). - In `/setup`, `u` and `d` act only on the working-agreement step. Pressed on another step they no longer close the wizard and switch your agreement off. +- `/setup` no longer says "You're ready." when no model is connected; its last + step asks for a provider and lists the P and C keys it accepts. A rejected + API key's reason stays visible at narrow widths. +- First run with a local Ollama model continues to the trust question and the + ready screen instead of ending onboarding, and the next launch no longer + starts onboarding again. +- A turn that ended in failure reads as an error, live and after resume. Retry + notices stay in the transcript instead of flashing a red error that hid the + recovery. +- File edits show as patches with their change summary once. Tool status + words read `done`, `failed` and `loaded, not run` everywhere, and a tool + that only loaded no longer gets a done check. +- Delegated agents' cards name the tool they are running instead of + "Core tool ". +- Markdown: a line like `#6235 fixed it` is no longer a heading, headings + render inline code and emphasis, and tables draw one rule under the header + instead of one between every row. +- The approval card shows its field labels and description in your language in + every locale, not only Simplified Chinese. +- `/mode work` says "Switched to Work mode.", and `/zidong` says it switched to + Full Access. `/overview` and `/setup` call the mode Work. +- The language screen points to `/config locale --save`, the Korean + Never chip says it allows nothing, and the elevation dialog's Abort says it + skips only that tool call. +- Less work on the UI thread and less memory per turn: + - the context meter keeps its estimates for unchanged history; + - tool calls share the turn's history instead of copying it; + - compaction checks pressure with one pass; + - write_atomic sweeps a directory for stale temps at most hourly; + - the pet costs nothing while hidden. - `/resume`, `/load` and other session changes that cannot run yet now list what is still busy and how to stop it. - Sessions that ended without a clean exit show `interrupted` with their age diff --git a/crates/tui/CHANGELOG.md b/crates/tui/CHANGELOG.md index 381da94537..711fa91a83 100644 --- a/crates/tui/CHANGELOG.md +++ b/crates/tui/CHANGELOG.md @@ -36,6 +36,36 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 with "Job … not found" (#6931). - In `/setup`, `u` and `d` act only on the working-agreement step. Pressed on another step they no longer close the wizard and switch your agreement off. +- `/setup` no longer says "You're ready." when no model is connected; its last + step asks for a provider and lists the P and C keys it accepts. A rejected + API key's reason stays visible at narrow widths. +- First run with a local Ollama model continues to the trust question and the + ready screen instead of ending onboarding, and the next launch no longer + starts onboarding again. +- A turn that ended in failure reads as an error, live and after resume. Retry + notices stay in the transcript instead of flashing a red error that hid the + recovery. +- File edits show as patches with their change summary once. Tool status + words read `done`, `failed` and `loaded, not run` everywhere, and a tool + that only loaded no longer gets a done check. +- Delegated agents' cards name the tool they are running instead of + "Core tool ". +- Markdown: a line like `#6235 fixed it` is no longer a heading, headings + render inline code and emphasis, and tables draw one rule under the header + instead of one between every row. +- The approval card shows its field labels and description in your language in + every locale, not only Simplified Chinese. +- `/mode work` says "Switched to Work mode.", and `/zidong` says it switched to + Full Access. `/overview` and `/setup` call the mode Work. +- The language screen points to `/config locale --save`, the Korean + Never chip says it allows nothing, and the elevation dialog's Abort says it + skips only that tool call. +- Less work on the UI thread and less memory per turn: + - the context meter keeps its estimates for unchanged history; + - tool calls share the turn's history instead of copying it; + - compaction checks pressure with one pass; + - write_atomic sweeps a directory for stale temps at most hourly; + - the pet costs nothing while hidden. - `/resume`, `/load` and other session changes that cannot run yet now list what is still busy and how to stop it. - Sessions that ended without a clean exit show `interrupted` with their age From 811b6edf75e9e9cc7535a33aaa5f7d87f7e5cbf6 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 16:37:04 +0000 Subject: [PATCH 45/46] docs(changelog): 0.10.2 states its known limits Refs #6915, #6728, #6396, #6931. The 0.10.2 notes had no known-limits block, so four limits of the current candidate went unstated: - the Windows dead-owner start refusal ('Windows stale-owner recovery is not qualified', app-server/src/lib.rs); - idle CPU not measured on FreeBSD or Linux; - base-rate pricing above a Models.dev context tier, which can only happen after a live refresh because the bundled catalog has 0 tiered rows (checked); - delegated agents' shells shown in /jobs and the dock, not in the agent's detail. Any of these that the founder fixes before the tag comes out of this block. crates/tui/CHANGELOG.md is regenerated. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01BsXVvReYca2UxzimQtj7Mj --- CHANGELOG.md | 17 +++++++++++++++++ crates/tui/CHANGELOG.md | 17 +++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8492311b96..158abf027f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -393,6 +393,23 @@ launcher guidance. deadline set by its first unsaved event. Tool heartbeats and store polls no longer restart that wait, and heartbeats are kept in memory only. +### Known limits + +- Windows: when the previous app-server owner died without cleaning up, the + next start refuses with "Windows stale-owner recovery is not qualified" and + the stale publication has to be removed by hand. Unix recovers on its own + ([#6915](https://github.com/codewhale-hq/Codewhale/issues/6915)). +- Idle CPU has not been measured on FreeBSD or Linux for this release + ([#6728](https://github.com/codewhale-hq/Codewhale/issues/6728)). +- Models.dev prices with context tiers: a turn above a model's first tier can + be priced at its base rate. The bundled catalog has no tiered rows, so this + can only happen after a live models.dev refresh + ([#6396](https://github.com/codewhale-hq/Codewhale/issues/6396)). +- Shell commands run by delegated agents appear in `/jobs` and the work dock + with their owner, not in the agent's own detail view; named terminal + sessions live in the terminal dock + ([#6931](https://github.com/codewhale-hq/Codewhale/issues/6931)). + ### Maintenance - CI and test harness only, no change to the shipped binaries. The diff --git a/crates/tui/CHANGELOG.md b/crates/tui/CHANGELOG.md index 711fa91a83..316d86d45e 100644 --- a/crates/tui/CHANGELOG.md +++ b/crates/tui/CHANGELOG.md @@ -393,6 +393,23 @@ launcher guidance. deadline set by its first unsaved event. Tool heartbeats and store polls no longer restart that wait, and heartbeats are kept in memory only. +### Known limits + +- Windows: when the previous app-server owner died without cleaning up, the + next start refuses with "Windows stale-owner recovery is not qualified" and + the stale publication has to be removed by hand. Unix recovers on its own + ([#6915](https://github.com/codewhale-hq/Codewhale/issues/6915)). +- Idle CPU has not been measured on FreeBSD or Linux for this release + ([#6728](https://github.com/codewhale-hq/Codewhale/issues/6728)). +- Models.dev prices with context tiers: a turn above a model's first tier can + be priced at its base rate. The bundled catalog has no tiered rows, so this + can only happen after a live models.dev refresh + ([#6396](https://github.com/codewhale-hq/Codewhale/issues/6396)). +- Shell commands run by delegated agents appear in `/jobs` and the work dock + with their owner, not in the agent's own detail view; named terminal + sessions live in the terminal dock + ([#6931](https://github.com/codewhale-hq/Codewhale/issues/6931)). + ### Maintenance - CI and test harness only, no change to the shipped binaries. The From e107caf9f0a50cb316d652cdb80ad5318a20bed5 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 17:31:42 +0000 Subject: [PATCH 46/46] fix(tui): polish slice 2: honest chrome, quieter errors, cards that don't repeat themselves MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 0.10.2 UX audit's second slice. The judge marked it as needing no product decision; it was held back only by the 12-item cap. Every sub-item was re-checked against the tree before the change, and an adversarial review's three findings are fixed here. First run, chrome and errors: - Provider readiness says "endpoint reachable · model not checked" instead of "models endpoint 2xx". It deliberately does not say "key works": providers with a public /models answer 2xx to a bad key. - remote_control::Status is reachable through one typed accessor. '/rc failed' paints in Failure ink, not Info, and /setup reads the enum. It used to match English status-line prefixes, so a failed remote showed as "not available". - An error cell offers "Alt+V:full error" only when the error wraps past 4 rows. Alt+V still opens every error. - Clipboard: native copy and cut say "Copied to the clipboard" through a typed toast, and a failure says "Clipboard copy failed: {error}". That is the new MessageId ClipboardCopyFailed, translated in all 15 packs. The duplicate Cmd+C composer path calls copy_active_selection. - NotificationProviderFallback leads with the switch and puts the reason after a clause joint in every pack, so the toast survives at 80-100 columns instead of vanishing below about 146. - notice_budget also respects the permission chip's floor, so the kit never clips a notice with '...'. - The effort field reads "auto→high", not "auto: high". - The elevation card drops its duplicate in-body heading and the "Tool: exec_shell" row when a command is shown. ElevationTitleSandboxDenied had no other use and is removed from the enum and all packs. Transcript cards: - The checklist card drops its "checklist: " row. The legacy change card maps raw statuses through TodoStatus to four new translated MessageIds ("in progress", not "in_progress"). - A failed read or search entry carries its reason: ExploringEntry.error is set from the tool result on failure. - A single-entry card shows up to 6 rows and Alt+V when rows are hidden. - A multi-entry card shows only the first line, because the cell holds one detail record and Alt+V there could open a sibling's record. - Body rows no longer repeat what the header already shows in full: - exploring done/live, web-search query and image path; - a generic card's name, and its args when the header carries them. That compares the value alone or a " · value" suffix, not a raw suffix. - No double space, and the parallel strip lists only non-zero counts. - Every affordance row joins with " · ". "Output shortened" gets the transcript rail like its siblings. - MCP calls no longer collapse into a run summary (their side effects cannot be known). Run summaries show "read file", not "read_file". Tests whose pinned copy changed are updated: clipboard receipts, the effort label, elevation titles in en/zh-Hans/ja/zh-Hant, the checklist status, the collapsed-run summaries, readiness, and two ExploringEntry literals. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01BsXVvReYca2UxzimQtj7Mj --- crates/localization/locales/ca.json | 8 +- crates/localization/locales/de.json | 8 +- crates/localization/locales/en.json | 8 +- crates/localization/locales/es-419.json | 8 +- crates/localization/locales/fr.json | 8 +- crates/localization/locales/hi.json | 8 +- crates/localization/locales/id.json | 8 +- crates/localization/locales/ja.json | 8 +- crates/localization/locales/ko.json | 8 +- crates/localization/locales/pt-BR.json | 8 +- crates/localization/locales/ru.json | 8 +- crates/localization/locales/uk.json | 8 +- crates/localization/locales/vi.json | 8 +- crates/localization/locales/zh-Hans.json | 8 +- crates/localization/locales/zh-Hant.json | 8 +- crates/localization/src/lib.rs | 12 +- crates/tui/src/provider_readiness.rs | 4 +- crates/tui/src/remote_control.rs | 7 +- crates/tui/src/tui/active_cell.rs | 1 + crates/tui/src/tui/app.rs | 6 +- crates/tui/src/tui/app/tests.rs | 2 +- crates/tui/src/tui/approval/tests.rs | 31 ++-- crates/tui/src/tui/history.rs | 178 ++++++++++++++----- crates/tui/src/tui/history/checklist.rs | 28 +-- crates/tui/src/tui/history/tests.rs | 16 +- crates/tui/src/tui/history/tool_output.rs | 2 +- crates/tui/src/tui/history/tool_run.rs | 9 +- crates/tui/src/tui/mouse_ui.rs | 105 ++++++----- crates/tui/src/tui/mouse_ui/primary_tests.rs | 7 +- crates/tui/src/tui/phase_strip.rs | 13 +- crates/tui/src/tui/setup/mod.rs | 17 +- crates/tui/src/tui/tool_routing.rs | 9 + crates/tui/src/tui/ui/event_loop.rs | 17 +- crates/tui/src/tui/ui/tests.rs | 6 +- crates/tui/src/tui/widgets/mod.rs | 29 ++- crates/tui/src/tui/widgets/tool_card.rs | 2 +- 36 files changed, 400 insertions(+), 221 deletions(-) diff --git a/crates/localization/locales/ca.json b/crates/localization/locales/ca.json index 7d0e94fea1..d573c6b978 100644 --- a/crates/localization/locales/ca.json +++ b/crates/localization/locales/ca.json @@ -799,6 +799,7 @@ "ClipboardSentToTerminal": "S'ha enviat al porta-retalls del terminal (els terminals no ho confirmen)", "ClipboardCutKeptText": "S'ha enviat al porta-retalls del terminal; el text es manté perquè els terminals no confirmen les còpies", "ClipboardNothingToPaste": "No hi ha res per enganxar: el porta-retalls és buit o no s'ha pogut llegir", + "ClipboardCopyFailed": "Ha fallat la còpia al porta-retalls: {error}", "KbContextMenu": "Actua sobre el que hi ha sota el punter: copia, enganxa, obre els detalls o un file:line a l'editor, enfoca o atura un agent", "KbPointerScroll": "Desplaça la transcripció; sobre el redactor, mou el cursor línia a línia", "KbPointerClick": "Activa la fila, el xip o el bloc d'eina sota el punter", @@ -1229,7 +1230,6 @@ "ApprovalIntentLabel": "Intenció: ", "ApprovalMoreLines": " … (+{count} línies)", "ApprovalAutoDeniedSession": "{tool} denegat automàticament: ja has denegat una sol·licitud coincident en aquest torn. Envia un missatge nou perquè se't torni a preguntar.", - "ElevationTitleSandboxDenied": " ⚠ Sandbox denegat ", "ElevationTitleRequired": " Cal elevació del sandbox ", "ElevationFieldTool": " Eina: ", "ElevationFieldCmd": " Cmd: ", @@ -1313,6 +1313,10 @@ "ToolReceiptDone": "fet", "ToolReceiptLinesSingular": "1 línia", "ToolReceiptLinesPlural": "{count} línies", + "TodoStatusPending": "pendent", + "TodoStatusInProgress": "en curs", + "TodoStatusCompleted": "completada", + "TodoStatusCancelled": "cancel·lada", "CmdVoiceDescription": "Commuta l'entrada de veu: grava veu i transcriu-la al compositor", "CmdVoiceSendDescription": "Commuta l'enviament per veu: digues \"send it\" per enviar", "CmdVoiceControlDescription": "Commuta el control per veu: dictat que coneix l'esborrany", @@ -2454,7 +2458,7 @@ "NotificationElevationNeeded": "Permet o denega l'accés elevat per a '{tool}'", "NotificationDecisionWebHint": "Decideix aquí o al web.", "NotificationTurnFailed": "El torn ha fallat", - "NotificationProviderFallback": "S'ha canviat a {provider} (alternativa {position}/{total}) després d'un error recuperable del proveïdor.", + "NotificationProviderFallback": "S'ha canviat a {provider} (alternativa {position}/{total}), després d'un error recuperable del proveïdor.", "ApprovalNeverPostureBlocked": "S'ha bloquejat '{tool}': aquesta sessió no permet eines que requereixen aprovació.", "NotificationWebApproved": "Aprovat al web.", "NotificationWebDenied": "Denegat al web.", diff --git a/crates/localization/locales/de.json b/crates/localization/locales/de.json index d4bef1358d..d3abd66eb9 100644 --- a/crates/localization/locales/de.json +++ b/crates/localization/locales/de.json @@ -799,6 +799,7 @@ "ClipboardSentToTerminal": "An die Zwischenablage des Terminals gesendet (Terminals bestätigen das nicht)", "ClipboardCutKeptText": "An die Zwischenablage des Terminals gesendet; der Text bleibt stehen, weil Terminals Kopien nicht bestätigen", "ClipboardNothingToPaste": "Nichts einzufügen: Die Zwischenablage ist leer oder nicht lesbar", + "ClipboardCopyFailed": "Kopieren in die Zwischenablage fehlgeschlagen: {error}", "KbContextMenu": "Auf das Element unter dem Zeiger wirken: kopieren, einfügen, Details oder ein file:line im Editor öffnen, einen Agenten fokussieren oder stoppen", "KbPointerScroll": "Transkript scrollen; über dem Editor den Cursor zeilenweise bewegen", "KbPointerClick": "Zeile, Chip oder Werkzeugblock unter dem Zeiger aktivieren", @@ -1229,7 +1230,6 @@ "ApprovalIntentLabel": "Absicht: ", "ApprovalMoreLines": " … (+{count} Zeilen)", "ApprovalAutoDeniedSession": "Auto-abgelehnt {tool}: Sie haben eine passende Anfrage in dieser Runde bereits abgelehnt. Senden Sie eine neue Nachricht, um erneut gefragt zu werden.", - "ElevationTitleSandboxDenied": " ⚠ Sandbox verweigert ", "ElevationTitleRequired": " Sandbox-Elevation erforderlich ", "ElevationFieldTool": " Tool: ", "ElevationFieldCmd": " Befehl: ", @@ -1313,6 +1313,10 @@ "ToolReceiptDone": "fertig", "ToolReceiptLinesSingular": "1 Zeile", "ToolReceiptLinesPlural": "{count} Zeilen", + "TodoStatusPending": "ausstehend", + "TodoStatusInProgress": "in Arbeit", + "TodoStatusCompleted": "erledigt", + "TodoStatusCancelled": "abgebrochen", "CmdVoiceDescription": "Spracheingabe umschalten: Sprache aufnehmen und in den Composer transkribieren", "CmdVoiceSendDescription": "Sprach-Auto-Senden umschalten: \"send it\" sagen zum Absenden", "CmdVoiceControlDescription": "Sprachsteuerung umschalten: Diktat, das deinen Entwurf kennt", @@ -2454,7 +2458,7 @@ "NotificationElevationNeeded": "Erlaube oder verweigere erweiterte Zugriffsrechte für '{tool}'", "NotificationDecisionWebHint": "Entscheide hier oder im Web.", "NotificationTurnFailed": "Turn fehlgeschlagen", - "NotificationProviderFallback": "Nach einem behebbaren Anbieterfehler zu {provider} gewechselt (Ausweichoption {position}/{total}).", + "NotificationProviderFallback": "Zu {provider} gewechselt (Ausweichoption {position}/{total}), nach einem behebbaren Anbieterfehler.", "ApprovalNeverPostureBlocked": "'{tool}' wurde blockiert: Diese Sitzung erlaubt keine Tools, die eine Genehmigung erfordern.", "NotificationWebApproved": "Im Web genehmigt.", "NotificationWebDenied": "Im Web abgelehnt.", diff --git a/crates/localization/locales/en.json b/crates/localization/locales/en.json index aa19f7dc4e..a985d33bd5 100644 --- a/crates/localization/locales/en.json +++ b/crates/localization/locales/en.json @@ -816,6 +816,7 @@ "ClipboardSentToTerminal": "Sent to the terminal clipboard (terminals do not confirm it)", "ClipboardCutKeptText": "Sent to the terminal clipboard; the text stays because terminals do not confirm copies", "ClipboardNothingToPaste": "Nothing to paste: the clipboard is empty or could not be read", + "ClipboardCopyFailed": "Clipboard copy failed: {error}", "KbContextMenu": "Act on what is under the pointer: copy, paste, open details or a file:line in your editor, focus or stop an agent", "KbPointerScroll": "Scroll the transcript; over the composer, move the cursor a line at a time", "KbPointerClick": "Activate the row, chip, or tool block under the pointer", @@ -1252,7 +1253,6 @@ "ApprovalIntentLabel": "Intent: ", "ApprovalMoreLines": " … (+{count} lines)", "ApprovalAutoDeniedSession": "Auto-denied {tool}: you denied a matching request earlier in this turn. Send a new message to be asked again.", - "ElevationTitleSandboxDenied": " ⚠ Sandbox Denied ", "ElevationTitleRequired": " Sandbox Elevation Required ", "ElevationFieldTool": " Tool: ", "ElevationFieldCmd": " Cmd: ", @@ -1336,6 +1336,10 @@ "ToolReceiptDone": "done", "ToolReceiptLinesSingular": "1 line", "ToolReceiptLinesPlural": "{count} lines", + "TodoStatusPending": "pending", + "TodoStatusInProgress": "in progress", + "TodoStatusCompleted": "completed", + "TodoStatusCancelled": "cancelled", "CmdVoiceDescription": "Dictate into the composer", "CmdVoiceSendDescription": "Toggle voice auto-send: say \"send it\" to submit", "CmdVoiceControlDescription": "Toggle voice control: dictation that knows your draft", @@ -2454,7 +2458,7 @@ "NotificationElevationNeeded": "Allow or deny elevated access for '{tool}'", "NotificationDecisionWebHint": "Decide here or on the web.", "NotificationTurnFailed": "Turn failed", - "NotificationProviderFallback": "Switched to {provider} (fallback {position}/{total}) after a recoverable provider error.", + "NotificationProviderFallback": "Switched to {provider} (fallback {position}/{total}), after a recoverable provider error.", "ApprovalNeverPostureBlocked": "Blocked '{tool}': this session does not allow tools that require approval.", "NotificationWebApproved": "Approved on the web.", "NotificationWebDenied": "Denied on the web.", diff --git a/crates/localization/locales/es-419.json b/crates/localization/locales/es-419.json index f147275172..f3871c4109 100644 --- a/crates/localization/locales/es-419.json +++ b/crates/localization/locales/es-419.json @@ -816,6 +816,7 @@ "ClipboardSentToTerminal": "Enviado al portapapeles de la terminal (las terminales no lo confirman)", "ClipboardCutKeptText": "Enviado al portapapeles de la terminal; el texto se conserva porque las terminales no confirman las copias", "ClipboardNothingToPaste": "Nada para pegar: el portapapeles está vacío o no se pudo leer", + "ClipboardCopyFailed": "Falló la copia al portapapeles: {error}", "KbContextMenu": "Actuar sobre lo que está bajo el puntero: copiar, pegar, abrir detalles o un file:line en tu editor, enfocar o detener un agente", "KbPointerScroll": "Desplaza la transcripción; sobre el redactor, mueve el cursor línea por línea", "KbPointerClick": "Activa la fila, el chip o el bloque de herramienta bajo el puntero", @@ -1250,7 +1251,6 @@ "ApprovalIntentLabel": "Intención: ", "ApprovalMoreLines": " … (+{count} líneas)", "ApprovalAutoDeniedSession": "Se rechazó automáticamente {tool}: rechazaste antes una solicitud coincidente en este turno. Envía un mensaje nuevo para que se te vuelva a preguntar.", - "ElevationTitleSandboxDenied": " ⚠ Sandbox Denegado ", "ElevationTitleRequired": " Elevación de Sandbox Requerida ", "ElevationFieldTool": " Herramienta: ", "ElevationFieldCmd": " Comando: ", @@ -1334,6 +1334,10 @@ "ToolReceiptDone": "listo", "ToolReceiptLinesSingular": "1 línea", "ToolReceiptLinesPlural": "{count} líneas", + "TodoStatusPending": "pendiente", + "TodoStatusInProgress": "en curso", + "TodoStatusCompleted": "completada", + "TodoStatusCancelled": "cancelada", "CmdVoiceDescription": "Alternar entrada de voz: grabar voz y transcribir a texto", "CmdVoiceSendDescription": "Alternar envío automático por voz: di \"send it\" para enviar", "CmdVoiceControlDescription": "Alternar control por voz: dictado que conoce tu borrador", @@ -2454,7 +2458,7 @@ "NotificationElevationNeeded": "Permite o rechaza el acceso elevado para '{tool}'", "NotificationDecisionWebHint": "Decide aquí o en la web.", "NotificationTurnFailed": "El turno falló", - "NotificationProviderFallback": "Se cambió a {provider} (alternativa {position}/{total}) tras un error recuperable del proveedor.", + "NotificationProviderFallback": "Se cambió a {provider} (alternativa {position}/{total}), tras un error recuperable del proveedor.", "ApprovalNeverPostureBlocked": "Se bloqueó '{tool}': esta sesión no permite herramientas que requieren aprobación.", "NotificationWebApproved": "Aprobado en la web.", "NotificationWebDenied": "Rechazado en la web.", diff --git a/crates/localization/locales/fr.json b/crates/localization/locales/fr.json index b5f4100bce..82950899e0 100644 --- a/crates/localization/locales/fr.json +++ b/crates/localization/locales/fr.json @@ -799,6 +799,7 @@ "ClipboardSentToTerminal": "Envoyé au presse-papiers du terminal (les terminaux ne le confirment pas)", "ClipboardCutKeptText": "Envoyé au presse-papiers du terminal, le texte est conservé car les terminaux ne confirment pas les copies", "ClipboardNothingToPaste": "Rien à coller : le presse-papiers est vide ou illisible", + "ClipboardCopyFailed": "La copie dans le presse-papiers a échoué : {error}", "KbContextMenu": "Agir sur l'élément sous le pointeur : copier, coller, ouvrir les détails ou un file:line dans l'éditeur, cibler ou arrêter un agent", "KbPointerScroll": "Fait défiler la transcription ; sur le compositeur, déplace le curseur ligne par ligne", "KbPointerClick": "Active la ligne, la puce ou le bloc d'outil sous le pointeur", @@ -1229,7 +1230,6 @@ "ApprovalIntentLabel": "Intention : ", "ApprovalMoreLines": " … (+{count} lignes)", "ApprovalAutoDeniedSession": "{tool} refusé automatiquement : vous avez déjà refusé une demande correspondante pendant ce tour. Envoyez un nouveau message pour être à nouveau sollicité.", - "ElevationTitleSandboxDenied": " ⚠ Sandbox refusé ", "ElevationTitleRequired": " Élévation du sandbox requise ", "ElevationFieldTool": " Outil : ", "ElevationFieldCmd": " Cmde : ", @@ -1313,6 +1313,10 @@ "ToolReceiptDone": "terminé", "ToolReceiptLinesSingular": "1 ligne", "ToolReceiptLinesPlural": "{count} lignes", + "TodoStatusPending": "en attente", + "TodoStatusInProgress": "en cours", + "TodoStatusCompleted": "terminée", + "TodoStatusCancelled": "annulée", "CmdVoiceDescription": "Basculer l'entrée vocale : enregistrer la parole et transcrire dans le composeur", "CmdVoiceSendDescription": "Basculer l'envoi vocal : dites \"send it\" pour envoyer", "CmdVoiceControlDescription": "Basculer le contrôle vocal : dictée qui connaît le brouillon", @@ -2454,7 +2458,7 @@ "NotificationElevationNeeded": "Autorisez ou refusez les droits d'accès supplémentaires pour « {tool} »", "NotificationDecisionWebHint": "Décidez ici ou sur le Web.", "NotificationTurnFailed": "Le tour a échoué", - "NotificationProviderFallback": "Passage à {provider} (solution de repli {position}/{total}) après une erreur récupérable du fournisseur.", + "NotificationProviderFallback": "Passage à {provider} (solution de repli {position}/{total}), après une erreur récupérable du fournisseur.", "ApprovalNeverPostureBlocked": "« {tool} » a été bloqué : cette session n'autorise pas les outils qui nécessitent une approbation.", "NotificationWebApproved": "Approuvé sur le Web.", "NotificationWebDenied": "Refusé sur le Web.", diff --git a/crates/localization/locales/hi.json b/crates/localization/locales/hi.json index e0cb6d1324..aff79fba96 100644 --- a/crates/localization/locales/hi.json +++ b/crates/localization/locales/hi.json @@ -799,6 +799,7 @@ "ClipboardSentToTerminal": "टर्मिनल क्लिपबोर्ड पर भेजा गया (टर्मिनल इसकी पुष्टि नहीं करते)", "ClipboardCutKeptText": "टर्मिनल क्लिपबोर्ड पर भेजा गया; टेक्स्ट रखा गया है क्योंकि टर्मिनल कॉपी की पुष्टि नहीं करते", "ClipboardNothingToPaste": "पेस्ट करने को कुछ नहीं: क्लिपबोर्ड खाली है या पढ़ा नहीं जा सका", + "ClipboardCopyFailed": "क्लिपबोर्ड पर कॉपी नहीं हो सकी: {error}", "KbContextMenu": "पॉइंटर के नीचे की चीज़ पर काम करें: कॉपी, पेस्ट, विवरण या एडिटर में file:line खोलें, एजेंट पर फ़ोकस करें या रोकें", "KbPointerScroll": "ट्रांसक्रिप्ट स्क्रॉल करें; कंपोज़र पर, कर्सर एक पंक्ति खिसकाएँ", "KbPointerClick": "पॉइंटर के नीचे की पंक्ति, चिप या टूल ब्लॉक सक्रिय करें", @@ -1229,7 +1230,6 @@ "ApprovalIntentLabel": "इरादा: ", "ApprovalMoreLines": " … (+{count} पंक्तियाँ)", "ApprovalAutoDeniedSession": "स्वतः अस्वीकृत {tool}: आपने इसी टर्न में पहले मिलता-जुलता अनुरोध अस्वीकार किया था। फिर से पूछे जाने के लिए नया संदेश भेजें।", - "ElevationTitleSandboxDenied": " ⚠ सैंडबॉक्स अस्वीकृत ", "ElevationTitleRequired": " सैंडबॉक्स एलिवेशन आवश्यक ", "ElevationFieldTool": " टूल: ", "ElevationFieldCmd": " Cmd: ", @@ -1313,6 +1313,10 @@ "ToolReceiptDone": "पूर्ण", "ToolReceiptLinesSingular": "1 पंक्ति", "ToolReceiptLinesPlural": "{count} पंक्तियाँ", + "TodoStatusPending": "लंबित", + "TodoStatusInProgress": "जारी", + "TodoStatusCompleted": "पूरा हुआ", + "TodoStatusCancelled": "रद्द", "CmdVoiceDescription": "वॉइस इनपुट टॉगल करें: बोली रिकॉर्ड कर कम्पोज़र में ट्रांसक्राइब करें", "CmdVoiceSendDescription": "वॉइस ऑटो-सेंड टॉगल करें: भेजने के लिए \"send it\" बोलें", "CmdVoiceControlDescription": "वॉइस कंट्रोल टॉगल करें: आपके ड्राफ़्ट को समझने वाला श्रुतलेख", @@ -2454,7 +2458,7 @@ "NotificationElevationNeeded": "'{tool}' के लिए अतिरिक्त पहुँच की अनुमति दें या अस्वीकार करें", "NotificationDecisionWebHint": "यहाँ या वेब पर फ़ैसला करें।", "NotificationTurnFailed": "टर्न विफल", - "NotificationProviderFallback": "प्रदाता की ठीक की जा सकने वाली त्रुटि के बाद {provider} पर स्विच किया गया (वैकल्पिक प्रदाता {position}/{total})।", + "NotificationProviderFallback": "{provider} पर स्विच किया गया (वैकल्पिक प्रदाता {position}/{total}); प्रदाता की त्रुटि ठीक की जा सकने वाली थी।", "ApprovalNeverPostureBlocked": "'{tool}' को ब्लॉक किया गया: इस सत्र में मंज़ूरी की ज़रूरत वाले टूल की अनुमति नहीं है।", "NotificationWebApproved": "वेब पर मंज़ूरी दी गई।", "NotificationWebDenied": "वेब पर अस्वीकार किया गया।", diff --git a/crates/localization/locales/id.json b/crates/localization/locales/id.json index 45aa88c69f..4303fc65fc 100644 --- a/crates/localization/locales/id.json +++ b/crates/localization/locales/id.json @@ -799,6 +799,7 @@ "ClipboardSentToTerminal": "Dikirim ke papan klip terminal (terminal tidak mengonfirmasinya)", "ClipboardCutKeptText": "Dikirim ke papan klip terminal; teks tetap ada karena terminal tidak mengonfirmasi salinan", "ClipboardNothingToPaste": "Tidak ada yang bisa ditempel: papan klip kosong atau tidak dapat dibaca", + "ClipboardCopyFailed": "Penyalinan ke papan klip gagal: {error}", "KbContextMenu": "Bertindak pada yang ada di bawah penunjuk: salin, tempel, buka detail atau file:line di editor, fokus ke atau hentikan agen", "KbPointerScroll": "Gulir transkrip; di atas komposer, pindahkan kursor satu baris", "KbPointerClick": "Aktifkan baris, chip, atau blok alat di bawah penunjuk", @@ -1229,7 +1230,6 @@ "ApprovalIntentLabel": "Maksud: ", "ApprovalMoreLines": " … (+{count} baris)", "ApprovalAutoDeniedSession": "Ditolak otomatis {tool}: Anda sudah menolak permintaan yang cocok pada giliran ini. Kirim pesan baru agar ditanya lagi.", - "ElevationTitleSandboxDenied": " ⚠ Sandbox Ditolak ", "ElevationTitleRequired": " Perlu Elevasi Sandbox ", "ElevationFieldTool": " Tool: ", "ElevationFieldCmd": " Cmd: ", @@ -1313,6 +1313,10 @@ "ToolReceiptDone": "selesai", "ToolReceiptLinesSingular": "1 baris", "ToolReceiptLinesPlural": "{count} baris", + "TodoStatusPending": "tertunda", + "TodoStatusInProgress": "sedang dikerjakan", + "TodoStatusCompleted": "selesai", + "TodoStatusCancelled": "dibatalkan", "CmdVoiceDescription": "Alihkan input suara: rekam ucapan dan transkripsikan ke composer", "CmdVoiceSendDescription": "Alihkan kirim-otomatis suara: ucapkan \"send it\" untuk kirim", "CmdVoiceControlDescription": "Alihkan kontrol suara: dikte yang memahami draf Anda", @@ -2454,7 +2458,7 @@ "NotificationElevationNeeded": "Izinkan atau tolak akses yang lebih luas untuk '{tool}'", "NotificationDecisionWebHint": "Tentukan pilihan di sini atau di web.", "NotificationTurnFailed": "Giliran gagal", - "NotificationProviderFallback": "Beralih ke {provider} (alternatif {position}/{total}) setelah terjadi kesalahan penyedia yang dapat dipulihkan.", + "NotificationProviderFallback": "Beralih ke {provider} (alternatif {position}/{total}), setelah terjadi kesalahan penyedia yang dapat dipulihkan.", "ApprovalNeverPostureBlocked": "'{tool}' diblokir: sesi ini tidak mengizinkan alat yang memerlukan persetujuan.", "NotificationWebApproved": "Disetujui di web.", "NotificationWebDenied": "Ditolak di web.", diff --git a/crates/localization/locales/ja.json b/crates/localization/locales/ja.json index d90c78f90e..683bfa657e 100644 --- a/crates/localization/locales/ja.json +++ b/crates/localization/locales/ja.json @@ -816,6 +816,7 @@ "ClipboardSentToTerminal": "ターミナルのクリップボードに送信しました(ターミナルは受信を確認しません)", "ClipboardCutKeptText": "ターミナルのクリップボードに送信しました。ターミナルはコピーを確認しないため、テキストは残しています", "ClipboardNothingToPaste": "貼り付けるものがありません: クリップボードが空か、読み取れませんでした", + "ClipboardCopyFailed": "クリップボードへのコピーに失敗しました: {error}", "KbContextMenu": "ポインター下の対象を操作: コピー、貼り付け、詳細やエディターでの file:line を開く、エージェントへのフォーカスや停止", "KbPointerScroll": "トランスクリプトをスクロール。コンポーザー上ではカーソルを1行ずつ移動", "KbPointerClick": "ポインターの下にある行・チップ・ツールブロックを実行", @@ -1250,7 +1251,6 @@ "ApprovalIntentLabel": "意図:", "ApprovalMoreLines": " … (+{count} 行)", "ApprovalAutoDeniedSession": "{tool} を自動的に拒否しました: このターンで一致するリクエストをすでに拒否しています。もう一度確認するには新しいメッセージを送信してください。", - "ElevationTitleSandboxDenied": " ⚠ サンドボックス拒否 ", "ElevationTitleRequired": " サンドボックス昇格 ", "ElevationFieldTool": " ツール:", "ElevationFieldCmd": " コマンド:", @@ -1334,6 +1334,10 @@ "ToolReceiptDone": "完了", "ToolReceiptLinesSingular": "1 行", "ToolReceiptLinesPlural": "{count} 行", + "TodoStatusPending": "未着手", + "TodoStatusInProgress": "進行中", + "TodoStatusCompleted": "完了", + "TodoStatusCancelled": "キャンセル済み", "CmdVoiceDescription": "音声入力の切替:音声を録音してテキストに変換", "CmdVoiceSendDescription": "音声自動送信の切替:「send it」と言うと送信", "CmdVoiceControlDescription": "音声コントロールの切替:下書きを踏まえたディクテーション", @@ -2454,7 +2458,7 @@ "NotificationElevationNeeded": "「{tool}」への追加権限を許可または拒否してください", "NotificationDecisionWebHint": "ここ、またはウェブで判断してください。", "NotificationTurnFailed": "ターン失敗", - "NotificationProviderFallback": "復旧可能なプロバイダーエラーのため、{provider} に切り替えました(代替先 {position}/{total})。", + "NotificationProviderFallback": "{provider} に切り替えました(代替先 {position}/{total})。復旧可能なプロバイダーエラーが発生しました。", "ApprovalNeverPostureBlocked": "「{tool}」をブロックしました。このセッションでは承認が必要なツールは許可されていません。", "NotificationWebApproved": "ウェブで承認されました。", "NotificationWebDenied": "ウェブで拒否されました。", diff --git a/crates/localization/locales/ko.json b/crates/localization/locales/ko.json index a3335626bb..cf67b17d69 100644 --- a/crates/localization/locales/ko.json +++ b/crates/localization/locales/ko.json @@ -816,6 +816,7 @@ "ClipboardSentToTerminal": "터미널 클립보드로 보냈습니다(터미널은 수신을 확인하지 않습니다)", "ClipboardCutKeptText": "터미널 클립보드로 보냈습니다. 터미널은 복사를 확인하지 않으므로 텍스트를 그대로 둡니다", "ClipboardNothingToPaste": "붙여넣을 내용이 없습니다: 클립보드가 비어 있거나 읽을 수 없습니다", + "ClipboardCopyFailed": "클립보드 복사에 실패했습니다: {error}", "KbContextMenu": "포인터 아래 대상에 대한 작업: 복사, 붙여넣기, 세부 정보 또는 편집기에서 file:line 열기, 에이전트 포커스 또는 중지", "KbPointerScroll": "트랜스크립트 스크롤. 작성창 위에서는 커서를 한 줄씩 이동", "KbPointerClick": "포인터 아래의 행, 칩 또는 도구 블록 실행", @@ -1252,7 +1253,6 @@ "ApprovalIntentLabel": "의도: ", "ApprovalMoreLines": " … (+{count}줄)", "ApprovalAutoDeniedSession": "{tool} 자동 거부: 이번 턴에서 일치하는 요청을 이미 거부했습니다. 다시 확인받으려면 새 메시지를 보내세요.", - "ElevationTitleSandboxDenied": " ⚠ 샌드박스 거부됨 ", "ElevationTitleRequired": " 샌드박스 권한 상승 필요 ", "ElevationFieldTool": " 도구: ", "ElevationFieldCmd": " 명령: ", @@ -1336,6 +1336,10 @@ "ToolReceiptDone": "완료", "ToolReceiptLinesSingular": "1줄", "ToolReceiptLinesPlural": "{count}줄", + "TodoStatusPending": "대기 중", + "TodoStatusInProgress": "진행 중", + "TodoStatusCompleted": "완료됨", + "TodoStatusCancelled": "취소됨", "CmdVoiceDescription": "음성 입력 전환: 음성을 녹음해 입력창에 받아쓰기", "CmdVoiceSendDescription": "음성 자동 전송 전환: \"send it\"이라고 말하면 전송", "CmdVoiceControlDescription": "음성 제어 전환: 초안을 이해하는 받아쓰기", @@ -2454,7 +2458,7 @@ "NotificationElevationNeeded": "'{tool}'의 추가 접근 권한을 허용하거나 거부하세요", "NotificationDecisionWebHint": "여기 또는 웹에서 결정하세요.", "NotificationTurnFailed": "턴 실패", - "NotificationProviderFallback": "복구 가능한 제공업체 오류가 발생하여 {provider}(으)로 전환했습니다(대체 경로 {position}/{total}).", + "NotificationProviderFallback": "{provider}(으)로 전환했습니다(대체 경로 {position}/{total}). 복구 가능한 제공업체 오류가 발생했습니다.", "ApprovalNeverPostureBlocked": "'{tool}'이(가) 차단되었습니다. 이 세션에서는 승인이 필요한 도구를 허용하지 않습니다.", "NotificationWebApproved": "웹에서 승인되었습니다.", "NotificationWebDenied": "웹에서 거부되었습니다.", diff --git a/crates/localization/locales/pt-BR.json b/crates/localization/locales/pt-BR.json index 8c75c5fe29..b184ff8adf 100644 --- a/crates/localization/locales/pt-BR.json +++ b/crates/localization/locales/pt-BR.json @@ -816,6 +816,7 @@ "ClipboardSentToTerminal": "Enviado à área de transferência do terminal (terminais não confirmam)", "ClipboardCutKeptText": "Enviado à área de transferência do terminal; o texto foi mantido porque terminais não confirmam cópias", "ClipboardNothingToPaste": "Nada para colar: a área de transferência está vazia ou não pôde ser lida", + "ClipboardCopyFailed": "Falha ao copiar para a área de transferência: {error}", "KbContextMenu": "Agir sobre o que está sob o ponteiro: copiar, colar, abrir detalhes ou um file:line no editor, focar ou parar um agente", "KbPointerScroll": "Rola a transcrição; sobre o compositor, move o cursor linha a linha", "KbPointerClick": "Ativa a linha, o chip ou o bloco de ferramenta sob o ponteiro", @@ -1250,7 +1251,6 @@ "ApprovalIntentLabel": "Intenção: ", "ApprovalMoreLines": " … (+{count} linhas)", "ApprovalAutoDeniedSession": "{tool} foi negado automaticamente: você negou uma solicitação correspondente antes neste turno. Envie uma nova mensagem para ser perguntado de novo.", - "ElevationTitleSandboxDenied": " ⚠ Sandbox Negado ", "ElevationTitleRequired": " Elevação de Sandbox Necessária ", "ElevationFieldTool": " Ferramenta: ", "ElevationFieldCmd": " Comando: ", @@ -1334,6 +1334,10 @@ "ToolReceiptDone": "concluído", "ToolReceiptLinesSingular": "1 linha", "ToolReceiptLinesPlural": "{count} linhas", + "TodoStatusPending": "pendente", + "TodoStatusInProgress": "em andamento", + "TodoStatusCompleted": "concluída", + "TodoStatusCancelled": "cancelada", "CmdVoiceDescription": "Alternar entrada de voz: gravar fala e transcrever para texto", "CmdVoiceSendDescription": "Alternar envio por voz: diga \"send it\" para enviar", "CmdVoiceControlDescription": "Alternar controle por voz: ditado que conhece seu rascunho", @@ -2454,7 +2458,7 @@ "NotificationElevationNeeded": "Permita ou negue acesso elevado para '{tool}'", "NotificationDecisionWebHint": "Decida aqui ou na web.", "NotificationTurnFailed": "O turno falhou", - "NotificationProviderFallback": "Alternado para {provider} (alternativa {position}/{total}) após um erro recuperável do provedor.", + "NotificationProviderFallback": "Alternado para {provider} (alternativa {position}/{total}), após um erro recuperável do provedor.", "ApprovalNeverPostureBlocked": "'{tool}' foi bloqueado: esta sessão não permite ferramentas que exigem aprovação.", "NotificationWebApproved": "Aprovado na web.", "NotificationWebDenied": "Negado na web.", diff --git a/crates/localization/locales/ru.json b/crates/localization/locales/ru.json index 7cdf1d32f0..87d250a26f 100644 --- a/crates/localization/locales/ru.json +++ b/crates/localization/locales/ru.json @@ -799,6 +799,7 @@ "ClipboardSentToTerminal": "Отправлено в буфер обмена терминала (терминалы это не подтверждают)", "ClipboardCutKeptText": "Отправлено в буфер обмена терминала; текст оставлен, потому что терминалы не подтверждают копирование", "ClipboardNothingToPaste": "Нечего вставить: буфер обмена пуст или не читается", + "ClipboardCopyFailed": "Не удалось скопировать в буфер обмена: {error}", "KbContextMenu": "Действия с тем, что под указателем: копировать, вставить, открыть детали или file:line в редакторе, перейти к агенту или остановить его", "KbPointerScroll": "Прокручивает расшифровку; над полем ввода перемещает курсор построчно", "KbPointerClick": "Активирует строку, чип или блок инструмента под указателем", @@ -1229,7 +1230,6 @@ "ApprovalIntentLabel": "Намерение: ", "ApprovalMoreLines": " … (ещё {count} строк)", "ApprovalAutoDeniedSession": "{tool} отклонён автоматически: вы уже отклонили похожий запрос в этом ходе. Отправьте новое сообщение, чтобы вас спросили снова.", - "ElevationTitleSandboxDenied": " ⚠ Отказ песочницы ", "ElevationTitleRequired": " Требуется повышение прав песочницы ", "ElevationFieldTool": " Инструмент: ", "ElevationFieldCmd": " Команда: ", @@ -1313,6 +1313,10 @@ "ToolReceiptDone": "готово", "ToolReceiptLinesSingular": "1 строка", "ToolReceiptLinesPlural": "{count} строк", + "TodoStatusPending": "ожидает", + "TodoStatusInProgress": "в работе", + "TodoStatusCompleted": "выполнено", + "TodoStatusCancelled": "отменено", "CmdVoiceDescription": "Вкл/выкл голосовой ввод: записать речь и транскрибировать в поле ввода", "CmdVoiceSendDescription": "Вкл/выкл автоотправку голосом: скажите \"send it\"", "CmdVoiceControlDescription": "Вкл/выкл голосовое управление: диктовка с учётом черновика", @@ -2454,7 +2458,7 @@ "NotificationElevationNeeded": "Разрешите или отклоните расширенный доступ для '{tool}'", "NotificationDecisionWebHint": "Примите решение здесь или в веб-интерфейсе.", "NotificationTurnFailed": "Ход завершился с ошибкой", - "NotificationProviderFallback": "После устранимой ошибки провайдера выполнено переключение на {provider} (резервный вариант {position}/{total}).", + "NotificationProviderFallback": "Переключено на {provider} (резервный вариант {position}/{total}). Ошибка провайдера была устранимой.", "ApprovalNeverPostureBlocked": "'{tool}' заблокирован: в этом сеансе запрещены инструменты, требующие одобрения.", "NotificationWebApproved": "Одобрено в веб-интерфейсе.", "NotificationWebDenied": "Отклонено в веб-интерфейсе.", diff --git a/crates/localization/locales/uk.json b/crates/localization/locales/uk.json index a3f210d0bc..927fee9f18 100644 --- a/crates/localization/locales/uk.json +++ b/crates/localization/locales/uk.json @@ -799,6 +799,7 @@ "ClipboardSentToTerminal": "Надіслано до буфера обміну термінала (термінали цього не підтверджують)", "ClipboardCutKeptText": "Надіслано до буфера обміну термінала; текст залишено, бо термінали не підтверджують копіювання", "ClipboardNothingToPaste": "Нічого вставити: буфер обміну порожній або його не вдалося прочитати", + "ClipboardCopyFailed": "Не вдалося скопіювати до буфера обміну: {error}", "KbContextMenu": "Дії з тим, що під вказівником: копіювати, вставити, відкрити деталі або file:line у редакторі, перейти до агента чи зупинити його", "KbPointerScroll": "Прокручує розшифровку; над полем вводу переміщує курсор по рядках", "KbPointerClick": "Активує рядок, чип або блок інструмента під вказівником", @@ -1229,7 +1230,6 @@ "ApprovalIntentLabel": "Намір: ", "ApprovalMoreLines": " … (+{count} рядків)", "ApprovalAutoDeniedSession": "Автоматично відхилено {tool}: ви вже відхилили подібний запит у цьому ході. Надішліть нове повідомлення, щоб вас запитали знову.", - "ElevationTitleSandboxDenied": " ⚠ Пісочниця відхилена ", "ElevationTitleRequired": " Потрібне підвищення прав пісочниці ", "ElevationFieldTool": " Інструмент: ", "ElevationFieldCmd": " Команда: ", @@ -1313,6 +1313,10 @@ "ToolReceiptDone": "готово", "ToolReceiptLinesSingular": "1 рядок", "ToolReceiptLinesPlural": "{count} рядків", + "TodoStatusPending": "очікує", + "TodoStatusInProgress": "у роботі", + "TodoStatusCompleted": "виконано", + "TodoStatusCancelled": "скасовано", "CmdVoiceDescription": "Перемкнути голосове введення: записати мовлення й транскрибувати в редактор", "CmdVoiceSendDescription": "Перемкнути автонадсилання голосом: скажіть \"send it\"", "CmdVoiceControlDescription": "Перемкнути голосове керування: диктування, що знає чернетку", @@ -2454,7 +2458,7 @@ "NotificationElevationNeeded": "Дозвольте або відхиліть розширений доступ для '{tool}'", "NotificationDecisionWebHint": "Ухваліть рішення тут або у вебінтерфейсі.", "NotificationTurnFailed": "Хід завершився з помилкою", - "NotificationProviderFallback": "Після виправної помилки постачальника виконано перемикання на {provider} (резервний варіант {position}/{total}).", + "NotificationProviderFallback": "Перемкнено на {provider} (резервний варіант {position}/{total}). Помилка постачальника була виправною.", "ApprovalNeverPostureBlocked": "'{tool}' заблоковано: у цьому сеансі заборонені інструменти, що потребують схвалення.", "NotificationWebApproved": "Схвалено у вебінтерфейсі.", "NotificationWebDenied": "Відхилено у вебінтерфейсі.", diff --git a/crates/localization/locales/vi.json b/crates/localization/locales/vi.json index 9f0c5eb6f3..24fdbce2f2 100644 --- a/crates/localization/locales/vi.json +++ b/crates/localization/locales/vi.json @@ -816,6 +816,7 @@ "ClipboardSentToTerminal": "Đã gửi tới bộ nhớ tạm của terminal (terminal không xác nhận)", "ClipboardCutKeptText": "Đã gửi tới bộ nhớ tạm của terminal; văn bản được giữ lại vì terminal không xác nhận việc sao chép", "ClipboardNothingToPaste": "Không có gì để dán: bộ nhớ tạm trống hoặc không đọc được", + "ClipboardCopyFailed": "Sao chép vào bộ nhớ tạm không thành công: {error}", "KbContextMenu": "Thao tác với thứ dưới con trỏ: sao chép, dán, mở chi tiết hoặc file:line trong trình soạn thảo, tập trung hoặc dừng tác tử", "KbPointerScroll": "Cuộn bản ghi; trên khung soạn thảo, di chuyển con trỏ từng dòng", "KbPointerClick": "Kích hoạt hàng, chip hoặc khối công cụ dưới con trỏ", @@ -1250,7 +1251,6 @@ "ApprovalIntentLabel": "Ý định: ", "ApprovalMoreLines": " … (+{count} dòng)", "ApprovalAutoDeniedSession": "Đã tự động từ chối {tool}: bạn đã từ chối một yêu cầu khớp trước đó trong lượt này. Hãy gửi tin nhắn mới để được hỏi lại.", - "ElevationTitleSandboxDenied": " ⚠ Sandbox Bị Từ Chối ", "ElevationTitleRequired": " Yêu Cầu Nâng Cấp Sandbox ", "ElevationFieldTool": " Công cụ: ", "ElevationFieldCmd": " Lệnh: ", @@ -1334,6 +1334,10 @@ "ToolReceiptDone": "xong", "ToolReceiptLinesSingular": "1 dòng", "ToolReceiptLinesPlural": "{count} dòng", + "TodoStatusPending": "đang chờ", + "TodoStatusInProgress": "đang thực hiện", + "TodoStatusCompleted": "đã hoàn tất", + "TodoStatusCancelled": "đã hủy", "CmdVoiceDescription": "Bật/tắt nhập liệu bằng giọng nói: ghi âm và chuyển thành văn bản", "CmdVoiceSendDescription": "Bật/tắt tự gửi bằng giọng nói: nói \"send it\" để gửi", "CmdVoiceControlDescription": "Bật/tắt điều khiển giọng nói: đọc chính tả hiểu bản nháp", @@ -2454,7 +2458,7 @@ "NotificationElevationNeeded": "Cho phép hoặc từ chối nâng quyền truy cập cho '{tool}'", "NotificationDecisionWebHint": "Đưa ra quyết định tại đây hoặc trên web.", "NotificationTurnFailed": "Lượt thất bại", - "NotificationProviderFallback": "Đã chuyển sang {provider} (phương án dự phòng {position}/{total}) sau lỗi có thể khôi phục của nhà cung cấp.", + "NotificationProviderFallback": "Đã chuyển sang {provider} (dự phòng {position}/{total}), sau lỗi có thể khôi phục của nhà cung cấp.", "ApprovalNeverPostureBlocked": "Đã chặn '{tool}': phiên này không cho phép các công cụ cần được chấp thuận.", "NotificationWebApproved": "Đã chấp thuận trên web.", "NotificationWebDenied": "Đã từ chối trên web.", diff --git a/crates/localization/locales/zh-Hans.json b/crates/localization/locales/zh-Hans.json index e4a3f8d3c3..c18ec87f0f 100644 --- a/crates/localization/locales/zh-Hans.json +++ b/crates/localization/locales/zh-Hans.json @@ -816,6 +816,7 @@ "ClipboardSentToTerminal": "已发送到终端剪贴板(终端不会确认)", "ClipboardCutKeptText": "已发送到终端剪贴板;由于终端不会确认复制,文本已保留", "ClipboardNothingToPaste": "没有可粘贴的内容:剪贴板为空或无法读取", + "ClipboardCopyFailed": "复制到剪贴板失败:{error}", "KbContextMenu": "对指针下的内容操作:复制、粘贴、打开详情或在编辑器中打开 file:line、聚焦或停止智能体", "KbPointerScroll": "滚动记录;在输入框上方逐行移动光标", "KbPointerClick": "激活指针下的行、标签或工具块", @@ -1252,7 +1253,6 @@ "ApprovalIntentLabel": "意图: ", "ApprovalMoreLines": " … (还有 {count} 行)", "ApprovalAutoDeniedSession": "已自动拒绝 {tool}:本回合中已有匹配请求被你拒绝。若要重新询问,请发送新消息。", - "ElevationTitleSandboxDenied": " ⚠ 沙箱拒绝 ", "ElevationTitleRequired": " 沙箱提权 ", "ElevationFieldTool": " 工具: ", "ElevationFieldCmd": " 命令: ", @@ -1336,6 +1336,10 @@ "ToolReceiptDone": "完成", "ToolReceiptLinesSingular": "1 行", "ToolReceiptLinesPlural": "{count} 行", + "TodoStatusPending": "待处理", + "TodoStatusInProgress": "进行中", + "TodoStatusCompleted": "已完成", + "TodoStatusCancelled": "已取消", "CmdVoiceDescription": "切换语音输入:录制语音并转录为文字", "CmdVoiceSendDescription": "切换语音自动发送:说出「send it」即可提交", "CmdVoiceControlDescription": "切换语音控制:能理解你草稿内容的听写", @@ -2454,7 +2458,7 @@ "NotificationElevationNeeded": "请允许或拒绝为“{tool}”提升访问权限", "NotificationDecisionWebHint": "请在此处或网页上作出决定。", "NotificationTurnFailed": "本回合失败", - "NotificationProviderFallback": "提供商出现可恢复错误,已切换至 {provider}(备用选项 {position}/{total})。", + "NotificationProviderFallback": "已切换至 {provider}(备用选项 {position}/{total}),因提供商出现可恢复错误。", "ApprovalNeverPostureBlocked": "已阻止“{tool}”:此会话不允许使用需要批准的工具。", "NotificationWebApproved": "已在网页上批准。", "NotificationWebDenied": "已在网页上拒绝。", diff --git a/crates/localization/locales/zh-Hant.json b/crates/localization/locales/zh-Hant.json index 640a931e5e..c6f62ac6a9 100644 --- a/crates/localization/locales/zh-Hant.json +++ b/crates/localization/locales/zh-Hant.json @@ -814,6 +814,7 @@ "ClipboardSentToTerminal": "已傳送到終端機剪貼簿(終端機不會確認)", "ClipboardCutKeptText": "已傳送到終端機剪貼簿;由於終端機不會確認複製,文字已保留", "ClipboardNothingToPaste": "沒有可貼上的內容:剪貼簿為空或無法讀取", + "ClipboardCopyFailed": "複製到剪貼簿失敗:{error}", "KbContextMenu": "對指標下的內容操作:複製、貼上、開啟詳情或在編輯器中開啟 file:line、聚焦或停止代理", "KbPointerScroll": "捲動記錄;在輸入框上方逐行移動游標", "KbPointerClick": "啟動指標下的列、標籤或工具區塊", @@ -1250,7 +1251,6 @@ "ApprovalIntentLabel": "意圖: ", "ApprovalMoreLines": " … (還有 {count} 行)", "ApprovalAutoDeniedSession": "已自動拒絕 {tool}:本回合中已有相符請求被你拒絕。若要重新詢問,請傳送新訊息。", - "ElevationTitleSandboxDenied": " ⚠ 沙箱拒絕 ", "ElevationTitleRequired": " 沙箱提權 ", "ElevationFieldTool": " 工具: ", "ElevationFieldCmd": " 指令: ", @@ -1334,6 +1334,10 @@ "ToolReceiptDone": "完成", "ToolReceiptLinesSingular": "1 行", "ToolReceiptLinesPlural": "{count} 行", + "TodoStatusPending": "待處理", + "TodoStatusInProgress": "進行中", + "TodoStatusCompleted": "已完成", + "TodoStatusCancelled": "已取消", "CmdVoiceDescription": "切換語音輸入:錄製語音並轉錄為文字", "CmdVoiceSendDescription": "切換語音自動傳送:說出「send it」即可提交", "CmdVoiceControlDescription": "切換語音控制:能理解你草稿內容的聽寫", @@ -2452,7 +2456,7 @@ "NotificationElevationNeeded": "請允許或拒絕為「{tool}」提升存取權限", "NotificationDecisionWebHint": "請在此處或網頁上作出決定。", "NotificationTurnFailed": "本回合失敗", - "NotificationProviderFallback": "供應商發生可復原的錯誤,已切換至 {provider}(備用選項 {position}/{total})。", + "NotificationProviderFallback": "已切換至 {provider}(備用選項 {position}/{total}),因供應商發生可復原的錯誤。", "ApprovalNeverPostureBlocked": "已封鎖「{tool}」:此工作階段不允許使用需要核准的工具。", "NotificationWebApproved": "已在網頁上核准。", "NotificationWebDenied": "已在網頁上拒絕。", diff --git a/crates/localization/src/lib.rs b/crates/localization/src/lib.rs index 7823e0cf55..efbd761dbf 100644 --- a/crates/localization/src/lib.rs +++ b/crates/localization/src/lib.rs @@ -1022,6 +1022,7 @@ pub enum MessageId { ClipboardCutKeptText, /// Paste found nothing: the clipboard read came back empty or failed. ClipboardNothingToPaste, + ClipboardCopyFailed, ComposerOversizedSubmitHeld, /// Short toast form of [`Self::ComposerOversizedSubmitHeld`]; the toast row /// sheds clauses to fit, so the full text goes to the transcript instead. @@ -1501,7 +1502,6 @@ pub enum MessageId { ApprovalMoreLines, ApprovalAutoDeniedSession, // Sandbox elevation dialog. - ElevationTitleSandboxDenied, ElevationTitleRequired, ElevationFieldTool, ElevationFieldCmd, @@ -1589,6 +1589,10 @@ pub enum MessageId { ToolReceiptDone, ToolReceiptLinesSingular, ToolReceiptLinesPlural, + TodoStatusPending, + TodoStatusInProgress, + TodoStatusCompleted, + TodoStatusCancelled, // Voice commands (/voice, /voice-send, /voice-control) CmdVoiceDescription, CmdVoiceSendDescription, @@ -3657,6 +3661,7 @@ pub const ALL_MESSAGE_IDS: &[MessageId] = &[ MessageId::ClipboardSentToTerminal, MessageId::ClipboardCutKeptText, MessageId::ClipboardNothingToPaste, + MessageId::ClipboardCopyFailed, MessageId::ComposerOversizedSubmitHeld, MessageId::ComposerOversizedSubmitHeldShort, MessageId::KbContextMenu, @@ -4105,7 +4110,6 @@ pub const ALL_MESSAGE_IDS: &[MessageId] = &[ MessageId::ApprovalIntentLabel, MessageId::ApprovalMoreLines, MessageId::ApprovalAutoDeniedSession, - MessageId::ElevationTitleSandboxDenied, MessageId::ElevationTitleRequired, MessageId::ElevationFieldTool, MessageId::ElevationFieldCmd, @@ -4189,6 +4193,10 @@ pub const ALL_MESSAGE_IDS: &[MessageId] = &[ MessageId::ToolReceiptDone, MessageId::ToolReceiptLinesSingular, MessageId::ToolReceiptLinesPlural, + MessageId::TodoStatusPending, + MessageId::TodoStatusInProgress, + MessageId::TodoStatusCompleted, + MessageId::TodoStatusCancelled, MessageId::CmdVoiceDescription, MessageId::CmdVoiceSendDescription, MessageId::CmdVoiceControlDescription, diff --git a/crates/tui/src/provider_readiness.rs b/crates/tui/src/provider_readiness.rs index 7f2b38d6a0..5dc414113a 100644 --- a/crates/tui/src/provider_readiness.rs +++ b/crates/tui/src/provider_readiness.rs @@ -457,7 +457,7 @@ impl ResolvedProviderReadiness { Self::LocalUnchecked => Cow::Borrowed("local · not checked"), Self::Ready => Cow::Borrowed("ready"), Self::ConnectionCheckedModelUnchecked => { - Cow::Borrowed("models endpoint 2xx · model not checked") + Cow::Borrowed("endpoint reachable · model not checked") } Self::SavedLastCheckFailed { category, .. } => { Cow::Owned(format!("last check failed ({category})")) @@ -1075,7 +1075,7 @@ mod tests { ); assert_eq!( ResolvedProviderReadiness::ConnectionCheckedModelUnchecked.label(), - "models endpoint 2xx · model not checked" + "endpoint reachable · model not checked" ); assert!(!ResolvedProviderReadiness::ConnectionCheckedModelUnchecked.can_attempt()); } diff --git a/crates/tui/src/remote_control.rs b/crates/tui/src/remote_control.rs index 40221fec6a..2e2b32b7b8 100644 --- a/crates/tui/src/remote_control.rs +++ b/crates/tui/src/remote_control.rs @@ -784,7 +784,7 @@ pub struct PendingRemoteApproval { } #[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] -enum Status { +pub(crate) enum Status { #[default] Off, Connecting, @@ -1631,6 +1631,11 @@ impl RemoteControlController { } } + #[must_use] + pub(crate) fn status(&self) -> Status { + self.status + } + /// Test-only: advertise a validated live session link without a control /// plane. Mirrors the shape the runner-lease parser installs (`Connected` /// plus validated links) so `/rc link`/`/rc open` routing can be exercised diff --git a/crates/tui/src/tui/active_cell.rs b/crates/tui/src/tui/active_cell.rs index 9c8f0a7610..efbbc34156 100644 --- a/crates/tui/src/tui/active_cell.rs +++ b/crates/tui/src/tui/active_cell.rs @@ -338,6 +338,7 @@ mod tests { entries: vec![ExploringEntry { label: label.to_string(), status: ToolStatus::Running, + error: None, }], })) } diff --git a/crates/tui/src/tui/app.rs b/crates/tui/src/tui/app.rs index 1e32c786dd..641f31d10b 100644 --- a/crates/tui/src/tui/app.rs +++ b/crates/tui/src/tui/app.rs @@ -7390,11 +7390,7 @@ impl App { return effective.display_label_for_provider(provider).to_string(); } let effective = effective.display_label_for_provider(provider); - if requested == ReasoningEffort::Auto { - format!("auto: {effective}") - } else { - format!("{}→{effective}", requested.short_label()) - } + format!("{}→{effective}", requested.short_label()) } EffectiveReasoningEffort::ThinkingEnabledGranularityUnavailable => format!( "{}→thinking enabled; granularity unavailable", diff --git a/crates/tui/src/tui/app/tests.rs b/crates/tui/src/tui/app/tests.rs index 3aa842d9a9..71aa61650e 100644 --- a/crates/tui/src/tui/app/tests.rs +++ b/crates/tui/src/tui/app/tests.rs @@ -396,7 +396,7 @@ fn reasoning_effort_display_label_keeps_codex_top_tiers_distinct() { app.reasoning_effort = ReasoningEffort::Auto; app.last_effective_reasoning_effort = Some(EffectiveReasoningEffort::Tier(ReasoningEffort::Max)); - assert_eq!(app.reasoning_effort_display_label(), "auto: max"); + assert_eq!(app.reasoning_effort_display_label(), "auto→max"); } #[test] diff --git a/crates/tui/src/tui/approval/tests.rs b/crates/tui/src/tui/approval/tests.rs index c0130c79f8..6c753d5df1 100644 --- a/crates/tui/src/tui/approval/tests.rs +++ b/crates/tui/src/tui/approval/tests.rs @@ -2577,10 +2577,13 @@ fn test_elevation_render_en_has_expected_strings() { let lines = render_elevation_lines(&view, 70, 22); let joined = compact_elevation_text(&lines); assert!( - joined.contains("SandboxDenied"), + joined.contains("SandboxElevationRequired"), "missing en title:\n{joined}" ); - assert!(joined.contains("Tool:"), "missing en tool label:\n{joined}"); + assert!( + !joined.contains("Tool:"), + "the command row already names the shell:\n{joined}" + ); assert!(joined.contains("Cmd:"), "missing en cmd label:\n{joined}"); assert!( joined.contains("Reason:"), @@ -2618,7 +2621,7 @@ fn elevation_always_paints_every_option_including_the_safe_exit() { ); } assert!( - joined.contains("SandboxDenied") || joined.contains("SandboxElevationRequired"), + joined.contains("SandboxElevationRequired"), "{w}x{h}: the card lost its title:\n{joined}" ); for key in ["↑/↓", "Enter", "Esc"] { @@ -2643,11 +2646,7 @@ fn test_elevation_render_zh_hans_localizes_copy() { let view = ElevationView::new(elevation_shell_request(), Locale::ZhHans); let lines = render_elevation_lines(&view, 70, 22); let joined = compact_elevation_text(&lines); - assert!(joined.contains("沙箱拒绝"), "missing zh title:\n{joined}"); - assert!( - joined.contains("工具:"), - "missing zh tool label:\n{joined}" - ); + assert!(joined.contains("沙箱提权"), "missing zh title:\n{joined}"); assert!(joined.contains("命令:"), "missing zh cmd label:\n{joined}"); assert!( joined.contains("原因:"), @@ -2658,7 +2657,7 @@ fn test_elevation_render_zh_hans_localizes_copy() { "missing zh impact header:\n{joined}" ); let en_artifacts = [ - "SandboxDenied", + "SandboxElevationRequired", "Tool:", "Cmd:", "Reason:", @@ -2683,13 +2682,9 @@ fn test_elevation_render_ja_has_translated_copy() { let lines = render_elevation_lines(&view, 70, 22); let joined = compact_elevation_text(&lines); assert!( - joined.contains("サンドボックス拒否"), + joined.contains("サンドボックス昇格"), "missing ja title:\n{joined}" ); - assert!( - joined.contains("ツール:"), - "missing ja tool label:\n{joined}" - ); assert!( joined.contains("コマンド:"), "missing ja cmd label:\n{joined}" @@ -2698,7 +2693,7 @@ fn test_elevation_render_ja_has_translated_copy() { joined.contains("理由:"), "missing ja reason label:\n{joined}" ); - for eng in &["SandboxDenied", "Tool:", "Cmd:", "Reason:"] as &[&str] { + for eng in &["SandboxElevationRequired", "Tool:", "Cmd:", "Reason:"] as &[&str] { assert!( !joined.contains(eng), "English leak '{eng}' in ja:\n{joined}" @@ -2712,13 +2707,9 @@ fn test_elevation_render_zh_hant_has_translated_copy() { let lines = render_elevation_lines(&view, 70, 22); let joined = compact_elevation_text(&lines); assert!( - joined.contains("沙箱拒絕"), + joined.contains("沙箱提權"), "missing zh-Hant title:\n{joined}" ); - assert!( - joined.contains("工具:"), - "missing zh-Hant tool label:\n{joined}" - ); assert!( joined.contains("指令:"), "missing zh-Hant cmd label:\n{joined}" diff --git a/crates/tui/src/tui/history.rs b/crates/tui/src/tui/history.rs index 52103f48c2..11f5c97dc6 100644 --- a/crates/tui/src/tui/history.rs +++ b/crates/tui/src/tui/history.rs @@ -958,6 +958,7 @@ impl ToolCell { || matches!( self, ToolCell::Exec(_) + | ToolCell::Mcp(_) | ToolCell::PatchSummary(_) | ToolCell::Review(_) | ToolCell::PlanUpdate(_) @@ -1008,9 +1009,7 @@ impl ToolCell { ) -> Vec> { match self { ToolCell::Exec(cell) => cell.render_with_locale(width, low_motion, mode, locale), - ToolCell::Exploring(cell) => { - cell.lines_with_motion_and_locale(width, low_motion, locale) - } + ToolCell::Exploring(cell) => cell.render_with_locale(width, low_motion, mode, locale), ToolCell::PlanUpdate(cell) => cell.lines_with_motion(width, low_motion), ToolCell::PatchSummary(cell) => cell.render( width, @@ -1279,11 +1278,22 @@ impl ExploringCell { self.lines_with_motion_and_locale(width, low_motion, Locale::En) } + #[cfg(test)] pub fn lines_with_motion_and_locale( &self, width: u16, low_motion: bool, locale: Locale, + ) -> Vec> { + self.render_with_locale(width, low_motion, RenderMode::Live, locale) + } + + pub(super) fn render_with_locale( + &self, + width: u16, + low_motion: bool, + mode: RenderMode, + locale: Locale, ) -> Vec> { let mut lines = Vec::new(); let status = self.status(); @@ -1309,14 +1319,19 @@ impl ExploringCell { // incoherent `read done · Searching …` (#4145). Read/list or mixed // cards keep the neutral `read` verb the Workspace card has always used. let family = exploring_card_family(&self.entries); - lines.push(render_tool_header_with_family_and_summary( + let header = render_tool_header_with_family_and_summary( family, header_summary.as_deref(), header_state.as_ref(), status, None, low_motion, - )); + ); + let header_shows_label = !multi_entry + && header_summary + .as_deref() + .is_some_and(|label| header_shows_in_full(&header, label, usize::from(width))); + lines.push(header); // Dot-grid status strip — one glyph per entry, showing parallel // fanout at a glance: ●=done ◐=running ✕=failed. @@ -1347,16 +1362,26 @@ impl ExploringCell { Style::default().fg(tool_glyph_color(e.status, family)), ) })); - let counts = format!( - " {done} done, {running} running{}", - if failed > 0 { - format!(", {failed} failed") - } else { - String::new() - }, - ); + let counts = [ + (done, codewhale_localization::MessageId::WorkflowCountDone), + ( + running, + codewhale_localization::MessageId::WorkflowCountRunning, + ), + ( + failed, + codewhale_localization::MessageId::WorkflowCountFailed, + ), + ] + .into_iter() + .filter(|(count, _)| *count > 0) + .map(|(count, id)| { + codewhale_localization::tr(locale, id).replace("{count}", &count.to_string()) + }) + .collect::>() + .join(", "); dot_spans.push(Span::styled( - counts, + format!(" {counts}"), Style::default().fg(tool_glyph_color(status, family)), )); lines.push(Line::from(dot_spans)); @@ -1370,7 +1395,7 @@ impl ExploringCell { tool_value_style(), width, )); - } else { + } else if !header_shows_label { let prefix = match entry.status { ToolStatus::Running => "live", ToolStatus::Success => "done", @@ -1385,6 +1410,26 @@ impl ExploringCell { width, )); } + if let Some(error) = entry.error.as_deref() { + if multi_entry { + if let Some(reason) = error.lines().map(str::trim).find(|line| !line.is_empty()) + { + lines.extend(render_card_detail_line( + None, + reason, + tool_value_style(), + width, + )); + } + } else { + lines.extend(render_tool_output_mode( + error, + width, + TOOL_FAILURE_PREVIEW_LINES, + mode, + )); + } + } } lines } @@ -1402,6 +1447,7 @@ impl ExploringCell { pub struct ExploringEntry { pub label: String, pub status: ToolStatus, + pub error: Option, } /// Calm outcome and exact evidence for a structured File mutation. @@ -1673,15 +1719,19 @@ impl ViewImageCell { /// Render the image view cell into lines. pub fn lines_with_motion(&self, width: u16, low_motion: bool) -> Vec> { let path = self.path.display().to_string(); - let mut lines = vec![render_tool_header_with_summary( + let header = render_tool_header_with_summary( "Image", Some(&path), "done", ToolStatus::Success, None, low_motion, - )]; - lines.extend(render_compact_kv("path", &path, tool_value_style(), width)); + ); + let header_shows_path = header_shows_in_full(&header, &path, usize::from(width)); + let mut lines = vec![header]; + if !header_shows_path { + lines.extend(render_compact_kv("path", &path, tool_value_style(), width)); + } lines } } @@ -1701,20 +1751,24 @@ impl WebSearchCell { /// Render the web search cell into lines. pub fn lines_with_motion(&self, width: u16, low_motion: bool) -> Vec> { let mut lines = Vec::new(); - lines.push(render_tool_header_with_summary( + let header = render_tool_header_with_summary( "Search", Some(&self.query), tool_status_label(self.status), self.status, None, low_motion, - )); - lines.extend(render_compact_kv( - "query", - &self.query, - tool_value_style(), - width, - )); + ); + let header_shows_query = header_shows_in_full(&header, &self.query, usize::from(width)); + lines.push(header); + if !header_shows_query { + lines.extend(render_compact_kv( + "query", + &self.query, + tool_value_style(), + width, + )); + } if let Some(source) = self.source.as_ref() { lines.extend(render_compact_kv( "source", @@ -1816,7 +1870,7 @@ impl GenericToolCell { // Issue #241: when the underlying tool is a checklist/todo update and // the output is parseable, render a purpose-built progress card // instead of dumping the JSON into the generic tool block. - if let Some(lines) = self.try_render_as_checklist(width, low_motion, mode) { + if let Some(lines) = self.try_render_as_checklist(width, low_motion, mode, locale) { return lines; } @@ -1904,15 +1958,21 @@ impl GenericToolCell { &self.name, self.input_summary.as_deref(), ); - lines.push(render_tool_header_with_family_and_summary( + let header = render_tool_header_with_family_and_summary( family, header_summary.as_deref(), &tool_receipt_label(family, self.status, self.output.as_deref(), locale), self.status, None, low_motion, - )); - if should_show_raw_tool_name(&self.name, family, mode) { + ); + let header_has_args = self.input_summary.as_deref().is_some_and(|args| { + header_repeats_args(&header, header_summary.as_deref(), args, usize::from(width)) + }); + lines.push(header); + if should_show_raw_tool_name(&self.name, family, mode) + && !crate::tui::widgets::tool_card::should_show_tool_name_in_header(&self.name, family) + { lines.extend(render_compact_kv( "name", &self.name, @@ -1940,8 +2000,9 @@ impl GenericToolCell { )); } } else { - let show_args = matches!(self.status, ToolStatus::Running | ToolStatus::Failed) - || self.output.is_none(); + let show_args = (matches!(self.status, ToolStatus::Running | ToolStatus::Failed) + || self.output.is_none()) + && !header_has_args; if show_args && let Some(summary) = self.input_summary.as_ref() { lines.extend(render_compact_kv( "args", @@ -2008,6 +2069,7 @@ impl GenericToolCell { width: u16, low_motion: bool, mode: RenderMode, + locale: Locale, ) -> Option>> { if !is_checklist_tool_name(&self.name) { return None; @@ -2032,6 +2094,7 @@ impl GenericToolCell { &change, width, low_motion, + locale, )); } @@ -2285,13 +2348,16 @@ fn render_spillover_annotation(width: u16) -> Line<'static> { // Matches the model-facing preview footer (truncate.rs) and the existing // "Alt+V opens …" hint style (#3256): one quiet line, no handles or paths. let affordance = format!( - "Output shortened — {}", + "Output shortened \u{00B7} {}", crate::tui::key_shortcuts::tool_details_shortcut_action_hint("output") ); - Line::from(Span::styled( - truncate_text(&affordance, usize::from(width).max(8)), + let room = usize::from(width) + .saturating_sub(UnicodeWidthStr::width(TRANSCRIPT_RAIL)) + .max(8); + details_affordance_line( + &truncate_text(&affordance, room), Style::default().fg(palette::TEXT_MUTED).italic(), - )) + ) } /// Detect a truncated-output preview: the current model-facing footer (which @@ -2666,10 +2732,11 @@ fn error_body_style(severity: crate::error_taxonomy::ErrorSeverity) -> Style { } /// Render an engine error without markdown interpretation. The live transcript -/// always advertises the dedicated full-error pager: terminal height, scroll -/// position, and adjacent tool cards can otherwise make a multiline recovery -/// instruction look like a clipped one-line failure. Transcript/pager mode -/// omits the recursive affordance while preserving every character. +/// advertises the dedicated full-error pager once the error wraps past four +/// rows: terminal height, scroll position, and adjacent tool cards can +/// otherwise make a multiline recovery instruction look like a clipped +/// one-line failure. Transcript/pager mode omits the recursive affordance +/// while preserving every character. fn render_error_message( message: &str, severity: crate::error_taxonomy::ErrorSeverity, @@ -2698,7 +2765,7 @@ fn render_error_message( for line in lines.iter_mut().skip(1) { line.spans.insert(0, Span::styled(rail.clone(), rail_style)); } - if show_full_error_affordance { + if show_full_error_affordance && lines.len() > 4 { lines.push(details_affordance_line( &crate::tui::key_shortcuts::tool_details_shortcut_action_hint("full error"), Style::default().fg(palette::TEXT_MUTED).italic(), @@ -2776,9 +2843,11 @@ fn render_tool_header_with_family_and_summary( ), Span::styled(format!("{glyph} "), glyph_style), Span::styled(verb.to_string(), tool_title_style(status)), - Span::styled(" ", Style::default()), - Span::styled(state_owned, tool_status_style(status, family)), ]; + if !state_owned.is_empty() { + spans.push(Span::styled(" ", Style::default())); + spans.push(Span::styled(state_owned, tool_status_style(status, family))); + } // #4148: don't let the summary echo the verb it sits next to — an // identity/summary that resolves to the family word itself would render a @@ -2798,6 +2867,29 @@ fn render_tool_header_with_family_and_summary( Line::from(spans) } +fn header_shows_in_full(header: &Line<'static>, summary: &str, available: usize) -> bool { + normalize_header_summary(summary).is_none_or(|summary| { + crate::tui::ui_text::text_display_width(&summary) <= TOOL_HEADER_SUMMARY_LIMIT + }) && header.width() <= available +} + +fn header_repeats_args( + header: &Line<'static>, + header_summary: Option<&str>, + args: &str, + available: usize, +) -> bool { + let args = args.trim(); + let shown = match args.split_once(": ") { + Some((_, value)) if !args.contains(", ") => value.trim(), + _ => args, + }; + header_summary.is_some_and(|summary| { + (summary == shown || summary.ends_with(&format!(" \u{00B7} {shown}"))) + && header_shows_in_full(header, summary, available) + }) +} + fn normalize_header_summary(summary: &str) -> Option { let normalized = summary .split_whitespace() diff --git a/crates/tui/src/tui/history/checklist.rs b/crates/tui/src/tui/history/checklist.rs index da7358c2bd..9ab71a46a9 100644 --- a/crates/tui/src/tui/history/checklist.rs +++ b/crates/tui/src/tui/history/checklist.rs @@ -5,10 +5,11 @@ use ratatui::text::{Line, Span}; use serde_json::Value; use unicode_width::UnicodeWidthStr; +use codewhale_localization::{Locale, MessageId, tr}; use codewhale_palette as palette; use super::{ - RenderMode, TRANSCRIPT_RAIL, ToolStatus, render_card_detail_line_single, render_compact_kv, + RenderMode, TRANSCRIPT_RAIL, ToolStatus, render_card_detail_line_single, render_tool_header_with_family_and_summary, tool_status_label, tool_value_style, truncate_text, wrap_text, }; @@ -136,6 +137,7 @@ pub(super) fn render_checklist_change_card( change: &ChecklistChange, width: u16, low_motion: bool, + locale: Locale, ) -> Vec> { let mut lines = Vec::new(); let header_summary = format!( @@ -168,7 +170,7 @@ pub(super) fn render_checklist_change_card( UnicodeWidthStr::width(TRANSCRIPT_RAIL) + UnicodeWidthStr::width(prefix.as_str()); let id_label = format!("Todo #{}", change.id); let arrow = " \u{2192} "; - let status_label = change.status.clone(); + let status_label = checklist_status_label(&change.status, locale); let title_budget = usize::from(width) .saturating_sub(prefix_width) .saturating_sub(UnicodeWidthStr::width(id_label.as_str())) @@ -197,7 +199,7 @@ pub(super) fn render_checklist_change_card( lines.push(render_card_detail_line_single( None, &format!( - "{} item{}; {}", + "{} item{} \u{00B7} {}", snapshot.total, if snapshot.total == 1 { "" } else { "s" }, crate::tui::key_shortcuts::tool_details_shortcut_action_hint("list") @@ -207,6 +209,18 @@ pub(super) fn render_checklist_change_card( lines } +fn checklist_status_label(status: &str, locale: Locale) -> String { + use crate::tools::todo::TodoStatus; + let id = match TodoStatus::from_str(status) { + Some(TodoStatus::Pending) => MessageId::TodoStatusPending, + Some(TodoStatus::InProgress) => MessageId::TodoStatusInProgress, + Some(TodoStatus::Completed) => MessageId::TodoStatusCompleted, + Some(TodoStatus::Cancelled) => MessageId::TodoStatusCancelled, + None => return status.to_string(), + }; + tr(locale, id).into_owned() +} + fn checklist_status_marker(status: &str) -> (&'static str, Color) { match status.to_ascii_lowercase().as_str() { "completed" | "done" => ("\u{2611}", palette::STATUS_SUCCESS), // ☑ @@ -241,12 +255,6 @@ pub(super) fn render_checklist_card( None, low_motion, )); - lines.extend(render_compact_kv( - "checklist", - name, - tool_value_style(), - width, - )); let cap = match mode { RenderMode::Live => CHECKLIST_LIVE_ITEM_LIMIT, @@ -286,7 +294,7 @@ pub(super) fn render_checklist_card( lines.push(render_card_detail_line_single( None, &format!( - "+{omitted} more; {}", + "+{omitted} more \u{00B7} {}", crate::tui::key_shortcuts::tool_details_shortcut_action_hint("list") ), Style::default().fg(palette::TEXT_DIM), diff --git a/crates/tui/src/tui/history/tests.rs b/crates/tui/src/tui/history/tests.rs index 3e0d273fbc..e098af60a2 100644 --- a/crates/tui/src/tui/history/tests.rs +++ b/crates/tui/src/tui/history/tests.rs @@ -1424,6 +1424,7 @@ fn a_card_verb_agrees_with_its_own_label_in_every_locale() { entries: vec![super::ExploringEntry { label: label.to_string(), status: ToolStatus::Success, + error: None, }], }; @@ -2083,11 +2084,12 @@ fn a_checklist_update_shows_only_the_item_that_changed() { }, 80, true, + codewhale_localization::Locale::En, ); assert!(lines.len() >= 3, "header, change, summary: {}", lines.len()); let change = line_text(&lines[1]); - for needle in ["#2", "Write the test", "in_progress"] { + for needle in ["#2", "Write the test", "in progress"] { assert!(change.contains(needle), "missing {needle:?}: {change:?}"); } for other in ["Land the PR", "Read the spec"] { @@ -2123,6 +2125,7 @@ fn a_checklist_update_shows_only_the_item_that_changed() { }, 80, true, + codewhale_localization::Locale::En, ); let change = line_text(&lines[1]); assert!(change.contains("#99") && change.contains("(missing title)")); @@ -2194,7 +2197,7 @@ fn fan_out_prompts_replace_the_inline_argument_summary() { ); let mut plain = generic_tool("file_search", ToolStatus::Running); - plain.input_summary = Some("query: foo".to_string()); + plain.input_summary = Some("query: foo, path: src".to_string()); let text = lines_text(&HistoryCell::Tool(ToolCell::Generic(plain)).lines(80)); assert!( text.contains("query: foo"), @@ -2563,7 +2566,7 @@ fn a_collapsed_run_summary_names_what_actually_happened() { ..Default::default() } )), - "Explored 4 files, 1 search: read_file, list_dir" + "Explored 4 files, 1 search: read file, list dir" ); assert_eq!( @@ -2575,7 +2578,7 @@ fn a_collapsed_run_summary_names_what_actually_happened() { ..Default::default() } )), - "Explored 2 files: read_file, ran 2 commands: run_tests, validate_data", + "Explored 2 files: read file, ran 2 commands: run tests, validate data", "each clause lists only its own families" ); @@ -2604,7 +2607,7 @@ fn a_collapsed_run_summary_names_what_actually_happened() { assert_eq!(commands[0].activity.commands, 3); assert_eq!( super::tool_run_summary(&commands[0]), - "Ran 3 commands: run_tests, run_verifiers, validate_data" + "Ran 3 commands: run tests, run verifiers, validate data" ); let git = super::detect_tool_runs( @@ -2618,7 +2621,7 @@ fn a_collapsed_run_summary_names_what_actually_happened() { assert_eq!(git[0].activity.files, 3); assert_eq!( super::tool_run_summary(&git[0]), - "Explored 3 files: git_log, git_show, git_blame" + "Explored 3 files: git log, git show, git blame" ); } @@ -2797,6 +2800,7 @@ fn exploring_cell_status_keeps_the_loudest_terminal_state() { .map(|status| ExploringEntry { label: "Reading src/foo.rs".to_string(), status: *status, + error: None, }) .collect(), }; diff --git a/crates/tui/src/tui/history/tool_output.rs b/crates/tui/src/tui/history/tool_output.rs index 84c85fd077..3a52d8967d 100644 --- a/crates/tui/src/tui/history/tool_output.rs +++ b/crates/tui/src/tui/history/tool_output.rs @@ -423,7 +423,7 @@ fn render_preserved_output_mode( if omitted > 0 { lines.push(details_affordance_line( &format!( - "{omitted} lines omitted; {}", + "{omitted} lines omitted \u{00B7} {}", crate::tui::key_shortcuts::tool_details_shortcut_action_hint("output") ), Style::default().fg(palette::TEXT_MUTED), diff --git a/crates/tui/src/tui/history/tool_run.rs b/crates/tui/src/tui/history/tool_run.rs index 08b2c84a87..e3dad10549 100644 --- a/crates/tui/src/tui/history/tool_run.rs +++ b/crates/tui/src/tui/history/tool_run.rs @@ -312,12 +312,11 @@ pub fn tool_run_summary(run: &ToolRun) -> String { } fn activity_family_summary(run: &ToolRun, activities: &[ToolRunActivity]) -> Option { - let mut families = Vec::new(); + let mut families: Vec = Vec::new(); for family in &run.tool_families { - if activities.contains(&classify_tool_name_activity(family)) - && !families.iter().any(|existing| existing == family) - { - families.push(family.as_str()); + let label = family.replace('_', " "); + if activities.contains(&classify_tool_name_activity(family)) && !families.contains(&label) { + families.push(label); } } diff --git a/crates/tui/src/tui/mouse_ui.rs b/crates/tui/src/tui/mouse_ui.rs index 04bbb13e78..edc4d7c5d1 100644 --- a/crates/tui/src/tui/mouse_ui.rs +++ b/crates/tui/src/tui/mouse_ui.rs @@ -1763,12 +1763,16 @@ pub(crate) fn apply_context_menu_action( } } } - ContextMenuAction::CopyText { text } => { - app.status_message = Some(match app.clipboard.write_text_status(&text) { - Ok(transport) => copy_receipt(app, transport, app.tr(MessageId::ClipboardCopied)), - Err(error) => format!("Copy failed: {error}"), - }); - } + ContextMenuAction::CopyText { text } => match app.clipboard.write_text_status(&text) { + Ok(transport) => { + let receipt = copy_receipt(app, transport, app.tr(MessageId::ClipboardCopied)); + app.push_status_toast(receipt, StatusToastLevel::Info, None); + } + Err(error) => { + let receipt = copy_failed_receipt(app, error); + app.push_status_toast(receipt, StatusToastLevel::Error, None); + } + }, ContextMenuAction::ToggleWindowPin => { crate::tui::window_control::toggle_pin(app); } @@ -1959,6 +1963,11 @@ pub(crate) fn copy_receipt( } } +pub(crate) fn copy_failed_receipt(app: &App, error: impl std::fmt::Display) -> String { + app.tr(MessageId::ClipboardCopyFailed) + .replace("{error}", &error.to_string()) +} + /// Ctrl+X on a composer selection. The text is deleted only after a native /// clipboard confirmed the copy; an OSC 52 / tmux copy is never confirmed, /// so the text stays and the receipt says why. @@ -1969,15 +1978,20 @@ pub(crate) fn cut_selection(app: &mut App) { } match app.clipboard.write_text_status(&sel) { Ok(crate::tui::clipboard::CopyTransport::Native) => { - app.push_status_toast("Cut to clipboard", StatusToastLevel::Info, None); + app.push_status_toast( + app.tr(MessageId::ClipboardCopied), + StatusToastLevel::Info, + None, + ); app.delete_selection(); } Ok(crate::tui::clipboard::CopyTransport::Terminal) => { let receipt = app.tr(MessageId::ClipboardCutKeptText).into_owned(); app.push_status_toast(receipt, StatusToastLevel::Info, None); } - Err(_) => { - app.push_status_toast("Cut failed", StatusToastLevel::Error, None); + Err(error) => { + let receipt = copy_failed_receipt(app, error); + app.push_status_toast(receipt, StatusToastLevel::Error, None); } } } @@ -2068,10 +2082,14 @@ pub(crate) fn copy_active_selection(app: &mut App) { if !sel.is_empty() { match app.clipboard.write_text_status(&sel) { Ok(transport) => { - app.status_message = Some(copy_receipt(app, transport, "Selection copied")); + let receipt = copy_receipt(app, transport, app.tr(MessageId::ClipboardCopied)); + app.push_status_toast(receipt, StatusToastLevel::Info, None); app.clear_selection(); } - Err(_) => app.status_message = Some("Copy failed".to_string()), + Err(error) => { + let receipt = copy_failed_receipt(app, error); + app.push_status_toast(receipt, StatusToastLevel::Error, None); + } } return; } @@ -2093,25 +2111,22 @@ pub(crate) fn copy_active_selection(app: &mut App) { .map(|text| (text, None)) }); if let Some((text, markdown_cells)) = payload { - let written = app.clipboard.write_text_status(&text); - if let Ok(crate::tui::clipboard::CopyTransport::Terminal) = written { - app.status_message = Some(copy_receipt( - app, - crate::tui::clipboard::CopyTransport::Terminal, - "", - )); - } else if written.is_ok() { - match markdown_cells { - Some(cells) => { - let toast = app + match app.clipboard.write_text_status(&text) { + Ok(transport) => { + let receipt = match (transport, markdown_cells) { + (crate::tui::clipboard::CopyTransport::Native, Some(cells)) => app .tr(MessageId::SelectionCopiedAsMarkdown) - .replace("{count}", &cells.to_string()); - app.push_status_toast(toast, StatusToastLevel::Info, None); - } - None => app.status_message = Some("Selection copied".to_string()), + .replace("{count}", &cells.to_string()), + (transport, _) => { + copy_receipt(app, transport, app.tr(MessageId::ClipboardCopied)) + } + }; + app.push_status_toast(receipt, StatusToastLevel::Info, None); + } + Err(error) => { + let receipt = copy_failed_receipt(app, error); + app.push_status_toast(receipt, StatusToastLevel::Error, None); } - } else { - app.status_message = Some("Copy failed".to_string()); } } else { clear_transcript_selection(app); @@ -3330,7 +3345,10 @@ mod tests { select(&mut app); super::apply_context_menu_action(&mut app, ContextMenuAction::CopySelection); assert_eq!(app.clipboard.last_written_text(), Some("hello")); - assert_eq!(app.status_message.as_deref(), Some("Selection copied")); + assert_eq!( + app.status_toasts.back().map(|toast| toast.text.as_str()), + Some("Copied to the clipboard") + ); select(&mut app); super::apply_context_menu_action(&mut app, ContextMenuAction::OpenSelection); @@ -3407,7 +3425,7 @@ mod tests { /// Ctrl+X deletes the selection only when a native clipboard confirmed /// the copy. A terminal (OSC 52 / tmux) copy keeps the text and says why; - /// a failed copy keeps it and says the cut failed. + /// a failed copy keeps it and says the copy failed. #[test] fn cut_deletes_only_after_a_native_copy() { fn select_all(app: &mut App, text: &str) { @@ -3424,7 +3442,7 @@ mod tests { super::cut_selection(&mut app); assert_eq!(app.clipboard.last_written_text(), Some("hello")); assert_eq!(app.input, "", "a native copy cuts"); - assert_eq!(last_toast(&app), Some("Cut to clipboard")); + assert_eq!(last_toast(&app), Some("Copied to the clipboard")); app.clipboard = crate::tui::clipboard::ClipboardHandler::terminal_only_for_test(); select_all(&mut app, "kept"); @@ -3444,7 +3462,11 @@ mod tests { select_all(&mut app, "also kept"); super::cut_selection(&mut app); assert_eq!(app.input, "also kept", "a failed copy keeps the text"); - assert_eq!(last_toast(&app), Some("Cut failed")); + assert!( + last_toast(&app).is_some_and(|toast| toast.starts_with("Clipboard copy failed: ")), + "{:?}", + last_toast(&app) + ); } /// A right-click on a row's inline stop zone opens the row's own menu; @@ -3486,6 +3508,10 @@ mod tests { /// the text was sent to the terminal, not that it was copied. #[test] fn copy_receipts_name_the_transport() { + fn last_toast(app: &App) -> Option<&str> { + app.status_toasts.back().map(|toast| toast.text.as_str()) + } + let mut app = create_test_app(); super::apply_context_menu_action( &mut app, @@ -3494,10 +3520,7 @@ mod tests { }, ); assert_eq!(app.clipboard.last_written_text(), Some("agent_1")); - assert_eq!( - app.status_message.as_deref(), - Some("Copied to the clipboard") - ); + assert_eq!(last_toast(&app), Some("Copied to the clipboard")); app.clipboard = crate::tui::clipboard::ClipboardHandler::terminal_only_for_test(); super::apply_context_menu_action( @@ -3507,7 +3530,7 @@ mod tests { }, ); assert_eq!( - app.status_message.as_deref(), + last_toast(&app), Some("Sent to the terminal clipboard (terminals do not confirm it)") ); @@ -3525,7 +3548,7 @@ mod tests { app.cursor_position = 5; super::apply_context_menu_action(&mut app, ContextMenuAction::CopySelection); assert_eq!( - app.status_message.as_deref(), + last_toast(&app), Some("Sent to the terminal clipboard (terminals do not confirm it)") ); @@ -3537,11 +3560,9 @@ mod tests { }, ); assert!( - app.status_message - .as_deref() - .is_some_and(|status| status.starts_with("Copy failed")), + last_toast(&app).is_some_and(|toast| toast.starts_with("Clipboard copy failed: ")), "{:?}", - app.status_message + last_toast(&app) ); } diff --git a/crates/tui/src/tui/mouse_ui/primary_tests.rs b/crates/tui/src/tui/mouse_ui/primary_tests.rs index 12a6a4ec7f..61fc93ac79 100644 --- a/crates/tui/src/tui/mouse_ui/primary_tests.rs +++ b/crates/tui/src/tui/mouse_ui/primary_tests.rs @@ -281,8 +281,11 @@ fn transcript_copy_falls_back_to_rendered_text_when_markdown_is_off() { let toasts_before = app.status_toasts.len(); copy_active_selection(&mut app); assert_eq!(app.clipboard.last_written_text(), Some(expected.as_str())); - assert_eq!(app.status_message.as_deref(), Some("Selection copied")); - assert_eq!(app.status_toasts.len(), toasts_before); + assert_eq!(app.status_toasts.len(), toasts_before + 1); + assert_eq!( + app.status_toasts.back().map(|toast| toast.text.as_str()), + Some("Copied to the clipboard") + ); } #[test] diff --git a/crates/tui/src/tui/phase_strip.rs b/crates/tui/src/tui/phase_strip.rs index 522e25830a..3c30387ca6 100644 --- a/crates/tui/src/tui/phase_strip.rs +++ b/crates/tui/src/tui/phase_strip.rs @@ -54,7 +54,7 @@ pub(crate) fn route_identity_fields( // A route that cannot prove its effective tier states no effort field // rather than `high→effective unavailable` (#5950): a placeholder that // can never resolve is noise, not a reading. First-party routes keep - // their tier, `auto: tier` and `req→eff` labels. + // their tier and `req→eff` labels. // Labeled, so a bare "max" never sits on the row unexplained (mark 8). let effort = app .provable_reasoning_effort_label() @@ -1099,7 +1099,9 @@ pub(crate) fn tideline_footer_from_app(app: &mut App, width: u16) -> TidelineFoo // MCP or plugin boot chip, else the remote-control state when it is on. // Clause-shed against half the row — the posture facts own the other // half. - let notice_budget = (usize::from(width) / 2).max(8); + let notice_budget = (usize::from(width) / 2) + .max(8) + .min(usize::from(width).saturating_sub(permission_chip.0.width() + 5)); let right = selected_notice(app.active_status_toast(phase), &phase_label) .map(|(text, ink, _urgent)| { ( @@ -1128,9 +1130,14 @@ pub(crate) fn tideline_footer_from_app(app: &mut App, width: u16) -> TidelineFoo }) .and_then(|(text, ink)| fit_notice(&text, notice_budget).map(|fitted| (fitted, ink))) .or_else(|| { + let ink = if app.remote_control.status() == crate::remote_control::Status::Failed { + ChromeInk::Failure + } else { + ChromeInk::Info + }; app.remote_control .status_word() - .map(|word| (format!("/rc {word}"), ChromeInk::Info)) + .map(|word| (format!("/rc {word}"), ink)) }); let (turn_clock, session_clock) = working_clock(app, phase, &phase_label); diff --git a/crates/tui/src/tui/setup/mod.rs b/crates/tui/src/tui/setup/mod.rs index 1602ecfd49..a5de68c911 100644 --- a/crates/tui/src/tui/setup/mod.rs +++ b/crates/tui/src/tui/setup/mod.rs @@ -547,17 +547,12 @@ impl SetupRuntimeFacts { remote_command_provider: remote.command_provider, remote_result: remote.result, remote_control_result: { - let status = app.remote_control.status_line(); - let message = if status.starts_with("Remote control: connected") { - MessageId::SetupRemoteStatusReady - } else if status.starts_with("Remote control: connecting") - || status.starts_with("Remote control: stopping") - { - MessageId::SetupStatusInProgress - } else if status.starts_with("Remote control: disconnected") { - MessageId::SetupRemoteStatusNeedsAction - } else { - MessageId::SetupRemoteStatusDisabled + use crate::remote_control::Status; + let message = match app.remote_control.status() { + Status::Connected => MessageId::SetupRemoteStatusReady, + Status::Connecting | Status::Stopping => MessageId::SetupStatusInProgress, + Status::Failed => MessageId::SetupRemoteStatusNeedsAction, + Status::Off => MessageId::SetupRemoteStatusDisabled, }; tr(app.ui_locale, message).into_owned() }, diff --git a/crates/tui/src/tui/tool_routing.rs b/crates/tui/src/tui/tool_routing.rs index 410c5bba07..0139e1e702 100644 --- a/crates/tui/src/tui/tool_routing.rs +++ b/crates/tui/src/tui/tool_routing.rs @@ -57,6 +57,7 @@ pub(super) fn handle_tool_call_started( ExploringEntry { label, status: ToolStatus::Running, + error: None, }, ) .map_or(0, |(_, inner)| inner); @@ -771,6 +772,14 @@ pub(super) fn handle_tool_call_complete( && let Some(entry) = cell.entries.get_mut(entry_index) { entry.status = tool_status_from_result(result); + entry.error = if entry.status == ToolStatus::Failed { + match result { + Ok(tool_result) => visible_tool_output(&tool_result.content), + Err(err) => Some(err.to_string()), + } + } else { + None + }; app.mark_history_updated(); // Mutating the in-flight exploring cell needs an active-cell // revision bump so the transcript cache invalidates the synthetic diff --git a/crates/tui/src/tui/ui/event_loop.rs b/crates/tui/src/tui/ui/event_loop.rs index 1752c80a68..517de23b63 100644 --- a/crates/tui/src/tui/ui/event_loop.rs +++ b/crates/tui/src/tui/ui/event_loop.rs @@ -2026,7 +2026,7 @@ pub(crate) async fn run_event_loop( if let Err(err) = completion { tracing::warn!(error = %err, "background terminal clipboard write failed"); app.push_status_toast( - format!("Clipboard copy failed: {err}"), + crate::tui::mouse_ui::copy_failed_receipt(app, &err), StatusToastLevel::Error, None, ); @@ -2911,7 +2911,7 @@ pub(crate) async fn run_event_loop( // to interrupt a long-running turn (#1367). Only // surface when the status_message slot is empty so // we don't trample over a real transient message - // (e.g. "/queue saved", "Selection copied"); the + // (e.g. "/queue saved"); the // hint then auto-clears as soon as anything else // updates the slot. if app.status_message.is_none() { @@ -6502,18 +6502,7 @@ pub(crate) async fn run_event_loop( KeyCode::Char('c') | KeyCode::Char('C') if key_shortcuts::is_copy_shortcut(&key) => { - let sel = app.selected_text(); - if !sel.is_empty() { - if let Ok(transport) = app.clipboard.write_text_status(&sel) { - let receipt = copy_receipt(app, transport, "Copied to clipboard"); - app.push_status_toast(receipt, StatusToastLevel::Info, None); - app.clear_selection(); - } else { - app.push_status_toast("Copy failed", StatusToastLevel::Error, None); - } - } else { - copy_active_selection(app); - } + copy_active_selection(app); } KeyCode::Char('c') if key.modifiers.contains(KeyModifiers::CONTROL) => { // Four behaviors layered on Ctrl+C in priority order — see diff --git a/crates/tui/src/tui/ui/tests.rs b/crates/tui/src/tui/ui/tests.rs index f98789c816..0cf24b0b6e 100644 --- a/crates/tui/src/tui/ui/tests.rs +++ b/crates/tui/src/tui/ui/tests.rs @@ -4641,8 +4641,8 @@ fn mouse_selection_fragment_drag_copies_exact_text() { "must not round out to the whole cell, got {copied:?}" ); assert_eq!( - app.status_message.as_deref(), - Some("Selection copied"), + app.status_toasts.back().map(|toast| toast.text.as_str()), + Some("Copied to the clipboard"), "fragment copy reports through the text-fallback receipt" ); assert!( @@ -24254,7 +24254,7 @@ fn auto_route_receipt_survives_session_snapshot_and_restore() { restored_app.last_effective_reasoning_effort, Some(EffectiveReasoningEffort::Tier(ReasoningEffort::High)) ); - assert_eq!(restored_app.reasoning_effort_display_label(), "auto: high"); + assert_eq!(restored_app.reasoning_effort_display_label(), "auto→high"); } #[test] diff --git a/crates/tui/src/tui/widgets/mod.rs b/crates/tui/src/tui/widgets/mod.rs index 0ec6707ae0..cd6b261d65 100644 --- a/crates/tui/src/tui/widgets/mod.rs +++ b/crates/tui/src/tui/widgets/mod.rs @@ -2528,16 +2528,15 @@ impl Renderable for ElevationWidget<'_> { let popup_width = 70.min(area.width.saturating_sub(4)); - let mut lines = vec![ - Line::from(""), - Line::from(vec![Span::styled( - tr(self.locale, MessageId::ElevationTitleSandboxDenied), - Style::default() - .fg(palette::STATUS_ERROR) - .add_modifier(Modifier::BOLD), - )]), - Line::from(""), - Line::from(vec![ + let mut lines = vec![match self.request.command.as_deref() { + Some(command) => Line::from(vec![ + Span::raw(tr(self.locale, MessageId::ElevationFieldCmd)), + Span::styled( + crate::utils::truncate_with_ellipsis(command, 45, "..."), + Style::default().fg(palette::TEXT_MUTED), + ), + ]), + None => Line::from(vec![ Span::raw(tr(self.locale, MessageId::ElevationFieldTool)), Span::styled( &self.request.tool_name, @@ -2546,15 +2545,7 @@ impl Renderable for ElevationWidget<'_> { .add_modifier(Modifier::BOLD), ), ]), - ]; - - if let Some(ref command) = self.request.command { - let cmd_display = crate::utils::truncate_with_ellipsis(command, 45, "..."); - lines.push(Line::from(vec![ - Span::raw(tr(self.locale, MessageId::ElevationFieldCmd)), - Span::styled(cmd_display, Style::default().fg(palette::TEXT_MUTED)), - ])); - } + }]; lines.push(Line::from("")); lines.push(Line::from(vec![ diff --git a/crates/tui/src/tui/widgets/tool_card.rs b/crates/tui/src/tui/widgets/tool_card.rs index 8e912913f7..c05cac6370 100644 --- a/crates/tui/src/tui/widgets/tool_card.rs +++ b/crates/tui/src/tui/widgets/tool_card.rs @@ -222,7 +222,7 @@ fn summary_value(summary: &str, key: &str) -> Option { None } -fn should_show_tool_name_in_header(name: &str, family: ToolFamily) -> bool { +pub(crate) fn should_show_tool_name_in_header(name: &str, family: ToolFamily) -> bool { (matches!(family, ToolFamily::Generic) && !is_known_metadata_tool_name(name)) || matches!(name, "git_log" | "git_show" | "git_blame") }