You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Compaction can project retained historical messages after a newer compact summary so the model receives a useful summary-first context. The prompt loop was also using that projected array position to identify the active turn, which allowed a retained pre-compaction user message to be mistaken for the newest user message. Follow-up tool calls could then be persisted with that historical user as their parent.
The VS Code transcript queue made the storage issue visible. #9195 intentionally grouped assistant output by parentID so tool output for a running turn remains above newer queued prompts. That grouping was broader than the queued-prompt case, so assistant messages with stale cross-compaction parents were moved back above the compact summary. The stale parent pattern became reachable after #10507 imported retained-tail compaction ordering from anomalyco/opencode#25851.
#10800 made this easier to reproduce by forcing oversized sessions through manual compaction, but it was reverted by #10826. This PR is based on current main after that revert and deliberately does not restore the post-prune payload hard stop. The ordering bug still matters for manual compaction, automatic compaction, and sessions that already persisted stale cross-boundary parents.
Reproduction
Open a large historical session with retained-tail compaction enabled.
Compact the session, then continue with a follow-up that produces tool calls or additional assistant steps.
Observe the stored sequence: the compact request and summary are followed chronologically by resumed assistant output, but that output can retain a parentID from the pre-compaction user turn.
Reopen the session in VS Code or watch the live transcript. The resumed output renders above the compact summary because transcript grouping reattaches it to the historical turn.
The persisted shape is equivalent to:
old user prompt
old assistant output
compact request
compact summary
resumed tool output parentID=old user prompt
resumed assistant step parentID=old user prompt
Fix
The CLI now records chronological order before retained-tail projection rearranges model-facing messages. Prompt-loop state selection uses that chronology for the active user, latest assistant, and latest completed assistant while leaving queue projection and FIFO task processing unchanged. Repeated compactions also select the newest completed summary chronologically instead of treating an older retained summary as the newest trim boundary.
The VS Code transcript treats compact requests as barriers. Assistant output encountered after a compact request cannot be reassociated with a user turn before that barrier. The same mapping is used for rendering, active-turn detection, queued-message partitioning, virtualization, and lazy history hydration. This repairs existing persisted sessions in addition to preventing new stale parent links from being created.
The shared upstream-owned edits are intentionally narrow: one pre-projection chronology annotation and the prompt-loop hook. Kilo-specific ordering logic lives under src/kilocode/ to keep the fork diff small. The CLI chronology direction aligns with the upstream work in anomalyco/opencode#29035.
The fix is well-structured and surgical. The core changes are:
CLI (packages/opencode/): A new KiloSessionMessageOrder module captures chronological order via a WeakMapbefore the retained-tail projection in filterCompacted rearranges the model-facing message array. The prompt loop then uses latest() for state selection and compare() for ordering decisions, replacing brittle ascending-ID comparisons.
VS Code transcript (packages/kilo-vscode/): A target() helper and updated messageTurns / active / done walk backward from an assistant message's parentID, snapping to the nearest compact-user barrier between the message and its declared parent. This correctly reparents cross-boundary tool calls to the compaction turn rather than the historical pre-compaction user.
The WeakMap approach for chronology annotation is memory-safe — entries are GC'd with their key objects. The compact variable in messageTurns being overwritten on each new compaction user is intentional: only the last compaction acts as the barrier for assistant messages whose parent index precedes it, which matches the expected session shape.
The tests are real-implementation tests (no mocks) and cover the exact persisted shape described in the PR. The created() fixture helper mutates msg.info.time.created directly, which is fine in test context.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Compaction can project retained historical messages after a newer compact summary so the model receives a useful summary-first context. The prompt loop was also using that projected array position to identify the active turn, which allowed a retained pre-compaction user message to be mistaken for the newest user message. Follow-up tool calls could then be persisted with that historical user as their parent.
The VS Code transcript queue made the storage issue visible. #9195 intentionally grouped assistant output by
parentIDso tool output for a running turn remains above newer queued prompts. That grouping was broader than the queued-prompt case, so assistant messages with stale cross-compaction parents were moved back above the compact summary. The stale parent pattern became reachable after #10507 imported retained-tail compaction ordering from anomalyco/opencode#25851.#10800 made this easier to reproduce by forcing oversized sessions through manual compaction, but it was reverted by #10826. This PR is based on current
mainafter that revert and deliberately does not restore the post-prune payload hard stop. The ordering bug still matters for manual compaction, automatic compaction, and sessions that already persisted stale cross-boundary parents.Reproduction
parentIDfrom the pre-compaction user turn.The persisted shape is equivalent to:
Fix
The CLI now records chronological order before retained-tail projection rearranges model-facing messages. Prompt-loop state selection uses that chronology for the active user, latest assistant, and latest completed assistant while leaving queue projection and FIFO task processing unchanged. Repeated compactions also select the newest completed summary chronologically instead of treating an older retained summary as the newest trim boundary.
The VS Code transcript treats compact requests as barriers. Assistant output encountered after a compact request cannot be reassociated with a user turn before that barrier. The same mapping is used for rendering, active-turn detection, queued-message partitioning, virtualization, and lazy history hydration. This repairs existing persisted sessions in addition to preventing new stale parent links from being created.
The shared upstream-owned edits are intentionally narrow: one pre-projection chronology annotation and the prompt-loop hook. Kilo-specific ordering logic lives under
src/kilocode/to keep the fork diff small. The CLI chronology direction aligns with the upstream work in anomalyco/opencode#29035.