fix(vscode): queue or restore prompts bounced by a busy session - #2818
fix(vscode): queue or restore prompts bounced by a busy session#2818matthiasgoergens wants to merge 2 commits into
Conversation
…l error The busy rejections in SessionRuntime threw a plain Error, which emitError maps to code "internal" and the webview renders as "Internal error occurred." — alarming and wrong for what is simply "a response is already being generated". Throw KimiError with the existing turn.agent_busy code instead so the UI shows the mapped "A message is being sent. Please wait." with the real detail attached. Fixes MoonshotAI#2796
🦋 Changeset detectedLatest commit: dd09e7d The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1bdcf39ff8
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (event.type === "TurnBegin" || event.type === "StepBegin" || event.type === "ContentPart") { | ||
| clearHandshakeTimer(); | ||
| set({ handshakeReceived: true }); | ||
| set({ handshakeReceived: true, ...(event.type === "TurnBegin" ? { awaitingTurnBegin: false } : {}) }); |
There was a problem hiding this comment.
Correlate TurnBegin before clearing the pending send
When the same session is open in two Webviews and both send nearly simultaneously, the winning request's TurnBegin is broadcast to every subscriber by SessionRuntime.emitStreamEvent. This unconditionally clears awaitingTurnBegin in the losing view even though its own request never began; when that request resolves with { bounced: true }, doSend returns early, and the winner's later stream_complete clears pendingInput, silently discarding the losing prompt instead of queueing it. Track the specific pending request/turn rather than treating every TurnBegin as belonging to this send.
Useful? React with 👍 / 👎.
A prompt sent while the session was busy with a turn the webview lost track of (isStreaming resets to false on loadSession even when the session has a live turn — e.g. after a window reload) was rejected with a busy warning, and the text sat parked in pendingInput — invisible, not queued — until the other turn's terminal event finally restored it. The runtime now marks the busy rejection with reason "busy" and the bridge result carries bounced: true, so the webview can tell "this send never started a turn" apart from a normal turn end. On a bounce the message moves into the send queue (flushed by the live turn's terminal event) and the composer keeps streaming state; any other send that fails before its TurnBegin rolls the text straight back into the composer. A bounce during an exclusive operation (terminal rejection, no later terminal event) is left to the error path as before.
1bdcf39 to
dd09e7d
Compare
Related Issue
Resolve #2817
(Stacked on #2798 — shares the
turn.agent_busybusy-rejection path.)Problem
See linked issue.
loadSessionresetsisStreaming: falseeven when the session has a live turn, so after a window reload/reattach the composer takes the send path for a session that is actually busy. The runtime rejects the send (turn.agent_busy, non-terminal), and the text is parked inpendingInputwith noTurnBegin— invisible limbo until the other turn's terminal event finally restores it. The bridge result was ignored, so the webview couldn't distinguish "bounced, never started" from "turn ended".What changed
SessionRuntime's busy rejection now returnsPromptResult.reason: "busy", andstreamChatmaps it to{ done, bounced: true }on the bridge.awaitingTurnBegin(set on send, cleared by the send's ownTurnBegin) and now handles the bridge result:stream_completelanded first (the reply arrived after the terminal event cleared the parked input) → the session is free, so the prompt is simply sent again;reasonfield.Checklist
gen-changesetsskill, or this PR needs no changeset. (changeset included:kimi-codepatch)gen-docsskill, or this PR needs no doc update.