From fbf33853956471f737e466583a2f17a598007818 Mon Sep 17 00:00:00 2001 From: Juan Camilo Auriti Date: Sat, 4 Apr 2026 11:38:47 +0200 Subject: [PATCH] fix: prevent cross-provider model env var leaks and sync Codex detection (#243) Two provider routing bugs that cause silent wrong-model failures: 1. model.ts: getUserSpecifiedModelSetting() read ANTHROPIC_MODEL || GEMINI_MODEL || OPENAI_MODEL with no provider check. A user switching from Anthropic to OpenAI with ANTHROPIC_MODEL still set would silently send the Anthropic model name to the OpenAI API. Now gates each env var behind the active provider from getAPIProvider(). 2. providers.ts: isCodexModel() maintained a hardcoded list of 8 model names that was missing gpt-5.4-mini and gpt-5.2 from the canonical CODEX_ALIAS_MODELS table in providerConfig.ts. This caused a split-brain: getAPIProvider() returned 'openai' while resolveProviderRequest() selected 'codex_responses' transport. Now delegates to the exported isCodexAlias() to keep both detection systems in sync. --- src/services/api/providerConfig.ts | 2 +- src/utils/model/model.ts | 10 +++++++++- src/utils/model/providers.ts | 17 ++++++----------- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/services/api/providerConfig.ts b/src/services/api/providerConfig.ts index 0e5fe44d..24de14c3 100644 --- a/src/services/api/providerConfig.ts +++ b/src/services/api/providerConfig.ts @@ -201,7 +201,7 @@ function parseModelDescriptor(model: string): ModelDescriptor { } } -function isCodexAlias(model: string): boolean { +export function isCodexAlias(model: string): boolean { const normalized = model.trim().toLowerCase() const base = normalized.split('?', 1)[0] ?? normalized return base in CODEX_ALIAS_MODELS diff --git a/src/utils/model/model.ts b/src/utils/model/model.ts index 97a74d95..002490ee 100644 --- a/src/utils/model/model.ts +++ b/src/utils/model/model.ts @@ -75,7 +75,15 @@ export function getUserSpecifiedModelSetting(): ModelSetting | undefined { specifiedModel = modelOverride } else { const settings = getSettings_DEPRECATED() || {} - specifiedModel = process.env.ANTHROPIC_MODEL || process.env.GEMINI_MODEL || process.env.OPENAI_MODEL || settings.model || undefined + // Read the model env var that matches the active provider to prevent + // cross-provider leaks (e.g. ANTHROPIC_MODEL sent to the OpenAI API). + const provider = getAPIProvider() + specifiedModel = + (provider === 'gemini' ? process.env.GEMINI_MODEL : undefined) || + (provider === 'openai' || provider === 'gemini' ? process.env.OPENAI_MODEL : undefined) || + (provider === 'firstParty' ? process.env.ANTHROPIC_MODEL : undefined) || + settings.model || + undefined } // Ignore the user-specified model if it's not in the availableModels allowlist. diff --git a/src/utils/model/providers.ts b/src/utils/model/providers.ts index 6b6d627e..2c25163b 100644 --- a/src/utils/model/providers.ts +++ b/src/utils/model/providers.ts @@ -1,4 +1,5 @@ import type { AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS } from '../../services/analytics/index.js' +import { isCodexAlias } from '../../services/api/providerConfig.js' import { isEnvTruthy } from '../envUtils.js' export type APIProvider = @@ -33,17 +34,11 @@ export function usesAnthropicAccountFlow(): boolean { return getAPIProvider() === 'firstParty' } function isCodexModel(): boolean { - const model = (process.env.OPENAI_MODEL || '').toLowerCase() - return ( - model === 'codexplan' || - model === 'codexspark' || - model === 'gpt-5.4' || - model === 'gpt-5.3-codex' || - model === 'gpt-5.3-codex-spark' || - model === 'gpt-5.2-codex' || - model === 'gpt-5.1-codex-max' || - model === 'gpt-5.1-codex-mini' - ) + const model = (process.env.OPENAI_MODEL || '').trim() + if (!model) return false + // Delegate to the canonical alias table in providerConfig to keep + // the two Codex detection systems (provider type + transport) in sync. + return isCodexAlias(model) } export function getAPIProviderForStatsig(): AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS {