Skip to content

fix(agent): pin the user's task and prior summaries across compaction#4048

Merged
esengine merged 1 commit into
main-v2from
fix/compaction-pin-user-facts
Jun 11, 2026
Merged

fix(agent): pin the user's task and prior summaries across compaction#4048
esengine merged 1 commit into
main-v2from
fix/compaction-pin-user-facts

Conversation

@esengine

Copy link
Copy Markdown
Owner

The bug

A fact the user states up front gets dropped from context after repeated compaction, and the agent then "forgets" it. Reproduced end-to-end against the real provider:

  • A deploy token stated in the first user turn was present in every request through the first fold, then vanished from every request after the second fold — and the agent could no longer recall it (wrote no answer, never restated it).
  • The smoking gun: the second fold's summary degraded to <compaction-summary>[assistant calls read_file] {"path":"data/f01.txt"}</compaction-summary> — the ## Goal was empty; the user's task and token were gone.

Root cause: planCompaction pinned only the system message (head = 1). The first user turn (the task brief + the user's stated facts/constraints) was always inside the summarized region, so its survival depended on a weak summarizer model re-extracting it correctly on every fold. One degenerate summary — which the model produces especially when re-summarizing an already-summarized region — drops the fact for good.

The fix

pinnedPrefixLen now keeps in the verbatim prefix:

  • the system prompt,
  • the first user turn when it's small enough to be a brief, and
  • any prior compaction summaries.

So a fold never summarizes the task away, and a later fold never re-folds an earlier summary into nothing. A large first turn (pasted content) stays foldable — gated by an absolute token ceiling and a window fraction — so pinning never starves the context.

Verification

Same scenario, same small window, identical work:

compaction result
before folds token dropped after 2nd fold → forgot, no answer
after folds task pinned verbatim at index 1 → token retained in every request, answered correctly
control (huge window) none retained, correct

Post-fold request structure after the fix: [system] [user: task verbatim, token present] [compaction-summary] [recent tail].

Tests: new TestPinnedPrefixLen (pins system+task+summaries; large/tiny-window turns stay foldable; a summary is not mistaken for the task turn); existing compaction/prune tests updated to the pinned-prefix behavior. go test ./internal/agent/..., control, and boot pass.

Compaction folded the first user turn — the task brief and the user's stated
facts and constraints — into a model-written summary, and a later fold then
re-summarized that summary, degrading it to nothing and dropping the user's
facts from context permanently. A real-provider run reproduced it: a token
stated up front vanished from every request after the second fold, and the
agent could no longer recall it.

planCompaction now pins a small first user turn and any prior summaries in the
verbatim prefix, so a fold never summarizes the task away and never re-folds an
earlier summary. A large first turn (pasted content) stays foldable, capped by
an absolute token ceiling and a window fraction, so pinning never starves the
context.
@esengine esengine requested a review from SivanCola as a code owner June 11, 2026 14:23
@github-actions github-actions Bot added v2 Go rewrite (1.x) — main-v2 branch, active development agent Core agent loop (internal/agent, internal/control) labels Jun 11, 2026
@esengine esengine merged commit 1c6c639 into main-v2 Jun 11, 2026
14 checks passed
@esengine esengine deleted the fix/compaction-pin-user-facts branch June 11, 2026 14:27
esengine added a commit that referenced this pull request Jun 11, 2026
…digest (#4052)

Builds on the first-turn pin (#4048): the deterministic floor now covers a fact
the user states at ANY point, not just the opening turn. Compaction keeps every
small user turn verbatim and folds only the assistant/tool work, so a mid-session
"always deploy to eu-west-3" survives regardless of how the summarizer behaves.

On top of that floor, the digest now leads with a structured "Standing facts &
constraints" section consolidating what the user stated into one tidy view —
redundant with the verbatim turns by design, so a weak summarizer dropping a fact
there loses nothing.

Co-authored-by: reasonix <reasonix@deepseek.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agent Core agent loop (internal/agent, internal/control) v2 Go rewrite (1.x) — main-v2 branch, active development

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant