fix(compaction): cap post-tool compression attempts per turn - #63870
Closed
dombejar wants to merge 1 commit into
Closed
fix(compaction): cap post-tool compression attempts per turn#63870dombejar wants to merge 1 commit into
dombejar wants to merge 1 commit into
Conversation
teknium1
reviewed
Jul 16, 2026
teknium1
left a comment
Contributor
There was a problem hiding this comment.
Thanks for identifying a real missing guard. Current main’s post-tool path at agent/conversation_loop.py:4956-4962 can compact without checking or incrementing the per-turn counter, while the equivalent pre-API path already uses the proposed compression_attempts < 3 / increment pattern at agent/conversation_loop.py:1043-1051.
Problems
tests/run_agent/test_post_tool_compression_attempt_cap.py:14-29readsrun_conversationsource withinspect.getsource()and asserts substrings.AGENTS.md:1370-1421explicitly bans source-shape tests; this would pass even if the counter were not reached on the runtime path.
Suggested changes
- Replace it with a behavioral conversation-loop test that simulates more than three qualifying post-tool compression opportunities and asserts
_compress_contextruns exactly three times in one turn.
Automated hermes-sweeper review.
|
|
||
|
|
||
| def _post_tool_compression_block() -> str: | ||
| from agent import conversation_loop |
Contributor
There was a problem hiding this comment.
Please replace this source inspection with a behavioral conversation-loop test. AGENTS.md:1370-1421 bans source-shape tests; substring assertions do not prove that post-tool compression is actually limited to three runtime invocations.
teknium1
added a commit
that referenced
this pull request
Jul 22, 2026
teknium1
added a commit
that referenced
this pull request
Jul 22, 2026
Follow-up to the salvaged #64010 (Kenmege) and #63870 (dombejar) commits, making one resolved compression.max_attempts cap govern ALL per-turn compression attempt sites: - conversation_loop: resolve max_compression_attempts ONCE at turn start (it was previously re-resolved inside the API-call loop) and route the pre-API pressure gate through it — that gate still hardcoded 'compression_attempts < 3' and logged 'attempt=%s/3'. - conversation_loop: the salvaged post-tool compaction gate now uses the resolved cap instead of a hardcoded 3. - turn_context: the preflight compaction loop was 'for _pass in range(3)'; it now sizes itself from the same resolved cap. - agent_init: harden the max_attempts parser — reject booleans (bool subclasses int; 'true' would coerce to 1), reject fractional floats instead of truncating them, keep accepting integral floats and numeric strings; anything else falls back to 3 (floor 1, ceiling 10 unchanged). - tests: replace #63870's inspect.getsource source-shape test with behavioral loop tests (post-tool compaction fires <= cap times per turn, shares its budget with the pre-API gate, resets between turns); add an e2e test proving a 4th preflight pass runs at config cap=6 while the unset default still stops at 3; extend the #64010 config tests with the bool/float parser semantics. Salvages #64010 by @Kenmege and #63870 by @dombejar.
13 tasks
Contributor
randlee
pushed a commit
to randlee/hermes-agent
that referenced
this pull request
Aug 11, 2026
randlee
pushed a commit
to randlee/hermes-agent
that referenced
this pull request
Aug 11, 2026
Follow-up to the salvaged NousResearch#64010 (Kenmege) and NousResearch#63870 (dombejar) commits, making one resolved compression.max_attempts cap govern ALL per-turn compression attempt sites: - conversation_loop: resolve max_compression_attempts ONCE at turn start (it was previously re-resolved inside the API-call loop) and route the pre-API pressure gate through it — that gate still hardcoded 'compression_attempts < 3' and logged 'attempt=%s/3'. - conversation_loop: the salvaged post-tool compaction gate now uses the resolved cap instead of a hardcoded 3. - turn_context: the preflight compaction loop was 'for _pass in range(3)'; it now sizes itself from the same resolved cap. - agent_init: harden the max_attempts parser — reject booleans (bool subclasses int; 'true' would coerce to 1), reject fractional floats instead of truncating them, keep accepting integral floats and numeric strings; anything else falls back to 3 (floor 1, ceiling 10 unchanged). - tests: replace NousResearch#63870's inspect.getsource source-shape test with behavioral loop tests (post-tool compaction fires <= cap times per turn, shares its budget with the pre-API gate, resets between turns); add an e2e test proving a 4th preflight pass runs at config cap=6 while the unset default still stops at 3; extend the NousResearch#64010 config tests with the bool/float parser semantics. Salvages NousResearch#64010 by @Kenmege and NousResearch#63870 by @dombejar.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The preflight and overflow compression paths share a
compression_attempts < 3per-turn cap, but the post-tool compression path inrun_conversation(agent/conversation_loop.py) neither checked nor incremented the counter. During a long tool turn that rapidly regenerates context (large tool responses / file reads adding 60-90K tokens between passes), post-tool compression can therefore re-fire indefinitely.Observed in production (v0.18.2, gpt-5.6-sol via openai-codex, compression threshold at 50%): one session compacted 4 times in ~13 minutes. Each compaction genuinely reduced estimated message tokens 30-43%, but the same long tool turn regrew past threshold and re-triggered — with each compaction itself a full main-model summarization call (
auxiliary.compression.provider: main), the session spent most of its wall clock compacting.Fix
Apply the same shared guard the other two paths use: gate post-tool compression on
compression_attempts < 3and increment the shared counter before compacting.Tests
tests/test_post_tool_compression_attempt_cap.py— failed before the change, passes after.https://claude.ai/code/session_015StmTvr85VriETdW9Gy9D5