@@ -172,6 +172,7 @@ import {
172172 restoreImageGenCallsInJson ,
173173} from "../responses-image-gen-repair" ;
174174import { composeSsePayloadRewrites , relaySseWithPayloadRewrite } from "../sse-payload-rewrite" ;
175+ import { createResponsesModelPayloadRewrite , rewriteResponsesModelJson } from "../responses-model-rewrite" ;
175176import type { EffectiveSubagentRoster , SpawnAgentSurface } from "../../codex/catalog" ;
176177
177178import { buildToolBridgeMaps , collabSurface , injectDeveloperMessage , multiAgentGuidanceText } from "./collaboration" ;
@@ -864,6 +865,12 @@ async function applyFinalRouteRequestNormalization(args: {
864865} ) : Promise < void > {
865866 const { parsed, route, config, req, logCtx, inboundWire, inboundTransport } = args ;
866867
868+ // Only Anthropic message routes retain the Codex-facing selector. Other providers must keep
869+ // their existing response.model contract even when their public and wire model ids differ.
870+ const responseModelId = parsed . modelId ;
871+ const preserveAnthropicResponseModel = route . providerName === "anthropic"
872+ || route . provider . adapter === "anthropic" ;
873+
867874 // Apply the routed model id upstream: routing may strip a "<provider>/" namespace.
868875 if ( route . modelId !== parsed . modelId ) {
869876 if ( parsed . _rawBody && typeof parsed . _rawBody === "object" ) {
@@ -882,6 +889,7 @@ async function applyFinalRouteRequestNormalization(args: {
882889 // Settle the wire once so logging, fast-mode, auth, and sidecars read the adapter
883890 // this request will actually use (#404).
884891 route . provider = resolveWireProtocolOverride ( route . providerName , route . modelId , route . provider , inboundWire ) ;
892+ if ( preserveAnthropicResponseModel ) parsed . _responseModelId = responseModelId ;
885893 logCtx . model = route . modelId ;
886894 logCtx . provider = route . providerName ;
887895 logCtx . providerAdapter = route . provider . adapter ;
@@ -899,6 +907,10 @@ async function applyFinalRouteRequestNormalization(args: {
899907
900908 // Virtual model rewriting: Pro aliases → base model + reasoning.mode="pro".
901909 applyOpenAiVirtualModel ( parsed , route , logCtx ) ;
910+ if ( parsed . _responseModelId !== undefined && parsed . _responseModelId !== parsed . modelId ) {
911+ logCtx . resolvedModel = route . modelId ;
912+ logCtx . preserveResolvedModelFromRoute = true ;
913+ }
902914
903915 // Fast mode override for OpenAI-routed models, only where the provider's Responses
904916 // route documents `service_tier` support (capability gate below strips everywhere else).
@@ -2061,13 +2073,21 @@ async function handleResponsesInner(
20612073 if ( isEventStream && upstreamResponse . body ) {
20622074 const repairConfig = route . provider . responsesItemIdRepair ;
20632075 const snapshotRepairEnabled = hasResponsesSnapshotRepair ( route . provider . responsesSnapshotRepair ) ;
2064- const needsClientRewrite = imageGenCallAliases . size > 0 || hasResponsesItemIdRepair ( repairConfig ) || snapshotRepairEnabled ;
2076+ const responseModelRewrite = parsed . _responseModelId !== undefined
2077+ && parsed . _responseModelId !== parsed . modelId
2078+ ? createResponsesModelPayloadRewrite ( parsed . _responseModelId )
2079+ : undefined ;
2080+ const needsClientRewrite = imageGenCallAliases . size > 0
2081+ || hasResponsesItemIdRepair ( repairConfig )
2082+ || snapshotRepairEnabled
2083+ || responseModelRewrite !== undefined ;
20652084 // Compose opt-in payload rewrites into one parse/stringify pass (image-gen restore first).
20662085 const payloadRewrites = [
20672086 createImageGenCallRestoreRewrite ( imageGenCallAliases ) ,
20682087 hasResponsesItemIdRepair ( repairConfig )
20692088 ? createResponsesItemIdPayloadRewrite ( repairConfig ! , translatorBudget )
20702089 : undefined ,
2090+ responseModelRewrite ,
20712091 ] . filter ( ( rewrite ) : rewrite is NonNullable < typeof rewrite > => rewrite !== undefined ) ;
20722092 // #893: sparse-snapshot gateways get field backfills AND lifecycle event
20732093 // injection at the block level, after payload rewrites. Defaults come
@@ -2259,14 +2279,19 @@ async function handleResponsesInner(
22592279 }
22602280 const clientJson = ( ( ) => {
22612281 const restored = restoreImageGenCallsInJson ( text , imageGenCallAliases ) ;
2262- if ( ! hasResponsesSnapshotRepair ( route . provider . responsesSnapshotRepair ) ) return restored ;
2263- let outbound : unknown ;
2264- try {
2265- outbound = JSON . parse ( request . body ) ;
2266- } catch {
2267- outbound = undefined ;
2268- }
2269- return repairResponsesSnapshotJson ( restored , outbound ) ;
2282+ const repaired = ( ( ) => {
2283+ if ( ! hasResponsesSnapshotRepair ( route . provider . responsesSnapshotRepair ) ) return restored ;
2284+ let outbound : unknown ;
2285+ try {
2286+ outbound = JSON . parse ( request . body ) ;
2287+ } catch {
2288+ outbound = undefined ;
2289+ }
2290+ return repairResponsesSnapshotJson ( restored , outbound ) ;
2291+ } ) ( ) ;
2292+ return parsed . _responseModelId !== undefined && parsed . _responseModelId !== parsed . modelId
2293+ ? rewriteResponsesModelJson ( repaired , parsed . _responseModelId )
2294+ : repaired ;
22702295 } ) ( ) ;
22712296 // #875: the transport-neutral reliability policy forced a bounded JSON
22722297 // upstream for a client that asked for SSE. Reframe the completed JSON
@@ -2569,7 +2594,7 @@ async function handleResponsesInner(
25692594 eventSource = preflight . stream ;
25702595 }
25712596 const sseStream = bridgeToResponsesSSE (
2572- eventSource , parsed . modelId , toolNsMap , freeformToolNames , toolSearchToolNames ,
2597+ eventSource , parsed . _responseModelId ?? parsed . modelId , toolNsMap , freeformToolNames , toolSearchToolNames ,
25732598 ( ) => {
25742599 runTurnAbort . abort ( ) ;
25752600 queue . close ( ) ;
@@ -2621,7 +2646,7 @@ async function handleResponsesInner(
26212646 }
26222647 }
26232648 let providerState : OcxProviderContinuationState | undefined ;
2624- const json = buildResponseJSON ( events , parsed . modelId , {
2649+ const json = buildResponseJSON ( events , parsed . _responseModelId ?? parsed . modelId , {
26252650 translatorBudget,
26262651 replayCacheScope : parsed . _clientThreadId ?? "global" ,
26272652 hideThinkingSummary : parsed . options . hideThinkingSummary ,
@@ -3263,7 +3288,7 @@ async function handleResponsesInner(
32633288 : initialEventStream ;
32643289 const { toolNsMap, freeformToolNames, toolSearchToolNames } = toolBridgeMaps ;
32653290 const sseStream = bridgeToResponsesSSE (
3266- eventStream , parsed . modelId , toolNsMap , freeformToolNames , toolSearchToolNames ,
3291+ eventStream , parsed . _responseModelId ?? parsed . modelId , toolNsMap , freeformToolNames , toolSearchToolNames ,
32673292 ( ) => upstream . abort ( ) , 2_000 ,
32683293 {
32693294 translatorBudget,
@@ -3323,7 +3348,7 @@ async function handleResponsesInner(
33233348 }
33243349 const { toolNsMap, freeformToolNames, toolSearchToolNames } = toolBridgeMaps ;
33253350 let providerState : OcxProviderContinuationState | undefined ;
3326- const json = buildResponseJSON ( events , parsed . modelId , {
3351+ const json = buildResponseJSON ( events , parsed . _responseModelId ?? parsed . modelId , {
33273352 translatorBudget,
33283353 replayCacheScope : parsed . _clientThreadId ?? "global" ,
33293354 hideThinkingSummary : parsed . options . hideThinkingSummary ,
0 commit comments