diff --git a/desktop/src/components/controls/ModelSelector.test.tsx b/desktop/src/components/controls/ModelSelector.test.tsx index 6dc35d7d..73a16ca0 100644 --- a/desktop/src/components/controls/ModelSelector.test.tsx +++ b/desktop/src/components/controls/ModelSelector.test.tsx @@ -1069,6 +1069,7 @@ describe('ModelSelector', () => { }) useSettingsStore.setState({ locale: 'en', + effortLevel: 'max', availableModels: openAIModels, currentModel: openAIModels[0], activeProviderName: 'ChatGPT Official', diff --git a/desktop/src/pages/EmptySession.test.tsx b/desktop/src/pages/EmptySession.test.tsx index c76c0b07..f4ae7845 100644 --- a/desktop/src/pages/EmptySession.test.tsx +++ b/desktop/src/pages/EmptySession.test.tsx @@ -865,6 +865,30 @@ describe('EmptySession', () => { }) }) + it('carries the global effort into the first draft selection for ChatGPT Official', async () => { + useSettingsStore.setState({ effortLevel: 'low' }) + useProviderStore.setState({ + providers: [], + activeId: 'openai-official', + providerOrder: ['claude-official', 'openai-official', 'grok-official'], + hasLoadedProviders: true, + }) + + render() + + setComposerText('draft question', 14) + fireEvent.click(screen.getByRole('button', { name: /Run/i })) + + await waitFor(() => { + expect(mocks.createSession).toHaveBeenCalledWith({ permissionMode: 'default' }) + }) + + expect(useSessionRuntimeStore.getState().selections['draft-session']).toMatchObject({ + providerId: 'openai-official', + effortLevel: 'low', + }) + }) + it.each([true, false])('materializes raw provider models with 1M=%s before the first draft message', async (enabled) => { useProviderStore.setState({ providers: [{ @@ -902,7 +926,7 @@ describe('EmptySession', () => { expect(useSessionRuntimeStore.getState().selections['draft-session']).toEqual({ providerId: 'provider-minimax', modelId: enabled ? 'MiniMax-M3[1m]' : 'MiniMax-M3', - effortLevel: 'max', + effortLevel: 'low', }) expect(mocks.wsSend.mock.calls.slice(0, 3)).toEqual([ [ @@ -911,7 +935,7 @@ describe('EmptySession', () => { type: 'set_runtime_config', providerId: 'provider-minimax', modelId: enabled ? 'MiniMax-M3[1m]' : 'MiniMax-M3', - effortLevel: 'max', + effortLevel: 'low', }, ], ['draft-session', { type: 'prewarm_session' }], diff --git a/desktop/src/pages/EmptySession.tsx b/desktop/src/pages/EmptySession.tsx index 1e519e43..b0d96062 100644 --- a/desktop/src/pages/EmptySession.tsx +++ b/desktop/src/pages/EmptySession.tsx @@ -34,6 +34,8 @@ import { SlashCommandMenu, } from '../components/chat/SlashCommandMenu' import { useMobileViewport } from '../hooks/useMobileViewport' +import { GROK_OFFICIAL_PROVIDER_ID } from '../constants/grokOfficialProvider' +import { OPENAI_OFFICIAL_PROVIDER_ID } from '../constants/openaiOfficialProvider' import { isDesktopRuntime } from '../lib/desktopRuntime' import { normalizeRuntimeSelection, @@ -400,7 +402,15 @@ export function EmptySession() { activeCustomProvider.apiFormat, getBundledPresetReasoningProviderKind(activeCustomProvider.presetId), ) - : defaultActiveProviderSelection + : defaultActiveProviderSelection && ( + defaultActiveProviderSelection.providerId === OPENAI_OFFICIAL_PROVIDER_ID || + defaultActiveProviderSelection.providerId === GROK_OFFICIAL_PROVIDER_ID + ) + // Built-in providers are not in the saved list. Without an explicit + // effort the server runs the model default while the selector shows + // the global value, so resolve it against the model's catalog here. + ? normalizeRuntimeSelection({ ...defaultActiveProviderSelection, effortLevel }) + : defaultActiveProviderSelection const claudeOAuthRuntimeSelection = !explicitDraftSelection && authStatus.source === 'claude-oauth' && activeProviderId === null && diff --git a/desktop/src/stores/settingsStore.test.ts b/desktop/src/stores/settingsStore.test.ts index 4ae3908a..2a8be34d 100644 --- a/desktop/src/stores/settingsStore.test.ts +++ b/desktop/src/stores/settingsStore.test.ts @@ -160,6 +160,15 @@ describe('settingsStore locale defaults', () => { }) }) +describe('settingsStore effort default', () => { + it('starts at low before the server value loads', async () => { + vi.resetModules() + const { useSettingsStore } = await import('./settingsStore') + + expect(useSettingsStore.getState().effortLevel).toBe('low') + }) +}) + describe('settingsStore UI zoom', () => { beforeEach(() => { vi.resetModules() diff --git a/desktop/src/stores/settingsStore.ts b/desktop/src/stores/settingsStore.ts index afad44cd..34f51ee3 100644 --- a/desktop/src/stores/settingsStore.ts +++ b/desktop/src/stores/settingsStore.ts @@ -204,7 +204,7 @@ let autoQuestionUpdateQueue: Promise = Promise.resolve() export const useSettingsStore = create((set, get) => ({ permissionMode: 'default', currentModel: null, - effortLevel: 'max', + effortLevel: 'low', thinkingEnabled: true, workflowKeywordTriggerEnabled: true, agentTeamsEnabled: true, diff --git a/src/server/__tests__/e2e/business-flow.test.ts b/src/server/__tests__/e2e/business-flow.test.ts index 58645edb..df713c06 100644 --- a/src/server/__tests__/e2e/business-flow.test.ts +++ b/src/server/__tests__/e2e/business-flow.test.ts @@ -457,9 +457,9 @@ describe('Business Flow: Models & Effort', () => { expect(status).toBe(400) }) - it('should default effort to max', async () => { + it('should default effort to low', async () => { const { data } = await api('GET', '/api/effort') - expect(data.level).toBe('max') + expect(data.level).toBe('low') expect(data.available).toEqual(['low', 'medium', 'high', 'xhigh', 'max']) }) diff --git a/src/server/__tests__/settings.test.ts b/src/server/__tests__/settings.test.ts index 518bc526..7bf13d01 100644 --- a/src/server/__tests__/settings.test.ts +++ b/src/server/__tests__/settings.test.ts @@ -1548,7 +1548,7 @@ describe('Models API', () => { expect(res.status).toBe(200) const body = await res.json() - expect(body.level).toBe('max') + expect(body.level).toBe('low') expect(body.available).toEqual(['low', 'medium', 'high', 'xhigh', 'max']) }) @@ -1561,7 +1561,7 @@ describe('Models API', () => { expect(res.status).toBe(200) const body = await res.json() - expect(body.level).toBe('max') + expect(body.level).toBe('low') expect(body.available).toEqual(['low', 'medium', 'high', 'xhigh', 'max']) }) diff --git a/src/server/api/models.ts b/src/server/api/models.ts index b9af8ae5..7787eb4e 100644 --- a/src/server/api/models.ts +++ b/src/server/api/models.ts @@ -119,7 +119,7 @@ const EFFORT_LEVELS = MODEL_REASONING_EFFORTS // CLI's own default runs. Keep this in step with what the `opus` alias resolves to there // (getDefaultOpusModel) so the UI names the model that actually runs. const DEFAULT_MODEL = 'claude-opus-5-5' -const DEFAULT_EFFORT = 'max' +const DEFAULT_EFFORT = 'low' const settingsService = new SettingsService() const providerService = new ProviderService()