diff --git a/src/cliproxy/executor/__tests__/launch-settings.test.ts b/src/cliproxy/executor/__tests__/launch-settings.test.ts index 6080ab27..0b313b84 100644 --- a/src/cliproxy/executor/__tests__/launch-settings.test.ts +++ b/src/cliproxy/executor/__tests__/launch-settings.test.ts @@ -57,6 +57,30 @@ describe('buildLaunchSettingsOverlay', () => { expect(env.ANTHROPIC_AUTH_TOKEN).toBe('ccs-internal-managed'); }); + it('overlays the extended-context extra model keys so a saved bare value cannot clobber --1m/--no-1m', () => { + writePersisted({ + env: { + ANTHROPIC_BASE_URL: 'http://127.0.0.1:8317/api/provider/claude', + ANTHROPIC_MODEL: 'claude-opus-5[1m]', + ANTHROPIC_DEFAULT_MODEL: 'claude-opus-5[1m]', + CLAUDE_CODE_SUBAGENT_MODEL: 'claude-sonnet-5[1m]', + }, + }); + + const { settings, changed } = buildLaunchSettingsOverlay(settingsPath, { + ANTHROPIC_BASE_URL: 'http://127.0.0.1:8317/api/provider/claude', + ANTHROPIC_MODEL: 'claude-opus-5', + ANTHROPIC_DEFAULT_MODEL: 'claude-opus-5', + CLAUDE_CODE_SUBAGENT_MODEL: 'claude-sonnet-5', + } as NodeJS.ProcessEnv); + + expect(changed).toBe(true); + const env = settings.env as Record; + expect(env.ANTHROPIC_MODEL).toBe('claude-opus-5'); + expect(env.ANTHROPIC_DEFAULT_MODEL).toBe('claude-opus-5'); + expect(env.CLAUDE_CODE_SUBAGENT_MODEL).toBe('claude-sonnet-5'); + }); + it('preserves non-env settings (permissions, hooks, etc.)', () => { writePersisted({ env: { ANTHROPIC_BASE_URL: 'http://127.0.0.1:8317/api/provider/codex' }, diff --git a/src/cliproxy/executor/launch-settings.ts b/src/cliproxy/executor/launch-settings.ts index 5e33bf7e..aadbd96e 100644 --- a/src/cliproxy/executor/launch-settings.ts +++ b/src/cliproxy/executor/launch-settings.ts @@ -26,6 +26,7 @@ import * as os from 'os'; import * as path from 'path'; import { ANTHROPIC_MODEL_ENV_KEYS, ANTHROPIC_ROUTING_ENV_KEYS } from '../../utils/shell-executor'; +import { EXTRA_EXTENDED_CONTEXT_MODEL_ENV_KEYS } from '../../shared/extended-context-utils'; // SIBLING HELPER: src/utils/openai-compat-launch-settings.ts solves the same // "persisted --settings env clobbers runtime routing env" problem by STRIPPING @@ -38,9 +39,15 @@ import { ANTHROPIC_MODEL_ENV_KEYS, ANTHROPIC_ROUTING_ENV_KEYS } from '../../util * Environment keys that control provider routing/model selection and are read * by Claude from the settings `env` block. These must reflect the resolved * proxy-chain environment, not the persisted on-disk values. Reuses the - * canonical routing/model key lists from shell-executor. + * canonical routing/model key lists from shell-executor, plus the extra model + * keys the [1m] preference manages (subagent + startup default): a saved bare + * value there would otherwise clobber the resolved --1m/--no-1m result. */ -const ROUTING_ENV_KEYS = [...ANTHROPIC_ROUTING_ENV_KEYS, ...ANTHROPIC_MODEL_ENV_KEYS]; +const ROUTING_ENV_KEYS = [ + ...ANTHROPIC_ROUTING_ENV_KEYS, + ...ANTHROPIC_MODEL_ENV_KEYS, + ...EXTRA_EXTENDED_CONTEXT_MODEL_ENV_KEYS, +]; function isRecord(value: unknown): value is Record { return typeof value === 'object' && value !== null && !Array.isArray(value); diff --git a/src/shared/extended-context-utils.ts b/src/shared/extended-context-utils.ts index 8c717720..ebed49c9 100644 --- a/src/shared/extended-context-utils.ts +++ b/src/shared/extended-context-utils.ts @@ -19,8 +19,15 @@ export type AnthropicModelEnvKey = (typeof ANTHROPIC_MODEL_ENV_KEYS)[number]; * model id, so the extended-context preference has to cover them too. Kept * separate from ANTHROPIC_MODEL_ENV_KEYS, which also drives routing, model-id * normalization and profile validation. + * + * ANTHROPIC_DEFAULT_MODEL is Claude Code's lowest-priority startup model + * (after --model, ANTHROPIC_MODEL and the settings `model` field), so it needs + * the same [1m] treatment whenever a profile carries it. */ -export const EXTRA_EXTENDED_CONTEXT_MODEL_ENV_KEYS = ['CLAUDE_CODE_SUBAGENT_MODEL'] as const; +export const EXTRA_EXTENDED_CONTEXT_MODEL_ENV_KEYS = [ + 'CLAUDE_CODE_SUBAGENT_MODEL', + 'ANTHROPIC_DEFAULT_MODEL', +] as const; /** Every model env key the [1m] preference is applied to. */ export const EXTENDED_CONTEXT_MODEL_ENV_KEYS = [ diff --git a/tests/unit/shared/extended-context-utils.test.ts b/tests/unit/shared/extended-context-utils.test.ts index a65af8b1..c1ce474a 100644 --- a/tests/unit/shared/extended-context-utils.test.ts +++ b/tests/unit/shared/extended-context-utils.test.ts @@ -28,13 +28,16 @@ describe('Anthropic model env keys', () => { }); describe('extended-context model env keys', () => { - it('covers the Anthropic tiers plus the subagent model key', () => { + it('covers the Anthropic tiers plus the subagent and startup-default model keys', () => { expect([...EXTENDED_CONTEXT_MODEL_ENV_KEYS]).toEqual([ ...ANTHROPIC_MODEL_ENV_KEYS, 'CLAUDE_CODE_SUBAGENT_MODEL', + 'ANTHROPIC_DEFAULT_MODEL', ]); expect(isExtendedContextModelEnvKey('CLAUDE_CODE_SUBAGENT_MODEL')).toBe(true); + expect(isExtendedContextModelEnvKey('ANTHROPIC_DEFAULT_MODEL')).toBe(true); expect(isAnthropicModelEnvKey('CLAUDE_CODE_SUBAGENT_MODEL')).toBe(false); + expect(isAnthropicModelEnvKey('ANTHROPIC_DEFAULT_MODEL')).toBe(false); }); }); @@ -64,6 +67,7 @@ describe('applyExtendedContextPreferenceToAnthropicModels', () => { const env = applyExtendedContextPreferenceToAnthropicModels( { ANTHROPIC_MODEL: 'claude-opus-5[1m]', + ANTHROPIC_DEFAULT_MODEL: 'claude-opus-5[1m]', ANTHROPIC_DEFAULT_FABLE_MODEL: 'claude-fable-5-1[1m]', CLAUDE_CODE_SUBAGENT_MODEL: 'claude-fable-5-1[1m]', }, @@ -72,11 +76,21 @@ describe('applyExtendedContextPreferenceToAnthropicModels', () => { expect(env).toEqual({ ANTHROPIC_MODEL: 'claude-opus-5', + ANTHROPIC_DEFAULT_MODEL: 'claude-opus-5', ANTHROPIC_DEFAULT_FABLE_MODEL: 'claude-fable-5-1', CLAUDE_CODE_SUBAGENT_MODEL: 'claude-fable-5-1', }); }); + it('suffixes the startup-default model key when the preference is on', () => { + const env = applyExtendedContextPreferenceToAnthropicModels( + { ANTHROPIC_DEFAULT_MODEL: 'claude-opus-5' }, + true + ); + + expect(env).toEqual({ ANTHROPIC_DEFAULT_MODEL: 'claude-opus-5[1m]' }); + }); + it('honors a caller compatibility predicate', () => { const env = applyExtendedContextPreferenceToAnthropicModels( {