fix(vscode): stop mid-turn core errors from corrupting the active turn - #1807
Conversation
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9d9332b096
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| if (this.isBusy) { | ||
| throw new Error("A response is already being generated for this session."); | ||
| // A re-entrant turn request must never disturb the active turn: surface | ||
| // the rejection as a mid-turn warning and fail only this call. | ||
| this.emitError(new Error(ALREADY_GENERATING_MESSAGE), "runtime", { terminal: false }); |
There was a problem hiding this comment.
Treat exclusive-operation rejections as terminal
When isBusy is true solely because exclusiveActionActive is set, there is no active turn whose eventual stream_complete can clear this non-terminal warning. This occurs, for example, when another Webview sends a prompt while ForkKimiSession is materializing a fork through runExclusiveAfterCancelling; that Webview sets isStreaming before sending, ignores this warning for lifecycle purposes, and remains locked indefinitely. Emit terminal: false only when hasActiveWork guarantees a later terminal event, and reject exclusive-only conflicts as terminal.
Useful? React with 👍 / 👎.
commit: |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 08c3637b40
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| this.emitError( | ||
| new Error(ALREADY_GENERATING_MESSAGE), | ||
| "runtime", | ||
| { terminal: this.hasActiveWork ? false : undefined }, |
There was a problem hiding this comment.
Settle only the rejected caller's request
When two Webviews send nearly simultaneously, or a newly attached Webview sends while another view's turn is active, this marks the busy rejection as non-terminal and emitError broadcasts it to every subscriber. The rejected caller therefore remains in isStreaming; when the original turn later broadcasts stream_complete, that caller clears its own pendingInput and silently loses the message that was never submitted. If the active work is a host action that fails, no shared completion is emitted and the caller can remain locked indefinitely. The busy rejection needs to terminally settle only the initiating Webview without disturbing the existing turn.
Useful? React with 👍 / 👎.
MoonshotAI#1807) * fix(vscode): stop mid-turn core errors from corrupting the active turn * fix(vscode): reject prompts during exclusive operations with a terminal error
MoonshotAI#1807) * fix(vscode): stop mid-turn core errors from corrupting the active turn * fix(vscode): reject prompts during exclusive operations with a terminal error
Related Issue
None — reported from user feedback: during a running turn the extension surfaced
Internal error occurred: A response is already being generated for this session.and showed a Retry button while the turn was still alive.Problem
Two host-side flaws chained into that corruption:
errorevents mid-turn (e.g.records.write_failed, MCP tool-name collisions, compaction failures). The extension forwarded them to the Webview as plain turn-ending errors: the composer unlocked, Retry appeared, and queued messages auto-flushed after 50ms. The next send then collided with the still-active turn.SessionRuntime.runTurnAction, the catch path settled the original, still-running prompt as failed — orphaning the live engine turn and displaying the internalalready being generatederror.What changed
SessionRuntime.runTurnAction: a busy call now fails only itself, emitting a non-terminal warning, and never touchesthis.activePrompt. The catch also only settles the prompt this call created (previously, when the event pipeline had already settled it, a duplicate error was emitted).SessionRuntime.onSdkEvent: core errors forwarded while the active turn is started are markedterminal: falseon the wire (StreamError.terminalis documented inshared/types.ts).chat.store:errorevents withterminal: falseare shown as a transient toast and never touchisStreaming, the pending input, or the message queue — the composer only unlocks onstream_completeor genuine terminal errors. This mirrors the TUI, where busy state is driven solely by turn lifecycle events.beginHostActionreuses the shared busy message constant; no behavior change.Tests
test/kimi-runtime.test.ts: fake-sessionemit/prompt knobs added; new cases cover reentrant-prompt rejection, mid-turn error marking (terminal: false, turn survives to completion), and unchanged preflight-failure behavior.test/kimi-harness.integration.test.ts: real-stack reproduction — a concurrent prompt during a running turn returnsfailed, the original turn still finishes,isBusystays correct throughout.test/settings-store.test.ts: non-terminal error shows a toast, keeps the composer locked and the queue intact; the followingstream_completestill flushes the queue.Checklist
gen-changesetsskill, or this PR needs no changeset. → Needs none: the VS Code extension (apps/vscode, private package) is versioned manually outside the changesets flow; the fix rides the next extension release.gen-docsskill, or this PR needs no doc update. → No doc change: this restores the intended error-handling behavior instead of introducing new UX.