CODING AGENT HARNESS · SOURCE AUDITREPORT 16 / 18
16

CodeWhale

Rust 单体工作台以 DeepSeek 原生路由为核心,把事件驱动主循环、预算化上下文、执行策略与多 Agent Fleet 放在同一控制面。

Rust · DeepSeek-native Governed Fleet HarnessMITmain
SOURCE
VERIFIED
Repository
Hmbown/CodeWhale
Commit
b63e48331b7d06e0517970cd9b9033a4cbbe6fff
Commit date
2026-08-04T00:31:42-07:00
Findings
38
Citations
125
Tracked files
1,415
EXECUTIVE READING

先给结论,再进入源码

核心机制

事件驱动 Engine;流式 turn + tool admission + goal continuation

上下文

ContextBudget 预留输出/安全余量;compaction + working-set pins + prefix fingerprint

安全边界

Seatbelt / bwrap / 可选 OpenSandbox;workspace trust + execpolicy + approval

适用建设

DeepSeek/OpenAI-compatible、本地 TUI/CLI、ACP/Web/remote control、长目标 Fleet

值得借鉴

  • 把 DeepSeek reasoning、prefix cache、tool-call repair 做成 provider/runtime 共同边界
  • 工具 catalog、审批、资源冲突、read-repeat/stuck guard 和证据交付串成一条执行链
  • goal/Fleet/subagent/mailbox 与 session snapshots 适合长任务和可恢复协作

需要警惕

  • Rust workspace + 超大 TUI 单体,扩展和二次开发复杂度高
  • sandbox 是否强制取决于模式/平台/配置,Full Access/Trust 仍有放宽路径
  • 工具、MCP、plugin、workflow、remote control 叠加后,治理面和测试矩阵非常大

直接带走

  • ContextBudget + PrefixStabilityManager 的稳定前缀与预算化输入输出
  • Typed ToolSurfacePolicy + resource admission + read-repeat/stuck guards
  • Goal/Fleet/SubAgent mailbox + snapshot/replay/evidence receipt
00 · METHOD

研究口径:先锁提交,再沿运行链读代码

README POLICY

README 只用于确认入口、命名和产品边界;实现结论沿 crates/tui 的 Engine/turn/tool/sandbox/mcp/session/subagent/fleet 路径,连同 state、execpolicy、插件、Skills、Hook 与测试模块逐段阅读,固定提交后再记录行号。

FACT POLICY

优先引用运行实现、类型契约和测试;明确区分默认值、用户可配置能力、平台可用后端与真正由代码强制的边界。代码注释只有在解释紧邻实现时作为辅助,不能替代实现证据。

INFERENCE POLICY

不推断 DeepSeek 服务端内部,不把 Plan/Trust/YOLO 或文档承诺当成 OS 隔离;源码没有证明强制启用的地方标为限制或风险,并把平台/配置条件写出来。

L1运行实现
L2接口契约
L3测试证明
L4文档佐证
L5明确推断

本页引用 37 个不同源码/测试文件;证据角色分布:契约 31 · 实现 75 · 配置 9 · Prompt 1 · 测试 8 · 迁移 1。代码块是固定提交中的原文截取,长区间仅在中部折叠,首尾行号保持真实。

01 · TECHNICAL MAPS

架构总图与单轮执行链路

两张图均由本页证据账本生成,并通过 Archify showcase 9 项校验(0 error / 0 warning)。图可单独打开、搜索、缩放和追踪关系。

FIGURE 01CodeWhale Harness 架构图全屏打开 ↗
FIGURE 02用户输入到工具回写的技术链路全屏打开 ↗
02 · COVERAGE MAP

审计维度与证据等级

架构与 Agent Loop verified L1 / L2 / L3

事件驱动 Core、EngineConfig、宿主 turn admission、工具目录单一装配点与异常收尾。

Provider、流式与重试 verified L1 / L2 / L3

turn 流式循环、steer、流重试、工具调用预算、reasoning/prefix 稳定检查与请求快照。

上下文、压缩与恢复 verified L1 / L2 / L3

预算数学、working set pin、工具调用配对、摘要 ladder、live state rehydrate 与机械 fallback。

上下文、压缩与记忆 verified L1 / L2 / L3

immutable prefix、tool catalog LRU、goal continuation 与压缩后继任者 brief。

工具分发与结果治理 verified L1 / L2 / L3

Typed ToolSpec/Registry、prepare、approval、并行资源、heartbeat、MCP/插件执行与审计。

执行环境与沙箱 verified L1 / L2 / L3

workspace-write 策略、Seatbelt、可选 bubblewrap、外部 OpenSandbox 与 fail-open 条件。

权限与安全 verified L1 / L2 / L3

ExecPolicy 分层规则、ExecutionEnvelope 能力分类、子进程 authority envelope 与路径边界。

MCP 与连接器 verified L1 / L2 / L3

stdio/Streamable HTTP/SSE/OAuth、secret placeholder、body cap、reviewed plugin recheck 与连接池。

指令、Skills 与插件 verified L1 / L2 / L3

instructions 配置、兼容/owned Skills roots、SKILL.md parser、插件 trust/enable 与 Hook gates。

子 Agent 与协作 verified L1 / L2 / L3

agent/Fleet roles、mailbox、预算/深度/超时、resident lease、Git worktree 与本地/SSH worker host。

持久化与观测 verified L1 / L2 / L3

Session 原子保存/恢复、SQLite WAL/JSONL projection、side-git snapshots、typed events、cost receipts 与 tool audit。

测试、基准与成熟度 verified L1 / L3

compaction、sandbox、execution envelope、plugin、Skills、MCP、subagent 等模块带有大量契约测试;本报告不把测试通过推成生产安全保证。

01
DIMENSION · ARCHITECTURE-LOOP

架构与 Agent Loop

本章共 4 个可定位结论;结论按“实现事实 → 白话解释 → 工程影响 → 源码摘录”展开。

01
L1事实codewhale-arch-001

Core 把 UI 与 AI 交互拆成事件驱动的控制面

源码事实

core/mod.rs 明确把 engine、events、ops、session、tool_parser、turn 分成独立模块,并在 TUI alt-screen 中禁止直接 print_stdout/print_stderr,要求通过 tracing 和事件通道输出。

白话解释

终端画面只是一个事件消费者;真正决定模型、工具和会话怎么走的是 Core Engine。这样换成 Web、ACP 或测试宿主时,不必再复制一套 Agent Loop。

对自研 Harness 的含义

自研时应先定义 operation/event 契约,再让 TUI、HTTP 和自动化入口共享同一运行时;禁止 UI 层私自执行工具或改会话。

关键源码 · 契约
crates/tui/src/core/mod.rs · L1–L15
    1  //! Core engine module for `DeepSeek` CLI.
    2  //!
    3  //! This module provides the event-driven architecture that separates
    4  //! the UI from the AI interaction logic:
    5  //!
    6  //! - `engine`: The main engine that processes operations
    7  //! - `events`: Events emitted by the engine to the UI
    8  //! - `ops`: Operations submitted by the UI to the engine
    9  //! - `session`: Session state management
   10  //! - `turn`: Turn context and tracking
   11  
   12  // Engine code runs inside the TUI alt-screen — see `runtime_log` for why
   13  // raw stdio prints must not appear here. Use `tracing::*` instead.
   14  #![deny(clippy::print_stdout)]
   15  #![deny(clippy::print_stderr)]
查看全部 2 处证据
  • 契约 crates/tui/src/core/mod.rs:1–15 event-driven architecture 与 stdio 输出禁令。
  • 实现 crates/tui/src/core/mod.rs:17–33 engine/events/ops/session/turn 模块装配。
02
L1事实codewhale-arch-002

EngineConfig 是把能力、预算和权限拧在一起的运行时总闸

源码事实

EngineConfig 同时保存 workspace、allow_shell、trust_mode、MCP/Skills/instructions、max_steps、subagent concurrency/depth/token budget、compaction、snapshots、memory、allowed/disallowed tools、max_tool_calls、hooks、strict tool mode、network policy 与 sandbox 偏好。

白话解释

CodeWhale 不是把安全、长任务和扩展散落在命令行参数里,而是先形成一份“本次会话的有效配置”,后面的 turn、工具和子 Agent 都从这份配置派生。

对自研 Harness 的含义

自研要区分静态配置、turn authority 和工具调用 authority;不要让每个工具重新解释一遍全局配置。

关键源码 · 契约
crates/tui/src/core/engine.rs · L221–L298
  221  /// Configuration for the engine
  222  #[derive(Debug, Clone)]
  223  pub struct EngineConfig {
  224      /// Model identifier to use for responses.
  225      pub model: String,
  226      /// Route/offering limits for the active provider+model, when the runtime
  227      /// route resolver had concrete catalog facts.
  228      pub active_route_limits: Option<codewhale_config::route::RouteLimits>,
  229      /// Workspace root for tool execution and file operations.
  230      pub workspace: PathBuf,
  231      /// Allow shell tool execution when true.
  232      pub allow_shell: bool,
  233      /// Enable trust mode (skip approvals) when true.
  234      pub trust_mode: bool,
  235      /// Path to the notes file used by the notes tool.
  236      pub notes_path: PathBuf,
  237      /// Path to the MCP configuration file.
  238      pub mcp_config_path: PathBuf,
  239      /// Directory containing discoverable skills.
  240      pub skills_dir: PathBuf,
  241      /// Restrict skill discovery to CodeWhale-owned roots plus explicit
  242      /// `skills_dir` configuration.
  243      pub skills_scan_codewhale_only: bool,
  244      /// Immutable plugin authority snapshot scoped to `workspace`. Normal App
      … 44 lines omitted; exact range 221–298 …
  289      /// `[subagents] max_depth = N` in `~/.codewhale/config.toml`.
  290      pub max_spawn_depth: u32,
  291      /// Optional aggregate token budget for each root sub-agent run.
  292      /// Descendant agents inherit the root pool unless a child starts a new
  293      /// budget scope with an explicit per-call override.
  294      pub subagent_token_budget: Option<u64>,
  295      /// Per-domain network policy decider (#135). Shared across the session so
  296      /// session-scoped approvals (`/network allow <host>`) persist for the
  297      /// remainder of the run.
  298      pub network_policy: Option<crate::network_policy::NetworkPolicyDecider>,
查看全部 2 处证据
  • 契约 crates/tui/src/core/engine.rs:221–298 EngineConfig 的模型、工作区、工具、子 Agent、压缩和网络字段。
  • 配置 crates/tui/src/core/engine.rs:406–481 默认 shell、trust、步骤、subagent、compaction、snapshot、memory、bwrap 与 exec policy。
03
L1事实codewhale-arch-003

Turn 运行前冻结事实,运行后再做持久化与继续决策

源码事实

Engine::run 在每个 operation 前应用待处理 runtime authority;SendMessage 路径先解析路由、输入策略、tool surface 和快照,再进入 turn loop;异常会被 catch_unwind 转成失败 TurnComplete,成功完成才触发 goal continuation,post-turn snapshot 在完成事件后异步执行。

白话解释

它先把“这次请求到底用哪个模型、哪些工具、什么权限”钉住,再让模型开跑;失败不会把会话炸掉,也不会误判成完成。

对自研 Harness 的含义

长任务系统需要 pre-dispatch receipt、失败终态和 post-turn checkpoint 三个明确边界,不能只靠一个 finally。

关键源码 · 实现
crates/tui/src/core/engine.rs · L1920–L1942
 1920      /// Run the engine event loop
 1921      #[allow(clippy::too_many_lines)]
 1922      pub async fn run(mut self) {
 1923          // RuntimeThreadManager owns durable turn claims and installs a thread
 1924          // id in runtime services. Only the interactive TUI may autonomously
 1925          // create a new turn while the engine is otherwise idle; a hosted
 1926          // engine must wait for its host to claim and explicitly dispatch the
 1927          // next turn so events cannot be attached to the wrong durable record.
 1928          let host_managed_turns = self.host_managed_turns();
 1929  
 1930          loop {
 1931              let Some(input) = self.next_run_input(host_managed_turns).await else {
 1932                  break;
 1933              };
 1934  
 1935              // Runtime posture updates publish through shared typed state
 1936              // before attempting their best-effort wake-up. If the mailbox was
 1937              // already full, its next queued operation is the wake-up: apply
 1938              // the latest authority before doing any work under an obsolete
 1939              // policy.
 1940              if matches!(&input, EngineRunInput::Operation(_)) {
 1941                  self.apply_pending_runtime_authority().await;
 1942              }
查看全部 3 处证据
  • 实现 crates/tui/src/core/engine.rs:1920–1942 event loop 与 authority 更新顺序。
  • 实现 crates/tui/src/core/engine.rs:4097–4123 turn panic 转失败事件,维持 session。
  • 实现 crates/tui/src/core/engine.rs:4161–4203 post-turn snapshot 与只在成功完成后续跑 goal。
04
L1事实codewhale-arch-004

Tool catalog 与 preview 共享同一装配函数,但 preview 是纯观察

源码事实

build_turn_tool_registry_and_catalog 被标为下一请求工具注册表和目录的唯一权威;live 模式允许连接 MCP、创建 mailbox、fork context 和 subagent runtime,preview 模式使用 Inert/PassiveSnapshot,不连接、不启动、不写 engine state,却输出同一 catalog 投影。

白话解释

用户点“查看这次会发给模型什么工具”时,不会因为预览动作偷偷启动 MCP 或子 Agent;预览和真实请求走同一个拼装逻辑,减少两套实现漂移。

对自研 Harness 的含义

Harness 应提供 side-effect-free request manifest,既服务调试也能做审批前可解释性。

关键源码 · 契约
crates/tui/src/core/engine.rs · L3385–L3418
 3385      /// Build the turn's tool registry and the model-facing tool catalog.
 3386      ///
 3387      /// This is the single authority for "what tools would the next request
 3388      /// carry". `handle_send_message` calls it with [`SubAgentWiring::Live`]
 3389      /// and [`McpAccess::Connect`]; `/preview-request` calls it with
 3390      /// [`SubAgentWiring::Inert`] and [`McpAccess::PassiveSnapshot`], which
 3391      /// together remove every side effect of the build — no fork snapshot, no
 3392      /// spawned mailbox drainer, no pool creation, no `connect_all`, no status
 3393      /// events — while producing a byte-identical catalog for the state that
 3394      /// is already live.
 3395      ///
 3396      /// The session's `last_tool_catalog` is never an acceptable substitute:
 3397      /// it is one turn stale and stores the pre-activation catalog rather than
 3398      /// the active subset the provider would actually receive.
 3399      ///
 3400      /// `allowed_tools` is the command-scoped allow-list gate the catalog is
 3401      /// filtered under. It is an explicit **parameter**, not a read of
 3402      /// `self.config.allowed_tools`, because the preview's gate belongs to a
 3403      /// turn that has not been installed: writing it onto the engine and
 3404      /// restoring it afterwards would leave the wrong gate installed across
 3405      /// every `.await` in this function, and would leave it installed
 3406      /// permanently if the task were cancelled or panicked between the two
 3407      /// writes.
 3408      #[allow(clippy::too_many_arguments)]
 3409      async fn build_turn_tool_registry_and_catalog(
 3410          &mut self,
 3411          input_policy: &TurnAuthority,
 3412          dynamic_tools: &[DynamicToolSpec],
 3413          allowed_tools: Option<Vec<String>>,
 3414          wiring: SubAgentWiring,
 3415          mcp_access: McpAccess,
 3416          route: TurnRouteContext,
 3417          turn_id: &str,
 3418      ) -> TurnToolBuild {
查看全部 4 处证据
  • 契约 crates/tui/src/core/engine.rs:3385–3418 live 与 passive preview 的单一权威说明。
  • 实现 crates/tui/src/core/engine.rs:3423–3463 MCP connect 条件、fork context 与 live mailbox。
  • 实现 crates/tui/src/core/engine.rs:3601–3665 插件、MCP、catalog、surface budget 与 policy。
  • 契约 crates/tui/src/core/engine.rs:2221–2244 PreviewOutboundRequest 不启动 turn/消息/请求。
02
DIMENSION · PROVIDER-STREAMING

Provider、流式与重试

本章共 2 个可定位结论;结论按“实现事实 → 白话解释 → 工程影响 → 源码摘录”展开。

05
L1事实codewhale-provider-001

单轮流式循环有取消、steer、工具预算和子 Agent 结果注入

源码事实

handle_deepseek_turn 为一个外部用户 turn 保留 StuckGuard、ReadRepeatGuard、context recovery counter、ToolCallBudget、goal continuation counter 和 stream retry counter;每次 provider 请求前检查取消、runtime authority、steer 输入、子 Agent completion、max steps 与目标 token budget。

白话解释

模型还在思考时,用户可以插话;子 Agent 做完的结果会在下一次请求前被父 Agent 看见;工具调用总数和流断线重试都有单轮账本。

对自研 Harness 的含义

Steer、cancel 和 child completion 都应在 provider request boundary 注入,才能避免“已取消但又发了一次请求”。

关键源码 · 实现
crates/tui/src/core/engine/turn_loop.rs · L364–L412
  364      pub(super) async fn handle_deepseek_turn(
  365          &mut self,
  366          turn: &mut TurnContext,
  367          tool_policy: ToolSurfacePolicy,
  368          // Out-of-request facts resolved once for this turn. `None` means the
  369          // caller captured none, and the projection reports every
  370          // registry-derived field as unknown rather than guessing.
  371          inspection_surface: Option<crate::tool_inspection::ToolSurfaceContext>,
  372      ) -> (TurnOutcomeStatus, Option<String>) {
  373          // Only interactive TUI hosts own terminal chrome. Headless exec,
  374          // app-server, and stream-json stdout must remain byte-clean.
  375          if self.config.terminal_chrome_enabled {
  376              crate::tui::notifications::set_taskbar_progress_busy();
  377              crate::tui::notifications::start_title_animation("Codewhale");
  378          }
  379  
  380          let client = self
  381              .model_client
  382              .clone()
  383              .expect("model client should be configured");
  384  
  385          let mut consecutive_tool_error_steps = 0u32;
  386          let mut stuck_guard = StuckGuard::default();
  387          // Scoped to this external user turn: counts survive all model/tool
      … 15 lines omitted; exact range 364–412 …
  403          let mut tool_call_budget = ToolCallBudget::new(tool_policy.max_tool_calls);
  404          let mut goal_continuations_this_turn = 0u32;
  405          // Outer stream-retry counter: when the chunked-transfer connection
  406          // dies mid-stream and either nothing useful was streamed (#103
  407          // Phase 3) or the host slept mid-turn (#2990), we silently re-issue
  408          // the SAME request up to MAX_STREAM_RETRIES times before surfacing
  409          // the failure to the user.
  410          let mut stream_retry_attempts: u32 = 0;
  411  
  412          loop {
查看全部 3 处证据
  • 实现 crates/tui/src/core/engine/turn_loop.rs:364–412 turn loop 状态与工具/流预算。
  • 实现 crates/tui/src/core/engine/turn_loop.rs:412–481 cancel、authority、steer、子 Agent completion 与 budget stop。
  • 实现 crates/tui/src/core/engine/turn_loop.rs:583–613 请求前 context input budget 与 recovery ladder。
06
L1事实codewhale-provider-002

prefix cache 不是口号,而是每次请求前的可诊断一致性检查

源码事实

turn loop 在构造 request 前用 PrefixStabilityManager 对 system prompt 和 active tools 做 fingerprint check,发出 changed/stable 事件;随后还用 frozen_prefix 做三段式 immutable prefix 校验,记录 drift 而不默默重写历史。

白话解释

工具排序、描述或系统提示一变,CodeWhale 会知道 DeepSeek 的 KV 前缀可能失效,而不是把缓存 miss 当成模型随机变慢。

对自研 Harness 的含义

自研时应把缓存稳定性做成可观察指标,并把 system/tool catalog 与 append-only history 分区。

关键源码 · 实现
crates/tui/src/core/engine/turn_loop.rs · L620–L687
  620              // Build the request. Tool selection goes through the same
  621              // helper that seeded this turn and that `/preview-request`
  622              // reports, so a deferred tool activated mid-turn is reflected
  623              // identically in both places.
  624              let active_tools =
  625                  active_tools_for_request(&tool_catalog, &active_tool_names, strict_tool_mode);
  626  
  627              // Resolve `auto` reasoning_effort to a concrete tier (#663).
  628              let effective_reasoning_effort = resolve_auto_effort(
  629                  self.session.reasoning_effort.as_deref(),
  630                  &self.session.messages,
  631                  self.api_provider,
  632                  &self.api_config.deepseek_base_url(),
  633                  &self.config.model,
  634              );
  635  
  636              // Check prefix-cache stability before building the request.
  637              // This detects system-prompt or tool-set drift that would
  638              // invalidate DeepSeek's KV prefix cache for this turn.
  639              // Sends an event on EVERY check so the TUI can maintain
  640              // its own counter for the stable-checks tally.
  641              if let Some(pm) = self.session.prefix_stability.as_mut() {
  642                  let system_text =
  643                      crate::prefix_cache::system_prompt_text(self.session.system_prompt.as_ref());
      … 34 lines omitted; exact range 620–687 …
  678                                  system_prompt_changed: false,
  679                                  tools_changed: false,
  680                                  stability_pct: (pm.stability_ratio() * 100.0).round() as u32,
  681                                  changed: false,
  682                                  pinned_combined_hash: pinned_hash,
  683                              })
  684                              .await;
  685                      }
  686                  }
  687              }
查看全部 4 处证据
  • 实现 crates/tui/src/core/engine/turn_loop.rs:620–687 active tool selection、reasoning effort 与 prefix stability event。
  • 实现 crates/tui/src/core/engine/turn_loop.rs:689–710 three-zone frozen prefix verify。
  • 契约 crates/tui/src/prefix_cache.rs:1–29 immutable prefix、append-only history、latest user turn 模型。
  • 实现 crates/tui/src/prefix_cache.rs:40–106 system/tool/combo SHA-256 fingerprint 与工具 JSON 排序。
03
DIMENSION · CONTEXT-COMPACTION

上下文、压缩与恢复

本章共 2 个可定位结论;结论按“实现事实 → 白话解释 → 工程影响 → 源码摘录”展开。

07
L1事实codewhale-context-001

ContextBudget 用饱和数学先给输出留空间,再决定压缩

源码事实

ContextBudget 计算 route window、已用输入、clamped output cap、headroom、可用 input、75% compaction trigger 和 Low/Medium/High/Critical pressure;所有减法使用 saturating arithmetic,避免小窗口被大输出预留打成负预算。

白话解释

模型要回答的空间先保留,输入预算才是剩下的;即使配置了一个夸张的输出上限,也不会把可用输入预算算成负数。

对自研 Harness 的含义

上下文工程的第一层不是摘要,而是一个独立、可单测、不会下溢的预算模块。

关键源码 · 契约
crates/tui/src/context_budget.rs · L1–L32
    1  //! Unified context-budget math for the TUI.
    2  //!
    3  //! Given a model's context window, the current input token estimate, and a
    4  //! configured output cap, [`ContextBudget`] derives the four numbers the rest
    5  //! of the app needs to reason about a turn:
    6  //!
    7  //!   * **available input budget** — how many input tokens may still be spent
    8  //!     after reserving room for the model's output;
    9  //!   * **output token cap** — the output reservation actually used to compute
   10  //!     that budget (clamped so it never starves the window);
   11  //!   * **compaction trigger** — the input-token level at which compaction
   12  //!     should be suggested (default: ~75% of the spendable input ceiling);
   13  //!   * **[`PressureLevel`]** — a coarse Low/Medium/High/Critical signal the UI
   14  //!     can render without re-deriving thresholds.
   15  //!
   16  //! This module is the budget-math *foundation*. It is intentionally pure (no
   17  //! I/O, no clock, no engine/config types) so it can be unit-tested in isolation
   18  //! and later consumed by the engine capacity checkpoints and the TUI pressure
   19  //! indicator. Those consumers are wired in a separate pass; nothing here calls
   20  //! into them.
   21  //!
   22  //! ### Why the output reservation is window-dependent
   23  //!
   24  //! The engine's existing input-budget helper
   25  //! (`core::engine::context::context_input_budget_for_window`) computes
   26  //! `window - reserved_output - headroom` and learned the hard way that
   27  //! reserving a large fixed output (262K for V4-class interleaved thinking) on a
   28  //! *small* self-hosted window (e.g. a 256K vLLM deployment) underflows to a
   29  //! negative budget and silently disables every preflight/recovery path. We
   30  //! mirror that lesson here with saturating arithmetic and an output cap that is
   31  //! always clamped to leave at least [`MIN_INPUT_BUDGET_TOKENS`] of input room,
   32  //! so the budget can never collapse to zero on a legitimately sized window.
查看全部 3 处证据
  • 契约 crates/tui/src/context_budget.rs:1–32 预算数学的目标与下溢教训。
  • 配置 crates/tui/src/context_budget.rs:46–74 压力阈值、headroom、最小输入预算。
  • 实现 crates/tui/src/context_budget.rs:133–199 ContextBudget 字段与 saturating new。
08
L1事实codewhale-context-002

压缩先保留尾部、工作集、错误和补丁,再维护工具调用配对

源码事实

plan_compaction 会固定最近消息,依据 repo-aware working set 和外部 pin 保留路径,识别错误/patch,最后通过 fixpoint 规则确保 assistant tool call 与 tool result 不被拆开;用户消息始终保留。

白话解释

压缩不是从前往后粗暴删消息:正在改的文件、刚出现的错误、补丁和最近对话会被钉住,工具调用的“发票”和“回执”也不能只剩一半。

对自研 Harness 的含义

上下文压缩必须是结构化 plan + invariant,而非一个 `messages.slice()`。

关键源码 · 实现
crates/tui/src/compaction.rs · L473–L507
  473  fn should_pin_message(text: &str, working_set_paths: &HashSet<String>) -> bool {
  474      let lower = text.to_lowercase();
  475  
  476      let mentions_working_set = working_set_paths.iter().any(|p| text.contains(p));
  477      if mentions_working_set {
  478          return true;
  479      }
  480  
  481      let error_markers = [
  482          "error:",
  483          "error ",
  484          "failed",
  485          "panic",
  486          "traceback",
  487          "stack trace",
  488          "assertion failed",
  489          "test failed",
  490      ];
  491      if error_markers.iter().any(|m| lower.contains(m)) {
  492          return true;
  493      }
  494  
  495      let patch_markers = [
  496          "diff --git",
      … 1 lines omitted; exact range 473–507 …
  498          "--- a/",
  499          "*** begin patch",
  500          "*** update file:",
  501          "*** add file:",
  502          "*** delete file:",
  503          "```diff",
  504          "apply_patch",
  505      ];
  506      patch_markers.iter().any(|m| lower.contains(m))
  507  }
查看全部 4 处证据
  • 实现 crates/tui/src/compaction.rs:473–507 工作集、错误与 patch pin 识别。
  • 实现 crates/tui/src/compaction.rs:509–555 最近尾部、working set 与外部 pins。
  • 实现 crates/tui/src/compaction.rs:588–680 tool-call pairing fixpoint。
  • 实现 crates/tui/src/compaction.rs:765–805 以 token plan 而不是消息数量触发。
04
DIMENSION · CONTEXT-COMPACTION-MEMORY

上下文、压缩与记忆

本章共 3 个可定位结论;结论按“实现事实 → 白话解释 → 工程影响 → 源码摘录”展开。

09
L1事实codewhale-context-003

摘要失败时有本地 prune、重试和机械 fallback,并把 live state 重新注入

源码事实

compact_messages_safe 先尝试本地 tool-result prune,再按配置调用摘要,允许 transient retry;摘要 ladder 会检测 degenerate/overflow,必要时回退到机械折叠。CompactionConfig 还携带 mode、permission、background shells、workers 和 open approvals 的 live state,供 successor brief 使用。

白话解释

摘要模型挂了并不会让会话消失;系统会先剪工具输出,摘要不合格就重试,再不行就用规则折叠,并把正在跑的 worker、shell 和审批重新告诉下一任 Agent。

对自研 Harness 的含义

可靠压缩要有非模型 fallback 与运行中世界状态,否则长任务恢复只是“希望摘要没漏掉”。

关键源码 · 实现
crates/tui/src/compaction.rs · L1172–L1281
 1172  /// Compact messages with retry and backoff for transient errors.
 1173  ///
 1174  /// This function wraps `compact_messages` with retry logic to handle
 1175  /// transient network errors and rate limits. It uses exponential backoff
 1176  /// with delays of 1s, 2s, 4s between retries.
 1177  ///
 1178  /// # Safety
 1179  /// - Never panics
 1180  /// - Never corrupts the original messages (returns error instead)
 1181  /// - Only retries on transient errors (network, rate limit, etc.)
 1182  pub async fn compact_messages_safe(
 1183      client: &dyn ModelClient,
 1184      messages: &[Message],
 1185      config: &CompactionConfig,
 1186      workspace: Option<&Path>,
 1187      external_pins: Option<&[usize]>,
 1188      external_working_set_paths: Option<&[String]>,
 1189  ) -> Result<CompactionResult> {
 1190      const MAX_RETRIES: u32 = 3;
 1191      const BASE_DELAY_MS: u64 = 1000;
 1192  
 1193      let was_over_threshold = should_compact(
 1194          messages,
 1195          config,
      … 76 lines omitted; exact range 1172–1281 …
 1272              Ok((msgs, prompt, removed)) => {
 1273                  drop(removed);
 1274                  return Ok(CompactionResult {
 1275                      messages: sanitize_retained_messages(msgs),
 1276                      summary_prompt: prompt,
 1277                      retries_used: attempt,
 1278                  });
 1279              }
 1280              Err(e) => {
 1281                  // Only retry on transient errors
查看全部 3 处证据
  • 实现 crates/tui/src/compaction.rs:1172–1281 safe compaction 的本地 prune、summary 与 retry。
  • 实现 crates/tui/src/compaction.rs:1327–1435 summary ladder、live state、degenerate/overflow 与 mechanical fallback。
  • Prompt crates/tui/src/compaction.rs:1848–1877 固定九段 successor brief。
10
L1事实codewhale-context-004

工具目录用排序、memoization 和有界 LRU 支持 cache-stable prefix

源码事实

ToolRegistry 对 API tools 按名称排序、清理并规范化 schema,注册/删除时清空 cache;PrefixStabilityManager 的 ToolCatalogCache 以工具内容身份为 key,容量固定为 8,只保存 digest,插件 hot reload 或 MCP attach 可以显式 invalidate。

白话解释

HashMap 每次启动的随机顺序不会再把整个 tool schema 变成新前缀;同一工具集合重复检查时只算一次,扩展变化时又能主动失效。

对自研 Harness 的含义

要追求 provider cache 命中,catalog 的排序、schema canonicalization、cache invalidation 和扩展生命周期必须绑定设计。

关键源码 · 实现
crates/tui/src/tools/registry.rs · L200–L244
  200      ///
  201      /// Output is sorted by tool name for **prefix-cache stability** (#263).
  202      /// Rust's `HashMap` uses a randomly-seeded hasher per process, so a raw
  203      /// `self.tools.values()` iteration emits tools in a different order on
  204      /// every `deepseek` launch, invalidating DeepSeek's KV prefix cache for
  205      /// every cross-session resume. Sorting here matches the way Claude Code
  206      /// stabilises its tool array (`assembleToolPool` in their reference).
  207      ///
  208      /// The serialised catalog is memoised on first call and pinned across
  209      /// reads so each tool's `description()` and `input_schema()` are sampled
  210      /// exactly once per registration. MCP adapters whose upstream description
  211      /// drifts on reconnect would otherwise rewrite the catalog mid-session
  212      /// and bust the prefix cache. The cache is invalidated on `register`,
  213      /// `remove`, and `clear`.
  214      #[must_use]
  215      pub fn to_api_tools(&self) -> Vec<Tool> {
  216          self.api_cache
  217              .get_or_init(|| self.build_api_tools())
  218              .clone()
  219      }
  220  
  221      fn build_api_tools(&self) -> Vec<Tool> {
  222          let mut tools: Vec<&Arc<dyn ToolSpec>> = self.tools.values().collect();
  223          tools.sort_by(|a, b| a.name().cmp(b.name()));
      … 11 lines omitted; exact range 200–244 …
  235                      input_schema: schema,
  236                      allowed_callers: Some(vec!["direct".to_string()]),
  237                      defer_loading: Some(tool.defer_loading()),
  238                      input_examples: None,
  239                      strict: None,
  240                      cache_control: None,
  241                  }
  242              })
  243              .collect()
  244      }
查看全部 3 处证据
  • 实现 crates/tui/src/tools/registry.rs:200–244 按 name 排序、schema sanitize/canonicalize 与 memoized catalog。
  • 配置 crates/tui/src/prefix_cache.rs:188–205 有界 8 项 catalog cache。
  • 实现 crates/tui/src/prefix_cache.rs:234–269 内容身份、LRU eviction 与 invalidate。
11
L1事实codewhale-context-005

Goal loop 是持久目标层,不是把 max_steps 放大

源码事实

goal_loop 定义 Active/Completed/Blocked、token/time budget、durable progress 和最多 10 次自动 continuation;决策优先尊重模型终态,再检查预算,再检查 circuit breaker。state store 以 SQL 原子累加 token/time/continuation。

白话解释

一个用户目标可以跨多个 turn 继续,但达到完成、阻塞、花费上限或十次没有终态时都会停下来。

对自研 Harness 的含义

长任务应有独立 goal 状态与跨 turn 预算,不能只靠一次请求的 steps 上限。

关键源码 · 契约
crates/tui/src/goal_loop.rs · L1–L23
    1  //! Goal loop orchestrator — the persistent-objective control layer (#3215, and
    2  //! its lineage #891 / #1976 / #2058 / #2029).
    3  //!
    4  //! This is the **Workflow goal layer**: the decision core that turns a one-shot
    5  //! `/goal` into a persistent work loop. Given the durable goal status, the
    6  //! accumulated usage (from the per-goal accounting wired in `crates/state`
    7  //! `record_thread_goal_usage`), and a budget, it decides whether to **continue**
    8  //! (re-dispatch another worker turn toward the objective) or **stop** with a
    9  //! terminal status. It is the orchestrator in the Workflow≈ultracode mapping —
   10  //! the loop that fans work out to workers (`worker_profile`) and verifies before
   11  //! committing.
   12  //!
   13  //! Scope: **decision logic + types**. The engine (`core/engine.rs`) reads the
   14  //! `SharedGoalState` snapshot after each turn and calls `decide_continuation`
   15  //! to decide whether to re-dispatch. A small cross-turn circuit breaker keeps
   16  //! an unbounded goal from silently spending forever when the model never emits
   17  //! a terminal signal; explicit token/time budgets still take precedence.
   18  
   19  /// Maximum automatic cross-turn continuation passes for one goal.
   20  ///
   21  /// This matches the conservative run-cap used by the peer goal lifecycle while
   22  /// avoiding its much larger classifier/strategist subsystem.
   23  pub const MAX_GOAL_CONTINUATIONS: u32 = 10;
查看全部 4 处证据
  • 契约 crates/tui/src/goal_loop.rs:1–23 目标层、预算和 circuit breaker 定位。
  • 契约 crates/tui/src/goal_loop.rs:55–71 持久进度与预算类型。
  • 实现 crates/tui/src/goal_loop.rs:104–143 终态、预算、continuation limit 优先级。
  • 实现 crates/state/src/lib.rs:847–913 goal usage 与 continuation 的 SQL 原子更新。
05
DIMENSION · TOOL-DISPATCH

工具分发与结果治理

本章共 5 个可定位结论;结论按“实现事实 → 白话解释 → 工程影响 → 源码摘录”展开。

12
L1事实codewhale-tools-001

ToolSpec 把能力、审批、只读、并行和资源声明放到同一输入特化接口

源码事实

ToolSpec 要求 name/description/schema/capabilities,默认按 ExecutesCode/WritesFiles/其他能力映射 Required/Suggest/Auto;每个具体 input 还能决定 read-only、approval、parallel、detached 和 resource claims,prepare 生成 PreparedToolCall。

白话解释

工具不是只有一个名字和一个函数;系统会问“这一次具体参数是否只读、能否并行、需要什么审批、会占什么资源”。

对自研 Harness 的含义

工具能力要来自类型和实际 input,而不是维护一份会漏掉插件/MCP 的名字黑名单。

关键源码 · 契约
crates/tui/src/tools/spec.rs · L1158–L1217
 1158  /// The core trait that all tools must implement.
 1159  #[async_trait]
 1160  pub trait ToolSpec: Send + Sync {
 1161      /// Returns the unique name of this tool (used in API calls).
 1162      fn name(&self) -> &str;
 1163  
 1164      /// Returns a human-readable description of what this tool does.
 1165      fn description(&self) -> &str;
 1166  
 1167      /// Returns the JSON Schema for the tool's input parameters.
 1168      fn input_schema(&self) -> Value;
 1169  
 1170      /// Returns the capabilities this tool has.
 1171      fn capabilities(&self) -> Vec<ToolCapability>;
 1172  
 1173      /// Returns the approval requirement for this tool.
 1174      fn approval_requirement(&self) -> ApprovalRequirement {
 1175          let caps = self.capabilities();
 1176          if caps.contains(&ToolCapability::ExecutesCode) {
 1177              ApprovalRequirement::Required
 1178          } else if caps.contains(&ToolCapability::WritesFiles) {
 1179              ApprovalRequirement::Suggest
 1180          } else {
 1181              ApprovalRequirement::Auto
      … 26 lines omitted; exact range 1158–1217 …
 1208  
 1209      /// Returns whether this tool can be executed in parallel with others.
 1210      fn supports_parallel(&self) -> bool {
 1211          false
 1212      }
 1213  
 1214      /// Returns whether this concrete tool input can run in parallel.
 1215      fn supports_parallel_for(&self, _input: &Value) -> bool {
 1216          self.supports_parallel()
 1217      }
查看全部 2 处证据
  • 契约 crates/tui/src/tools/spec.rs:1158–1217 ToolSpec 能力与 input-specific policy。
  • 实现 crates/tui/src/tools/spec.rs:1219–1242 PreparedToolCall 与默认 GlobalExclusive resource claim。
13
L1事实codewhale-tools-002

Registry 执行前会重新施加 machine authority,并提供只读事实投影

源码事实

ToolRegistry execute_full 在真正执行前调用 enforce_tool_authority;registry_facts 只导出名称、描述、可见性、能力、审批和插件标记,不暴露可执行对象;名称解析有大小写、空格/连字符、CamelCase、后缀和模糊匹配的确定性阶梯。

白话解释

预览层拿到的是一份不能执行的菜单,执行层还要再验一次;模型叫错 `read-file` 也会按固定规则解析,不会随机挑工具。

对自研 Harness 的含义

把 facts projection 和 executable registry 分离,是构建 request inspector、审计 UI 和安全边界的关键。

关键源码 · 实现
crates/tui/src/tools/registry.rs · L91–L99
   91      /// Execute a tool by name, returning the full `ToolResult`.
   92      pub async fn execute_full(&self, name: &str, input: Value) -> Result<ToolResult, ToolError> {
   93          let tool = self
   94              .get(name)
   95              .ok_or_else(|| ToolError::not_available(format!("tool '{name}' is not registered")))?;
   96  
   97          enforce_tool_authority(name, &input, tool.as_ref(), &self.context)?;
   98          tool.execute(input, &self.context).await
   99      }
查看全部 3 处证据
  • 实现 crates/tui/src/tools/registry.rs:91–99 execute_full 前置 authority。
  • 契约 crates/tui/src/tools/registry.rs:262–293 只读 registry facts 投影。
  • 实现 crates/tui/src/tools/registry.rs:295–330 确定性工具名解析。
14
L1事实codewhale-tools-003

并行工具只允许 read-only、Auto approval 且声明 supports_parallel

源码事实

execute_parallel_tool 对每个调用检查 MCP parallel-safe 白名单,普通工具必须 registered、input-specific read-only、approval=Auto、supports_parallel=true;通过后用 FuturesUnordered 并发,shell 额外受 Semaphore 限流。

白话解释

并行不是模型说了算:写文件、需要询问用户或没声明线程安全的工具都不能塞进并行批次。

对自研 Harness 的含义

并发调度要让工具自己声明安全性,再由 scheduler 做交叉检查;不能只按工具名字分组。

关键源码 · 实现
crates/tui/src/core/engine/tool_execution.rs · L230–L287
  230      pub(super) async fn execute_parallel_tool(
  231          &mut self,
  232          input: serde_json::Value,
  233          tool_registry: Option<&crate::tools::ToolRegistry>,
  234          tool_exec_lock: Arc<RwLock<()>>,
  235          context_override: Option<crate::tools::ToolContext>,
  236      ) -> Result<ToolResult, ToolError> {
  237          let calls = parse_parallel_tool_calls(&input)?;
  238          let mcp_pool = if calls.iter().any(|(tool, _)| McpPool::is_mcp_tool(tool)) {
  239              Some(self.ensure_mcp_pool().await?)
  240          } else {
  241              None
  242          };
  243          let Some(registry) = tool_registry else {
  244              return Err(ToolError::not_available(
  245                  "tool registry unavailable for multi_tool_use.parallel",
  246              ));
  247          };
  248  
  249          let result_count = calls.len();
  250          let mut tasks = FuturesUnordered::new();
  251          let shell_permits = Arc::new(tokio::sync::Semaphore::new(MAX_PARALLEL_SHELL_EXEC));
  252          for (index, (tool_name, tool_input)) in calls.into_iter().enumerate() {
  253              if tool_name == MULTI_TOOL_PARALLEL_NAME {
      … 24 lines omitted; exact range 230–287 …
  278                      return Err(ToolError::invalid_input(format!(
  279                          "Tool '{tool_name}' requires approval and cannot run in parallel"
  280                      )));
  281                  }
  282                  if !spec.supports_parallel_for(&tool_input) {
  283                      return Err(ToolError::invalid_input(format!(
  284                          "Tool '{tool_name}' does not support parallel execution"
  285                      )));
  286                  }
  287              }
查看全部 2 处证据
  • 实现 crates/tui/src/core/engine/tool_execution.rs:230–287 parallel MCP/read-only/approval/support checks。
  • 实现 crates/tui/src/core/engine/tool_execution.rs:289–350 FuturesUnordered、shell semaphore 与结果顺序。
15
L1事实codewhale-tools-004

工具执行有 heartbeat、读写锁、交互终端 RAII 和结构化结束日志

源码事实

execute_tool_with_lock 在抢锁前启动 ToolHeartbeatGuard,read-only/parallel 走读锁、其他工具走写锁;InteractiveTerminalGuard 在取消时也保证 ResumeEvents,结束日志记录 dispatch、耗时、输出大小和 typed error kind。

白话解释

一个长时间 build 不会被 UI 误判为死掉;并发读不会阻塞,写会排他;交互终端中途取消也会恢复 TUI 状态。

对自研 Harness 的含义

可观测性要覆盖等待资源的时间,而不只是工具函数真正开始后的耗时。

关键源码 · 实现
crates/tui/src/core/engine/tool_execution.rs · L353–L406
  353      #[allow(clippy::too_many_arguments)]
  354      pub(super) async fn execute_tool_with_lock(
  355          lock: Arc<RwLock<()>>,
  356          supports_parallel: bool,
  357          interactive: bool,
  358          tx_event: mpsc::Sender<Event>,
  359          tool_name: String,
  360          tool_input: serde_json::Value,
  361          workspace: PathBuf,
  362          registry: Option<&crate::tools::ToolRegistry>,
  363          mcp_pool: Option<Arc<AsyncMutex<McpPool>>>,
  364          context_override: Option<crate::tools::ToolContext>,
  365      ) -> Result<ToolResult, ToolError> {
  366          // This guard starts before lock acquisition, so contention as well as
  367          // registry/MCP/interpreter execution remains visibly live.
  368          let _heartbeat = ToolHeartbeatGuard::start(tx_event.clone(), TOOL_HEARTBEAT_INTERVAL);
  369          let started_at = std::time::Instant::now();
  370          let dispatch = if McpPool::is_mcp_tool(&tool_name) {
  371              "mcp"
  372          } else if matches!(
  373              tool_name.as_str(),
  374              CODE_EXECUTION_TOOL_NAME | JS_EXECUTION_TOOL_NAME
  375          ) {
  376              "interpreter"
      … 20 lines omitted; exact range 353–406 …
  397          } else {
  398              ToolExecGuard::Write(lock.write().await)
  399          };
  400  
  401          // RAII pause/resume: ensures `Event::ResumeEvents` always fires on
  402          // drop, even if the tool future is cancelled mid-await. See
  403          // `InteractiveTerminalGuard` doc-comment for the regression this
  404          // closes (parent terminal scrollback hijacking the TUI after a
  405          // cancelled interactive tool).
  406          let _terminal = InteractiveTerminalGuard::engage(tx_event, interactive).await;
查看全部 3 处证据
  • 实现 crates/tui/src/core/engine/tool_execution.rs:353–406 heartbeat、读写锁和 terminal RAII。
  • 实现 crates/tui/src/core/engine/tool_execution.rs:454–489 typed tool.exec.end 日志。
  • 实现 crates/tui/src/core/engine/tool_execution.rs:177–213 JSONL tool audit log。
16
L1事实codewhale-tools-005

跨进程 Fleet 的重命令用文件锁和内存压力收紧 admission

源码事实

resource_admission 默认只开放 2 个 heavy command permit,最大 16;permit 由 CODEWHALE_HOME 下的文件锁实现,跨进程有效。内存压力 constrained 时减半,critical 时变成 0 个 slot,等待过程每 50ms 检查取消。

白话解释

多个子进程同时编译不会因为各自都有一个 Tokio semaphore 就把机器打爆;它们共享磁盘上的锁,内存紧张时新任务排队。

对自研 Harness 的含义

多 Agent 资源治理必须跨进程设计;只在单进程内限并发不够。

关键源码 · 契约
crates/tui/src/tools/resource_admission.rs · L1–L27
    1  //! Cross-process admission for expensive local commands.
    2  //!
    3  //! Fleet and Workflow workers execute in separate Codewhale processes, so an
    4  //! in-process semaphore cannot protect the host. Heavy shell commands instead
    5  //! take one of a small number of filesystem-backed permits under
    6  //! `CODEWHALE_HOME`. The default of two permits is deliberately conservative
    7  //! for the 36 GiB laptop class from #4864.
    8  
    9  use std::fs::{File, OpenOptions};
   10  use std::io;
   11  use std::path::{Path, PathBuf};
   12  use std::time::{Duration, Instant};
   13  
   14  use anyhow::{Context, Result, anyhow};
   15  use fd_lock::{RwLock, RwLockWriteGuard};
   16  use tokio_util::sync::CancellationToken;
   17  
   18  pub(crate) const DEFAULT_HEAVY_COMMAND_LIMIT: usize = 2;
   19  const MAX_HEAVY_COMMAND_LIMIT: usize = 16;
   20  const ADMISSION_POLL_INTERVAL: Duration = Duration::from_millis(50);
   21  
   22  /// When the host free-RAM fraction drops to/below these thresholds the
   23  /// effective heavy-command admission limit tightens so a saturated host stops
   24  /// admitting new link graphs (#4864 req 7). Values are deliberately generous
   25  /// because the measurement is advisory, not authoritative.
   26  const CONSTRAINED_FREE_FRACTION: f64 = 0.30;
   27  const CRITICAL_FREE_FRACTION: f64 = 0.15;
查看全部 3 处证据
  • 契约 crates/tui/src/tools/resource_admission.rs:1–27 跨进程 permit、默认/最大数量与压力阈值。
  • 实现 crates/tui/src/tools/resource_admission.rs:35–80 MemoryPressure 与 effective limit。
  • 实现 crates/tui/src/tools/resource_admission.rs:176–219 文件锁 slot、取消与轮询。
06
DIMENSION · EXECUTION-SANDBOX

执行环境与沙箱

本章共 2 个可定位结论;结论按“实现事实 → 白话解释 → 工程影响 → 源码摘录”展开。

17
L1事实codewhale-sandbox-001

默认 sandbox policy 是 workspace-write,但所有政策仍允许全盘读

源码事实

SandboxPolicy 提供 DangerFullAccess、ReadOnly、ExternalSandbox 和 WorkspaceWrite;默认 WorkspaceWrite 只把 cwd/额外 roots(以及可选 /tmp/TMPDIR)设为可写且关闭网络,但 has_full_disk_read_access 对当前所有政策都返回 true。

白话解释

默认不是“只能看到项目”,而是“能看全盘但只能写项目和指定目录”;这对分析工具方便,对密钥读取风险更敏感。

对自研 Harness 的含义

自研要把读取边界、写入边界和网络边界分别建模,不能把 workspace-write 误称为全隔离。

关键源码 · 契约
crates/tui/src/sandbox/policy.rs · L17–L87
   17  /// Determines execution restrictions for shell commands.
   18  ///
   19  /// The sandbox policy controls filesystem access, network access, and other
   20  /// system resources for executed commands. Choose the most restrictive policy
   21  /// that still allows your command to function.
   22  #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
   23  #[serde(tag = "type", rename_all = "kebab-case")]
   24  pub enum SandboxPolicy {
   25      /// No restrictions whatsoever. Use with extreme caution.
   26      ///
   27      /// This policy disables all sandboxing and allows full system access.
   28      /// Only use this when absolutely necessary and the command source is trusted.
   29      #[serde(rename = "danger-full-access")]
   30      DangerFullAccess,
   31  
   32      /// Read-only access to the entire filesystem.
   33      ///
   34      /// The process can read any file but cannot write anywhere.
   35      /// Useful for analysis tools that need broad read access.
   36      #[serde(rename = "read-only")]
   37      ReadOnly,
   38  
   39      /// Indicates the process is already running in an external sandbox.
   40      ///
      … 37 lines omitted; exact range 17–87 …
   78      /// Returns the default policy: workspace-write with no extra roots and no network.
   79      fn default() -> Self {
   80          SandboxPolicy::WorkspaceWrite {
   81              writable_roots: vec![],
   82              network_access: false,
   83              exclude_tmpdir: false,
   84              exclude_slash_tmp: false,
   85          }
   86      }
   87  }
查看全部 3 处证据
  • 契约 crates/tui/src/sandbox/policy.rs:17–87 四种 policy 与 workspace-write 默认。
  • 实现 crates/tui/src/sandbox/policy.rs:110–140 全盘 read、network、should_sandbox。
  • 实现 crates/tui/src/sandbox/policy.rs:142–218 writable roots、git metadata 与 .codewhale/.deepseek protected subpaths。
18
L1限制codewhale-sandbox-002

OS sandbox 的可用性强依赖平台、安装物和配置

源码事实

macOS 只有 sandbox-exec probe 成功时使用 Seatbelt;Linux 只有用户显式 prefer_bwrap 且 /usr/bin/bwrap 可执行时使用 bubblewrap;bwrap 不存在时命令会以 unwrapped 形式运行且不标成 sandboxed;Windows 当前不声明 OS sandbox,OpenHarmony 也不声明 Linux 后端。

白话解释

代码写了 Seatbelt 和 bwrap,不代表每一次执行都有它们;平台不支持或用户没装/没打开时,默认可能退回宿主进程。

对自研 Harness 的含义

生产部署必须在启动时做 sandbox capability attestation,并把未隔离状态显式呈现给用户和审计系统。

边界
  • 这里描述的是源码分支和 fallback,不代表具体机器的运行结果。
  • ExternalSandbox/DangerFullAccess 本来就会绕过本地 wrapper。
关键源码 · 契约
crates/tui/src/sandbox/mod.rs · L3–L20
    3  //! Sandbox module for secure command execution.
    4  //!
    5  //! This module provides sandboxing capabilities for shell commands executed by
    6  //! CodeWhale. Sandboxing restricts what system resources a command can access,
    7  //! preventing accidental or malicious damage to the system.
    8  //!
    9  //! # Platform Support
   10  //!
   11  //! - **macOS**: Uses Seatbelt (`sandbox-exec`) when the runtime probe succeeds
   12  //! - **Linux**: Uses bubblewrap only when the user opts in and `/usr/bin/bwrap`
   13  //!   is executable. Landlock and seccomp helpers are not wired into child
   14  //!   execution yet and therefore are not advertised.
   15  //! - **OpenHarmony**: No local Linux sandbox is advertised. Bubblewrap,
   16  //!   Landlock, seccomp, and Linux `prctl` hardening are gated out under
   17  //!   `target_env = "ohos"`.
   18  //! - **Windows**: No OS sandbox is advertised yet. The planned first helper
   19  //!   contract is process-tree containment only via a Windows Job Object; it
   20  //!   must not claim filesystem, network, registry, or AppContainer isolation.
查看全部 4 处证据
  • 契约 crates/tui/src/sandbox/mod.rs:3–20 平台支持与未接线后端声明。
  • 配置 crates/tui/src/sandbox/mod.rs:61–68 实际可选的 public sandbox backends。
  • 实现 crates/tui/src/sandbox/bwrap.rs:7–36 prefer_bwrap、unshare、缺失时 unwrapped fallback。
  • 实现 crates/tui/src/sandbox/bwrap.rs:87–126 bwrap unshare-all、ro-bind、writable mounts、protected descendants。
07
DIMENSION · PERMISSIONS-SECURITY

权限与安全

本章共 3 个可定位结论;结论按“实现事实 → 白话解释 → 工程影响 → 源码摘录”展开。

19
L1事实codewhale-security-001

ExecPolicy 是 Builtin/Agent/User 三层规则,deny 优先且支持 arity-aware shell 判断

源码事实

execpolicy 定义 PermissionAction Allow/Ask/Deny、AskForApproval 语义和 Builtin/Agent/User ruleset layer;高层规则覆盖低层,同层 deny 优先于 ask/allow,检查顺序先 denied prefix、再 trusted 单段命令、链式调用 typed deny、最后 ask rule,并考虑参数个数。

白话解释

用户自己的规则可以补充策略,但不能把更高优先级的拒绝抹掉;`cargo test` 和 `cargo test --config ...` 也不会被当成同一件事。

对自研 Harness 的含义

命令策略需要明确层级、优先级、链式命令和参数形态;只做字符串前缀 allowlist 很容易漏洞。

关键源码 · 契约
crates/execpolicy/src/lib.rs · L10–L32
   10  /// Priority layer for typed permission-rule selection. Higher ordinal = higher
   11  /// priority. Matching typed rules compare layer before action and specificity.
   12  /// Hard denied prefixes are merged across layers and checked first.
   13  #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
   14  #[serde(rename_all = "snake_case")]
   15  pub enum RulesetLayer {
   16      BuiltinDefault = 0,
   17      Agent = 1,
   18      User = 2,
   19  }
   20  
   21  /// A named set of allow/deny prefix rules at a given priority layer.
   22  #[derive(Debug, Clone, Serialize, Deserialize)]
   23  pub struct Ruleset {
   24      /// Priority layer this ruleset belongs to.
   25      pub layer: RulesetLayer,
   26      /// Command prefixes that are allowed without requiring approval.
   27      pub trusted_prefixes: Vec<String>,
   28      /// Command prefixes that are always blocked, regardless of trust rules.
   29      pub denied_prefixes: Vec<String>,
   30      /// Typed rules that mark specific tool invocations as requiring approval.
   31      #[serde(default, skip_serializing_if = "Vec::is_empty")]
   32      pub ask_rules: Vec<ToolAskRule>,
查看全部 4 处证据
  • 契约 crates/execpolicy/src/lib.rs:10–32 Builtin/Agent/User ruleset。
  • 实现 crates/execpolicy/src/lib.rs:73–128 Allow/Ask/Deny 与层内优先级。
  • 契约 crates/execpolicy/src/lib.rs:194–248 approval mode 与 requirement。
  • 实现 crates/execpolicy/src/lib.rs:437–520 拒绝、trusted、链式命令和 ask 匹配顺序。
20
L1事实codewhale-security-002

子 Agent 的执行权限从真实 capability 和 input 分类出来,并且只能收窄

源码事实

ExecutionEnvelope 不是工具名黑名单:它读取 ToolCapability 和 input-specific is_read_only,区分 bounded verification、ExecutesCode、WritesFiles、Network;narrow 用字段交集,后代不能扩大父级的 write/network/shell 权限。

白话解释

即使未来加了一个新 MCP 或插件,只要它声明会写文件/跑代码/联网,就会自动落入相应门槛;子 Agent 不能通过自定义角色把权限变宽。

对自研 Harness 的含义

多 Agent 权限应是可序列化能力 envelope,继承规则用交集,不要按角色名字散落在多处。

关键源码 · 契约
crates/tui/src/tools/execution_envelope.rs · L1–L59
    1  //! The one place that decides whether a delegated child may make a call that
    2  //! **executes**, **mutates**, or **reaches the network**.
    3  //!
    4  //! Before this module the answer was spread across three hand-maintained name
    5  //! lists ([`crate::fleet::exact::RAW_SHELL_DENYLIST`] and its siblings) plus a
    6  //! role posture that keyed on `ShellPolicy::Full`. That shape had a structural
    7  //! hole: a name list can only deny the execution primitives someone remembered
    8  //! to write down, and `shell = "full"` was being read as "may run arbitrary
    9  //! code" by every tool whose approval requirement is `Required`. So a member
   10  //! saved as read-only-with-checks (`write = false`, `shell = "full"` — the
   11  //! `tester`/`verifier` preset, and any `custom` member shaped like it) lost
   12  //! `Bash` and kept:
   13  //!
   14  //! - `tasks{action:"gate_run"}` — runs an operator-supplied command line;
   15  //! - `automation{action:"run"}` / `{action:"create"}` — executes or schedules a
   16  //!   stored automation, with its own cwd and prompt;
   17  //! - `start_mcp_server` — spawns a process and opens a socket;
   18  //! - every repository plugin tool, which is a shell command by definition;
   19  //!
   20  //! each of which mutates the workspace and reaches the network exactly as well
   21  //! as the shell that was just removed, while the receipt said `write=false`.
   22  //!
   23  //! ## What is enforced
   24  //!
      … 25 lines omitted; exact range 1–59 …
   50  //!   exactly `write = false, shell = "full"`. Classifying it by tool name would
   51  //!   either take the role's job away or hand it a program launcher, so the
   52  //!   bound is read off the concrete call by [`classify_verification`]:
   53  //!   argument-free and pure test *selection* both cost shell authority (each
   54  //!   forks a process, which `analyst`/`scout` were never granted), and
   55  //!   anything that can name a program is held to the raw-shell bar. Every
   56  //!   consumer of that contract — the catalog filter, the dispatch guard, and
   57  //!   `reject_unbounded_verification` / `is_delegated_builtin_verification` in
   58  //!   [`crate::tools::subagent`] — reads this one classifier rather than
   59  //!   re-deriving it.
查看全部 4 处证据
  • 契约 crates/tui/src/tools/execution_envelope.rs:1–59 能力分类表与 fail-closed 设计。
  • 实现 crates/tui/src/tools/execution_envelope.rs:66–114 ExecutionEnvelope 与 narrowing。
  • 实现 crates/tui/src/tools/execution_envelope.rs:287–342 根据实际 input/capability 分类调用。
  • 实现 crates/tui/src/tools/execution_envelope.rs:344–428 fail-closed enforcement。
21
L1事实codewhale-security-003

ToolAuthorityEnvelope 对 headless/Fleet worker 做一次性外层封顶

源码事实

ToolAuthorityEnvelope 记录 schema、owner、ReadOnly/ScopedWrite、network、writable_roots/files 和 coordination contracts;normalized 拒绝空 scope 或 read-only 携带写权限,permit_mutation_path 只接受精确文件或 root,process authority 只能安装一次且不能被替换。

白话解释

Fleet 启动子进程时把“最多能写哪些地方”作为机器参数传进去;子 Agent 内部可以再收紧,但不能把它改成全盘写。

对自研 Harness 的含义

外层 orchestrator 传给 worker 的 authority 应版本化、拒绝未知字段、绑定 owner 和不可替换。

关键源码 · 契约
crates/tui/src/tools/spec.rs · L150–L218
  150  /// Machine-readable mutation boundary for a headless worker process.
  151  ///
  152  /// Fleet serializes this envelope onto the exact `codewhale exec` argv. The
  153  /// child installs it before constructing its engine, and every ToolContext in
  154  /// that process inherits the same outer cap. Nested agents may narrow this
  155  /// boundary, but cannot remove or expand it.
  156  #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
  157  #[serde(deny_unknown_fields)]
  158  pub struct ToolAuthorityEnvelope {
  159      pub schema_version: u32,
  160      pub owner: String,
  161      pub authority: ToolMutationAuthority,
  162      /// Optional outer network cap for headless workers. `None` preserves the
  163      /// behavior of v1 envelopes written before this field existed; new Fleet
  164      /// launches always carry the resolved worker permission explicitly.
  165      #[serde(default, skip_serializing_if = "Option::is_none")]
  166      pub network_access: Option<bool>,
  167      #[serde(default)]
  168      pub writable_roots: Vec<String>,
  169      #[serde(default)]
  170      pub writable_files: Vec<String>,
  171      #[serde(default)]
  172      pub coordination_contracts: Vec<String>,
  173  }
      … 35 lines omitted; exact range 150–218 …
  209              );
  210          }
  211          if self.authority == ToolMutationAuthority::ReadOnly
  212              && (!self.writable_roots.is_empty()
  213                  || !self.writable_files.is_empty()
  214                  || !self.coordination_contracts.is_empty())
  215          {
  216              return Err("read_only authority cannot carry mutation scope".to_string());
  217          }
  218          Ok(self)
查看全部 3 处证据
  • 契约 crates/tui/src/tools/spec.rs:150–218 ToolAuthorityEnvelope schema、normalization 和 read-only invariants。
  • 实现 crates/tui/src/tools/spec.rs:252–296 精确/root path permission 与一次性 process install。
  • 实现 crates/tui/src/core/engine/tool_execution.rs:408–429 child worker 拒绝 mutating MCP 与 arbitrary code。
08
DIMENSION · MCP-CONNECTORS

MCP 与连接器

本章共 3 个可定位结论;结论按“实现事实 → 白话解释 → 工程影响 → 源码摘录”展开。

22
L1事实codewhale-mcp-001

MCP 连接器覆盖 stdio、Streamable HTTP、SSE 和 OAuth,并有连接池

源码事实

mcp.rs 实现 tools/list 自动发现、连接池、stdio/streamable HTTP/SSE transport、OAuth/header/bearer token、每 server timeout;URL 默认先走 Streamable HTTP,遇兼容性状态才切 SSE。

白话解释

MCP 在这里不是一个 HTTP helper,而是有连接生命周期、能力发现、超时和认证的子系统;不同 server 的连接可以复用。

对自研 Harness 的含义

连接器层要把 transport、auth、discovery、timeout 和 pool 分开,避免某个网络协议细节侵入 Agent loop。

关键源码 · 契约
crates/tui/src/mcp.rs · L1–L37
    1  //! Async MCP (Model Context Protocol) Implementation
    2  //!
    3  //! This module provides full async support for MCP servers with:
    4  //! - Connection pooling for server reuse
    5  //! - Automatic tool discovery via `tools/list`
    6  //! - Configurable timeouts per-server and globally
    7  
    8  use std::collections::{HashMap, HashSet};
    9  use std::ffi::{OsStr, OsString};
   10  use std::fs;
   11  use std::future::Future;
   12  use std::io::{Read, Seek};
   13  use std::path::{Component, Path, PathBuf};
   14  use std::sync::Arc;
   15  use std::sync::atomic::{AtomicU64, Ordering};
   16  use std::time::Duration;
   17  
   18  use anyhow::{Context, Result};
   19  use parking_lot::RwLock;
   20  use serde::{Deserialize, Serialize};
   21  use sha2::Digest as _;
   22  
   23  pub mod external_import;
   24  mod headers;
      … 3 lines omitted; exact range 1–37 …
   28  mod streamable_http;
   29  
   30  use self::headers::{apply_safe_custom_headers, with_default_mcp_http_headers};
   31  use self::sse::SseTransport;
   32  use self::stdio::StdioTransport;
   33  #[cfg(all(test, unix))]
   34  use self::stdio::{STDIO_SHUTDOWN_GRACE, StderrTail};
   35  use self::streamable_http::{StreamableHttpTransport, StreamableSendError};
   36  use crate::network_policy::{Decision, NetworkPolicyDecider, host_from_url};
   37  use crate::utils::write_atomic;
查看全部 4 处证据
  • 契约 crates/tui/src/mcp.rs:1–37 async MCP、pool、tools/list 与 transport 概览。
  • 配置 crates/tui/src/mcp.rs:479–519 server map 与默认 connect/execute/read timeout。
  • 契约 crates/tui/src/mcp.rs:522–612 stdio/HTTP/SSE/OAuth/headers 与 plugin provenance。
  • 实现 crates/tui/src/mcp.rs:1307–1341 Streamable HTTP 优先与 SSE fallback。
23
L1事实codewhale-mcp-002

MCP secrets 不进入错误文本,远端响应和 body 也有边界

源码事实

MCP config 支持 `${NAME}` 环境占位符,缺失/非法时只报告变量名;URL/proxy 日志会 mask userinfo/query;Streamable HTTP 绑定 session id、过滤危险 headers、限制 Content-Length 和实际流式 body 字节数。

白话解释

API key 可以来自环境而不是 mcp.json;服务端返回一个超大 chunk 或把密码塞进 URL,也不会原样写进日志或无限吃内存。

对自研 Harness 的含义

所有 connector 都要在“配置展开、错误预览、response body、session identity”四个地方做 secret/size 防护。

关键源码 · 实现
crates/tui/src/mcp.rs · L57–L90
   57  /// Expand `${NAME}` placeholders in an MCP config value from the process
   58  /// environment. This lets secrets (API keys, bearer tokens, …) be supplied
   59  /// through environment variables instead of being written in cleartext into
   60  /// the MCP config file on disk.
   61  ///
   62  /// On a missing or malformed placeholder the error names only the offending
   63  /// variable, never the surrounding value, so a secret-bearing string is never
   64  /// echoed into logs or error output.
   65  fn expand_env_placeholders_with(
   66      value: &str,
   67      environment: Option<&crate::plugins::HostEnvironment>,
   68  ) -> Result<String> {
   69      let mut out = String::new();
   70      let mut rest = value;
   71      while let Some(start) = rest.find("${") {
   72          out.push_str(&rest[..start]);
   73          let after = &rest[start + 2..];
   74          let Some(end) = after.find('}') else {
   75              anyhow::bail!("unterminated environment placeholder in MCP config value");
   76          };
   77          let name = &after[..end];
   78          if name.is_empty() || !name.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') {
   79              anyhow::bail!("invalid environment placeholder in MCP config value");
   80          }
   81          let env_value = environment
   82              .map_or_else(|| std::env::var(name), |env| env.var(name))
   83              .with_context(|| {
   84                  format!("environment variable {name} required by MCP config is not set")
   85              })?;
   86          out.push_str(&env_value);
   87          rest = &after[end + 1..];
   88      }
   89      out.push_str(rest);
   90      Ok(out)
查看全部 4 处证据
  • 实现 crates/tui/src/mcp.rs:57–90 环境占位符与 secret-safe errors。
  • 实现 crates/tui/src/mcp.rs:316–360 URL/proxy secret masking。
  • 实现 crates/tui/src/mcp/streamable_http.rs:45–111 auth headers、session id 与 status handling。
  • 实现 crates/tui/src/mcp/streamable_http.rs:119–187 declared/stream body size cap。
24
L1事实codewhale-mcp-003

reviewed plugin 的 MCP 在 launch、origin 和 catalog 暴露前都要复核 authority

源码事实

reviewed plugin stdio launch 会重算 staged manifest content/capability/file hashes,要求 cwd 在 staged root;remote endpoint 要与 reviewed origin 一致;catalog_is_current 在发布工具、prompt 或 resource 前再次校验 mutable source 与 CodeWhale-owned stage。

白话解释

插件被信任后文件仍可能变化,CodeWhale 不会只相信旧 receipt;真正启动和把能力展示给模型前还会再验。

对自研 Harness 的含义

供应链治理必须覆盖“发现、信任、启用、启动、catalog 发布”全链路,而不是只在安装时验一次。

关键源码 · 实现
crates/tui/src/mcp.rs · L641–L695
  641      pub(crate) fn validate_before_stdio_spawn(&self, server_name: &str) -> Result<()> {
  642          self.validate_before_use(server_name, "spawn")
  643      }
  644  
  645      pub(crate) fn prepare_stdio_launch(
  646          &self,
  647          server_name: &str,
  648          command: &str,
  649          args: &[String],
  650          cwd: Option<&Path>,
  651      ) -> Result<ReviewedStdioLaunch> {
  652          self.validate_before_stdio_spawn(server_name)?;
  653          let staged_root = self
  654              .authority
  655              .staged_manifest
  656              .parent()
  657              .context("reviewed plugin stage manifest has no parent")?;
  658          let validated = crate::plugins::manifest::PluginManifest::validate_from_path(
  659              &self.authority.staged_manifest,
  660          )
  661          .map_err(|_| anyhow::anyhow!("reviewed plugin stage could not be opened for launch"))?;
  662          if validated.content_hash != self.authority.content_hash
  663              || validated.capability_hash != self.authority.capability_hash
  664          {
      … 21 lines omitted; exact range 641–695 …
  686              if !cwd.starts_with(staged_root) {
  687                  anyhow::bail!("reviewed plugin stdio cwd escaped its staged root");
  688              }
  689              launch.bind_cwd(cwd)?;
  690          }
  691          // A final authority pass detects any non-executed companion/config
  692          // drift while handles were opened. Execution itself uses the handles.
  693          self.validate_before_stdio_spawn(server_name)?;
  694          Ok(launch)
  695      }
查看全部 3 处证据
  • 实现 crates/tui/src/mcp.rs:641–695 stage hash、cwd containment、final authority pass。
  • 实现 crates/tui/src/mcp.rs:697–728 authority verify、remote origin 与 catalog current。
  • 实现 crates/tui/src/mcp/stdio.rs:135–160 reviewed plugin env allowlist 与 child spawn。
09
DIMENSION · INSTRUCTIONS-SKILLS-PLUGINS

指令、Skills 与插件

本章共 3 个可定位结论;结论按“实现事实 → 白话解释 → 工程影响 → 源码摘录”展开。

25
L1事实codewhale-extension-001

Skills 同时兼容生态目录与 CodeWhale owned roots,支持 explicit-only 和 locale 描述

源码事实

SkillDiscoveryMode 在 Compatible 与 CodeWhaleOnly 之间切换;Skill 结构记录 description/localized_descriptions、ModelAndUser/ExplicitOnly invocation、aliases、实际 SKILL.md path 与 Native/Plugin source。目录遍历有深度、隐藏目录、canonical visited 和 shadowing warning。

白话解释

它能读取 `.agents`、Claude、OpenCode、Cursor 等兼容 Skills,也能只读自己的 `.codewhale/skills`;技能可以不出现在模型菜单里,只有用户点名才加载。

对自研 Harness 的含义

Skills 是 prompt 注入面,应有来源、调用方式、别名和路径 provenance,而不是简单把所有 Markdown 拼进 system prompt。

关键源码 · 契约
crates/tui/src/skills/mod.rs · L131–L224
  131  // === Defaults ===
  132  
  133  #[must_use]
  134  pub fn default_skills_dir() -> PathBuf {
  135      crate::config::effective_home_dir().map_or_else(
  136          || PathBuf::from("/tmp/codewhale/skills"),
  137          |p| p.join(".codewhale").join("skills"),
  138      )
  139  }
  140  
  141  /// Global agentskills.io-compatible skills directory (`~/.agents/skills`).
  142  #[must_use]
  143  pub fn agents_global_skills_dir() -> Option<PathBuf> {
  144      crate::config::effective_home_dir().map(|p| p.join(".agents").join("skills"))
  145  }
  146  
  147  // === Types ===
  148  
  149  /// Session-time skill discovery scope.
  150  #[derive(Debug, Clone, Copy, PartialEq, Eq)]
  151  pub enum SkillDiscoveryMode {
  152      /// Preserve the existing broad compatibility scan across CodeWhale,
  153      /// agentskills.io, Claude, OpenCode, Cursor, and legacy DeepSeek roots.
  154      Compatible,
      … 60 lines omitted; exact range 131–224 …
  215  
  216  #[derive(Debug, Clone, PartialEq, Eq)]
  217  pub enum SkillSource {
  218      Native,
  219      Plugin {
  220          plugin_id: String,
  221          plugin_name: String,
  222          authority: Box<crate::plugins::types::PluginAuthority>,
  223      },
  224  }
查看全部 3 处证据
  • 契约 crates/tui/src/skills/mod.rs:131–224 default roots、discovery mode、Skill metadata 与 source。
  • 实现 crates/tui/src/skills/mod.rs:357–487 递归 discovery、hidden skip、parse、shadowing 和 cycle guard。
  • 实现 crates/tui/src/skills/roots.rs:134–218 兼容 roots 与 owned CodeWhale project root。
26
L1事实codewhale-extension-002

插件 trust 和 enable 分离,内容/能力 hash 变化会自动失信

源码事实

PluginRegistry 在加载 persisted state 时比较 content_hash/capability_hash,变化标为 ContentChanged/CapabilitiesChanged;trust 会 stage bundle、写 receipt 但强制 enabled=false;enable 要求 trusted、staged snapshot、host applicability 和 supported capabilities 全部满足。

白话解释

看过插件不等于立即打开插件;信任是审核记录,启用是另一个明确动作。插件内容或能力变了,旧审核不能继续生效。

对自研 Harness 的含义

自研插件市场要把 review receipt、runtime snapshot、enable state 和 capability drift 做成不同状态,禁止一次“安装”打开全部能力。

关键源码 · 实现
crates/tui/src/plugins/registry.rs · L123–L169
  123      fn register_loaded(&mut self, plugin: LoadedPlugin) {
  124          self.names
  125              .insert(plugin.name().to_string(), plugin.id.clone());
  126          self.plugins.insert(plugin.id.clone(), plugin);
  127      }
  128  
  129      fn apply_state(&mut self) {
  130          let state_path = self.state_path.clone();
  131          for (id, plugin) in &mut self.plugins {
  132              let persisted = self.state.plugins.get(id);
  133              plugin.state_generation = persisted.map_or(0, |state| state.generation);
  134              plugin.enabled = persisted.is_some_and(|state| state.enabled);
  135              plugin.trust_status = match persisted.and_then(|state| state.trust.as_ref()) {
  136                  Some(receipt) if receipt.capability_hash != plugin.capability_hash => {
  137                      PluginTrustStatus::CapabilitiesChanged
  138                  }
  139                  Some(receipt) if receipt.content_hash != plugin.content_hash => {
  140                      PluginTrustStatus::ContentChanged
  141                  }
  142                  Some(_) => PluginTrustStatus::Trusted,
  143                  None => PluginTrustStatus::NeverReviewed,
  144              };
  145              if self.state_error.is_some() {
  146                  plugin.enabled = false;
      … 13 lines omitted; exact range 123–169 …
  160                      Ok(snapshots) => plugin.skill_snapshots = snapshots,
  161                      Err(error) => {
  162                          plugin.staged_root = None;
  163                          plugin.enabled = false;
  164                          plugin.diagnostics.push(PluginDiagnostic::error(
  165                              "staged-skill-invalid",
  166                              format!("Plugin runtime Skill snapshot is fail-closed: {error}"),
  167                              Some(staged_root),
  168                          ));
  169                      }
查看全部 3 处证据
  • 实现 crates/tui/src/plugins/registry.rs:123–169 content/capability drift 让 plugin fail-closed。
  • 实现 crates/tui/src/plugins/registry.rs:300–334 trust staging、receipt 与不隐式 enable。
  • 实现 crates/tui/src/plugins/registry.rs:353–393 enable 的 trust/stage/applicability/capability gates。
27
L1事实codewhale-extension-003

Hook 事件覆盖 turn/tool/subagent,ToolCallBefore 失败可按 strict gate fail-closed

源码事实

HookEvent 契约列出 session_start/end、turn_end、message_submit、tool_call_before/after、mode_change、on_error、subagent_spawn/complete、shell_env 共 11 种事件。ToolCallBefore 在 blocking worker 执行,合并 deny/ask/updatedInput/additionalContext;严格且 continue_on_error=false 的 hook 失去 verdict 会阻断工具。

白话解释

Hook 不只是“执行一个脚本”:它能在工具落地前改参数、要求审批或阻止调用;Hook executor 崩掉时,严格 gate 不会被当成允许。

对自研 Harness 的含义

扩展 hook 要明确同步/后台语义、超时、失联处理和 input re-prepare,否则自定义治理逻辑会变成绕过点。

关键源码 · 测试
crates/tui/src/hooks/config.rs · L656–L697
  656      /// `config.toml`, in `/hooks events`, and in `docs/HOOKS.md`. A rename is
  657      /// a breaking change, and a new variant must be added deliberately.
  658      #[test]
  659      fn all_eleven_event_names_are_stable_and_exhaustive() {
  660          let names: Vec<&str> = ALL_HOOK_EVENTS.iter().map(|e| e.as_str()).collect();
  661          assert_eq!(
  662              names,
  663              vec![
  664                  "session_start",
  665                  "session_end",
  666                  "turn_end",
  667                  "message_submit",
  668                  "tool_call_before",
  669                  "tool_call_after",
  670                  "mode_change",
  671                  "on_error",
  672                  "subagent_spawn",
  673                  "subagent_complete",
  674                  "shell_env",
  675              ]
  676          );
  677  
  678          // Exhaustiveness: every variant appears exactly once. The `match` here
  679          // fails to compile if a variant is added without updating the list.
      … 8 lines omitted; exact range 656–697 …
  688                  | HookEvent::ModeChange
  689                  | HookEvent::OnError
  690                  | HookEvent::SubagentSpawn
  691                  | HookEvent::SubagentComplete
  692                  | HookEvent::ShellEnv => true,
  693              };
  694              assert!(covered);
  695          }
  696          let unique: std::collections::HashSet<&str> = names.iter().copied().collect();
  697          assert_eq!(unique.len(), 11);
查看全部 4 处证据
  • 测试 crates/tui/src/hooks/config.rs:656–697 11 个 HookEvent 名称的稳定契约测试。
  • 测试 crates/tui/src/hooks/config.rs:716–730 Hook 默认 timeout/background/continue_on_error。
  • 实现 crates/tui/src/core/engine/turn_loop.rs:1883–1920 prepare 与 ToolCallBefore hook。
  • 实现 crates/tui/src/core/engine/turn_loop.rs:1968–2024 deny/ask/updatedInput 与 strict fail-closed。
10
DIMENSION · PERSISTENCE-OBSERVABILITY

持久化与观测

本章共 5 个可定位结论;结论按“实现事实 → 白话解释 → 工程影响 → 源码摘录”展开。

28
L1事实codewhale-persistence-001

Session 保存是原子写,恢复会校验 schema 并修复 tool history

源码事实

SessionManager 限制最多 50 个 session,保存路径先校验 id,再通过 temp+fsync+rename 原子写;in-flight turn 有按 session 分离的 checkpoint。load_session 拒绝未来 schema,并调用 repair_tool_call_pairs 修复 persisted tool call/result 配对。

白话解释

进程崩溃时不会留下半个 JSON;下一次加载也不会把孤儿 tool result 原样塞回 provider。

对自研 Harness 的含义

会话持久化要把 atomicity、schema compatibility、crash checkpoint 和 history repair 作为一条恢复链。

关键源码 · 配置
crates/tui/src/session_manager.rs · L26–L40
   26  /// Maximum number of sessions to retain
   27  const MAX_SESSIONS: usize = 50;
   28  /// Maximum session title length, in `char`s. Matches the bound the session
   29  /// picker's rename prompt has always enforced.
   30  pub const MAX_SESSION_TITLE_CHARS: usize = 100;
   31  const WORK_GRAPH_IMPORT_ARCHIVE_DIR: &str = ".work-graph-import-archive";
   32  const CURRENT_SESSION_SCHEMA_VERSION: u32 = 1;
   33  const CURRENT_QUEUE_SCHEMA_VERSION: u32 = 1;
   34  
   35  const fn default_session_schema_version() -> u32 {
   36      CURRENT_SESSION_SCHEMA_VERSION
   37  }
   38  
   39  const fn default_queue_schema_version() -> u32 {
   40      CURRENT_QUEUE_SCHEMA_VERSION
查看全部 3 处证据
  • 配置 crates/tui/src/session_manager.rs:26–40 session/queue schema 与 50 条保留上限。
  • 实现 crates/tui/src/session_manager.rs:587–617 atomic save 与 per-session checkpoint。
  • 实现 crates/tui/src/session_manager.rs:825–856 schema check 与 tool-call/result repair。
29
L1事实codewhale-persistence-002

StateStore 用 SQLite 做投影,同时保留 append-only session index 和树状消息关系

源码事实

StateStore 由长连接 SQLite 与 append-only JSONL index 组成,开启 WAL、foreign_keys 和 5 秒 busy_timeout;schema 记录 threads、dynamic tools、messages、checkpoints、jobs,消息用 parent_entry_id/current_leaf_id 支持 fork,goal usage/continuation 以原子 SQL 累加。

白话解释

数据库负责查询和并发,JSONL 保留轻量索引;消息不是一条不可分叉的数组,而是带父节点和当前叶子的树。

对自研 Harness 的含义

企业 Harness 可以用 SQLite 做 projection,但要保留事件/索引事实源和可重建的消息拓扑。

关键源码 · 契约
crates/state/src/lib.rs · L262–L338
  262  /// Persistent storage for conversation threads, messages, checkpoints, and jobs.
  263  ///
  264  /// Backed by a SQLite database and an append-only JSONL session index file.
  265  /// The database schema is automatically initialized and migrated on [`open`](Self::open).
  266  #[derive(Debug, Clone)]
  267  pub struct StateStore {
  268      db_path: PathBuf,
  269      session_index_path: PathBuf,
  270      // Single long-lived connection shared by all clones. SQLite pragmas are
  271      // per-connection, so opening once in `open` and applying them there keeps
  272      // every operation consistent without re-opening the database per call.
  273      conn: Arc<Mutex<Connection>>,
  274  }
  275  
  276  impl StateStore {
  277      /// Open (or create) a state store at the given database path.
  278      ///
  279      /// If `path` is `None`, the default location (`~/.codewhale/state.db`, with
  280      /// `~/.deepseek/state.db` as a legacy fallback) is used.
  281      /// The database schema is created automatically if it does not exist.
  282      pub fn open(path: Option<PathBuf>) -> Result<Self> {
  283          let db_path = path.unwrap_or_else(default_state_db_path);
  284          let session_index_path = db_path
  285              .parent()
      … 43 lines omitted; exact range 262–338 …
  329                  .pragma_update_and_check(None, "journal_mode", "WAL", |row| row.get(0))
  330                  .with_context(|| format!("failed to enable WAL for {}", db_path.display()))?;
  331              if !configured_mode.eq_ignore_ascii_case("wal") {
  332                  anyhow::bail!(
  333                      "failed to enable WAL for {}: SQLite retained journal mode {configured_mode}",
  334                      db_path.display()
  335                  );
  336              }
  337          }
  338          Ok(())
查看全部 4 处证据
  • 契约 crates/state/src/lib.rs:262–338 SQLite + JSONL、WAL、busy timeout、foreign keys。
  • 迁移 crates/state/src/lib.rs:355–428 threads/messages/checkpoints/jobs schema。
  • 实现 crates/state/src/lib.rs:1072–1113 append message transaction 与 parent/current leaf。
  • 实现 crates/state/src/lib.rs:1214–1250 fork message transaction。
30
L1事实codewhale-persistence-003

side-git 快照保护用户仓库且把失败当成安全网降级

源码事实

snapshot 模块在每轮前后写入独立 side git,始终显式传 --git-dir 与 --work-tree,默认最多 50 个快照;首次初始化有 2GB/20 万条目估算上限,side repo 超过 500MB 会 prune 到约 400MB;git 缺失、磁盘满或只读时快照失败只记录 warning,turn 继续。

白话解释

CodeWhale 不碰用户自己的 .git,而是另存一份可 restore 的工作区历史;它是回滚保险,不是发布 gate。

对自研 Harness 的含义

恢复能力应与 Agent 正确性解耦:快照失败要可见,但不能在错误处理里把会话一起打死。

关键源码 · 契约
crates/tui/src/snapshot/mod.rs · L1–L34
    1  //! Workspace snapshots — pre/post-turn safety net.
    2  //!
    3  //! Each turn the engine takes a `pre-turn:<seq>` snapshot of the user's
    4  //! workspace into a side git repo at
    5  //! `~/.deepseek/snapshots/<project_hash>/<worktree_hash>/.git`, then a
    6  //! matching `post-turn:<seq>` snapshot when the turn finishes. Users
    7  //! can roll back via `/restore N` (slash command) or, when the model
    8  //! recognises an "undo my last edit" intent, the `revert_turn` tool.
    9  //!
   10  //! ## Why a side repo?
   11  //!
   12  //! - The user's own `.git` is never touched. `--git-dir` and
   13  //!   `--work-tree` are *always* set together when we shell out to git;
   14  //!   that single invariant is what keeps snapshots and the user's repo
   15  //!   completely independent.
   16  //! - Workspaces without git still get snapshots.
   17  //! - `git`'s own deduplication (object packfiles) keeps the disk
   18  //!   footprint tractable — typical 100 MB workspace × 12 turns ≈ 1.2 GB
   19  //!   uncompressed but git's content-addressed storage usually brings
   20  //!   that down 10-30×. We mitigate further with:
   21  //!     - 7-day default retention (`session_manager` prunes at session
   22  //!       start via [`prune::prune_older_than`]).
   23  //!     - `gc.auto = 0` on the side repo (we don't want background gcs
   24  //!       firing mid-turn) plus an explicit `git gc --prune=now` after
   25  //!       prune.
   26  //!     - Startup cleanup for stale `tmp_pack_*` files left by interrupted
   27  //!       git pack operations.
   28  //!
   29  //! ## Failure model
   30  //!
   31  //! Pre/post-turn snapshot calls are **non-fatal**. If `git` is missing,
   32  //! the disk is full, or the workspace is on a read-only filesystem, the
   33  //! turn proceeds and the engine logs a warning. The snapshot is a
   34  //! safety net, not a correctness gate.
查看全部 4 处证据
  • 契约 crates/tui/src/snapshot/mod.rs:1–34 pre/post turn side git 与 non-fatal failure model。
  • 契约 crates/tui/src/snapshot/repo.rs:1–13 --git-dir/--work-tree 安全不碰用户 .git。
  • 配置 crates/tui/src/snapshot/repo.rs:55–80 500MB storage cap、400MB prune target、2GB workspace cap。
  • 实现 crates/tui/src/snapshot/repo.rs:314–431 add/write-tree/commit-tree/update-ref 与 pruning。
31
L1事实codewhale-observe-001

事件类型区分流式内容、工具生命周期和冻结路由/计费 receipt

源码事实

Event 定义 Message/Thinking delta、ToolCallStarted/Heartbeat/Complete、TurnStarted、ToolRequestSnapshot、RouteDispatched、TurnComplete;TurnRoute 保存 provider identity/model/base_url/billing product,RouteBillingEnvelope 只在真正发到 wire 时出现。

白话解释

UI 可以实时显示思考和工具,但计费不会拿“计划使用的模型”冒充“实际发出的请求”;路由事实在 dispatch 边界冻结。

对自研 Harness 的含义

可观测事件要区分 planned、started、dispatched、completed 四个时间点,尤其是成本和身份。

关键源码 · 契约
crates/tui/src/core/events.rs · L19–L105
   19  /// Final status for a turn.
   20  #[derive(Debug, Clone, Copy, PartialEq, Eq)]
   21  pub enum TurnOutcomeStatus {
   22      Completed,
   23      Interrupted,
   24      Failed,
   25  }
   26  
   27  /// Provider/model route resolved for a model-backed turn.
   28  ///
   29  /// Emitted at `RouteDispatched` so hosts retain provenance until the matching
   30  /// `TurnComplete` without relying on mutable global selection state. Non-model
   31  /// turns such as composer `!` shell commands use no route.
   32  #[derive(Debug, Clone, PartialEq, Eq)]
   33  pub struct TurnRoute {
   34      pub provider: ApiProvider,
   35      /// Exact non-secret configured route key. Named custom providers all map
   36      /// to [`ApiProvider::Custom`], so the enum alone is not provenance.
   37      pub provider_identity: String,
   38      pub model: String,
   39      pub auto_model: bool,
   40      /// Secret-free proof of the endpoint and credential generation the turn's
   41      /// client was *installed* on, minted from that client rather than re-read
   42      /// from config later.
      … 53 lines omitted; exact range 19–105 …
   96  ///   actually put on the wire, when*. A planned-but-unsent route has no
   97  ///   metering surface and no dispatch instant, so it must be structurally
   98  ///   absent rather than defaulted.
   99  #[derive(Debug, Clone, PartialEq, Eq)]
  100  pub struct RouteBillingEnvelope {
  101      pub billing_surface: Option<String>,
  102      pub endpoint_fingerprint: Option<String>,
  103      pub billing_mode: crate::cost_status::RouteBillingMode,
  104      pub dispatched_at: DateTime<Utc>,
  105  }
查看全部 3 处证据
  • 契约 crates/tui/src/core/events.rs:19–105 TurnRoute 与 billing envelope 的冻结语义。
  • 契约 crates/tui/src/core/events.rs:163–256 stream/tool/turn lifecycle events。
  • 实现 crates/tui/src/cost_status.rs:867–914 redacted route receipt fields。
32
L1事实codewhale-observe-002

后台子 Agent 的用量按 owner lease 归属,记录有界且不会因 parent 结束丢账

源码事实

cost_status 为每个 runtime owner 注册同步 sink,子 Agent 通过 cloned RuntimeUsageLease 保持 owner 存活;journal 每 owner 最多保留固定记录数并统计 dropped_records,parent terminal 只有在所有 child lease drop 后才清理。

白话解释

父 Agent 已经显示完成,但后台 worker 还在花 token 时,账仍记在正确 owner 下;不会因为 mailbox 关闭就把成本丢掉。

对自研 Harness 的含义

多 Agent 成本和 trace 需要独立于 UI mailbox 的 owner scope 与 lease 生命周期。

关键源码 · 实现
crates/tui/src/cost_status.rs · L629–L659
  629  fn record_runtime_usage(
  630      owner: &str,
  631      source_id: &str,
  632      route: &EffectiveRouteEnvelope,
  633      usage: &Usage,
  634  ) {
  635      let owner = owner.trim();
  636      if owner.is_empty() {
  637          return;
  638      }
  639      let record = RuntimeUsageRecord {
  640          source_id: source_id.to_string(),
  641          usage: EffectiveRouteUsage {
  642              route: route.sanitized_for_persistence(),
  643              usage: usage.clone(),
  644          },
  645      };
  646      let sink =
  647          with_runtime_usage_sinks(|sinks| sinks.get(owner).map(|entry| Arc::clone(&entry.sink)));
  648      if sink.is_some_and(|sink| sink(record.clone())) {
  649          return;
  650      }
  651      with_runtime_usage_journal_mut(|journal| {
  652          let owner_journal = journal.entry(owner.to_string()).or_default();
  653          if owner_journal.records.len() == MAX_RUNTIME_USAGE_RECORDS_PER_OWNER {
  654              owner_journal.records.pop_front();
  655              owner_journal.dropped_records = owner_journal.dropped_records.saturating_add(1);
  656          }
  657          owner_journal.records.push_back(record);
  658      });
  659  }
查看全部 2 处证据
  • 实现 crates/tui/src/cost_status.rs:629–659 owner-scoped usage journal 与有界记录。
  • 实现 crates/tui/src/cost_status.rs:661–756 sink、lease clone/drop、terminal cleanup。
11
DIMENSION · SUBAGENTS-COLLABORATION

子 Agent 与协作

本章共 5 个可定位结论;结论按“实现事实 → 白话解释 → 工程影响 → 源码摘录”展开。

33
L1事实codewhale-collab-001

agent 是模型可见的创建面,coordination tools 复用同一 mailbox/checkpoint machinery

源码事实

subagent 模块明确以 agent 作为创建工具,agents/list/message/followup/interrupt/coordinate/wait 只是窄协作面;Engine spawn 会给 child Agent mode(不继承 YOLO)、role model、MCP/tool context、max depth、API timeout、denied tools 和 fleet roster。

白话解释

子 Agent 不是一套旁路脚本:父子共享结构化协调协议,但子 Agent 默认不会继承主 Agent 的全权模式。

对自研 Harness 的含义

协作 API 应与 worker 生命周期共用数据结构,同时让安全 posture 在 child boundary 重新计算。

关键源码 · 契约
crates/tui/src/tools/subagent/mod.rs · L1–L11
    1  //! Sub-agent spawning system.
    2  //!
    3  //! Provides tools to spawn background sub-agents, query their status,
    4  //! and retrieve results. Sub-agents run with a filtered toolset and
    5  //! inherit the workspace configuration from the main session.
    6  //!
    7  //! The model-facing creation surface is the `agent` tool. Narrow coordination
    8  //! tools (`agents/list`, `agents/message`, `agents/followup`,
    9  //! `agents/interrupt`, `agents/coordinate`, `agents/wait`) wrap the same runtime without restoring
   10  //! the retired lifecycle theater. Older manager helpers remain executable for
   11  //! persisted records and internal recovery.
查看全部 3 处证据
  • 契约 crates/tui/src/tools/subagent/mod.rs:1–11 agent creation 与 coordination tools 定位。
  • 实现 crates/tui/src/core/engine.rs:2102–2160 child Agent mode、runtime wiring、depth/timeout/denied tools。
  • 实现 crates/tui/src/core/engine.rs:2161–2198 route resolution 与 background spawn。
34
L1事实codewhale-collab-002

Mailbox 用单调序列、fanout、close-as-cancel 和结构化工作状态传递协作事实

源码事实

MailboxMessage 覆盖 Started/Progress/ToolCall/ChildSpawned/Completed/Failed/Interrupted/Cancelled/WorkState/TokenUsage;MailboxEnvelope 的 seq 在整个 mailbox 单调递增,多个 subscriber 可独立 drain,send_gate 防止 TurnComplete 后追加 token usage,close 同时传播 cancellation。

白话解释

UI 卡片、父 Agent 和成本账本看到的是同一条有序消息流;子 Agent 被取消时,取消信号和“已取消”事件不会互相错位。

对自研 Harness 的含义

多 Agent 状态应是结构化事件流,不要让父 Agent 从一段最终摘要里猜 child 是否真正完成。

关键源码 · 契约
crates/tui/src/tools/subagent/mailbox.rs · L1–L92
    1  //! Mailbox abstraction for sub-agent runtime coordination.
    2  //!
    3  //! Monotonic sequence numbers give every consumer a consistent ordering even
    4  //! when multiple subscribers (e.g. UI card + parent agent) drain
    5  //! independently; close-as-cancel lets a single signal both stop new mail and
    6  //! propagate cancellation through nested children.
    7  
    8  use std::collections::VecDeque;
    9  use std::sync::Arc;
   10  use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
   11  #[cfg(test)]
   12  use std::time::Duration;
   13  
   14  use serde::{Deserialize, Serialize};
   15  use tokio::sync::{mpsc, watch};
   16  use tokio_util::sync::CancellationToken;
   17  
   18  #[cfg(test)]
   19  use crate::config::ApiProvider;
   20  use crate::models::Usage;
   21  use crate::tools::todo::TodoListSnapshot;
   22  
   23  use super::FleetRole;
   24  
      … 58 lines omitted; exact range 1–92 …
   83          /// Stable identity of the provider response. Runtime accounting uses
   84          /// this across direct durability, mailbox replay, and restart dedupe.
   85          source_id: String,
   86          /// Immutable provider/model/billing evidence captured before the
   87          /// child request was sent.
   88          route: crate::cost_status::EffectiveRouteEnvelope,
   89          /// Provider usage payload, including cache-hit/cache-miss fields.
   90          usage: Usage,
   91      },
   92  }
查看全部 3 处证据
  • 契约 crates/tui/src/tools/subagent/mailbox.rs:1–92 mailbox message lifecycle 与 usage envelope。
  • 实现 crates/tui/src/tools/subagent/mailbox.rs:150–185 monotonic sequence、clone/fanout 与 send gate。
  • 实现 crates/tui/src/tools/subagent/mailbox.rs:191–225 close-as-cancel mailbox construction 与 subscriber。
35
L1事实codewhale-collab-003

子 Agent 有 bounded resident context、步骤/时间/响应预算和持久 checkpoint

源码事实

SubAgentRuntime 对 resident_file 设 64KiB 上限并做全局 lease;child steps 最大 2000、默认 wall time 30 分钟、tool timeout 300 秒、response cap 16384 tokens;checkpoint tail 256KiB、transcript tail 1MiB,状态写入 subagents.v1.json 且热路径 1.5 秒 debounce。

白话解释

子 Agent 可以长期跑,但不能无限带着整仓库文件和无限 transcript 常驻内存;它会把进度压到有界 checkpoint,重启后还能恢复。

对自研 Harness 的含义

agent fanout 的内存/磁盘预算要和 token budget 一起限制,否则并发数一上来就会拖垮宿主。

关键源码 · 实现
crates/tui/src/tools/subagent/mod.rs · L98–L228
   98  // === Constants ===
   99  
  100  /// Global ownership table for cache-aware resident file sub-agents (#529).
  101  /// Maps file path → agent id. Agents hold a lease on a file while running;
  102  /// the lease is released when the agent reaches a terminal state.
  103  static RESIDENT_LEASES: std::sync::OnceLock<
  104      parking_lot::Mutex<std::collections::HashMap<String, String>>,
  105  > = std::sync::OnceLock::new();
  106  const MAX_RESIDENT_CONTEXT_BYTES: u64 = 64 * 1024;
  107  
  108  /// Release all resident file leases held by `agent_id`. Called when an
  109  /// agent transitions to a terminal state (completed, failed, cancelled).
  110  fn release_resident_leases_for(agent_id: &str) {
  111      if let Some(lock) = RESIDENT_LEASES.get() {
  112          let mut guard = lock.lock();
  113          guard.retain(|_, owner| owner != agent_id);
  114      }
  115  }
  116  
  117  fn reserve_resident_lease(lease_key: &str, display_path: &str) -> Result<(), ToolError> {
  118      let leases = RESIDENT_LEASES.get_or_init(|| parking_lot::Mutex::new(HashMap::new()));
  119      let mut guard = leases.lock();
  120      if let Some(owner) = guard.get(lease_key) {
  121          return Err(ToolError::invalid_input(format!(
      … 97 lines omitted; exact range 98–228 …
  219      Ok(ResidentContext {
  220          display_path,
  221          // The lease table is process-wide, so a repo-relative path alone
  222          // would falsely collide across unrelated workspaces. The authority
  223          // resolver returned a canonical in-workspace file; use that exact
  224          // identity internally while keeping only the relative label visible.
  225          lease_key: path.to_string_lossy().into_owned(),
  226          contents,
  227      })
  228  }
查看全部 3 处证据
  • 实现 crates/tui/src/tools/subagent/mod.rs:98–228 resident file lease 与 64KiB bounded context。
  • 配置 crates/tui/src/tools/subagent/mod.rs:230–245 steps、wall time、tool timeout。
  • 配置 crates/tui/src/tools/subagent/mod.rs:266–323 response/checkpoint/transcript/state/debounce limits。
36
L1事实codewhale-collab-004

子 Agent 可在父 workspace 内运行,也可创建隔离 Git worktree

源码事实

prepare_child_workspace 会 canonicalize requested cwd 并拒绝离开 parent workspace;传入 worktree 请求时验证 repo/branch,执行 git worktree add -b,默认根目录为 .codewhale-worktrees。

白话解释

不指定 worktree 时,子 Agent 只能在父项目里选一个存在的目录;需要并行改代码时,可以让系统创建独立分支和工作树。

对自研 Harness 的含义

协作执行应把 cwd containment 和 worktree isolation 做成显式参数,避免“每个 child 都能随便 cd”。

关键源码 · 契约
crates/tui/src/tools/subagent/worktree.rs · L1–L47
    1  //! Workspace validation and first-class git worktree isolation for sub-agents.
    2  
    3  use std::fs;
    4  use std::path::{Path, PathBuf};
    5  
    6  use uuid::Uuid;
    7  
    8  use crate::dependencies::{ExternalTool, Git};
    9  use crate::tools::spec::ToolError;
   10  
   11  use super::FleetRole;
   12  
   13  const SUBAGENT_WORKTREE_ROOT_DIR: &str = ".codewhale-worktrees";
   14  
   15  #[derive(Debug, Clone, PartialEq, Eq)]
   16  pub(super) struct SubAgentWorktreeRequest {
   17      pub(super) branch: Option<String>,
   18      pub(super) path: Option<PathBuf>,
   19      pub(super) base_ref: Option<String>,
   20  }
   21  
   22  pub(super) fn prepare_child_workspace(
   23      parent_workspace: &Path,
   24      requested_cwd: Option<&Path>,
      … 13 lines omitted; exact range 1–47 …
   38          return create_isolated_worktree(&discovery_anchor, worktree, session_name, agent_type)
   39              .map(Some);
   40      }
   41  
   42      if requested_cwd.is_some() {
   43          return Ok(Some(discovery_anchor));
   44      }
   45  
   46      Ok(None)
   47  }
查看全部 3 处证据
  • 契约 crates/tui/src/tools/subagent/worktree.rs:1–47 child workspace 解析与 worktree 分支。
  • 实现 crates/tui/src/tools/subagent/worktree.rs:49–74 cwd canonicalization 与 parent containment。
  • 实现 crates/tui/src/tools/subagent/worktree.rs:77–123 git worktree add -b 与结果路径。
37
L1事实codewhale-collab-005

Fleet 把 worker host、运行状态、artifact 和控制面做成可观测协议

源码事实

FleetHostAdapter 抽象 local process 与 SSH host,统一 start/read_status/read_logs/interrupt/restart/stop/cleanup,worker status 带 pid/exit/memory/retryable;fleet control 使用 workspace 下的 .codewhale/fleet.jsonl,CLI 与 TUI 复用同一 durable ledger/renderers,worker/artifact 行数有上限。

白话解释

Fleet 不要求控制台知道 worker 是本机进程还是 SSH;它只消费统一的状态和 artifact 事件,状态页不会因为刷新而偷偷创建空 ledger。

对自研 Harness 的含义

远程协作面应把 host adapter 与产品控制面分开,并让状态查询默认为纯观察。

关键源码 · 契约
crates/tui/src/fleet/host.rs · L1–L5
    1  //! Fleet worker host adapters.
    2  //!
    3  //! Adapters own process boundaries for worker hosts. The manager can lease and
    4  //! observe work through this trait without knowing whether the worker is a
    5  //! local child process or an SSH-backed remote command.
查看全部 4 处证据
  • 契约 crates/tui/src/fleet/host.rs:1–5 local/SSH process boundary adapter。
  • 契约 crates/tui/src/fleet/host.rs:101–170 worker request/status 与 lifecycle trait。
  • 契约 crates/tui/src/fleet/control.rs:1–46 CLI/TUI 共用 durable ledger、probe 与 row caps。
  • 实现 crates/tui/src/fleet/control.rs:92–120 typed worker event labels。
12
DIMENSION · TESTS-BENCHMARKS-MATURITY

测试、基准与成熟度

本章共 1 个可定位结论;结论按“实现事实 → 白话解释 → 工程影响 → 源码摘录”展开。

38
L3事实codewhale-maturity-001

关键治理模块都有契约测试,但测试覆盖不等于运行时强制全开

源码事实

仓库为 compaction、bwrap、execution_envelope、plugins、MCP、Skills、subagent 和 core engine 提供独立测试模块;测试能证明设计意图和回归约束,不能证明用户一定启用了 OS sandbox、插件或某个 provider。

白话解释

源码里不仅有实现,也有大量“规则不能被改坏”的测试;但测试通过只说明代码在测试场景下遵守规则,不能替代生产环境 capability 检查。

对自研 Harness 的含义

评测体系应把单元/契约测试、真实 sandbox probe、provider replay 和端到端交付演练分开统计。

边界
  • 本账本未执行全量 cargo test;这里只记录仓库中可定位的测试实现。
  • 部分测试包含在超大模块底部,报告代码摘录会按行号折叠。
关键源码 · 测试
crates/tui/src/compaction.rs · L2135–L2148
 2135  mod tests {
 2136      use crate::models::{ImageUrlContent, Message};
 2137  
 2138      #[test]
 2139      fn inline_image_estimates_nonzero_tokens() {
 2140          let msg = Message {
 2141              role: "user".to_string(),
 2142              content: vec![ContentBlock::ImageUrl {
 2143                  image_url: ImageUrlContent {
 2144                      url: "data:image/png;base64,AAAA".to_string(),
 2145                  },
 2146              }],
 2147          };
 2148          assert!(
查看全部 6 处证据
  • 测试 crates/tui/src/compaction.rs:2135–2148 compaction tests module。
  • 测试 crates/tui/src/sandbox/bwrap.rs:198–238 bubblewrap command tests。
  • 测试 crates/tui/src/tools/execution_envelope.rs:431–490 capability envelope test module。
  • 测试 crates/tui/src/plugins/tests.rs:32–77 trust/enable separation test。
  • 测试 crates/tui/src/skills/tests.rs:1–40 Skills discovery contract tests。
  • 测试 crates/tui/src/tools/subagent/tests.rs:1–40 subagent runtime contract tests。
APPENDIX · SOURCE INDEX

本报告引用过的实现文件

这是一份代码阅读索引,不是仓库文件总表。机器候选扫描覆盖整个仓库;进入结论的文件必须经人工沿调用链复核。

  1. 01crates/tui/src/core/mod.rsL1–15, 17–33
  2. 02crates/tui/src/core/engine.rsL221–298, 406–481, 1920–1942, 4097–4123, 4161–4203, 3385–3418, 3423–3463, 3601–3665, 2221–2244, 2102–2160, 2161–2198
  3. 03crates/tui/src/core/engine/turn_loop.rsL364–412, 412–481, 583–613, 620–687, 689–710, 1883–1920, 1968–2024
  4. 04crates/tui/src/prefix_cache.rsL1–29, 40–106, 188–205, 234–269
  5. 05crates/tui/src/context_budget.rsL1–32, 46–74, 133–199
  6. 06crates/tui/src/compaction.rsL473–507, 509–555, 588–680, 765–805, 1172–1281, 1327–1435, 1848–1877, 2135–2148
  7. 07crates/tui/src/tools/registry.rsL200–244, 91–99, 262–293, 295–330
  8. 08crates/tui/src/goal_loop.rsL1–23, 55–71, 104–143
  9. 09crates/state/src/lib.rsL847–913, 262–338, 355–428, 1072–1113, 1214–1250
  10. 10crates/tui/src/tools/spec.rsL1158–1217, 1219–1242, 150–218, 252–296
  11. 11crates/tui/src/core/engine/tool_execution.rsL230–287, 289–350, 353–406, 454–489, 177–213, 408–429
  12. 12crates/tui/src/tools/resource_admission.rsL1–27, 35–80, 176–219
  13. 13crates/tui/src/sandbox/policy.rsL17–87, 110–140, 142–218
  14. 14crates/tui/src/sandbox/mod.rsL3–20, 61–68
  15. 15crates/tui/src/sandbox/bwrap.rsL7–36, 87–126, 198–238
  16. 16crates/execpolicy/src/lib.rsL10–32, 73–128, 194–248, 437–520
  17. 17crates/tui/src/tools/execution_envelope.rsL1–59, 66–114, 287–342, 344–428, 431–490
  18. 18crates/tui/src/mcp.rsL1–37, 479–519, 522–612, 1307–1341, 57–90, 316–360, 641–695, 697–728
  19. 19crates/tui/src/mcp/streamable_http.rsL45–111, 119–187
  20. 20crates/tui/src/mcp/stdio.rsL135–160
  21. 21crates/tui/src/skills/mod.rsL131–224, 357–487
  22. 22crates/tui/src/skills/roots.rsL134–218
  23. 23crates/tui/src/plugins/registry.rsL123–169, 300–334, 353–393
  24. 24crates/tui/src/hooks/config.rsL656–697, 716–730
  25. 25crates/tui/src/session_manager.rsL26–40, 587–617, 825–856
  26. 26crates/tui/src/snapshot/mod.rsL1–34
  27. 27crates/tui/src/snapshot/repo.rsL1–13, 55–80, 314–431
  28. 28crates/tui/src/core/events.rsL19–105, 163–256
  29. 29crates/tui/src/cost_status.rsL867–914, 629–659, 661–756
  30. 30crates/tui/src/tools/subagent/mod.rsL1–11, 98–228, 230–245, 266–323
  31. 31crates/tui/src/tools/subagent/mailbox.rsL1–92, 150–185, 191–225
  32. 32crates/tui/src/tools/subagent/worktree.rsL1–47, 49–74, 77–123
  33. 33crates/tui/src/fleet/host.rsL1–5, 101–170
  34. 34crates/tui/src/fleet/control.rsL1–46, 92–120
  35. 35crates/tui/src/plugins/tests.rsL32–77
  36. 36crates/tui/src/skills/tests.rsL1–40
  37. 37crates/tui/src/tools/subagent/tests.rsL1–40