diff --git a/packages/cli/src/ui/hooks/useQuotaAndFallback.test.ts b/packages/cli/src/ui/hooks/useQuotaAndFallback.test.ts index f2f752df40..60064957e8 100644 --- a/packages/cli/src/ui/hooks/useQuotaAndFallback.test.ts +++ b/packages/cli/src/ui/hooks/useQuotaAndFallback.test.ts @@ -69,6 +69,7 @@ describe('useQuotaAndFallback', () => { setFallbackHandlerSpy = vi.spyOn(mockConfig, 'setFallbackModelHandler'); vi.spyOn(mockConfig, 'setQuotaErrorOccurred'); + vi.spyOn(mockConfig, 'setModel'); }); afterEach(() => { @@ -163,6 +164,9 @@ describe('useQuotaAndFallback', () => { const intent = await promise!; expect(intent).toBe('retry_always'); + // Verify setModel was called with isFallbackModel=true + expect(mockConfig.setModel).toHaveBeenCalledWith('gemini-flash', true); + // The pending request should be cleared from the state expect(result.current.proQuotaRequest).toBeNull(); expect(mockHistoryManager.addItem).toHaveBeenCalledTimes(1); @@ -274,6 +278,9 @@ describe('useQuotaAndFallback', () => { const intent = await promise!; expect(intent).toBe('retry_always'); + // Verify setModel was called with isFallbackModel=true + expect(mockConfig.setModel).toHaveBeenCalledWith('model-B', true); + // The pending request should be cleared from the state expect(result.current.proQuotaRequest).toBeNull(); expect(mockConfig.setQuotaErrorOccurred).toHaveBeenCalledWith(true); @@ -328,6 +335,13 @@ To disable gemini-3-pro-preview, disable "Preview features" in /settings.`, const intent = await promise!; expect(intent).toBe('retry_always'); + + // Verify setModel was called with isFallbackModel=true + expect(mockConfig.setModel).toHaveBeenCalledWith( + 'gemini-2.5-pro', + true, + ); + expect(result.current.proQuotaRequest).toBeNull(); }); }); @@ -411,6 +425,9 @@ To disable gemini-3-pro-preview, disable "Preview features" in /settings.`, expect(intent).toBe('retry_always'); expect(result.current.proQuotaRequest).toBeNull(); + // Verify setModel was called with isFallbackModel=true + expect(mockConfig.setModel).toHaveBeenCalledWith('gemini-flash', true); + // Check for the "Switched to fallback model" message expect(mockHistoryManager.addItem).toHaveBeenCalledTimes(1); const lastCall = (mockHistoryManager.addItem as Mock).mock.calls[0][0]; diff --git a/packages/cli/src/ui/hooks/useQuotaAndFallback.ts b/packages/cli/src/ui/hooks/useQuotaAndFallback.ts index ff4bc6c4b8..1bf97efd4a 100644 --- a/packages/cli/src/ui/hooks/useQuotaAndFallback.ts +++ b/packages/cli/src/ui/hooks/useQuotaAndFallback.ts @@ -130,9 +130,10 @@ export function useQuotaAndFallback({ isDialogPending.current = false; // Reset the flag here if (choice === 'retry_always') { - // Explicitly set the model to the fallback model to persist the user's choice. + // Set the model to the fallback model for the current session. // This ensures the Footer updates and future turns use this model. - config.setModel(proQuotaRequest.fallbackModel); + // The change is not persisted, so the original model is restored on restart. + config.setModel(proQuotaRequest.fallbackModel, true); historyManager.addItem( { diff --git a/packages/core/src/config/config.test.ts b/packages/core/src/config/config.test.ts index 03414a3da2..c33fc1e3ed 100644 --- a/packages/core/src/config/config.test.ts +++ b/packages/core/src/config/config.test.ts @@ -1641,6 +1641,18 @@ describe('Config getHooks', () => { expect(onModelChange).toHaveBeenCalledWith(DEFAULT_GEMINI_MODEL); }); + + it('should NOT call onModelChange when a new model is set as a fallback', () => { + const onModelChange = vi.fn(); + const config = new Config({ + ...baseParams, + onModelChange, + }); + + config.setModel(DEFAULT_GEMINI_MODEL, true); + + expect(onModelChange).not.toHaveBeenCalled(); + }); }); }); diff --git a/packages/core/src/config/config.ts b/packages/core/src/config/config.ts index fa90ac3afe..d8a7e5b541 100644 --- a/packages/core/src/config/config.ts +++ b/packages/core/src/config/config.ts @@ -877,13 +877,13 @@ export class Config { return this.model; } - setModel(newModel: string): void { + setModel(newModel: string, isFallbackModel: boolean = false): void { if (this.model !== newModel || this._activeModel !== newModel) { this.model = newModel; // When the user explicitly sets a model, that becomes the active model. this._activeModel = newModel; coreEvents.emitModelChanged(newModel); - if (this.onModelChange) { + if (this.onModelChange && !isFallbackModel) { this.onModelChange(newModel); } }