feat(session): warm resume for multi-message prompt interjections#57
Merged
Conversation
justin-carper
added a commit
that referenced
this pull request
Jul 6, 2026
Address PR #57 review findings: - language-model: on a count/tail mismatch (classifier invariant broken), never degrade to sending only the latest message — that silently drops messages 1..N-1 while the record keeps the full fingerprint. Clear the resume id pre-acquire so a fresh agent gets the full transcript instead. - agent-events: sendAgentTurnSilently now treats any terminal status other than "finished" as non-delivery and throws, except cancellation caused by our own abort signal (caller drops the record on abort). - document silent-turn trade-offs: tool invisibility, serial latency, and why concatenating queued messages was rejected (message fidelity).
Two-or-more user messages queued while the agent is busy previously classified as divergence -> fresh agent + full transcript replay, discarding the pooled Cursor agent's warm context. Now a strict-prefix transcript whose new messages form a contiguous trailing run of user turns classifies as continuation-multi: the pooled agent is resumed and each new message is sent sequentially - leading ones silently (no deltas), only the final run streams back. Mirrors opencode's coalesced-loop model where interjected messages fold into one visible turn. Safety: - Interleaved assistant turns in the tail (abort-then-interject) stay divergence: a tail-only replay would silently drop earlier messages. - Pool record is written optimistically before delivery, so an error or abort mid-sequence drops the session record; the next turn replays fresh instead of resuming on top of undelivered messages. - Edit/revert (non-prefix) unchanged: divergence -> full replay. Known trade-off: silent turns' token usage is not reported (no onDelta observer), so multi-message turns slightly undercount usage.
Adds the one M4 gap the reviewer flagged that wasn't yet covered at the agentRun level: when the abort signal fires between sequential silent sends, the loop must stop before the next send, never stream the final turn, drop the session record, and still finish the stream cleanly with no error part.
Address PR #57 review findings: - language-model: on a count/tail mismatch (classifier invariant broken), never degrade to sending only the latest message — that silently drops messages 1..N-1 while the record keeps the full fingerprint. Clear the resume id pre-acquire so a fresh agent gets the full transcript instead. - agent-events: sendAgentTurnSilently now treats any terminal status other than "finished" as non-delivery and throws, except cancellation caused by our own abort signal (caller drops the record on abort). - document silent-turn trade-offs: tool invisibility, serial latency, and why concatenating queued messages was rejected (message fidelity).
76c12bb to
84d916d
Compare
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Problem
When a user sends two or more messages while the Cursor agent is still processing (opencode queues them and replays the grown transcript next turn), the provider classified the turn as
divergence→ fresh agent + full transcript replay. Warm agent context was discarded; every multi-message interjection paid a cold-start.Change
New
continuation-multiturn kind: when the prior user-message sequence is a strict prefix and the new messages form a contiguous trailing run of user turns, resume the pooled agent and replay just the new messages as sequential turns — leading ones sent silently (no deltas), only the final run streams back to opencode. This mirrors opencode's own coalesced-loop model, where interjected messages fold into one visible assistant turn.transcript-fingerprint.tscontinuation-multikind +newUserCount+ contiguous-tail guardmessage-map.tstrailingUserMessages(prompt, count)+ shareduserTurnToCursorMessageagent-events.tssendAgentTurnSilently+ sharedsendWithBusyRetry(busylocal.forceretry)language-model.tssession-pool.tsdropSessionRecordfor mid-sequence failure recoverySafety properties
divergence→ full replay.divergence.AgentLikecontract (in-process SDK + sidecar).Known trade-off
Silent turns' token usage is not reported (no
onDeltaobserver on runs 1..N-1), so multi-message turns slightly undercount usage. Documented in code.Review
Code-reviewed (agent): 1 critical (non-contiguous tail could drop messages) + 1 important (stale record after mid-sequence failure) — both fixed with dedicated regression tests.
Verification
tsc --noEmitcleandoStreamend-to-end with a mocked Cursor runtime)tsupbuild success