diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index cedcbfae2..d93c7a165 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -1,7 +1,6 @@ # Changelog ## [Unreleased] - ### Added - Support for image file mentions—images are now automatically detected, resized, and attached when referenced with @filepath syntax @@ -14,6 +13,7 @@ ### Fixed +- Fixed model switches deferred during streaming to apply correctly when the stream completes, preventing model changes from being lost - Fixed plan mode toggles during streaming to inject plan-mode context immediately, preventing file edits while in plan mode - Fixed plan mode model switches during streaming to defer model changes until the current turn completes diff --git a/packages/coding-agent/src/modes/controllers/event-controller.ts b/packages/coding-agent/src/modes/controllers/event-controller.ts index ab981ff58..a3632befb 100644 --- a/packages/coding-agent/src/modes/controllers/event-controller.ts +++ b/packages/coding-agent/src/modes/controllers/event-controller.ts @@ -269,6 +269,7 @@ export class EventController { this.ctx.streamingComponent = undefined; this.ctx.streamingMessage = undefined; } + await this.ctx.flushPendingModelSwitch(); this.ctx.pendingTools.clear(); this.ctx.ui.requestRender(); this.sendCompletionNotification(); diff --git a/packages/coding-agent/src/modes/interactive-mode.ts b/packages/coding-agent/src/modes/interactive-mode.ts index 1882290a4..4f5b9aed0 100644 --- a/packages/coding-agent/src/modes/interactive-mode.ts +++ b/packages/coding-agent/src/modes/interactive-mode.ts @@ -136,6 +136,7 @@ export class InteractiveMode implements InteractiveModeContext { private readonly changelogMarkdown: string | undefined; private planModePreviousTools: string[] | undefined; private planModePreviousModel: Model | undefined; + private pendingModelSwitch: Model | undefined; private planModeHasEntered = false; public readonly lspServers: | Array<{ name: string; status: "ready" | "error"; fileTypes: string[]; error?: string }> @@ -533,6 +534,10 @@ export class InteractiveMode implements InteractiveModeContext { return; } this.planModePreviousModel = currentModel; + if (this.session.isStreaming) { + this.pendingModelSwitch = planModel; + return; + } try { await this.session.setModelTemporary(planModel); } catch (error) { @@ -542,6 +547,20 @@ export class InteractiveMode implements InteractiveModeContext { } } + /** Apply any deferred model switch after the current stream ends. */ + async flushPendingModelSwitch(): Promise { + const model = this.pendingModelSwitch; + if (!model) return; + this.pendingModelSwitch = undefined; + try { + await this.session.setModelTemporary(model); + } catch (error) { + this.showWarning( + `Failed to switch model after streaming: ${error instanceof Error ? error.message : String(error)}`, + ); + } + } + private async enterPlanMode(options?: { planFilePath?: string; workflow?: "parallel" | "iterative"; @@ -588,7 +607,11 @@ export class InteractiveMode implements InteractiveModeContext { await this.session.setActiveToolsByName(previousTools); } if (this.planModePreviousModel) { - await this.session.setModelTemporary(this.planModePreviousModel); + if (this.session.isStreaming) { + this.pendingModelSwitch = this.planModePreviousModel; + } else { + await this.session.setModelTemporary(this.planModePreviousModel); + } } this.session.setPlanModeState(undefined);