fix(cli): preserve editor context prompt prefix - #13379
Conversation
Code Review SummaryStatus: No Issues Found | Recommendation: Merge Files Reviewed (2 files)
Previous Review Summaries (2 snapshots, latest commit 955b2c5)Current summary above is authoritative. Previous snapshots are kept for context only. Previous review (commit 955b2c5)Status: No Issues Found | Recommendation: Merge Files Reviewed (5 files)
Previous review (commit c658cd4)Status: 3 Issues Found | Recommendation: Address before merge Overview
Issue Details (click to expand)SUGGESTION
Files Reviewed (6 files)
Reviewed by grok-4.6 · Input: 97.8K · Output: 5.3K · Cached: 205.3K Review guidance: REVIEW.md from base branch |
iscekic
left a comment
There was a problem hiding this comment.
Approved, bot remarks below, feel free to ignore
Bot remarks outside the diff lines:
packages/opencode/test/kilocode/editor-context-injection.test.ts:320— The test verifies the reconstructed workspace root by computing path.resolve(session.directory, ".."), which re-derives the value with the exact same formula as the implementation, so it cannot detect a defect in the reconstruction logic. The effect is that the new route.worktree computation is effectively untested for anything beyond the trivial single-".." case.
|
Map.getOrInsertComputed: explicitly informational, no action needed. |
<environment_details>is currently generated only for the newest user message in each request. On the next user turn, that synthetic part disappears from its previous position and is regenerated on the new message. Prefix caches therefore stop matching where the previous block began, even though the conversation before that point did not change.This fixes #13336 by reconstructing a deterministic block for every retained user message. Each block uses that message's stored editor context, creation time, and the session's existing route metadata. Historical messages remain byte-identical across later turns, while the newest message still receives its current editor context. The blocks remain ephemeral prompt scaffolding: they are not written as message parts, emitted as durable events, synced, or shared.
The timezone representation is not the cache problem.
2026-08-25T12:36:48+02:00and2026-08-25T10:36:48Zidentify the same instant, and either form is cache-safe when its bytes remain fixed. The regression came from regeneratingCurrent timeand moving the complete block between messages. This change records the user turn's creation instant as stable UTCMessage time, strips fractional seconds consistently, and appends a new timestamp only with the newly appended user turn.Regression history
f6da5ded83introduced per-turn<environment_details>user content. Commit376cffa3c4made the injection ephemeral to stop stale blocks from accumulating in stored messages. Together, those changes left only the newest user message decorated in each request, so the block moved at every turn boundary.<environment_details>moves to the newest user message on every turn, discarding the prompt cache #13336 reproduced the remaining problem on 7.4.23 with an automatic prefix-cache provider and showed that it occurs at every user-turn boundary.Confirmed impact
The capture in #13336 used five arithmetic turns and compared each outgoing prompt byte-for-byte with its predecessor. All four boundaries diverged at the previous location of
<environment_details>and discarded exactly 204 characters each. The same report measured a synthetic post-compaction turn retaining 16,172 of 24,386 characters, and a real 356,786-character session retaining only the first 70,819 characters, leaving 285,968 characters to recompute.An independent end-to-end CLI capture reproduced the same mechanism through an OpenAI-compatible local route. On four identical turn boundaries, the unmodified 7.4.23 path retained 99.3% of the previous toy prompt and discarded 167 characters at the old block position. A stable-history path retained 100.0% and discarded zero previous characters. The smaller absolute block size reflects the smaller captured environment, not a different failure mode.
The regression assertion in this branch operates on
MessageV2.toModelMessages, the serialized model-message path rather than a test-only representation. Against the base implementation, turn two is not prefixed by the complete serialized turn-one prompt. With this change, it is. Repeated loop iterations produce identical bytes, user-authored<environment_details>text is not mistaken for injected scaffolding, per-turn editor state remains attached to its original message, and route fallback is derived from stable session metadata rather than the current Agent Manager route.Durable behavior
Message time, avoiding a stale historical value presented as the current wall clock.directoryand relativepath; imported messages that already contain route fields keep those fields.The first request for an existing session after upgrading can acquire deterministic blocks on older retained user messages that previously had none. After that one-time prompt-shape change, subsequent requests preserve those bytes. Each retained user turn also keeps one compact environment block in model history; this intentional linear token cost replaces repeated invalidation of the much larger conversation suffix.
This does not change the content-part separator behavior tracked in #13110.