Skip to content

fix(cli): prevent compaction request replay loops - #13374

Merged
marius-kilocode merged 10 commits into
mainfrom
fix-compaction-message-reinjection
Aug 28, 2026
Merged

fix(cli): prevent compaction request replay loops#13374
marius-kilocode merged 10 commits into
mainfrom
fix-compaction-message-reinjection

Conversation

@marius-kilocode

@marius-kilocode marius-kilocode commented Aug 24, 2026

Copy link
Copy Markdown
Collaborator

What Problem This Solves

Automatic compaction can turn an already-handled user request into a new request. The completed-response path passes overflow: false, which the replay code treats as a pending preflight turn. This can repeat completed work and tool calls. The prior-usage preflight path has the opposite problem: it does not request replay, so a pending request can disappear behind the summary.

Fixes #13323 and #13317.

Why This Change Was Made

Keep the fix at the existing compaction boundaries rather than add a new recovery system:

  • Mark prior-usage preflight compaction using the existing pending-turn flag.
  • Replay only when the selected user turn has no assistant progress. Preserve completed tool/text progress in the summary instead of restarting that request.
  • Do not compact a terminally completed response. Continue compacting tool-bearing responses, including a provider length finish, preserving the correction from fix(cli): handle tool progress during compaction #13424.
  • Do not retain a second copy of a request that is already being replayed into the model context.
  • Keep replay text synthetic and preserve the original editor context and existing media-recovery behavior.

The final diff is four files: two runtime files, focused regressions in an existing Kilo-owned test file, and one patch changeset. It introduces no persisted schema fields, SDK changes, queue scheduler, UI changes, or import/fork machinery.

User Impact

Pending requests continue after automatic compaction without becoming a second visible user prompt. Completed answers and tool actions are not restarted merely because the context crossed its threshold. Preflight recovery preserves attachments; provider-overflow recovery retains the original attachment in storage while using the existing media placeholder on retry. The existing three-compaction safety cap remains in place.

Evidence

The focused regressions failed on the baseline: a pending request was absent from the outgoing context, a terminal response caused three model calls instead of one, and saved tool progress was excluded from the summary.

Validation of the simplified implementation:

  • Seven relevant compaction, payload-recovery, and prompt-queue suites passed through the repository test runner.
  • The focused prompt-compaction file passed all 11 tests.
  • CLI typecheck, upstream annotation checks, the Promise-facade guard, and whitespace checks passed. Lint reported no errors.
  • Seven live scenarios were run against the worktree source backend with an isolated deterministic local provider and a separate SQLite database: terminal response, pending request with image/editor context, tool-call finish, tool-bearing length finish, provider-overflow media recovery, repeated preflight compaction, and compaction exhaustion.
  • Direct SQLite inspection after backend shutdown found no empty user messages or foreign-key violations, and integrity_check returned ok.
  • The pending prompt reached the model once, each tool scenario stored one completed tool execution, and repeated provider errors stopped after three compactions with ContextOverflowError.

The live self-test used a deterministic local provider, not an external paid model. Internal synthetic replay rows remain stored by the existing replay mechanism; they are not additional visible user text. Broader crash-recovery and transcript redesign work is intentionally outside this PR.

@kilo-code-bot

This comment has been minimized.

Comment thread packages/opencode/src/session/compaction.ts
Comment thread packages/opencode/src/session/compaction.ts
Comment thread packages/opencode/src/session/compaction.ts
Comment thread packages/opencode/src/session/compaction.ts
@WebReflection

Copy link
Copy Markdown
Contributor

the only real concern I have is that slice().some(() => some()) in a for loop over the same list ... I am not sure that's the best we can do there to avoid choking on that part of the logic but if the list is always small enough maybe we don't care, in such case it would be nice to have a comment that states // not ideal but simple and constrained to a few entries or something.

@marius-kilocode
marius-kilocode merged commit c1608c8 into main Aug 28, 2026
44 of 47 checks passed
@marius-kilocode
marius-kilocode deleted the fix-compaction-message-reinjection branch August 28, 2026 08:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Automatic re-injection of last user message to the chat after automatic compaction

2 participants