fix(tools): offer retrieve_tool_result whenever a result names a saved copy

Since #6508 (v0.10.1), one budget decides how much of a tool result the model
sees inline: 3% of the route's context window. A larger result is cut to a
head and tail around a footer that names the saved artifact and says to call
retrieve_tool_result with its art_ ref. Two paths left that instruction
pointing at a tool the model did not have:
- activate_result_dependencies admitted retrieve_tool_result only for results
  marked evidence_available. A result cut by the inline budget gets an
  artifact ref from spillover or preserve_full_output_for_model_context, but
  never that flag.
- `exec --allowed-tools` dropped retrieve_tool_result from the catalog.

On a 16,384-token route the budget is about 1,966 characters, so nearly any
real tool output is cut. The CodewhaleOS session reproduced this on 0.10.2
(93061349b3): a custom 16K openai-compatible route ran `exec --auto
--allowed-tools <one MCP tool>` against a tool returning about 4 KB. The
model received about 1,928 characters plus the retrieval hint, answered with
whitespace, and exec failed as "model tool response is malformed, truncated
or violates the declared tools".

Now the dependency is admitted whenever the result's metadata carries an art_
ref (context::offers_retrieval, on the same recovery_ref the view uses for
its footer). The allow-list always passes retrieve_tool_result: it is
read-only and resolves only the session's own artifacts, and an explicit
deny still removes it.

Not changed, follow-ups: the 3% budget has no floor for small windows, and an
empty or whitespace-only reply is still reported as a malformed tool
response.

Verification: k90 nextest (crates/tui lib) includes core::engine:: and tools::truncate:: filters: 4838 tests run, 4835 passed; the 3 failures were a coordination-lock collision and a frozen-painter parity test, neither in this change. cargo check -p codewhale-tui --lib --tests: exit 0.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
CodeWhale BotandClaude Opus 5.5 committed 2026-10-21 20:35:44 -07:00
1 parent 59bbd679f2
commit 9d3ef3ce40
2 files changed
+7 -2

No files matched your search

+4
View File
@@ -54,6 +54,10 @@ struct RecoveryRef<'a> {
artifact_id: Option<&'a str>,
}
pub(super) fn offers_retrieval(metadata: Option<&Value>) -> bool {
recovery_ref(metadata).is_some_and(|recovery| recovery.artifact_id.is_some())
}
fn recovery_ref(metadata: Option<&Value>) -> Option<RecoveryRef<'_>> {
let obj = metadata?.as_object()?;
let text = |key: &str| {
+3 -2
View File
@@ -461,7 +461,8 @@ pub(crate) fn activate_result_dependencies(
.as_ref()
.and_then(|metadata| metadata.get("evidence_available"))
.and_then(Value::as_bool)
== Some(true);
== Some(true)
|| super::context::offers_retrieval(result.metadata.as_ref());
if !needs_retrieval {
return false;
}
@@ -626,7 +627,7 @@ pub(super) fn tool_allowed(allowed_tools: Option<&[String]>, tool_name: &str) ->
let Some(allowed_tools) = allowed_tools else {
return true;
};
tool_matches_any_rule(allowed_tools, tool_name)
tool_name == TOOL_RESULT_RETRIEVAL_NAME || tool_matches_any_rule(allowed_tools, tool_name)
}
pub(crate) fn tool_denied(disallowed_tools: Option<&[String]>, tool_name: &str) -> bool {