From fe6d824074b67ab7571d7fb07dac18830db3a9b4 Mon Sep 17 00:00:00 2001 From: MorAlekss Date: Mon, 22 Jun 2026 12:17:40 -0700 Subject: [PATCH 1/2] fix(compression): skip session rotation when compress returns messages unchanged --- agent/conversation_compression.py | 10 +++++ .../test_infinite_compaction_loop.py | 40 +++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/agent/conversation_compression.py b/agent/conversation_compression.py index 94fff28389349..9dff01dcc8c88 100644 --- a/agent/conversation_compression.py +++ b/agent/conversation_compression.py @@ -482,6 +482,16 @@ def _release_lock() -> None: _release_lock() # compression aborted — no rotation will happen return messages, _existing_sp + # If compress() returned messages unchanged without setting + # _last_compress_aborted (e.g. compress_start >= compress_end), + # there is nothing to rotate — skip session rotation entirely. + if len(compressed) >= _pre_msg_count: + _existing_sp = getattr(agent, "_cached_system_prompt", None) + if not _existing_sp: + _existing_sp = agent._build_system_prompt(system_message) + _release_lock() # no-op compression — no rotation will happen + return messages, _existing_sp + summary_error = getattr(agent.context_compressor, "_last_summary_error", None) if summary_error: if getattr(agent, "_last_compression_summary_warning", None) != summary_error: diff --git a/tests/run_agent/test_infinite_compaction_loop.py b/tests/run_agent/test_infinite_compaction_loop.py index 930df3381cc1b..c40679f030fe6 100644 --- a/tests/run_agent/test_infinite_compaction_loop.py +++ b/tests/run_agent/test_infinite_compaction_loop.py @@ -248,3 +248,43 @@ def test_below_threshold_allows(self): comp = _make_compressor(config_context_length=96000) comp.last_prompt_tokens = 10_000 assert not comp.should_compress(10_000) + + +class TestNoOpCompressionSkipsRotation: + """When compress() returns messages unchanged (no-op), session rotation + must be skipped — no end_session, no new session_id.""" + + def test_no_op_compression_does_not_rotate_session(self): + """compress_start >= compress_end path must not trigger session rotation.""" + from unittest.mock import MagicMock + from agent.conversation_compression import compress_context + + messages = _build_session(10, words_per_turn=10) + + agent = MagicMock() + agent.session_id = "test-no-op-session" + agent.compression_in_place = False + agent.compression_enabled = True + agent._compression_feasibility_checked = True + agent._cached_system_prompt = "system prompt" + agent._memory_manager = None + agent._todo_store = MagicMock() + agent._todo_store.format_for_injection.return_value = "" + + session_db = MagicMock() + session_db.try_acquire_compression_lock.return_value = True + agent._session_db = session_db + + compressor = MagicMock() + compressor._last_compress_aborted = False + compressor._last_summary_error = None + compressor._last_aux_model_failure_model = None + compressor.compress.return_value = list(messages) + agent.context_compressor = compressor + + result_messages, _ = compress_context(agent, messages, "system", approx_tokens=1000) + + session_db.end_session.assert_not_called() + assert agent.session_id == "test-no-op-session", ( + "Session ID must not change when compression is a no-op" + ) From 0815845e1fd19d8a979b21b6d365484875350d21 Mon Sep 17 00:00:00 2001 From: MorAlekss Date: Mon, 22 Jun 2026 12:38:13 -0700 Subject: [PATCH 2/2] fix(compression): narrow no-op guard to savings_pct == 0 to avoid blocking legitimate compression --- agent/conversation_compression.py | 6 +++++- tests/run_agent/test_infinite_compaction_loop.py | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/agent/conversation_compression.py b/agent/conversation_compression.py index 9dff01dcc8c88..2064100a6f938 100644 --- a/agent/conversation_compression.py +++ b/agent/conversation_compression.py @@ -485,7 +485,11 @@ def _release_lock() -> None: # If compress() returned messages unchanged without setting # _last_compress_aborted (e.g. compress_start >= compress_end), # there is nothing to rotate — skip session rotation entirely. - if len(compressed) >= _pre_msg_count: + # Guard on both length (no reduction) AND savings_pct == 0 so that + # a legitimate compression that happens to produce the same message + # count (e.g. summary replaces many messages 1:1) is not blocked. + _savings_pct = getattr(agent.context_compressor, "_last_compression_savings_pct", 100.0) + if len(compressed) >= _pre_msg_count and _savings_pct == 0.0: _existing_sp = getattr(agent, "_cached_system_prompt", None) if not _existing_sp: _existing_sp = agent._build_system_prompt(system_message) diff --git a/tests/run_agent/test_infinite_compaction_loop.py b/tests/run_agent/test_infinite_compaction_loop.py index c40679f030fe6..f46766214d644 100644 --- a/tests/run_agent/test_infinite_compaction_loop.py +++ b/tests/run_agent/test_infinite_compaction_loop.py @@ -279,6 +279,7 @@ def test_no_op_compression_does_not_rotate_session(self): compressor._last_compress_aborted = False compressor._last_summary_error = None compressor._last_aux_model_failure_model = None + compressor._last_compression_savings_pct = 0.0 compressor.compress.return_value = list(messages) agent.context_compressor = compressor