diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 1106d340050..dbaab430a1e 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -431,11 +431,15 @@ export class ClineProvider await this.updateGlobalState("listApiConfigMeta", await this.providerSettingsManager.listConfig()) if (result.activeProfileChanged && result.activeProfileId) { - // Reload full settings for new active profile. - const profile = await this.providerSettingsManager.getProfile({ - id: result.activeProfileId, - }) - await this.activateProviderProfile({ name: profile.name }) + // Only switch the active profile if the user hasn't locked it across modes. + const lockApiConfig = this.context.workspaceState.get("lockApiConfigAcrossModes", true) + if (!lockApiConfig) { + // Reload full settings for new active profile. + const profile = await this.providerSettingsManager.getProfile({ + id: result.activeProfileId, + }) + await this.activateProviderProfile({ name: profile.name }) + } } await this.postStateToWebviewWithoutClineMessages() @@ -996,7 +1000,7 @@ export class ClineProvider // Load the saved API config for the restored mode if it exists. // Skip mode-based profile activation if historyItem.apiConfigName exists, // since the task's specific provider profile will override it anyway. - const lockApiConfigAcrossModes = this.context.workspaceState.get("lockApiConfigAcrossModes", false) + const lockApiConfigAcrossModes = this.context.workspaceState.get("lockApiConfigAcrossModes", true) if (!historyItem.apiConfigName && !lockApiConfigAcrossModes && !skipProfileRestoreFromHistory) { const savedConfigId = await this.providerSettingsManager.getModeConfigId(historyItem.mode) @@ -1427,7 +1431,7 @@ export class ClineProvider this.emit(RooCodeEventName.ModeChanged, newMode) // If workspace lock is on, keep the current API config — don't load mode-specific config - const lockApiConfigAcrossModes = this.context.workspaceState.get("lockApiConfigAcrossModes", false) + const lockApiConfigAcrossModes = this.context.workspaceState.get("lockApiConfigAcrossModes", true) if (lockApiConfigAcrossModes) { await this.postStateToWebview() return @@ -2341,7 +2345,7 @@ export class ClineProvider profileThresholds: profileThresholds ?? {}, cloudApiUrl: getRooCodeApiUrl(), hasOpenedModeSelector: this.getGlobalState("hasOpenedModeSelector") ?? false, - lockApiConfigAcrossModes: lockApiConfigAcrossModes ?? false, + lockApiConfigAcrossModes: lockApiConfigAcrossModes ?? true, alwaysAllowFollowupQuestions: alwaysAllowFollowupQuestions ?? false, followupAutoApproveTimeoutMs: followupAutoApproveTimeoutMs ?? 60000, includeDiagnosticMessages: includeDiagnosticMessages ?? true, @@ -2562,7 +2566,7 @@ export class ClineProvider stateValues.codebaseIndexConfig?.codebaseIndexOpenRouterSpecificProvider, }, profileThresholds: stateValues.profileThresholds ?? {}, - lockApiConfigAcrossModes: this.context.workspaceState.get("lockApiConfigAcrossModes", false), + lockApiConfigAcrossModes: this.context.workspaceState.get("lockApiConfigAcrossModes", true), includeDiagnosticMessages: stateValues.includeDiagnosticMessages ?? true, maxDiagnosticMessages: stateValues.maxDiagnosticMessages ?? 50, includeTaskHistoryInEnhance: stateValues.includeTaskHistoryInEnhance ?? true, diff --git a/src/core/webview/__tests__/ClineProvider.lockApiConfig.spec.ts b/src/core/webview/__tests__/ClineProvider.lockApiConfig.spec.ts index 2cf9d4cae8b..5e9fc84a6ee 100644 --- a/src/core/webview/__tests__/ClineProvider.lockApiConfig.spec.ts +++ b/src/core/webview/__tests__/ClineProvider.lockApiConfig.spec.ts @@ -320,6 +320,27 @@ describe("ClineProvider - Lock API Config Across Modes", () => { await provider.resolveWebviewView(mockWebviewView) }) + it("skips mode-specific config lookup/load by default (lockApiConfigAcrossModes defaults to true)", async () => { + // Do NOT set lockApiConfigAcrossModes - verify default behavior is locked + 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") + + expect(getModeConfigIdSpy).not.toHaveBeenCalled() + expect(listConfigSpy).not.toHaveBeenCalled() + expect(activateProviderProfileSpy).not.toHaveBeenCalled() + }) + it("skips mode-specific config lookup/load when lockApiConfigAcrossModes is true", async () => { await mockContext.workspaceState.update("lockApiConfigAcrossModes", true) diff --git a/src/core/webview/__tests__/ClineProvider.spec.ts b/src/core/webview/__tests__/ClineProvider.spec.ts index da0fb2003fb..b492b786ae5 100644 --- a/src/core/webview/__tests__/ClineProvider.spec.ts +++ b/src/core/webview/__tests__/ClineProvider.spec.ts @@ -1451,6 +1451,8 @@ describe("ClineProvider", () => { beforeEach(async () => { // Set up webview for each test await provider.resolveWebviewView(mockWebviewView) + // Unlock API config across modes so mode-specific configs are loaded + await mockContext.workspaceState.update("lockApiConfigAcrossModes", false) }) it("loads saved API config when switching modes", async () => { diff --git a/webview-ui/src/context/ExtensionStateContext.tsx b/webview-ui/src/context/ExtensionStateContext.tsx index ce7a607d9a8..899803a9f2b 100644 --- a/webview-ui/src/context/ExtensionStateContext.tsx +++ b/webview-ui/src/context/ExtensionStateContext.tsx @@ -262,7 +262,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode openRouterImageGenerationSelectedModel: "", includeCurrentTime: true, includeCurrentCost: true, - lockApiConfigAcrossModes: false, + lockApiConfigAcrossModes: true, }) const [didHydrateState, setDidHydrateState] = useState(false)