Skip to content
This repository was archived by the owner on May 15, 2026. It is now read-only.

Commit 711b29e

Browse files
committed
fix: default lockApiConfigAcrossModes to true to prevent silent profile switching
Changes the default value of lockApiConfigAcrossModes from false to true, so that by default, switching modes (whether user-initiated or AI-triggered) will NOT change the active API configuration/model. Users who want per-mode API configs can still unlock this via the lock icon in the API config selector popover. Also guards the cloud profile sync path to respect the lock setting, preventing silent profile changes from background cloud sync. Addresses #12222 and #12237.
1 parent ad25634 commit 711b29e

4 files changed

Lines changed: 37 additions & 10 deletions

File tree

src/core/webview/ClineProvider.ts

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -431,11 +431,15 @@ export class ClineProvider
431431
await this.updateGlobalState("listApiConfigMeta", await this.providerSettingsManager.listConfig())
432432

433433
if (result.activeProfileChanged && result.activeProfileId) {
434-
// Reload full settings for new active profile.
435-
const profile = await this.providerSettingsManager.getProfile({
436-
id: result.activeProfileId,
437-
})
438-
await this.activateProviderProfile({ name: profile.name })
434+
// Only switch the active profile if the user hasn't locked it across modes.
435+
const lockApiConfig = this.context.workspaceState.get("lockApiConfigAcrossModes", true)
436+
if (!lockApiConfig) {
437+
// Reload full settings for new active profile.
438+
const profile = await this.providerSettingsManager.getProfile({
439+
id: result.activeProfileId,
440+
})
441+
await this.activateProviderProfile({ name: profile.name })
442+
}
439443
}
440444

441445
await this.postStateToWebviewWithoutClineMessages()
@@ -996,7 +1000,7 @@ export class ClineProvider
9961000
// Load the saved API config for the restored mode if it exists.
9971001
// Skip mode-based profile activation if historyItem.apiConfigName exists,
9981002
// since the task's specific provider profile will override it anyway.
999-
const lockApiConfigAcrossModes = this.context.workspaceState.get("lockApiConfigAcrossModes", false)
1003+
const lockApiConfigAcrossModes = this.context.workspaceState.get("lockApiConfigAcrossModes", true)
10001004

10011005
if (!historyItem.apiConfigName && !lockApiConfigAcrossModes && !skipProfileRestoreFromHistory) {
10021006
const savedConfigId = await this.providerSettingsManager.getModeConfigId(historyItem.mode)
@@ -1427,7 +1431,7 @@ export class ClineProvider
14271431
this.emit(RooCodeEventName.ModeChanged, newMode)
14281432

14291433
// If workspace lock is on, keep the current API config — don't load mode-specific config
1430-
const lockApiConfigAcrossModes = this.context.workspaceState.get("lockApiConfigAcrossModes", false)
1434+
const lockApiConfigAcrossModes = this.context.workspaceState.get("lockApiConfigAcrossModes", true)
14311435
if (lockApiConfigAcrossModes) {
14321436
await this.postStateToWebview()
14331437
return
@@ -2341,7 +2345,7 @@ export class ClineProvider
23412345
profileThresholds: profileThresholds ?? {},
23422346
cloudApiUrl: getRooCodeApiUrl(),
23432347
hasOpenedModeSelector: this.getGlobalState("hasOpenedModeSelector") ?? false,
2344-
lockApiConfigAcrossModes: lockApiConfigAcrossModes ?? false,
2348+
lockApiConfigAcrossModes: lockApiConfigAcrossModes ?? true,
23452349
alwaysAllowFollowupQuestions: alwaysAllowFollowupQuestions ?? false,
23462350
followupAutoApproveTimeoutMs: followupAutoApproveTimeoutMs ?? 60000,
23472351
includeDiagnosticMessages: includeDiagnosticMessages ?? true,
@@ -2562,7 +2566,7 @@ export class ClineProvider
25622566
stateValues.codebaseIndexConfig?.codebaseIndexOpenRouterSpecificProvider,
25632567
},
25642568
profileThresholds: stateValues.profileThresholds ?? {},
2565-
lockApiConfigAcrossModes: this.context.workspaceState.get("lockApiConfigAcrossModes", false),
2569+
lockApiConfigAcrossModes: this.context.workspaceState.get("lockApiConfigAcrossModes", true),
25662570
includeDiagnosticMessages: stateValues.includeDiagnosticMessages ?? true,
25672571
maxDiagnosticMessages: stateValues.maxDiagnosticMessages ?? 50,
25682572
includeTaskHistoryInEnhance: stateValues.includeTaskHistoryInEnhance ?? true,

src/core/webview/__tests__/ClineProvider.lockApiConfig.spec.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,27 @@ describe("ClineProvider - Lock API Config Across Modes", () => {
320320
await provider.resolveWebviewView(mockWebviewView)
321321
})
322322

323+
it("skips mode-specific config lookup/load by default (lockApiConfigAcrossModes defaults to true)", async () => {
324+
// Do NOT set lockApiConfigAcrossModes - verify default behavior is locked
325+
const getModeConfigIdSpy = vi
326+
.spyOn(provider.providerSettingsManager, "getModeConfigId")
327+
.mockResolvedValue("architect-profile-id")
328+
const listConfigSpy = vi
329+
.spyOn(provider.providerSettingsManager, "listConfig")
330+
.mockResolvedValue([
331+
{ name: "architect-profile", id: "architect-profile-id", apiProvider: "anthropic" },
332+
])
333+
const activateProviderProfileSpy = vi
334+
.spyOn(provider, "activateProviderProfile")
335+
.mockResolvedValue(undefined)
336+
337+
await provider.handleModeSwitch("architect")
338+
339+
expect(getModeConfigIdSpy).not.toHaveBeenCalled()
340+
expect(listConfigSpy).not.toHaveBeenCalled()
341+
expect(activateProviderProfileSpy).not.toHaveBeenCalled()
342+
})
343+
323344
it("skips mode-specific config lookup/load when lockApiConfigAcrossModes is true", async () => {
324345
await mockContext.workspaceState.update("lockApiConfigAcrossModes", true)
325346

src/core/webview/__tests__/ClineProvider.spec.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1451,6 +1451,8 @@ describe("ClineProvider", () => {
14511451
beforeEach(async () => {
14521452
// Set up webview for each test
14531453
await provider.resolveWebviewView(mockWebviewView)
1454+
// Unlock API config across modes so mode-specific configs are loaded
1455+
await mockContext.workspaceState.update("lockApiConfigAcrossModes", false)
14541456
})
14551457

14561458
it("loads saved API config when switching modes", async () => {

webview-ui/src/context/ExtensionStateContext.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
262262
openRouterImageGenerationSelectedModel: "",
263263
includeCurrentTime: true,
264264
includeCurrentCost: true,
265-
lockApiConfigAcrossModes: false,
265+
lockApiConfigAcrossModes: true,
266266
})
267267

268268
const [didHydrateState, setDidHydrateState] = useState(false)

0 commit comments

Comments
 (0)