diff --git a/agent/context_compressor.py b/agent/context_compressor.py index a16ec913461d8..2884e057ba759 100644 --- a/agent/context_compressor.py +++ b/agent/context_compressor.py @@ -2988,7 +2988,13 @@ This compaction should PRIORITISE preserving all information related to the focu indicators and aren't what the reporter means by "the output of the last message you sent" (#29824). - Falling back to the most recent assistant message of ANY kind + Context-compaction handoff banners can also carry + ``role="assistant"``. They are internal continuity state, not a + user-visible reply, so ignore them both as text-bearing anchors and + as candidates for the fallback below. This mirrors the user-role + summary exclusion in ``_find_last_user_message_idx``. + + Falling back to the most recent non-summary assistant message of ANY kind only kicks in when no content-bearing assistant message exists in the compressible region — typically a fresh session that just started a multi-step tool sequence with no prior reply @@ -2998,7 +3004,9 @@ This compaction should PRIORITISE preserving all information related to the focu last_any = -1 for i in range(len(messages) - 1, head_end - 1, -1): msg = messages[i] - if msg.get("role") != "assistant": + if msg.get("role") != "assistant" or self._is_context_summary_content( + msg.get("content") + ): continue if last_any < 0: last_any = i diff --git a/tests/agent/test_compressor_assistant_tail_anchor.py b/tests/agent/test_compressor_assistant_tail_anchor.py index a8be6dc3fef1b..0f870a832828c 100644 --- a/tests/agent/test_compressor_assistant_tail_anchor.py +++ b/tests/agent/test_compressor_assistant_tail_anchor.py @@ -74,6 +74,35 @@ def compressor(): class TestFindLastAssistantMessageIdx: + def test_skips_assistant_role_context_summary_marker(self, compressor): + """A persisted assistant-role handoff is internal continuity state, + not the last reply the user saw. Tool-call-only assistant messages + after it must remain eligible for the fallback anchor.""" + from agent.context_compressor import SUMMARY_PREFIX + + messages = [ + {"role": "assistant", "content": f"{SUMMARY_PREFIX}\nold handoff"}, + {"role": "user", "content": "continue the task"}, + {"role": "assistant", "content": None, + "tool_calls": [{"function": {"name": "t", + "arguments": "{}"}}]}, + {"role": "tool", "content": "result", "tool_call_id": "c1"}, + ] + assert compressor._find_last_assistant_message_idx( + messages, head_end=0 + ) == 2 + + def test_all_assistant_messages_are_summaries_returns_minus_one(self, compressor): + from agent.context_compressor import SUMMARY_PREFIX + + messages = [ + {"role": "assistant", "content": f"{SUMMARY_PREFIX}\nold handoff"}, + {"role": "user", "content": "continue the task"}, + ] + assert compressor._find_last_assistant_message_idx( + messages, head_end=0 + ) == -1 + def test_finds_content_bearing_assistant(self, compressor): messages = [ {"role": "system", "content": "sys"},