@@ -88,6 +88,7 @@ import {
8888import { isStoredCredentialCompatibleWithAuthProvider } from "./auth-profiles/order.js" ;
8989import { clearSessionAuthProfileOverride } from "./auth-profiles/session-override.js" ;
9090import { ensureAuthProfileStore } from "./auth-profiles/store.js" ;
91+ import { isStaleAutoFallbackOriginOverride } from "./auto-fallback-stale-origin.js" ;
9192import { isHeartbeatLifecycleRunKind } from "./bootstrap-mode.js" ;
9293import {
9394 createAgentAttemptLifecycleCallbacks ,
@@ -409,6 +410,61 @@ const OVERRIDE_FIELDS_CLEARED_BY_DELETE: OverrideFieldClearedByDelete[] = [
409410
410411const OVERRIDE_VALUE_MAX_LENGTH = 256 ;
411412
413+ type StaleAutoFallbackOriginCleanupGuard = Pick <
414+ SessionEntry ,
415+ | "providerOverride"
416+ | "modelOverride"
417+ | "modelOverrideSource"
418+ | "modelOverrideFallbackOriginProvider"
419+ | "modelOverrideFallbackOriginModel"
420+ | "authProfileOverride"
421+ | "authProfileOverrideSource"
422+ | "authProfileOverrideCompactionCount"
423+ | "fallbackNoticeSelectedModel"
424+ | "fallbackNoticeActiveModel"
425+ | "fallbackNoticeReason"
426+ > ;
427+
428+ function captureStaleAutoFallbackOriginCleanupGuard (
429+ entry : SessionEntry ,
430+ ) : StaleAutoFallbackOriginCleanupGuard {
431+ return {
432+ providerOverride : entry . providerOverride ,
433+ modelOverride : entry . modelOverride ,
434+ modelOverrideSource : entry . modelOverrideSource ,
435+ modelOverrideFallbackOriginProvider : entry . modelOverrideFallbackOriginProvider ,
436+ modelOverrideFallbackOriginModel : entry . modelOverrideFallbackOriginModel ,
437+ authProfileOverride : entry . authProfileOverride ,
438+ authProfileOverrideSource : entry . authProfileOverrideSource ,
439+ authProfileOverrideCompactionCount : entry . authProfileOverrideCompactionCount ,
440+ fallbackNoticeSelectedModel : entry . fallbackNoticeSelectedModel ,
441+ fallbackNoticeActiveModel : entry . fallbackNoticeActiveModel ,
442+ fallbackNoticeReason : entry . fallbackNoticeReason ,
443+ } ;
444+ }
445+
446+ function entryMatchesStaleAutoFallbackOriginCleanupGuard (
447+ entry : SessionEntry | undefined ,
448+ guard : StaleAutoFallbackOriginCleanupGuard | undefined ,
449+ ) : boolean {
450+ if ( ! entry || ! guard ) {
451+ return false ;
452+ }
453+ return (
454+ entry . providerOverride === guard . providerOverride &&
455+ entry . modelOverride === guard . modelOverride &&
456+ entry . modelOverrideSource === guard . modelOverrideSource &&
457+ entry . modelOverrideFallbackOriginProvider === guard . modelOverrideFallbackOriginProvider &&
458+ entry . modelOverrideFallbackOriginModel === guard . modelOverrideFallbackOriginModel &&
459+ entry . authProfileOverride === guard . authProfileOverride &&
460+ entry . authProfileOverrideSource === guard . authProfileOverrideSource &&
461+ entry . authProfileOverrideCompactionCount === guard . authProfileOverrideCompactionCount &&
462+ entry . fallbackNoticeSelectedModel === guard . fallbackNoticeSelectedModel &&
463+ entry . fallbackNoticeActiveModel === guard . fallbackNoticeActiveModel &&
464+ entry . fallbackNoticeReason === guard . fallbackNoticeReason
465+ ) ;
466+ }
467+
412468async function persistSessionEntry (
413469 params : PersistSessionEntryParams & {
414470 shouldPersist ?: ( entry : SessionEntry | undefined ) => boolean ;
@@ -1402,7 +1458,7 @@ async function agentCommandInternal(
14021458 }
14031459 }
14041460
1405- const storedProviderOverride = hasLegacyAutoFallbackOverrideWithoutOrigin
1461+ let storedProviderOverride = hasLegacyAutoFallbackOverrideWithoutOrigin
14061462 ? undefined
14071463 : sessionEntry ?. providerOverride ?. trim ( ) ;
14081464 let storedModelOverride = hasLegacyAutoFallbackOverrideWithoutOrigin
@@ -1449,6 +1505,21 @@ async function agentCommandInternal(
14491505 : null ;
14501506 const primaryProvider = normalizedChannelOverride ?. provider ?? defaultProvider ;
14511507 const primaryModel = normalizedChannelOverride ?. model ?? defaultModel ;
1508+ const hasStaleAutoFallbackOriginOverride =
1509+ hasStoredOverride &&
1510+ ! hasExplicitRunOverride &&
1511+ isStaleAutoFallbackOriginOverride ( {
1512+ entry : sessionEntry ,
1513+ defaultProvider,
1514+ defaultModel,
1515+ primaryProvider,
1516+ primaryModel,
1517+ } ) ;
1518+ if ( hasStaleAutoFallbackOriginOverride ) {
1519+ storedProviderOverride = undefined ;
1520+ storedModelOverride = undefined ;
1521+ storedModelOverrideSource = undefined ;
1522+ }
14521523 const hasEffectiveStoredOverride = Boolean ( storedProviderOverride || storedModelOverride ) ;
14531524 if ( normalizedChannelOverride && ! hasEffectiveStoredOverride ) {
14541525 provider = normalizedChannelOverride . provider ;
@@ -1483,6 +1554,14 @@ async function agentCommandInternal(
14831554 autoFallbackPrimaryProbeSessionEntry = { ...sessionEntry } ;
14841555 clearAutoFallbackPrimaryProbeSelection ( autoFallbackPrimaryProbeSessionEntry ) ;
14851556 }
1557+ let staleAutoFallbackOriginAttemptSessionEntry : SessionEntry | undefined ;
1558+ let staleAutoFallbackOriginCleanupGuard : StaleAutoFallbackOriginCleanupGuard | undefined ;
1559+ if ( hasStaleAutoFallbackOriginOverride && sessionEntry ) {
1560+ staleAutoFallbackOriginCleanupGuard =
1561+ captureStaleAutoFallbackOriginCleanupGuard ( sessionEntry ) ;
1562+ staleAutoFallbackOriginAttemptSessionEntry = { ...sessionEntry } ;
1563+ clearAutoFallbackPrimaryProbeSelection ( staleAutoFallbackOriginAttemptSessionEntry ) ;
1564+ }
14861565 let providerForAuthProfileValidation = provider ;
14871566 if ( hasExplicitRunOverride ) {
14881567 const explicitRef = explicitModelOverride
@@ -1536,7 +1615,10 @@ async function agentCommandInternal(
15361615 workspaceDir,
15371616 } ) ;
15381617
1539- let sessionEntryForAttempt = autoFallbackPrimaryProbeSessionEntry ?? sessionEntry ;
1618+ let sessionEntryForAttempt =
1619+ autoFallbackPrimaryProbeSessionEntry ??
1620+ staleAutoFallbackOriginAttemptSessionEntry ??
1621+ sessionEntry ;
15401622 if ( sessionEntryForAttempt ) {
15411623 const authProfileId = sessionEntryForAttempt . authProfileOverride ;
15421624 if ( authProfileId ) {
@@ -1585,7 +1667,11 @@ async function agentCommandInternal(
15851667 } ) ,
15861668 ) ;
15871669 if ( ! profileMatchesRuntime ) {
1588- if ( hasExplicitRunOverride || autoFallbackPrimaryProbe ) {
1670+ if (
1671+ hasExplicitRunOverride ||
1672+ autoFallbackPrimaryProbe ||
1673+ staleAutoFallbackOriginAttemptSessionEntry
1674+ ) {
15891675 sessionEntryForAttempt = {
15901676 ...entry ,
15911677 authProfileOverride : undefined ,
@@ -2021,6 +2107,36 @@ async function agentCommandInternal(
20212107 } ) ;
20222108 sessionEntry = persistedEntry ?? sessionEntry ;
20232109 }
2110+ if (
2111+ ! fallbackExhausted &&
2112+ hasStaleAutoFallbackOriginOverride &&
2113+ sessionEntry &&
2114+ sessionStore &&
2115+ sessionKey &&
2116+ ! suppressVisibleSessionEffects &&
2117+ ! preserveUserFacingSessionModelState
2118+ ) {
2119+ const staleCleanupSourceEntry = sessionStore [ sessionKey ] ?? sessionEntry ;
2120+ const nextSessionEntry = { ...staleCleanupSourceEntry } ;
2121+ const { updated } = applyModelOverrideToSessionEntry ( {
2122+ entry : nextSessionEntry ,
2123+ selection : { provider : primaryProvider , model : primaryModel , isDefault : true } ,
2124+ } ) ;
2125+ if ( updated ) {
2126+ const persistedEntry = await persistSessionEntry ( {
2127+ sessionStore,
2128+ sessionKey,
2129+ storePath,
2130+ entry : nextSessionEntry ,
2131+ shouldPersist : ( current ) =>
2132+ entryMatchesStaleAutoFallbackOriginCleanupGuard (
2133+ current ,
2134+ staleAutoFallbackOriginCleanupGuard ,
2135+ ) ,
2136+ } ) ;
2137+ sessionEntry = persistedEntry ?? sessionEntry ;
2138+ }
2139+ }
20242140 if ( fallbackResult . attempts . length > 0 && result . meta . agentMeta ) {
20252141 result = {
20262142 ...result ,
0 commit comments