Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/core/tools/SwitchModeTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,9 @@ export class SwitchModeTool extends BaseTool<"switch_mode"> {
return
}

// Switch the mode using shared handler
await task.providerRef.deref()?.handleModeSwitch(mode_slug)
// Switch the mode using shared handler, preserving the current API config
// so the user's selected model doesn't change during AI-initiated switches.
await task.providerRef.deref()?.handleModeSwitch(mode_slug, { preserveApiConfig: true })

pushToolResult(
`Successfully switched from ${getModeBySlug(currentMode)?.name ?? currentMode} mode to ${
Expand Down
8 changes: 5 additions & 3 deletions src/core/webview/ClineProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1391,7 +1391,7 @@ export class ClineProvider
* Handle switching to a new mode, including updating the associated API configuration
* @param newMode The mode to switch to
*/
public async handleModeSwitch(newMode: Mode) {
public async handleModeSwitch(newMode: Mode, options?: { preserveApiConfig?: boolean }) {
const task = this.getCurrentTask()

if (task) {
Expand Down Expand Up @@ -1426,9 +1426,11 @@ export class ClineProvider

this.emit(RooCodeEventName.ModeChanged, newMode)

// If workspace lock is on, keep the current API config — don't load mode-specific config
// If workspace lock is on, or the caller explicitly requested preserving the current
// API config (e.g. AI-initiated mode switches via the switch_mode tool), keep the
// current API config — don't load mode-specific config.
const lockApiConfigAcrossModes = this.context.workspaceState.get("lockApiConfigAcrossModes", false)
if (lockApiConfigAcrossModes) {
if (lockApiConfigAcrossModes || options?.preserveApiConfig) {
await this.postStateToWebview()
return
}
Expand Down
22 changes: 22 additions & 0 deletions src/core/webview/__tests__/ClineProvider.lockApiConfig.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,28 @@ describe("ClineProvider - Lock API Config Across Modes", () => {
expect(activateProviderProfileSpy).not.toHaveBeenCalled()
})

it("skips mode-specific config lookup/load when preserveApiConfig option is true", async () => {
await mockContext.workspaceState.update("lockApiConfigAcrossModes", false)

const getModeConfigIdSpy = vi
.spyOn(provider.providerSettingsManager, "getModeConfigId")
.mockResolvedValue("architect-profile-id")
const listConfigSpy = vi
.spyOn(provider.providerSettingsManager, "listConfig")
.mockResolvedValue([
{ name: "architect-profile", id: "architect-profile-id", apiProvider: "anthropic" },
])
const activateProviderProfileSpy = vi
.spyOn(provider, "activateProviderProfile")
.mockResolvedValue(undefined)

await provider.handleModeSwitch("architect", { preserveApiConfig: true })

expect(getModeConfigIdSpy).not.toHaveBeenCalled()
expect(listConfigSpy).not.toHaveBeenCalled()
expect(activateProviderProfileSpy).not.toHaveBeenCalled()
})

it("keeps normal mode-specific lookup/load behavior when lockApiConfigAcrossModes is false", async () => {
await mockContext.workspaceState.update("lockApiConfigAcrossModes", false)

Expand Down
Loading