diff --git a/agent/conversation_loop.py b/agent/conversation_loop.py index a27c084ee77d..0cc8c03ad923 100644 --- a/agent/conversation_loop.py +++ b/agent/conversation_loop.py @@ -5177,7 +5177,12 @@ def run_conversation( messages, tools=agent.tools or None ) - if agent.compression_enabled and _compressor.should_compress(_real_tokens): + if ( + agent.compression_enabled + and compression_attempts < 3 + and _compressor.should_compress(_real_tokens) + ): + compression_attempts += 1 agent._safe_print(" ⟳ compacting context…") messages, active_system_prompt = agent._compress_context( messages, system_message, diff --git a/tests/run_agent/test_post_tool_compression_attempt_cap.py b/tests/run_agent/test_post_tool_compression_attempt_cap.py new file mode 100644 index 000000000000..5d3a9b69e533 --- /dev/null +++ b/tests/run_agent/test_post_tool_compression_attempt_cap.py @@ -0,0 +1,30 @@ +"""Regression guard for the post-tool automatic-compression attempt cap. + +The pre-API and overflow compression paths share ``compression_attempts`` as a +three-pass per-turn backstop. The post-tool path must use the same counter; +otherwise a long tool loop can compact after every tool response for the +lifetime of the turn. +""" +from __future__ import annotations + +import inspect + + +def _post_tool_compression_block() -> str: + from agent import conversation_loop + + source = inspect.getsource(conversation_loop.run_conversation) + start = source.index("# Use real token counts from the API response") + end = source.index("# Save session log incrementally", start) + return source[start:end] + + +def test_post_tool_compression_uses_shared_per_turn_attempt_cap(): + block = _post_tool_compression_block() + + assert "compression_attempts < 3" in block, ( + "post-tool compression must stop after the shared three attempts per turn" + ) + assert "compression_attempts += 1" in block, ( + "post-tool compression must consume one shared per-turn attempt" + )