From 344f3771cb4f9134de14ec76ded1254b9ca7c8b1 Mon Sep 17 00:00:00 2001 From: teknium1 Date: Tue, 17 Mar 2026 04:08:37 -0700 Subject: [PATCH] fix(compressor): summary role can create consecutive same-role messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The summary message role was determined only by the last head message, ignoring the first tail message. This could create consecutive user messages (rejected by Anthropic) when the tail started with 'user'. Now checks both neighbors. Priority: avoid colliding with the head (already committed). If the chosen role also collides with the tail, flip it — but only if flipping wouldn't re-collide with the head. --- agent/context_compressor.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/agent/context_compressor.py b/agent/context_compressor.py index aa05a8daa..22ce32f34 100644 --- a/agent/context_compressor.py +++ b/agent/context_compressor.py @@ -313,7 +313,19 @@ Write only the summary body. Do not include any preamble or prefix; the system w if summary: last_head_role = messages[compress_start - 1].get("role", "user") if compress_start > 0 else "user" - summary_role = "user" if last_head_role in ("assistant", "tool") else "assistant" + first_tail_role = messages[compress_end].get("role", "user") if compress_end < n_messages else "user" + # Pick a role that avoids consecutive same-role with both neighbors. + # Priority: avoid colliding with head (already committed), then tail. + if last_head_role in ("assistant", "tool"): + summary_role = "user" + else: + summary_role = "assistant" + # If the chosen role collides with the tail AND flipping wouldn't + # collide with the head, flip it. + if summary_role == first_tail_role: + flipped = "assistant" if summary_role == "user" else "user" + if flipped != last_head_role: + summary_role = flipped compressed.append({"role": summary_role, "content": summary}) else: if not self.quiet_mode: