You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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-132format_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.
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.rsformat_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.rsmemory_store, engine.rs, root + crates/tui/Cargo.toml dependency additions, main.rs/ui.rsmemory_db_path, yolo.md) are NOT to be taken.
Plan
crates/tui/src/core/engine/dispatch.rs — apply the PR feat: hippocampal memory system, improved tool/subagent error messages, YOLO mode cleanup #2933format_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.
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.
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.rs — classify_error_message feeds the session failure classifier and retry logic; keep the blast radius inside the subagent annotator.
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".
In Plan mode, a blocked exec_shell tool error reads exactly the Plan-mode message ("switch to Agent, Goal, or YOLO mode...") with no "Adjust approval mode or request permission." suffix appended.
A PermissionDenied/NotAvailable error whose message does NOT name a cause still gets the existing generic suffix (no regression for opaque errors).
Opening a sub-agent with an in-use session name yields an error containing agent_id, status, AND started <elapsed> ago.
A child-agent failure whose raw error is Model Not Exist (or "does not exist or you do not have access") is annotated with the child model id and the agent_open retry hint; connection reset by peer still passes through unannotated.
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.
Changing classify_error_message patterns in crates/tui/src/error_taxonomy.rs (shared with session failure classification/retry — separate issue if wanted).
#2655-style "did you mean" suggestions (already shipped in tool_catalog.rs/rlm.rs); subagent model routing/validation rework (covered by the subagent-model-routing v0.8.58 issue — no file overlap except distinct regions of subagent/mod.rs).
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.
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 (newcrates/memory/crate,memorize/recalltools), 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:crates/tui/src/core/engine/dispatch.rs:102-132format_tool_error: theNotAvailablearm (: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."; thePermissionDeniedarm (:128-130) ALWAYS appends". Adjust approval mode or request permission.". But upstream messages are already self-explanatory: Plan-mode gateturn_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_shellhalf 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).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: Instantexists atmod.rs:1087.crates/tui/src/tools/subagent/mod.rs:5611-5624annotate_child_model_error(shipped v0.8.53, used at:3859,3875,3897inrun_subagent_task) appends the "child modelXmay be unavailable" hint only whencrate::error_taxonomy::classify_error_message(crates/tui/src/error_taxonomy.rs:302-380) returnsAuthorizationorState. Real provider rejections like DeepSeek"Model Not Exist"or OpenAI"The modelmdoes not exist or you do not have access to it"match no taxonomy pattern, classify asInternal, 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.gh pr diff 2933: exactly one hunk in each file):dispatch.rsformat_tool_errorpass-through (skip suffix when message already containsmode/allow_shell/feature flag/denied by user) andsubagent/mod.rs:1496elapsed-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 undercrates/memory/,memorize.rs,recall.rs,tools/mod.rs+tools/registry.rsmemorize/recall registration,tool_setup.rs,spec.rsmemory_store,engine.rs, root +crates/tui/Cargo.tomldependency additions,main.rs/ui.rsmemory_db_path,yolo.md) are NOT to be taken.Plan
crates/tui/src/core/engine/dispatch.rs— apply the PR feat: hippocampal memory system, improved tool/subagent error messages, YOLO mode cleanup #2933format_tool_errorchange: inNotAvailable, also pass through when the lowercased message contains"mode","allow_shell", or"feature flag"; inPermissionDenied, pass through when it contains"mode","allow_shell", or"denied by user", else keep the existing suffix. No other arms change.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: computeexisting.started_at.elapsed()and render(status: {}, started {since})with{N}s agounder 120s, else{N}m{M}s ago. Keep agent_id, status, and the agent_eval/agent_close recovery sentence intact.crates/tui/src/tools/subagent/mod.rs:5611-5624— extendannotate_child_model_error: besides theAuthorization | Statecategories, 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 toucherror_taxonomy.rs—classify_error_messagefeeds the session failure classifier and retry logic; keep the blast radius inside the subagent annotator.tool_error_messages_include_actionable_hints(or add a sibling) incrates/tui/src/core/engine/tests.rs:477-489— Plan-modePermissionDeniedmessage comes back verbatim (no "Adjust approval mode" suffix); a bareNotAvailable/PermissionDeniedmessage still gets its suffix. Extendannotate_child_model_error_adds_actionable_hintincrates/tui/src/tools/subagent/tests.rs:2060-2078with"Model Not Exist"and an OpenAI-style "does not exist or you do not have access" message. Add a conflict test insubagent/tests.rs(pure-fn or viaSubAgentManager::new(PathBuf::from("."), N)liketests.rs:1496) asserting the error contains"started "and" ago".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. Ifghwrite 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
exec_shelltool error reads exactly the Plan-mode message ("switch to Agent, Goal, or YOLO mode...") with no "Adjust approval mode or request permission." suffix appended.PermissionDenied/NotAvailableerror whose message does NOT name a cause still gets the existing generic suffix (no regression for opaque errors).started <elapsed> ago.Model Not Exist(or "does not exist or you do not have access") is annotated with the child model id and theagent_openretry hint;connection reset by peerstill passes through unannotated.crates/memory/, nomemorize/recall, nomemory_store/memory_db_path, noyolo.mdchange).Co-authored-by: cy2311 <...>; PR body closes subagents: unavailable child model can look like a protected-content or authorization failure #2653/subagents: session name conflicts are hard for agents to diagnose #2656/modes: agents cannot easily tell why a tool is unavailable #2657 and references feat: hippocampal memory system, improved tool/subagent error messages, YOLO mode cleanup #2933.Verification
Manual (no API key):
cargo run -p codewhale-tui, switch to Plan mode, ask "runlswith exec_shell" — the tool error must show the Plan-mode message without the approval suffix. With an API key: ask the agent toagent_opentwice withname: "dup-test"— second call showsstarted Ns ago;agent_openwithmodel: "deepseek-reason"— failure text names the model and suggests retryingagent_openwith a differentmodel.Out of scope
memorize/recalltools, engine wiring) — leave on the contributor's PR.yolo.mdhunk is not needed).classify_error_messagepatterns incrates/tui/src/error_taxonomy.rs(shared with session failure classification/retry — separate issue if wanted).tool_catalog.rs/rlm.rs); subagent model routing/validation rework (covered by thesubagent-model-routingv0.8.58 issue — no file overlap except distinct regions ofsubagent/mod.rs).Hints
key_fileslistedtools/spec.rs— wrong: thespec.rshunk 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 iscore/engine/dispatch.rs(format_tool_error);turn_loop.rs:1366-1411is where blocked-tool errors originate and needs no change.MessageId) — they are model-facing, English-only, matching all existingformat_tool_errorarms. Keep them self-correcting: name the cause AND the concrete fix (seeshell_tool_allow_shell_hint,tool_catalog.rs:537-542, as the gold standard).format_tool_errorispub(super); tests for it live incrates/tui/src/core/engine/tests.rs(mod declared insidecore::engine), not a separate integration test.annotate_child_model_erroris a pure free function at the bottom ofsubagent/mod.rs— test it directly likesubagent/tests.rs:2061does;stub_runtime()exists there if you need a runtime.started_at = instant_from_duration(Duration::from_millis(persisted.duration_ms))at restore (mod.rs:1313; helper atmod.rs:2144-2148computesInstant::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-2933works read-only if you want to cherry-pick hunks locally instead of re-typing them; take only thedispatch.rsandsubagent/mod.rs:1496hunks.