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
fix(agentic): make truncated Write recovery actionable end-to-end
80cd279 introduced truncation recovery and injected an "use Edit to
continue" hint into the assistant result. In practice the end-to-end loop
never closed because:
1. DeepResearchAgent did not include Edit in its allowed_tools, so the
model's Edit attempts (correctly following the hint) were rejected at
the allowed-list check and the model fell back to Read.
2. The fallback Reads were identical (same file, same range) every round,
producing a tight tool-call loop that nothing prevented.
3. The hint itself used "verify the result" wording that nudged the model
toward Read instead of Edit.
Closes all three gaps:
- Add Edit to DeepResearchAgent::default_tools so the hint is actionable.
Add a test assertion to lock in the invariant.
- Tighten the recovery hint: explicitly forbid re-reading the just-written
file, point the model at its own previous tool_use.input.content as the
source for old_string, and give it a clean escape hatch ("tell the user
the output was truncated") when no concrete continuation plan exists.
- Add a per-session tool-call loop detector on ToolPipeline. When the same
tool is called with deep-equal arguments TOOL_CALL_LOOP_THRESHOLD (=3)
consecutive times in a session, the (THRESHOLD+1)-th call is rejected
with an error naming the loop and instructing the model to switch tools
or finish. Counts *consecutive* matches at the tail of a bounded
10-entry ring buffer, so a legitimate intervening call resets the
streak. clear_session_tool_call_history is exposed so callers can drop
the buffer when a session ends.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
// Loop detection: refuse to execute the same tool call repeatedly with
695
+
// identical arguments. Triggered on the (THRESHOLD + 1)-th consecutive
696
+
// identical call within the per-session sliding window.
697
+
ifself.check_and_record_tool_call(
698
+
&task.context.session_id,
699
+
&tool_name,
700
+
&tool_args,
701
+
){
702
+
let error_msg = format!(
703
+
"Tool-call loop blocked: '{}' was already called {} times in a row in this session with identical arguments. Refusing to execute this {}th identical call. Issue a different tool call, or stop tool-calling and respond to the user. If you wrote a file recently and want to continue it, its full content is already visible in your earlier tool_use message — use Edit with `old_string` taken from the end of that content; do not Read the file again.",
// Security check: check if the tool is in the allowed list
628
732
// If allowed_tools is not empty, only allow execution of tools in the whitelist
629
733
if !task.context.allowed_tools.is_empty()
@@ -923,7 +1027,7 @@ impl ToolPipeline {
923
1027
if recovered_from_truncation {
924
1028
let original = tool_result.result_for_assistant.unwrap_or_default();
925
1029
let notice = format!(
926
-
"[WARNING: tool arguments were truncated by the model (likely hit max_tokens) and were auto-repaired before this {} call executed. The written content stops at the truncation point and may be incomplete; verify the result and, if needed, continue with a follow-up call that appends the remaining content (do not rewrite the whole file from scratch). Original tool result follows.]\n\n",
1030
+
"[Your previous {} call was truncated mid-stream by max_tokens and was auto-repaired before execution; the file was written with the partial content. The full truncated content — including the exact stopping point — is visible in the `input` of your previous tool_use message, so you do NOT need to read the file again. To finish it, issue ONE Edit call where `old_string` is the final unique substring of your truncated content and `new_string` is that same substring plus the continuation. If you do not have a concrete plan for the continuation, stop tool-calling and tell the user the output was truncated (suggest raising max_tokens). Do NOT call Read on this file and do NOT rewrite the whole file with Write.]\n\nOriginal tool result follows.\n\n",
0 commit comments