diff --git a/src/renderer/src/components/onboarding/onboarding-folder-agent-startup.test.ts b/src/renderer/src/components/onboarding/onboarding-folder-agent-startup.test.ts index 1e5710260..180688173 100644 --- a/src/renderer/src/components/onboarding/onboarding-folder-agent-startup.test.ts +++ b/src/renderer/src/components/onboarding/onboarding-folder-agent-startup.test.ts @@ -14,8 +14,7 @@ describe('buildOnboardingFolderAgentStartup', () => { }) expect(startup).toEqual({ - command: - "codex '-m' 'gpt-5.6-sol' '-c' 'model_reasoning_effort=medium' '--dangerously-bypass-approvals-and-sandbox'", + command: "codex '--dangerously-bypass-approvals-and-sandbox'", env: {}, launchAgent: 'codex', launchConfig: { @@ -23,7 +22,7 @@ describe('buildOnboardingFolderAgentStartup', () => { agentArgs: '--dangerously-bypass-approvals-and-sandbox', agentEnv: {} }, - sessionOptions: { model: 'gpt-5.6-sol', effort: 'medium' }, + sessionOptions: undefined, telemetry: { agent_kind: 'codex', launch_source: 'onboarding', @@ -99,8 +98,7 @@ describe('buildOnboardingFolderAgentStartup', () => { false ) ).toEqual({ - command: - "echo onboarding-folder-agent '-m' 'gpt-5.6-sol' '-c' 'model_reasoning_effort=medium' '--dangerously-bypass-approvals-and-sandbox'", + command: "echo onboarding-folder-agent '--dangerously-bypass-approvals-and-sandbox'", env: {}, launchAgent: 'codex', launchConfig: { @@ -108,7 +106,7 @@ describe('buildOnboardingFolderAgentStartup', () => { agentArgs: '--dangerously-bypass-approvals-and-sandbox', agentEnv: {} }, - sessionOptions: { model: 'gpt-5.6-sol', effort: 'medium' }, + sessionOptions: undefined, telemetry: { agent_kind: 'codex', launch_source: 'onboarding', diff --git a/src/renderer/src/lib/launch-agent-in-new-tab.test.ts b/src/renderer/src/lib/launch-agent-in-new-tab.test.ts index b3c5029a3..c43f98ef9 100644 --- a/src/renderer/src/lib/launch-agent-in-new-tab.test.ts +++ b/src/renderer/src/lib/launch-agent-in-new-tab.test.ts @@ -340,8 +340,7 @@ describe('launchAgentInNewTab', () => { environmentId: 'web-runtime', targetGroupId: 'group-1', activate: true, - command: - "codex '-m' 'gpt-5.6-sol' '-c' 'model_reasoning_effort=medium' '--model' 'gpt-5' '--reasoning-effort' 'high' 'fix the spinner'", + command: "codex '--model' 'gpt-5' '--reasoning-effort' 'high' 'fix the spinner'", env: { CODEX_PROFILE: 'captured' }, startupCommandDelivery: 'shell-ready', launchConfig: { @@ -486,8 +485,7 @@ describe('launchAgentInNewTab', () => { expect(mockQueueTabStartupCommand).toHaveBeenCalledWith( 'tab-1', expect.objectContaining({ - command: - "claude '--model' 'sonnet' '--effort' 'high' '--dangerously-skip-permissions' --prefill 'review Bob''s change'" + command: "claude '--dangerously-skip-permissions' --prefill 'review Bob''s change'" }) ) }) @@ -505,7 +503,7 @@ describe('launchAgentInNewTab', () => { expect(mockQueueTabStartupCommand).toHaveBeenCalledWith( 'tab-1', expect.objectContaining({ - command: 'claude "--model" "sonnet" "--effort" "high" "--dangerously-skip-permissions"' + command: 'claude "--dangerously-skip-permissions"' }) ) }) @@ -523,7 +521,7 @@ describe('launchAgentInNewTab', () => { expect(mockQueueTabStartupCommand).toHaveBeenCalledWith( 'tab-1', expect.objectContaining({ - command: "claude '--model' 'sonnet' '--effort' 'high' '--dangerously-skip-permissions'" + command: "claude '--dangerously-skip-permissions'" }) ) }) @@ -543,8 +541,7 @@ describe('launchAgentInNewTab', () => { expect(mockQueueTabStartupCommand).toHaveBeenCalledWith( 'tab-1', expect.objectContaining({ - command: - 'codex "-m" "gpt-5.6-sol" "-c" "model_reasoning_effort=medium" "--model" "gpt-5" "fix the spinner"' + command: 'codex "--model" "gpt-5" "fix the spinner"' }) ) }) @@ -564,8 +561,7 @@ describe('launchAgentInNewTab', () => { expect(mockQueueTabStartupCommand).toHaveBeenCalledWith( 'tab-1', expect.objectContaining({ - command: - "claude '--model' 'sonnet' '--effort' 'high' '--dangerously-skip-permissions' --prefill 'review Bob'\\''s change'" + command: "claude '--dangerously-skip-permissions' --prefill 'review Bob'\\''s change'" }) ) }) @@ -583,7 +579,7 @@ describe('launchAgentInNewTab', () => { expect(mockQueueTabStartupCommand).toHaveBeenCalledWith( 'tab-1', expect.objectContaining({ - command: "claude '--model' 'sonnet' '--effort' 'high' '--dangerously-skip-permissions'" + command: "claude '--dangerously-skip-permissions'" }) ) }) @@ -620,8 +616,7 @@ describe('launchAgentInNewTab', () => { expect(mockQueueTabStartupCommand).toHaveBeenCalledWith( 'tab-1', expect.objectContaining({ - command: - "claude '--model' 'sonnet' '--effort' 'high' '--dangerously-skip-permissions' --prefill 'review Bob'\\''s change'" + command: "claude '--dangerously-skip-permissions' --prefill 'review Bob'\\''s change'" }) ) }) @@ -642,7 +637,7 @@ describe('launchAgentInNewTab', () => { expect(mockQueueTabStartupCommand).toHaveBeenCalledWith( 'tab-1', expect.objectContaining({ - command: "claude '--model' 'sonnet' '--effort' 'high' '--dangerously-skip-permissions'" + command: "claude '--dangerously-skip-permissions'" }) ) expect(mockPasteDraftWhenAgentReady).toHaveBeenCalledWith( @@ -848,7 +843,7 @@ describe('launchAgentInNewTab', () => { expect(mockQueueTabStartupCommand).toHaveBeenCalledWith( 'tab-1', expect.objectContaining({ - command: "codex '-m' 'gpt-5.6-sol' '-c' 'model_reasoning_effort=medium' '--model' 'gpt-5.5'" + command: "codex '--model' 'gpt-5.5'" }) ) }) diff --git a/src/renderer/src/lib/launch-work-item-direct.test.ts b/src/renderer/src/lib/launch-work-item-direct.test.ts index 9eedb05be..c76c91bc0 100644 --- a/src/renderer/src/lib/launch-work-item-direct.test.ts +++ b/src/renderer/src/lib/launch-work-item-direct.test.ts @@ -402,7 +402,7 @@ describe('launchWorkItemDirect', () => { cmdOverrides: {}, agentArgs: '--dangerously-skip-permissions', agentEnv: {}, - sessionOptions: { model: 'sonnet', effort: 'high' }, + sessionOptions: undefined, platform: 'win32', isRemote: false }) @@ -526,7 +526,7 @@ describe('launchWorkItemDirect', () => { cmdOverrides: {}, agentArgs: '--yolo', agentEnv: {}, - sessionOptions: { model: 'auto' }, + sessionOptions: undefined, platform: 'linux', isRemote: true }) @@ -536,7 +536,7 @@ describe('launchWorkItemDirect', () => { cmdOverrides: {}, agentArgs: '--yolo', agentEnv: {}, - sessionOptions: { model: 'auto' }, + sessionOptions: undefined, platform: 'linux', isRemote: true, allowEmptyPromptLaunch: true diff --git a/src/renderer/src/lib/worktree-activation-created-agent.test.ts b/src/renderer/src/lib/worktree-activation-created-agent.test.ts index 89fcb83c9..dfa23851a 100644 --- a/src/renderer/src/lib/worktree-activation-created-agent.test.ts +++ b/src/renderer/src/lib/worktree-activation-created-agent.test.ts @@ -96,8 +96,7 @@ describe('activateAndRevealWorktree created agent reopen', () => { expect(result).toEqual({ primaryTabId: reopenedTab?.id }) expect(reopenedTab).toBeDefined() expect(state.pendingStartupByTabId[reopenedTab!.id]).toEqual({ - command: - "codex '-m' 'gpt-5.6-sol' '-c' 'model_reasoning_effort=medium' '--dangerously-bypass-approvals-and-sandbox'", + command: "codex '--dangerously-bypass-approvals-and-sandbox'", env: {}, launchAgent: 'codex', launchConfig: { @@ -106,7 +105,7 @@ describe('activateAndRevealWorktree created agent reopen', () => { agentEnv: {} }, launchToken: expect.any(String), - sessionOptions: { model: 'gpt-5.6-sol', effort: 'medium' }, + sessionOptions: undefined, telemetry: { agent_kind: 'codex', launch_source: 'sidebar', diff --git a/src/renderer/src/store/slices/repos-onboarding-folder-startup.test.ts b/src/renderer/src/store/slices/repos-onboarding-folder-startup.test.ts index b5c44f9ca..18f6cfb1f 100644 --- a/src/renderer/src/store/slices/repos-onboarding-folder-startup.test.ts +++ b/src/renderer/src/store/slices/repos-onboarding-folder-startup.test.ts @@ -58,8 +58,7 @@ describe('repo slice skipped-onboarding folder startup', () => { { sidebarRevealBehavior: 'auto', startup: { - command: - "codex '-m' 'gpt-5.6-sol' '-c' 'model_reasoning_effort=medium' '--dangerously-bypass-approvals-and-sandbox'", + command: "codex '--dangerously-bypass-approvals-and-sandbox'", env: {}, launchAgent: 'codex', launchConfig: { @@ -67,7 +66,7 @@ describe('repo slice skipped-onboarding folder startup', () => { agentArgs: '--dangerously-bypass-approvals-and-sandbox', agentEnv: {} }, - sessionOptions: { model: 'gpt-5.6-sol', effort: 'medium' }, + sessionOptions: undefined, telemetry: { agent_kind: 'codex', launch_source: 'onboarding', diff --git a/src/shared/agent-session-option-catalog.test.ts b/src/shared/agent-session-option-catalog.test.ts index c4e7eca14..ffabb087e 100644 --- a/src/shared/agent-session-option-catalog.test.ts +++ b/src/shared/agent-session-option-catalog.test.ts @@ -72,7 +72,7 @@ describe('agent session option catalog', () => { }) }) - it('restores defaults per model without leaking values across models', () => { + it('resolves only stored values without leaking values across models', () => { let persisted = updateNativeChatSessionOptionDefaults({ persisted: undefined, agent: 'claude', @@ -96,17 +96,56 @@ describe('agent session option catalog', () => { }) expect(resolveNativeChatSessionOptionDefaults(persisted, 'claude')).toEqual({ - model: 'sonnet', - effort: 'high' + model: 'sonnet' }) expect(persisted.claude?.valuesByModel?.opus).toEqual({ effort: 'xhigh' }) }) - it('seeds a fresh launch from the catalog defaults', () => { - expect(resolveNativeChatSessionOptionDefaults(undefined, 'claude')).toEqual({ - model: 'sonnet', - effort: 'high' - }) + it('spawns vanilla when the user has not explicitly selected a model', () => { + // Regression (#9085): a fresh launch must not force the catalog default + // model/effort — the agent must spawn exactly as its own CLI would. + expect(resolveNativeChatSessionOptionDefaults(undefined, 'claude')).toBeUndefined() + expect(resolveNativeChatSessionOptionDefaults({}, 'claude')).toBeUndefined() expect(resolveNativeChatSessionOptionDefaults({}, 'future-agent')).toBeUndefined() }) + + it('resolves an explicitly selected model and only its stored options', () => { + let persisted = updateNativeChatSessionOptionDefaults({ + persisted: undefined, + agent: 'claude', + modelId: 'opus', + optionId: 'model', + value: 'opus' + }) + expect(resolveNativeChatSessionOptionDefaults(persisted, 'claude')).toEqual({ + model: 'opus' + }) + persisted = updateNativeChatSessionOptionDefaults({ + persisted, + agent: 'claude', + modelId: 'opus', + optionId: 'effort', + value: 'xhigh' + }) + expect(resolveNativeChatSessionOptionDefaults(persisted, 'claude')).toEqual({ + model: 'opus', + effort: 'xhigh' + }) + }) + + it('keeps catalog option defaults after the user explicitly selects a model', () => { + const persisted = updateNativeChatSessionOptionDefaults({ + persisted: undefined, + agent: 'claude', + modelId: 'sonnet', + optionId: 'model', + value: 'sonnet' + }) + const defaults = resolveNativeChatSessionOptionDefaults(persisted, 'claude') + + expect(resolveAgentSessionOptionLaunch('claude', defaults)).toEqual({ + args: ['--model', 'sonnet', '--effort', 'high'], + appliedValues: { model: 'sonnet', effort: 'high' } + }) + }) }) diff --git a/src/shared/native-chat-session-option-defaults.ts b/src/shared/native-chat-session-option-defaults.ts index 98bd01871..5edc3c491 100644 --- a/src/shared/native-chat-session-option-defaults.ts +++ b/src/shared/native-chat-session-option-defaults.ts @@ -1,10 +1,5 @@ import type { AgentType } from './agent-status-types' -import { - catalogDefaultModel, - findCatalogModel, - getAgentSessionOptionCatalog, - sessionOptionValueIsValid -} from './agent-session-option-catalog' +import { sessionOptionValueIsValid } from './agent-session-option-catalog' import type { PersistedNativeChatSessionOptions, SessionOptionValue @@ -14,16 +9,10 @@ export function resolveNativeChatSessionOptionDefaults( persisted: PersistedNativeChatSessionOptions | null | undefined, agent: AgentType ): Record | undefined { - const catalog = getAgentSessionOptionCatalog(agent) const entry = persisted?.[agent] - // Why: fresh launches must be authoritative too, so resolve the catalog's - // declared fallback before the launch command is assembled. - const modelId = - typeof entry?.model === 'string' && entry.model.trim() - ? entry.model - : catalog - ? catalogDefaultModel(catalog)?.id - : undefined + // Why: untouched settings must preserve the agent CLI's configured defaults; + // only a model explicitly selected by the user authorizes launch flags. + const modelId = typeof entry?.model === 'string' && entry.model.trim() ? entry.model : undefined if (!modelId) { return undefined } @@ -36,11 +25,6 @@ export function resolveNativeChatSessionOptionDefaults( } } } - - const model = catalog ? findCatalogModel(catalog, modelId) : undefined - for (const option of model?.options ?? []) { - values[option.id] ??= option.kind.defaultValue - } return values }