Skip to content

fix(core): stop duplicating the boundary message in compaction head#33330

Open
Robin1987China wants to merge 1 commit into
anomalyco:devfrom
Robin1987China:compaction-boundary-fix
Open

fix(core): stop duplicating the boundary message in compaction head#33330
Robin1987China wants to merge 1 commit into
anomalyco:devfrom
Robin1987China:compaction-boundary-fix

Conversation

@Robin1987China

Copy link
Copy Markdown
Contributor

Issue for this PR

Closes #33329

Type of change

  • Bug fix

What does this PR do?

select in packages/core/src/session/compaction.ts partitions the conversation into head (summarized during auto-compaction) and recent (kept verbatim) at a token budget. When the boundary lands mid-message, that message is split into splitPrefix (head) and splitSuffix (recent).

The split branch set split = index + 1 and reused that single value for both the end of head's full-message slice and the start of recent's slice:

  • head = [...conversation.slice(0, split), splitPrefix]slice(0, index + 1) includes the full boundary message, then splitPrefix (a truncated copy of the same message) is appended → the boundary message is duplicated in head.
  • recent = [splitSuffix, ...conversation.slice(split)] → correct.

head is the exact text fed to the summarizer, so on every overflow-triggered compaction where the boundary lands mid-message (common for a large tool result), the boundary message was sent twice (full + truncated). This wastes input tokens and can push the summary prompt past the context limit, making compaction silently fail to recover from overflow.

The fix tracks two boundaries — headEnd (end of head's full-message slice) and recentStart (start of recent's slice). They differ only in the mid-message split case (headEnd = index, recentStart = index + 1); all other paths keep them equal, preserving existing behavior (including the remaining === 0 case where the boundary message goes entirely to head).

How did you verify your code works?

  • Exported select and added a regression test constructing entries whose budget boundary lands mid-message, asserting the boundary message appears in head only as the truncated prefix (not duplicated in full) and recent holds the suffix + following messages.
  • Added a fully-fitting sanity case (head empty, recent all).
  • bun test test/session-compaction.test.ts — 3 pass.
  • bun typecheck (packages/core) — clean.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

select() partitions the conversation into head (summarized) and recent
(kept) at a token budget. When the boundary lands mid-message, that message
is split into splitPrefix (head) and splitSuffix (recent). The split branch
set split = index + 1 and reused it for both head's slice end and recent's
slice start, so head = slice(0, index + 1) included the full boundary
message AND splitPrefix (a truncated copy) — duplicating it.

head is the exact text fed to the summarizer, so on every overflow-triggered
compaction where the boundary lands mid-message the boundary message was
sent twice (full + truncated), wasting tokens and risking pushing the
summary prompt past the context limit so compaction silently fails.

Track two boundaries: headEnd (end of head's full-message slice) and
recentStart (start of recent's slice). They differ only in the split case
(headEnd = index, recentStart = index + 1); all other paths keep them
equal, preserving existing behavior. Export select and add a regression
test for the duplication plus a fully-fitting sanity case.

Closes anomalyco#33329
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Auto-compaction duplicates the boundary message into the summarizer input

1 participant