fix(agent): demote oversized tool results in protected compression tail (#61932) - #61952
fix(agent): demote oversized tool results in protected compression tail (#61932)#61952giggling-ginger wants to merge 2 commits into
Conversation
After multiple in-place compactions, short tool-heavy sessions can leave nearly every remaining message inside protect_last_n while those messages are huge completed file/tool outputs. The middle compress window then makes no material token progress and the turn dies with "Cannot compress further" (NousResearch#61932). Cap the prune message floor at the same bound as tail-cut, and under pressure demote bulky protected-tail tool bodies (keeping a short recent floor) so preflight can reclaim headroom without wiping the active ask.
teknium1
left a comment
There was a problem hiding this comment.
Thanks for isolating the remaining mismatch between token-budget tail cutting and the prune pre-pass. Current main still passes the uncapped protect_last_n into pruning at agent/context_compressor.py:2853-2855, while tail cutting already bounds it at agent/context_compressor.py:2698; this change addresses that live gap.
Suggested changes
- Add a focused test for the documented absolute-last-resort path at
agent/context_compressor.py:1422-1432: retain one newest tool result larger than the soft ceiling after earlier pressure demotions, and assert that it is summarized without losing itstool_call_id. The current aggregate assertions intests/agent/test_protected_tail_pressure_61932.py:133-141and:173-181establish recovery but do not prove that specific fallback.
Automated hermes-sweeper review.
| ) | ||
| after = estimate_messages_tokens_rough(pruned) | ||
|
|
||
| assert n >= 1, "pressure demotion should touch at least one tool body" |
There was a problem hiding this comment.
Suggestion: add a focused case for the absolute-last-resort branch. These aggregate savings assertions can pass after earlier tool bodies are demoted even if the newest oversized tool result is never summarized; assert that result is demoted and retains its tool_call_id.
There was a problem hiding this comment.
Addressed in e1f8af0. Added a focused regression test that first demotes an earlier protected tool result, then forces the absolute-last-resort branch to summarize the newest tool result because it alone exceeds the soft ceiling. The test also asserts that its tool_call_id is preserved and the active user message remains unchanged. Verified with the focused test file (5 passed) and the related compressor suites (174 passed).
Summary
Fixes the #61932 dead-end class: after multiple in-place compactions, a tool-heavy session can be short enough that almost every remaining message sits inside
protect_last_n, yet those messages are huge completedread_file/ tool outputs. The middle compress window then makes little or no token progress, preflight stalls, and the turn fails with:This is a core compressor failure, not Desktop/Windows-specific. Algorithmic reproduction on Linux (no Desktop, no provider) matches the reported shape (~170k tokens stuck after multipass compress).
Changes
_MAX_TAIL_MESSAGE_FLOOR(same bound as tail-cut) so defaultprotect_last_n=20cannot freeze a bulky recent tool run against pruning.tail_token_budget * 1.5), demote large completed tool/file bodies inside that region while keeping a short recent floor (and the active user ask) intact. Last-resort demotion covers a single tool body larger than the soft budget alone.Algorithmic repro (what this covers)
Post-compaction transcript: head + handoff summary + N unique large
read_fileresults + large user prompt, withprotect_last_n=20. Before: multipass compress drops a couple of rows while tokens stay ~170k (over context). After: first prune/compress pass reclaims the bulk of the protected-tail tool payload and recovers under the window.Test plan
scripts/run_tests.sh tests/agent/test_protected_tail_pressure_61932.py(new [Regression]: auto-compression still reaches Cannot compress further when protected recent tail is large (Desktop/openai-codex) #61932 regression)scripts/run_tests.sh tests/agent/test_context_compressor.py tests/agent/test_compressor_tool_call_budget.py tests/run_agent/test_infinite_compaction_loop.py tests/agent/test_compression_progress.py(177 passed)Closes #61932