diff --git a/packages/cli/src/ui/components/ModelDialog.test.tsx b/packages/cli/src/ui/components/ModelDialog.test.tsx
index f73f1cb012..fbfddbfad1 100644
--- a/packages/cli/src/ui/components/ModelDialog.test.tsx
+++ b/packages/cli/src/ui/components/ModelDialog.test.tsx
@@ -47,7 +47,7 @@ describe('', () => {
const mockGetHasAccessToPreviewModel = vi.fn();
interface MockConfig extends Partial {
- setModel: (model: string) => void;
+ setModel: (model: string, isTemporary?: boolean) => void;
getModel: () => string;
getPreviewFeatures: () => boolean;
getHasAccessToPreviewModel: () => boolean;
@@ -89,9 +89,7 @@ describe('', () => {
it('renders the initial "main" view correctly', () => {
const { lastFrame } = renderComponent();
expect(lastFrame()).toContain('Select Model');
- expect(lastFrame()).toContain(
- 'Applies to this session and future Gemini CLI sessions.',
- );
+ expect(lastFrame()).toContain('Remember model for future sessions: false');
expect(lastFrame()).toContain('Auto');
expect(lastFrame()).toContain('Manual');
});
@@ -148,7 +146,10 @@ describe('', () => {
stdin.write('\r');
await waitForUpdate();
- expect(mockSetModel).toHaveBeenCalledWith(DEFAULT_GEMINI_MODEL_AUTO);
+ expect(mockSetModel).toHaveBeenCalledWith(
+ DEFAULT_GEMINI_MODEL_AUTO,
+ true, // Session only by default
+ );
expect(mockOnClose).toHaveBeenCalled();
});
@@ -165,7 +166,29 @@ describe('', () => {
stdin.write('\r');
await waitForUpdate();
- expect(mockSetModel).toHaveBeenCalledWith(DEFAULT_GEMINI_MODEL);
+ expect(mockSetModel).toHaveBeenCalledWith(DEFAULT_GEMINI_MODEL, true);
+ expect(mockOnClose).toHaveBeenCalled();
+ });
+
+ it('toggles persist mode with Tab key', async () => {
+ const { lastFrame, stdin } = renderComponent();
+
+ expect(lastFrame()).toContain('Remember model for future sessions: false');
+
+ // Press Tab to toggle persist mode
+ stdin.write('\t');
+ await waitForUpdate();
+
+ expect(lastFrame()).toContain('Remember model for future sessions: true');
+
+ // Select "Auto" (index 0)
+ stdin.write('\r');
+ await waitForUpdate();
+
+ expect(mockSetModel).toHaveBeenCalledWith(
+ DEFAULT_GEMINI_MODEL_AUTO,
+ false, // Persist enabled
+ );
expect(mockOnClose).toHaveBeenCalled();
});
diff --git a/packages/cli/src/ui/components/ModelDialog.tsx b/packages/cli/src/ui/components/ModelDialog.tsx
index fa5af46390..d1c12af8ce 100644
--- a/packages/cli/src/ui/components/ModelDialog.tsx
+++ b/packages/cli/src/ui/components/ModelDialog.tsx
@@ -32,6 +32,7 @@ interface ModelDialogProps {
export function ModelDialog({ onClose }: ModelDialogProps): React.JSX.Element {
const config = useContext(ConfigContext);
const [view, setView] = useState<'main' | 'manual'>('main');
+ const [persistMode, setPersistMode] = useState(false);
// Determine the Preferred Model (read once when the dialog opens).
const preferredModel = config?.getModel() || DEFAULT_GEMINI_MODEL_AUTO;
@@ -62,6 +63,9 @@ export function ModelDialog({ onClose }: ModelDialogProps): React.JSX.Element {
onClose();
}
}
+ if (key.name === 'tab') {
+ setPersistMode((prev) => !prev);
+ }
},
{ isActive: true },
);
@@ -157,13 +161,13 @@ export function ModelDialog({ onClose }: ModelDialogProps): React.JSX.Element {
}
if (config) {
- config.setModel(model);
+ config.setModel(model, persistMode ? false : true);
const event = new ModelSlashCommandEvent(model);
logModelSlashCommand(config, event);
}
onClose();
},
- [config, onClose],
+ [config, onClose, persistMode],
);
let header;
@@ -213,9 +217,15 @@ export function ModelDialog({ onClose }: ModelDialogProps): React.JSX.Element {
/>
-
- Applies to this session and future Gemini CLI sessions.
-
+
+
+ Remember model for future sessions:{' '}
+
+
+ {persistMode ? 'true' : 'false'}
+
+
+ (Press Tab to toggle)
diff --git a/packages/core/src/config/config.test.ts b/packages/core/src/config/config.test.ts
index 1f389cfaa0..ea357d690a 100644
--- a/packages/core/src/config/config.test.ts
+++ b/packages/core/src/config/config.test.ts
@@ -1630,19 +1630,19 @@ describe('Config getHooks', () => {
expect(config.getActiveModel()).toBe(originalModel);
});
- it('should call onModelChange when a new model is set', () => {
+ it('should call onModelChange when a new model is set and should persist', () => {
const onModelChange = vi.fn();
const config = new Config({
...baseParams,
onModelChange,
});
- config.setModel(DEFAULT_GEMINI_MODEL);
+ config.setModel(DEFAULT_GEMINI_MODEL, false);
expect(onModelChange).toHaveBeenCalledWith(DEFAULT_GEMINI_MODEL);
});
- it('should NOT call onModelChange when a new model is set as a fallback', () => {
+ it('should NOT call onModelChange when a new model is temporary', () => {
const onModelChange = vi.fn();
const config = new Config({
...baseParams,
diff --git a/packages/core/src/config/config.ts b/packages/core/src/config/config.ts
index f295cda720..3d9aba2bb8 100644
--- a/packages/core/src/config/config.ts
+++ b/packages/core/src/config/config.ts
@@ -909,13 +909,13 @@ export class Config {
return this.model;
}
- setModel(newModel: string, isFallbackModel: boolean = false): void {
+ setModel(newModel: string, isTemporary: boolean = true): 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 && !isFallbackModel) {
+ if (this.onModelChange && !isTemporary) {
this.onModelChange(newModel);
}
}