77 existsSync ,
88 mkdirSync ,
99 mkdtempSync ,
10+ renameSync ,
1011 readFileSync ,
1112 rmSync ,
1213 statSync ,
@@ -215,6 +216,47 @@ const REASONING_FALLBACKS = {
215216} ;
216217
217218const KNOWN_REASONING_EFFORTS = new Set ( Object . keys ( REASONING_FALLBACKS ) ) ;
219+ const REQUESTED_MODEL_ALIASES = new Map ( ) ;
220+
221+ function addRequestedModelAlias ( alias , normalizedModel ) {
222+ REQUESTED_MODEL_ALIASES . set ( alias , normalizedModel ) ;
223+ }
224+
225+ function addRequestedModelReasoningAliases ( alias , normalizedModel ) {
226+ addRequestedModelAlias ( alias , normalizedModel ) ;
227+ for ( const effort of KNOWN_REASONING_EFFORTS ) {
228+ addRequestedModelAlias ( `${ alias } -${ effort } ` , normalizedModel ) ;
229+ }
230+ }
231+
232+ function seedRequestedModelAliases ( ) {
233+ addRequestedModelReasoningAliases ( "gpt-5.4" , "gpt-5.4" ) ;
234+ addRequestedModelReasoningAliases ( "gpt-5.4-pro" , "gpt-5.4-pro" ) ;
235+ addRequestedModelReasoningAliases ( "gpt-5.2-pro" , "gpt-5.2-pro" ) ;
236+ addRequestedModelReasoningAliases ( "gpt-5-pro" , "gpt-5-pro" ) ;
237+ addRequestedModelReasoningAliases ( "gpt-5.2" , "gpt-5.2" ) ;
238+ addRequestedModelReasoningAliases ( "gpt-5.1" , "gpt-5.1" ) ;
239+ addRequestedModelReasoningAliases ( "gpt-5" , "gpt-5" ) ;
240+ addRequestedModelReasoningAliases ( "gpt-5-mini" , "gpt-5-mini" ) ;
241+ addRequestedModelReasoningAliases ( "gpt-5-nano" , "gpt-5-nano" ) ;
242+ addRequestedModelReasoningAliases ( "gpt-5.1-chat-latest" , "gpt-5.1" ) ;
243+ addRequestedModelReasoningAliases ( "gpt-5-chat-latest" , "gpt-5" ) ;
244+ addRequestedModelReasoningAliases ( "gpt-5.4-mini" , "gpt-5-mini" ) ;
245+ addRequestedModelReasoningAliases ( "gpt-5.4-nano" , "gpt-5-nano" ) ;
246+ addRequestedModelReasoningAliases ( "gpt-5-codex" , "gpt-5-codex" ) ;
247+ addRequestedModelReasoningAliases ( "gpt-5.3-codex-spark" , "gpt-5-codex" ) ;
248+ addRequestedModelReasoningAliases ( "gpt-5.3-codex" , "gpt-5-codex" ) ;
249+ addRequestedModelReasoningAliases ( "gpt-5.2-codex" , "gpt-5-codex" ) ;
250+ addRequestedModelReasoningAliases ( "gpt-5.1-codex" , "gpt-5-codex" ) ;
251+ addRequestedModelAlias ( "gpt_5_codex" , "gpt-5-codex" ) ;
252+ addRequestedModelReasoningAliases ( "codex-max" , "gpt-5.1-codex-max" ) ;
253+ addRequestedModelReasoningAliases ( "gpt-5.1-codex-max" , "gpt-5.1-codex-max" ) ;
254+ addRequestedModelAlias ( "codex-mini-latest" , "gpt-5.1-codex-mini" ) ;
255+ addRequestedModelReasoningAliases ( "gpt-5-codex-mini" , "gpt-5.1-codex-mini" ) ;
256+ addRequestedModelReasoningAliases ( "gpt-5.1-codex-mini" , "gpt-5.1-codex-mini" ) ;
257+ }
258+
259+ seedRequestedModelAliases ( ) ;
218260
219261function stripProviderPrefix ( model ) {
220262 return typeof model === "string" && model . includes ( "/" )
@@ -226,6 +268,10 @@ function normalizeRequestedModel(model) {
226268 const stripped = stripProviderPrefix ( model ?? "" ) ;
227269 const normalized = stripped . trim ( ) . toLowerCase ( ) ;
228270 if ( normalized . length === 0 ) return "" ;
271+ const exactMatch = REQUESTED_MODEL_ALIASES . get ( normalized ) ;
272+ if ( exactMatch ) {
273+ return exactMatch ;
274+ }
229275
230276 if (
231277 normalized . includes ( "gpt-5.1-codex-max" ) ||
@@ -442,6 +488,47 @@ function ensureTrailingNewline(value) {
442488 return value . endsWith ( "\n" ) ? value : `${ value } \n` ;
443489}
444490
491+ function captureShadowHomeState ( filePath ) {
492+ try {
493+ if ( ! existsSync ( filePath ) ) {
494+ return { exists : false , content : null } ;
495+ }
496+ return {
497+ exists : true ,
498+ content : readFileSync ( filePath , "utf8" ) ,
499+ } ;
500+ } catch {
501+ return { exists : true , content : null , unreadable : true } ;
502+ }
503+ }
504+
505+ function shadowHomeStateMatches ( left , right ) {
506+ return (
507+ left . exists === right . exists &&
508+ left . content === right . content &&
509+ Boolean ( left . unreadable ) === Boolean ( right . unreadable )
510+ ) ;
511+ }
512+
513+ function syncShadowHomeStateFile ( sourcePath , destinationPath ) {
514+ const tempPath = join (
515+ dirname ( destinationPath ) ,
516+ `.${ basename ( destinationPath ) } .codex-multi-auth-sync-${ process . pid } .tmp` ,
517+ ) ;
518+ try {
519+ mkdirSync ( dirname ( destinationPath ) , { recursive : true } ) ;
520+ copyFileSync ( sourcePath , tempPath ) ;
521+ renameSync ( tempPath , destinationPath ) ;
522+ } catch ( error ) {
523+ try {
524+ rmSync ( tempPath , { force : true } ) ;
525+ } catch {
526+ // Best-effort cleanup only.
527+ }
528+ throw error ;
529+ }
530+ }
531+
445532function rewriteConfigTomlReasoningEffort ( rawConfig , requestedModel ) {
446533 const lineEnding = rawConfig . includes ( "\r\n" ) ? "\r\n" : "\n" ;
447534 let changed = false ;
@@ -503,6 +590,12 @@ function createCompatibilityCodexHome(
503590 if ( ! existsSync ( configPath ) ) {
504591 return { args : processedArgs , env : baseEnv , cleanup : undefined } ;
505592 }
593+ const originalShadowHomeState = new Map (
594+ SHADOW_HOME_STATE_FILES . map ( ( name ) => [
595+ name ,
596+ captureShadowHomeState ( join ( originalCodexHome , name ) ) ,
597+ ] ) ,
598+ ) ;
506599
507600 const rawConfig = readFileSync ( configPath , "utf8" ) ;
508601 const compatConfig = rewriteConfigTomlReasoningEffort (
@@ -531,20 +624,23 @@ function createCompatibilityCodexHome(
531624 const syncShadowHomeStateBack = ( ) => {
532625 for ( const name of SHADOW_HOME_STATE_FILES ) {
533626 const shadowPath = join ( shadowCodexHome , name ) ;
534- if ( ! existsSync ( shadowPath ) ) {
627+ const shadowState = captureShadowHomeState ( shadowPath ) ;
628+ if ( ! shadowState . exists || shadowState . unreadable ) {
535629 continue ;
536630 }
537631
538632 try {
539- const shadowStats = statSync ( shadowPath ) ;
540633 const originalPath = join ( originalCodexHome , name ) ;
541- if ( existsSync ( originalPath ) ) {
542- const originalStats = statSync ( originalPath ) ;
543- if ( originalStats . isFile ( ) && originalStats . mtimeMs > shadowStats . mtimeMs ) {
544- continue ;
545- }
634+ const originalSnapshot =
635+ originalShadowHomeState . get ( name ) ?? { exists : false , content : null } ;
636+ const currentOriginalState = captureShadowHomeState ( originalPath ) ;
637+ if ( ! shadowHomeStateMatches ( currentOriginalState , originalSnapshot ) ) {
638+ continue ;
639+ }
640+ if ( shadowHomeStateMatches ( shadowState , originalSnapshot ) ) {
641+ continue ;
546642 }
547- copyFileSync ( shadowPath , originalPath ) ;
643+ syncShadowHomeStateFile ( shadowPath , originalPath ) ;
548644 tightenShadowHomePermissions ( originalPath ) ;
549645 } catch {
550646 // Best-effort only; runtime auth refreshes should not fail cleanup.
0 commit comments