Skip to content

v0.8.58: Harvest subagent/tool error-message fixes from PR #2933 — child-model availability, tool-unavailability reasons, session-name conflicts #3020

@Hmbown

Description

@Hmbown

Why

Three verified community reports (#2653, #2656, #2657, all OPEN) describe the same failure class: when a tool or sub-agent is unavailable, the error the model sees names the wrong layer, so agents debug the harness instead of the task. Community PR #2933 (author cy2311) contains small working fixes for two of them but bundles them with an out-of-scope hippocampal-memory feature (new crates/memory/ crate, memorize/recall tools), making it unmergeable as-is. Extract ONLY the error-message slices into one focused PR with attribution. Clear failure attribution is agent-facing harness quality for every provider — weaker models especially burn turns misreading a 403 as "protected content" (#2653) or re-asking for mode switches that won't help (#2657).

Current state

All verified on branch v0.8.58-constitution:

  • modes: agents cannot easily tell why a tool is unavailable #2657 — generic suffixes bury the real cause. crates/tui/src/core/engine/dispatch.rs:102-132 format_tool_error: the NotAvailable arm (:118-127) passes the message through only when it contains "current tool catalog" or "did you mean:", otherwise appends ". Check mode, feature flags, or tool name."; the PermissionDenied arm (:128-130) ALWAYS appends ". Adjust approval mode or request permission.". But upstream messages are already self-explanatory: Plan-mode gate turn_loop.rs:1378-1380 ("'{tool_name}' is not available in Plan mode — switch to Agent, Goal, or YOLO mode..."), allowed-tools gate :1386-1388, caller gate :1394-1397, hook denial :1469-1471, user denial :1959-1960 ("Tool '{tool_name}' denied by user"). So the agent reads "switch to Agent mode... Adjust approval mode or request permission" — two conflicting fixes. The catalog/allow_shell half of modes: agents cannot easily tell why a tool is unavailable #2657 already landed: crates/tui/src/core/engine/tool_catalog.rs:502-542 (missing_tool_error_message + shell_tool_allow_shell_hint).
  • subagents: session name conflicts are hard for agents to diagnose #2656 — conflict error omits agent age. crates/tui/src/tools/subagent/mod.rs:1494-1505: the session-name conflict error reports agent_id + status but not how long ago the holder started, so the agent can't distinguish a live worker from a stale/failed earlier spawn. SubAgent.started_at: Instant exists at mod.rs:1087.
  • subagents: unavailable child model can look like a protected-content or authorization failure #2653 — model-not-found errors escape the annotator. crates/tui/src/tools/subagent/mod.rs:5611-5624 annotate_child_model_error (shipped v0.8.53, used at :3859,3875,3897 in run_subagent_task) appends the "child model X may be unavailable" hint only when crate::error_taxonomy::classify_error_message (crates/tui/src/error_taxonomy.rs:302-380) returns Authorization or State. Real provider rejections like DeepSeek "Model Not Exist" or OpenAI "The model m does not exist or you do not have access to it" match no taxonomy pattern, classify as Internal, and pass through bare — exactly the misattribution subagents: unavailable child model can look like a protected-content or authorization failure #2653 reports. PR feat: hippocampal memory system, improved tool/subagent error messages, YOLO mode cleanup #2933 does NOT contain a subagents: unavailable child model can look like a protected-content or authorization failure #2653 fix; this slice is a small fresh patch.
  • PR feat: hippocampal memory system, improved tool/subagent error messages, YOLO mode cleanup #2933 diff hunks to harvest (verified against gh pr diff 2933: exactly one hunk in each file): dispatch.rs format_tool_error pass-through (skip suffix when message already contains mode/allow_shell/feature flag/denied by user) and subagent/mod.rs:1496 elapsed-time in the conflict error (started {X}s ago / {X}m{Y}s ago). Note the keyword pass-through only relieves the Plan-mode, allow_shell, and "denied by user" messages; the allowed-tools (turn_loop.rs:1386-1388), caller-gate (:1394-1397), and hook-denial (:1469-1471) messages contain none of the keywords and intentionally keep the suffix — acceptable, their text already names the cause. Its other hunks (everything under crates/memory/, memorize.rs, recall.rs, tools/mod.rs + tools/registry.rs memorize/recall registration, tool_setup.rs, spec.rs memory_store, engine.rs, root + crates/tui/ Cargo.toml dependency additions, main.rs/ui.rs memory_db_path, yolo.md) are NOT to be taken.

Plan

  1. crates/tui/src/core/engine/dispatch.rs — apply the PR feat: hippocampal memory system, improved tool/subagent error messages, YOLO mode cleanup #2933 format_tool_error change: in NotAvailable, also pass through when the lowercased message contains "mode", "allow_shell", or "feature flag"; in PermissionDenied, pass through when it contains "mode", "allow_shell", or "denied by user", else keep the existing suffix. No other arms change.
  2. crates/tui/src/tools/subagent/mod.rs:1494-1505 — apply the PR feat: hippocampal memory system, improved tool/subagent error messages, YOLO mode cleanup #2933 conflict-message change: compute existing.started_at.elapsed() and render (status: {}, started {since}) with {N}s ago under 120s, else {N}m{M}s ago. Keep agent_id, status, and the agent_eval/agent_close recovery sentence intact.
  3. crates/tui/src/tools/subagent/mod.rs:5611-5624 — extend annotate_child_model_error: besides the Authorization | State categories, annotate when the lowercased raw error contains a model-not-found phrasing ("model not exist", "model_not_found", "does not exist", "no such model", "invalid model"). Do NOT touch error_taxonomy.rsclassify_error_message feeds the session failure classifier and retry logic; keep the blast radius inside the subagent annotator.
  4. Tests: extend tool_error_messages_include_actionable_hints (or add a sibling) in crates/tui/src/core/engine/tests.rs:477-489 — Plan-mode PermissionDenied message comes back verbatim (no "Adjust approval mode" suffix); a bare NotAvailable/PermissionDenied message still gets its suffix. Extend annotate_child_model_error_adds_actionable_hint in crates/tui/src/tools/subagent/tests.rs:2060-2078 with "Model Not Exist" and an OpenAI-style "does not exist or you do not have access" message. Add a conflict test in subagent/tests.rs (pure-fn or via SubAgentManager::new(PathBuf::from("."), N) like tests.rs:1496) asserting the error contains "started " and " ago".
  5. Attribution + bookkeeping: commit with trailer Co-authored-by: cy2311 <EMAIL> — recover the exact author line from the PR's commits (gh pr view 2933 --repo Hmbown/CodeWhale --json commits --jq '.commits[].authors[]'). PR body: Fixes #2653, Fixes #2656, Fixes #2657, credit cy2311/feat: hippocampal memory system, improved tool/subagent error messages, YOLO mode cleanup #2933 for the harvested hunks. If gh write access is available, comment on feat: hippocampal memory system, improved tool/subagent error messages, YOLO mode cleanup #2933: the error-message slices landed with credit, the hippocampal-memory feature remains and should be rebased as its own PR against design: hippocampal memory system for infinite-context and cross-session recall #2935; otherwise put that note in the PR body for the maintainer to relay.

Acceptance criteria

Verification

cargo test -p codewhale-tui tool_error_messages
cargo test -p codewhale-tui annotate_child_model_error
cargo test -p codewhale-tui tools::subagent
cargo fmt --all -- --check
cargo clippy --workspace --all-features --locked -- -D warnings

Manual (no API key): cargo run -p codewhale-tui, switch to Plan mode, ask "run ls with exec_shell" — the tool error must show the Plan-mode message without the approval suffix. With an API key: ask the agent to agent_open twice with name: "dup-test" — second call shows started Ns ago; agent_open with model: "deepseek-reason" — failure text names the model and suggests retrying agent_open with a different model.

Out of scope

Hints

  • The spec's key_files listed tools/spec.rs — wrong: the spec.rs hunk in feat: hippocampal memory system, improved tool/subagent error messages, YOLO mode cleanup #2933 is memory-only. The real modes: agents cannot easily tell why a tool is unavailable #2657 seam is core/engine/dispatch.rs (format_tool_error); turn_loop.rs:1366-1411 is where blocked-tool errors originate and needs no change.
  • Tool-error strings are intentionally NOT localized (no MessageId) — they are model-facing, English-only, matching all existing format_tool_error arms. Keep them self-correcting: name the cause AND the concrete fix (see shell_tool_allow_shell_hint, tool_catalog.rs:537-542, as the gold standard).
  • format_tool_error is pub(super); tests for it live in crates/tui/src/core/engine/tests.rs (mod declared inside core::engine), not a separate integration test.
  • annotate_child_model_error is a pure free function at the bottom of subagent/mod.rs — test it directly like subagent/tests.rs:2061 does; stub_runtime() exists there if you need a runtime.
  • Restored-from-disk agents get started_at = instant_from_duration(Duration::from_millis(persisted.duration_ms)) at restore (mod.rs:1313; helper at mod.rs:2144-2148 computes Instant::now() - duration), i.e. backdated by the previously recorded run duration — so "started Xs ago" is approximately right across restarts. Acceptable; don't build wall-clock persistence for this.
  • git fetch origin pull/2933/head:pr-2933 works read-only if you want to cherry-pick hunks locally instead of re-typing them; take only the dispatch.rs and subagent/mod.rs:1496 hunks.

Metadata

Metadata

Assignees

No one assigned

    Labels

    agent-readyBody is self-sufficient per docs/AGENT_RUNNER.md; a remote agent may claim itbranch-leafBranch-and-leaf orchestration semanticsbugSomething isn't workingenhancementNew feature or requestv0.8.58Targeting v0.8.58

    Projects

    Status
    Done

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions