From 1058b7e64395da789f9a43a52f0c398bd8f39e6e Mon Sep 17 00:00:00 2001 From: claude-code-best Date: Sun, 26 Apr 2026 23:38:56 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BF=AE=E5=A4=8D=20Codex=20=E6=A8=A1?= =?UTF-8?q?=E5=9E=8B=E6=98=A0=E5=B0=84=E5=B9=B6=E6=B7=BB=E5=8A=A0=E7=99=BB?= =?UTF-8?q?=E5=BD=95=E5=90=8E=E6=A8=A1=E5=9E=8B=E9=85=8D=E7=BD=AE=E9=9D=A2?= =?UTF-8?q?=E6=9D=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - configs.ts: 将 codex 字段从 Anthropic 模型名改为实际 OpenAI 模型名 (opus→gpt-5.4, sonnet→gpt-5.4-mini, haiku→gpt-5.4-mini, opus47→gpt-5.5) - modelMapping.ts: 移除不存在的 gpt-5.4-nano,修复 haiku 映射,添加 opus47 - ConsoleOAuthFlow.tsx: OAuth 成功后显示模型配置面板,可编辑三种模型名称 - 已登录用户再次选择 Codex 时跳过 OAuth 直接进入模型配置 - Ctrl+R 快捷键清除登录状态并重新 OAuth - modelOptions.ts: codex provider 支持 CODEX_DEFAULT_*_MODEL 环境变量 Co-Authored-By: Claude Opus 4.7 --- .../codex/__tests__/modelMapping.test.ts | 6 +- .../src/providers/codex/modelMapping.ts | 7 +- src/components/ConsoleOAuthFlow.tsx | 305 ++++++++++++++++-- src/keybindings/defaultBindings.ts | 2 + src/keybindings/schema.ts | 2 + src/utils/model/configs.ts | 24 +- src/utils/model/modelOptions.ts | 36 ++- 7 files changed, 330 insertions(+), 52 deletions(-) diff --git a/packages/@ant/model-provider/src/providers/codex/__tests__/modelMapping.test.ts b/packages/@ant/model-provider/src/providers/codex/__tests__/modelMapping.test.ts index cbd7be7d9..6a7711a73 100644 --- a/packages/@ant/model-provider/src/providers/codex/__tests__/modelMapping.test.ts +++ b/packages/@ant/model-provider/src/providers/codex/__tests__/modelMapping.test.ts @@ -45,7 +45,7 @@ describe('resolveCodexModel', () => { }) test('maps known haiku model via DEFAULT_MODEL_MAP', () => { - expect(resolveCodexModel('claude-haiku-4-5-20251001')).toBe('gpt-5.4-nano') + expect(resolveCodexModel('claude-haiku-4-5-20251001')).toBe('gpt-5.4-mini') }) test('maps known opus model via DEFAULT_MODEL_MAP', () => { @@ -58,7 +58,7 @@ describe('resolveCodexModel', () => { }) test('maps legacy haiku models', () => { - expect(resolveCodexModel('claude-3-5-haiku-20241022')).toBe('gpt-5.4-nano') + expect(resolveCodexModel('claude-3-5-haiku-20241022')).toBe('gpt-5.4-mini') }) test('maps legacy opus models', () => { @@ -67,7 +67,7 @@ describe('resolveCodexModel', () => { }) test('uses family default for unrecognized haiku model', () => { - expect(resolveCodexModel('claude-haiku-99')).toBe('gpt-5.4-nano') + expect(resolveCodexModel('claude-haiku-99')).toBe('gpt-5.4-mini') }) test('uses family default for unrecognized sonnet model', () => { diff --git a/packages/@ant/model-provider/src/providers/codex/modelMapping.ts b/packages/@ant/model-provider/src/providers/codex/modelMapping.ts index 3bee191b4..ea70b8d7c 100644 --- a/packages/@ant/model-provider/src/providers/codex/modelMapping.ts +++ b/packages/@ant/model-provider/src/providers/codex/modelMapping.ts @@ -12,15 +12,16 @@ const DEFAULT_MODEL_MAP: Record = { 'claude-opus-4-1-20250805': 'gpt-5.4', 'claude-opus-4-5-20251101': 'gpt-5.4', 'claude-opus-4-6': 'gpt-5.4', - 'claude-haiku-4-5-20251001': 'gpt-5.4-nano', - 'claude-3-5-haiku-20241022': 'gpt-5.4-nano', + 'claude-opus-4-7': 'gpt-5.5', + 'claude-haiku-4-5-20251001': 'gpt-5.4-mini', + 'claude-3-5-haiku-20241022': 'gpt-5.4-mini', } /** * Default model for each family when an exact match is not in DEFAULT_MODEL_MAP. */ const DEFAULT_FAMILY_MAP: Record = { - haiku: 'gpt-5.4-nano', + haiku: 'gpt-5.4-mini', sonnet: 'gpt-5.4-mini', opus: 'gpt-5.4', } diff --git a/src/components/ConsoleOAuthFlow.tsx b/src/components/ConsoleOAuthFlow.tsx index 1270f84e9..58e7cc5fe 100644 --- a/src/components/ConsoleOAuthFlow.tsx +++ b/src/components/ConsoleOAuthFlow.tsx @@ -58,6 +58,18 @@ type OAuthStatus = } // Gemini Generate Content API platform | { state: 'codex_oauth_waiting'; url: string } // ChatGPT OAuth browser login in progress | { state: 'codex_oauth_start' } // Trigger ChatGPT OAuth flow + | { + state: 'codex_models' + haikuModel: string + sonnetModel: string + opusModel: string + activeField: 'haiku_model' | 'sonnet_model' | 'opus_model' + codexResult: { + apiKey: string | null + accessToken: string + refreshToken: string + } + } // Codex model name configuration after OAuth success | { state: 'ready_to_start' } // Flow started, waiting for browser to open | { state: 'waiting_for_login'; url: string } // Browser opened, waiting for user to login | { state: 'creating_api_key' } // Got access token, creating API key @@ -365,31 +377,19 @@ export function ConsoleOAuthFlow({ manualCode: manualCodePromise, }) - const env: Record = { - CODEX_API_KEY: result.apiKey ?? undefined, - CODEX_ACCESS_TOKEN: result.accessToken, - CODEX_REFRESH_TOKEN: result.refreshToken, - CODEX_LOGIN_METHOD: 'chatgpt_subscription', - } - updateSettingsForSource('userSettings', { - modelType: 'codex', - env, - } as any) - for (const [key, value] of Object.entries(env)) { - if (value !== undefined) { - process.env[key] = value - } - } - - setOAuthStatus({ state: 'success' }) - void sendNotification( - { - message: 'OpenAI Codex (ChatGPT) login successful', - notificationType: 'auth_success', + // Transition to model configuration panel with defaults + setOAuthStatus({ + state: 'codex_models', + haikuModel: process.env.CODEX_DEFAULT_HAIKU_MODEL || 'gpt-5.4-mini', + sonnetModel: process.env.CODEX_DEFAULT_SONNET_MODEL || 'gpt-5.4-mini', + opusModel: process.env.CODEX_DEFAULT_OPUS_MODEL || 'gpt-5.5', + activeField: 'haiku_model', + codexResult: { + apiKey: result.apiKey, + accessToken: result.accessToken, + refreshToken: result.refreshToken, }, - terminal, - ) - onDone() + }) } catch (err) { logError(err as Error) setOAuthStatus({ @@ -714,7 +714,37 @@ function OAuthStatusMessage({ }) } else if (value === 'codex_chatgpt') { logEvent('tengu_codex_chatgpt_selected', {}) - setOAuthStatus({ state: 'codex_oauth_start' }) + // Skip OAuth if already authenticated — go straight to model config + const settings = getSettings_DEPRECATED() + const hasToken = !!( + process.env.CODEX_ACCESS_TOKEN || + settings?.env?.CODEX_ACCESS_TOKEN + ) + if (hasToken) { + setOAuthStatus({ + state: 'codex_models', + haikuModel: + process.env.CODEX_DEFAULT_HAIKU_MODEL || + settings?.env?.CODEX_DEFAULT_HAIKU_MODEL || + 'gpt-5.4-mini', + sonnetModel: + process.env.CODEX_DEFAULT_SONNET_MODEL || + settings?.env?.CODEX_DEFAULT_SONNET_MODEL || + 'gpt-5.4-mini', + opusModel: + process.env.CODEX_DEFAULT_OPUS_MODEL || + settings?.env?.CODEX_DEFAULT_OPUS_MODEL || + 'gpt-5.5', + activeField: 'haiku_model', + codexResult: { + apiKey: process.env.CODEX_API_KEY || null, + accessToken: process.env.CODEX_ACCESS_TOKEN || '', + refreshToken: process.env.CODEX_REFRESH_TOKEN || '', + }, + }) + } else { + setOAuthStatus({ state: 'codex_oauth_start' }) + } } else if (value === 'gemini_api') { logEvent('tengu_gemini_api_selected', {}) setOAuthStatus({ @@ -1489,6 +1519,231 @@ function OAuthStatusMessage({ ) } + case 'codex_models': { + type CodexField = 'haiku_model' | 'sonnet_model' | 'opus_model' + const CODEX_FIELDS: CodexField[] = ['haiku_model', 'sonnet_model', 'opus_model'] + const cm = oauthStatus as { + state: 'codex_models' + activeField: CodexField + haikuModel: string + sonnetModel: string + opusModel: string + codexResult: { apiKey: string | null; accessToken: string; refreshToken: string } + } + const { activeField, haikuModel, sonnetModel, opusModel, codexResult } = cm + const codexDisplayValues: Record = { + haiku_model: haikuModel, + sonnet_model: sonnetModel, + opus_model: opusModel, + } + + const [codexModelInput, setCodexModelInput] = useState( + () => codexDisplayValues[activeField], + ) + const [codexModelCursor, setCodexModelCursor] = useState( + () => codexDisplayValues[activeField].length, + ) + + const buildCodexModelState = useCallback( + (field: CodexField, value: string, newActive?: CodexField) => { + const s = { + state: 'codex_models' as const, + activeField: newActive ?? activeField, + haikuModel, + sonnetModel, + opusModel, + codexResult, + } + switch (field) { + case 'haiku_model': + return { ...s, haikuModel: value } + case 'sonnet_model': + return { ...s, sonnetModel: value } + case 'opus_model': + return { ...s, opusModel: value } + } + }, + [activeField, haikuModel, sonnetModel, opusModel, codexResult], + ) + + const doCodexModelSave = useCallback(() => { + const finalVals = { ...codexDisplayValues, [activeField]: codexModelInput } + const env: Record = { + CODEX_API_KEY: codexResult.apiKey ?? undefined, + CODEX_ACCESS_TOKEN: codexResult.accessToken, + CODEX_REFRESH_TOKEN: codexResult.refreshToken, + CODEX_LOGIN_METHOD: 'chatgpt_subscription', + CODEX_DEFAULT_HAIKU_MODEL: finalVals.haiku_model, + CODEX_DEFAULT_SONNET_MODEL: finalVals.sonnet_model, + CODEX_DEFAULT_OPUS_MODEL: finalVals.opus_model, + } + const { error } = updateSettingsForSource('userSettings', { + modelType: 'codex' as any, + env, + } as any) + if (error) { + setOAuthStatus({ + state: 'error', + message: 'Failed to save settings. Please try again.', + toRetry: { + state: 'codex_models', + haikuModel: finalVals.haiku_model, + sonnetModel: finalVals.sonnet_model, + opusModel: finalVals.opus_model, + activeField: 'haiku_model', + codexResult, + }, + }) + } else { + for (const [k, v] of Object.entries(env)) { + if (v !== undefined) { + process.env[k] = v + } + } + setOAuthStatus({ state: 'success' }) + void onDone() + } + }, [activeField, codexModelInput, codexDisplayValues, codexResult, setOAuthStatus, onDone]) + + const handleCodexModelEnter = useCallback(() => { + const idx = CODEX_FIELDS.indexOf(activeField) + if (idx === CODEX_FIELDS.length - 1) { + setOAuthStatus(buildCodexModelState(activeField, codexModelInput)) + doCodexModelSave() + } else { + const next = CODEX_FIELDS[idx + 1]! + setOAuthStatus(buildCodexModelState(activeField, codexModelInput, next)) + setCodexModelInput(codexDisplayValues[next] ?? '') + setCodexModelCursor((codexDisplayValues[next] ?? '').length) + } + }, [ + activeField, + codexModelInput, + buildCodexModelState, + doCodexModelSave, + codexDisplayValues, + setOAuthStatus, + ]) + + useKeybinding( + 'tabs:next', + () => { + const idx = CODEX_FIELDS.indexOf(activeField) + if (idx < CODEX_FIELDS.length - 1) { + setOAuthStatus( + buildCodexModelState(activeField, codexModelInput, CODEX_FIELDS[idx + 1]), + ) + setCodexModelInput(codexDisplayValues[CODEX_FIELDS[idx + 1]!] ?? '') + setCodexModelCursor((codexDisplayValues[CODEX_FIELDS[idx + 1]!] ?? '').length) + } + }, + { context: 'FormField' }, + ) + useKeybinding( + 'tabs:previous', + () => { + const idx = CODEX_FIELDS.indexOf(activeField) + if (idx > 0) { + setOAuthStatus( + buildCodexModelState(activeField, codexModelInput, CODEX_FIELDS[idx - 1]), + ) + setCodexModelInput(codexDisplayValues[CODEX_FIELDS[idx - 1]!] ?? '') + setCodexModelCursor((codexDisplayValues[CODEX_FIELDS[idx - 1]!] ?? '').length) + } + }, + { context: 'FormField' }, + ) + useKeybinding( + 'confirm:no', + () => { + setOAuthStatus({ state: 'idle' }) + }, + { context: 'Confirmation' }, + ) + + // Ctrl+D: clear codex login state and re-login + useKeybinding( + 'oauth:codex-relogin', + () => { + // Clear codex credentials from process.env + delete process.env.CODEX_ACCESS_TOKEN + delete process.env.CODEX_REFRESH_TOKEN + delete process.env.CODEX_API_KEY + delete process.env.CODEX_LOGIN_METHOD + delete process.env.CODEX_DEFAULT_HAIKU_MODEL + delete process.env.CODEX_DEFAULT_SONNET_MODEL + delete process.env.CODEX_DEFAULT_OPUS_MODEL + // Clear from settings.json + updateSettingsForSource('userSettings', { + modelType: undefined, + env: { + CODEX_ACCESS_TOKEN: undefined, + CODEX_REFRESH_TOKEN: undefined, + CODEX_API_KEY: undefined, + CODEX_LOGIN_METHOD: undefined, + CODEX_DEFAULT_HAIKU_MODEL: undefined, + CODEX_DEFAULT_SONNET_MODEL: undefined, + CODEX_DEFAULT_OPUS_MODEL: undefined, + }, + } as any) + // Restart OAuth flow + setOAuthStatus({ state: 'codex_oauth_start' }) + }, + { context: 'FormField' }, + ) + + const codexModelColumns = useTerminalSize().columns - 20 + + const renderCodexModelRow = ( + field: CodexField, + label: string, + ) => { + const active = activeField === field + const val = codexDisplayValues[field] + return ( + + + {` ${label} `} + + + {active ? ( + + ) : val ? ( + {val} + ) : null} + + ) + } + + return ( + + Codex Model Configuration + + ChatGPT login successful. Configure model names (press Enter on last field to save). + + + {renderCodexModelRow('haiku_model', 'Haiku ')} + {renderCodexModelRow('sonnet_model', 'Sonnet ')} + {renderCodexModelRow('opus_model', 'Opus ')} + + + ↑↓/Tab to switch · Enter on last field to save · Ctrl+R to re-login · Esc to go back + + + ) + } + case 'platform_setup': return ( diff --git a/src/keybindings/defaultBindings.ts b/src/keybindings/defaultBindings.ts index 1d9ef10e2..9e1063ac7 100644 --- a/src/keybindings/defaultBindings.ts +++ b/src/keybindings/defaultBindings.ts @@ -156,6 +156,8 @@ export const DEFAULT_BINDINGS: KeybindingBlock[] = [ 'shift+tab': 'tabs:previous', up: 'tabs:previous', down: 'tabs:next', + // Re-login: clear codex credentials and restart OAuth + 'ctrl+r': 'oauth:codex-relogin', }, }, { diff --git a/src/keybindings/schema.ts b/src/keybindings/schema.ts index 83e6fb28d..231e7d106 100644 --- a/src/keybindings/schema.ts +++ b/src/keybindings/schema.ts @@ -109,6 +109,8 @@ export const KEYBINDING_ACTIONS = [ // Tabs navigation actions 'tabs:next', 'tabs:previous', + // OAuth re-login action (codex model config panel) + 'oauth:codex-relogin', // Transcript viewer actions 'transcript:toggleShowAll', 'transcript:exit', diff --git a/src/utils/model/configs.ts b/src/utils/model/configs.ts index 3a1bbf617..0cd23aa86 100644 --- a/src/utils/model/configs.ts +++ b/src/utils/model/configs.ts @@ -13,7 +13,7 @@ export const CLAUDE_3_7_SONNET_CONFIG = { foundry: 'claude-3-7-sonnet', openai: 'claude-3-7-sonnet-20250219', gemini: 'claude-3-7-sonnet-20250219', - codex: 'claude-3-7-sonnet-20250219', + codex: 'gpt-5.4-mini', grok: 'claude-3-7-sonnet-20250219', } as const satisfies ModelConfig @@ -24,7 +24,7 @@ export const CLAUDE_3_5_V2_SONNET_CONFIG = { foundry: 'claude-3-5-sonnet', openai: 'claude-3-5-sonnet-20241022', gemini: 'claude-3-5-sonnet-20241022', - codex: 'claude-3-5-sonnet-20241022', + codex: 'gpt-5.4-mini', grok: 'claude-3-5-sonnet-20241022', } as const satisfies ModelConfig @@ -35,7 +35,7 @@ export const CLAUDE_3_5_HAIKU_CONFIG = { foundry: 'claude-3-5-haiku', openai: 'claude-3-5-haiku-20241022', gemini: 'claude-3-5-haiku-20241022', - codex: 'claude-3-5-haiku-20241022', + codex: 'gpt-5.4-mini', grok: 'claude-3-5-haiku-20241022', } as const satisfies ModelConfig @@ -46,7 +46,7 @@ export const CLAUDE_HAIKU_4_5_CONFIG = { foundry: 'claude-haiku-4-5', openai: 'claude-haiku-4-5-20251001', gemini: 'claude-haiku-4-5-20251001', - codex: 'claude-haiku-4-5-20251001', + codex: 'gpt-5.4-mini', grok: 'claude-haiku-4-5-20251001', } as const satisfies ModelConfig @@ -57,7 +57,7 @@ export const CLAUDE_SONNET_4_CONFIG = { foundry: 'claude-sonnet-4', openai: 'claude-sonnet-4-20250514', gemini: 'claude-sonnet-4-20250514', - codex: 'claude-sonnet-4-20250514', + codex: 'gpt-5.4-mini', grok: 'claude-sonnet-4-20250514', } as const satisfies ModelConfig @@ -68,7 +68,7 @@ export const CLAUDE_SONNET_4_5_CONFIG = { foundry: 'claude-sonnet-4-5', openai: 'claude-sonnet-4-5-20250929', gemini: 'claude-sonnet-4-5-20250929', - codex: 'claude-sonnet-4-5-20250929', + codex: 'gpt-5.4-mini', grok: 'claude-sonnet-4-5-20250929', } as const satisfies ModelConfig @@ -79,7 +79,7 @@ export const CLAUDE_OPUS_4_CONFIG = { foundry: 'claude-opus-4', openai: 'claude-opus-4-20250514', gemini: 'claude-opus-4-20250514', - codex: 'claude-opus-4-20250514', + codex: 'gpt-5.4', grok: 'claude-opus-4-20250514', } as const satisfies ModelConfig @@ -90,7 +90,7 @@ export const CLAUDE_OPUS_4_1_CONFIG = { foundry: 'claude-opus-4-1', openai: 'claude-opus-4-1-20250805', gemini: 'claude-opus-4-1-20250805', - codex: 'claude-opus-4-1-20250805', + codex: 'gpt-5.4', grok: 'claude-opus-4-1-20250805', } as const satisfies ModelConfig @@ -101,7 +101,7 @@ export const CLAUDE_OPUS_4_5_CONFIG = { foundry: 'claude-opus-4-5', openai: 'claude-opus-4-5-20251101', gemini: 'claude-opus-4-5-20251101', - codex: 'claude-opus-4-5-20251101', + codex: 'gpt-5.4', grok: 'claude-opus-4-5-20251101', } as const satisfies ModelConfig @@ -112,7 +112,7 @@ export const CLAUDE_OPUS_4_6_CONFIG = { foundry: 'claude-opus-4-6', openai: 'claude-opus-4-6', gemini: 'claude-opus-4-6', - codex: 'claude-opus-4-6', + codex: 'gpt-5.4', grok: 'claude-opus-4-6', } as const satisfies ModelConfig @@ -123,7 +123,7 @@ export const CLAUDE_OPUS_4_7_CONFIG = { foundry: 'claude-opus-4-7', openai: 'claude-opus-4-7', gemini: 'claude-opus-4-7', - codex: 'claude-opus-4-7', + codex: 'gpt-5.5', grok: 'claude-opus-4-7', } as const satisfies ModelConfig @@ -134,7 +134,7 @@ export const CLAUDE_SONNET_4_6_CONFIG = { foundry: 'claude-sonnet-4-6', openai: 'claude-sonnet-4-6', gemini: 'claude-sonnet-4-6', - codex: 'claude-sonnet-4-6', + codex: 'gpt-5.4-mini', grok: 'claude-sonnet-4-6', } as const satisfies ModelConfig diff --git a/src/utils/model/modelOptions.ts b/src/utils/model/modelOptions.ts index 754963955..7e4e4d55a 100644 --- a/src/utils/model/modelOptions.ts +++ b/src/utils/model/modelOptions.ts @@ -83,7 +83,9 @@ function getCustomSonnetOption(): ModelOption | undefined { ? process.env.OPENAI_DEFAULT_SONNET_MODEL : provider === 'gemini' ? process.env.GEMINI_DEFAULT_SONNET_MODEL - : process.env.ANTHROPIC_DEFAULT_SONNET_MODEL + : provider === 'codex' + ? process.env.CODEX_DEFAULT_SONNET_MODEL + : process.env.ANTHROPIC_DEFAULT_SONNET_MODEL // When a 3P user has a custom sonnet model string, show it directly if (is3P && customSonnetModel) { const is1m = has1mContext(customSonnetModel) @@ -93,13 +95,17 @@ function getCustomSonnetOption(): ModelOption | undefined { ? process.env.OPENAI_DEFAULT_SONNET_MODEL_NAME : provider === 'gemini' ? process.env.GEMINI_DEFAULT_SONNET_MODEL_NAME - : process.env.ANTHROPIC_DEFAULT_SONNET_MODEL_NAME + : provider === 'codex' + ? process.env.CODEX_DEFAULT_SONNET_MODEL_NAME + : process.env.ANTHROPIC_DEFAULT_SONNET_MODEL_NAME const descEnv = provider === 'openai' ? process.env.OPENAI_DEFAULT_SONNET_MODEL_DESCRIPTION : provider === 'gemini' ? process.env.GEMINI_DEFAULT_SONNET_MODEL_DESCRIPTION - : process.env.ANTHROPIC_DEFAULT_SONNET_MODEL_DESCRIPTION + : provider === 'codex' + ? process.env.CODEX_DEFAULT_SONNET_MODEL_DESCRIPTION + : process.env.ANTHROPIC_DEFAULT_SONNET_MODEL_DESCRIPTION return { value: 'sonnet', label: nameEnv ?? customSonnetModel, @@ -132,7 +138,9 @@ function getCustomOpusOption(): ModelOption | undefined { ? process.env.OPENAI_DEFAULT_OPUS_MODEL : provider === 'gemini' ? process.env.GEMINI_DEFAULT_OPUS_MODEL - : process.env.ANTHROPIC_DEFAULT_OPUS_MODEL + : provider === 'codex' + ? process.env.CODEX_DEFAULT_OPUS_MODEL + : process.env.ANTHROPIC_DEFAULT_OPUS_MODEL // When a 3P user has a custom opus model string, show it directly if (is3P && customOpusModel) { const is1m = has1mContext(customOpusModel) @@ -142,13 +150,17 @@ function getCustomOpusOption(): ModelOption | undefined { ? process.env.OPENAI_DEFAULT_OPUS_MODEL_NAME : provider === 'gemini' ? process.env.GEMINI_DEFAULT_OPUS_MODEL_NAME - : process.env.ANTHROPIC_DEFAULT_OPUS_MODEL_NAME + : provider === 'codex' + ? process.env.CODEX_DEFAULT_OPUS_MODEL_NAME + : process.env.ANTHROPIC_DEFAULT_OPUS_MODEL_NAME const descEnv = provider === 'openai' ? process.env.OPENAI_DEFAULT_OPUS_MODEL_DESCRIPTION : provider === 'gemini' ? process.env.GEMINI_DEFAULT_OPUS_MODEL_DESCRIPTION - : process.env.ANTHROPIC_DEFAULT_OPUS_MODEL_DESCRIPTION + : provider === 'codex' + ? process.env.CODEX_DEFAULT_OPUS_MODEL_DESCRIPTION + : process.env.ANTHROPIC_DEFAULT_OPUS_MODEL_DESCRIPTION return { value: 'opus', label: nameEnv ?? customOpusModel, @@ -232,7 +244,9 @@ function getCustomHaikuOption(): ModelOption | undefined { ? process.env.OPENAI_DEFAULT_HAIKU_MODEL : provider === 'gemini' ? process.env.GEMINI_DEFAULT_HAIKU_MODEL - : process.env.ANTHROPIC_DEFAULT_HAIKU_MODEL + : provider === 'codex' + ? process.env.CODEX_DEFAULT_HAIKU_MODEL + : process.env.ANTHROPIC_DEFAULT_HAIKU_MODEL // When a 3P user has a custom haiku model string, show it directly if (is3P && customHaikuModel) { // Use appropriate NAME/DESCRIPTION env vars based on provider @@ -241,13 +255,17 @@ function getCustomHaikuOption(): ModelOption | undefined { ? process.env.OPENAI_DEFAULT_HAIKU_MODEL_NAME : provider === 'gemini' ? process.env.GEMINI_DEFAULT_HAIKU_MODEL_NAME - : process.env.ANTHROPIC_DEFAULT_HAIKU_MODEL_NAME + : provider === 'codex' + ? process.env.CODEX_DEFAULT_HAIKU_MODEL_NAME + : process.env.ANTHROPIC_DEFAULT_HAIKU_MODEL_NAME const descEnv = provider === 'openai' ? process.env.OPENAI_DEFAULT_HAIKU_MODEL_DESCRIPTION : provider === 'gemini' ? process.env.GEMINI_DEFAULT_HAIKU_MODEL_DESCRIPTION - : process.env.ANTHROPIC_DEFAULT_HAIKU_MODEL_DESCRIPTION + : provider === 'codex' + ? process.env.CODEX_DEFAULT_HAIKU_MODEL_DESCRIPTION + : process.env.ANTHROPIC_DEFAULT_HAIKU_MODEL_DESCRIPTION return { value: 'haiku', label: nameEnv ?? customHaikuModel,