@yansigit/opencodex 2.32.0 → 2.33.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/README.md +2 -2
  2. package/gui/dist/assets/index-DKLr4LTE.js +102 -0
  3. package/gui/dist/assets/index-DrSQdTRd.css +1 -0
  4. package/gui/dist/index.html +2 -2
  5. package/package.json +6 -5
  6. package/src/adapters/anthropic.ts +20 -6
  7. package/src/adapters/azure.ts +20 -4
  8. package/src/adapters/base.ts +3 -1
  9. package/src/adapters/command-code.ts +40 -7
  10. package/src/adapters/cursor/live-transport.ts +1 -1
  11. package/src/adapters/cursor/protobuf-events.ts +158 -7
  12. package/src/adapters/cursor/protobuf-request.ts +33 -15
  13. package/src/adapters/cursor/request-builder.ts +4 -3
  14. package/src/adapters/cursor/tool-definitions.ts +27 -1
  15. package/src/adapters/cursor/types.ts +4 -3
  16. package/src/adapters/cursor.ts +9 -0
  17. package/src/adapters/google-antigravity-replay.ts +2 -2
  18. package/src/adapters/google-antigravity-wire.ts +7 -0
  19. package/src/adapters/google-errors.ts +6 -2
  20. package/src/adapters/google-http.ts +30 -7
  21. package/src/adapters/google-truncation.ts +5 -0
  22. package/src/adapters/google-wire-compiler.ts +38 -6
  23. package/src/adapters/google.ts +148 -26
  24. package/src/adapters/kiro-tools.ts +20 -9
  25. package/src/adapters/openai-chat.ts +9 -0
  26. package/src/adapters/openai-responses.ts +1 -1
  27. package/src/bridge.ts +112 -9
  28. package/src/claude/context-windows.ts +16 -9
  29. package/src/cli/doctor.ts +2 -2
  30. package/src/cli/index.ts +9 -2
  31. package/src/cli/provider.ts +6 -0
  32. package/src/cli/status.ts +23 -0
  33. package/src/codex/auth-api.ts +4 -2
  34. package/src/codex/autostart-health.ts +16 -0
  35. package/src/codex/catalog/aggregation.ts +12 -12
  36. package/src/codex/catalog/effort.ts +18 -3
  37. package/src/codex/catalog/metadata.ts +27 -1
  38. package/src/codex/catalog/model-metadata.ts +39 -12
  39. package/src/codex/catalog/parsing.ts +38 -27
  40. package/src/codex/catalog/provider-fetch.ts +198 -133
  41. package/src/codex/catalog/sync.ts +1 -1
  42. package/src/codex/convergence.ts +5 -0
  43. package/src/codex/shim.ts +56 -3
  44. package/src/config/provider-validation.ts +37 -0
  45. package/src/config.ts +78 -2
  46. package/src/generated/compatibility-version.json +120 -96
  47. package/src/images/loop.ts +37 -6
  48. package/src/lib/azure-identity.ts +154 -0
  49. package/src/lib/debug.ts +42 -0
  50. package/src/lib/errors.ts +14 -0
  51. package/src/lib/provider-outbound.ts +45 -33
  52. package/src/lib/provider-tls-profile.ts +309 -0
  53. package/src/lib/proxy-env.ts +49 -0
  54. package/src/lib/redact.ts +10 -1
  55. package/src/oauth/antigravity-routing.ts +282 -236
  56. package/src/oauth/callback-server.ts +22 -2
  57. package/src/oauth/command-code.ts +5 -16
  58. package/src/oauth/google-antigravity.ts +42 -5
  59. package/src/oauth/index.ts +15 -3
  60. package/src/oauth/kimi.ts +9 -1
  61. package/src/oauth/open-browser-choice.ts +26 -0
  62. package/src/oauth/store.ts +6 -0
  63. package/src/providers/antigravity-quota.ts +3 -1
  64. package/src/providers/api-keys.ts +2 -1
  65. package/src/providers/auto-compact-budget.ts +65 -0
  66. package/src/providers/derive.ts +4 -0
  67. package/src/providers/key-failover.ts +5 -1
  68. package/src/providers/openai-tiers.ts +5 -0
  69. package/src/providers/provider-id-rewrite.ts +1 -0
  70. package/src/providers/quota.ts +59 -13
  71. package/src/providers/registry.ts +3 -1
  72. package/src/providers/request-pacing.ts +33 -6
  73. package/src/providers/xai-transport.ts +21 -0
  74. package/src/responses/google-provider-options.ts +36 -0
  75. package/src/responses/namespace-tool-compat.ts +84 -4
  76. package/src/responses/parser.ts +11 -0
  77. package/src/responses/provider-opaque-metadata.ts +3 -3
  78. package/src/responses/schema.ts +37 -0
  79. package/src/responses/state.ts +94 -4
  80. package/src/router.ts +8 -2
  81. package/src/server/auth-cors.ts +28 -0
  82. package/src/server/images.ts +19 -35
  83. package/src/server/management/agent-settings-routes.ts +205 -15
  84. package/src/server/management/combo-routes.ts +6 -0
  85. package/src/server/management/config-routes.ts +31 -5
  86. package/src/server/management/model-rows.ts +4 -0
  87. package/src/server/management/oauth-account-routes.ts +25 -4
  88. package/src/server/management/provider-routes.ts +113 -15
  89. package/src/server/management/routing-profile-routes.ts +3 -0
  90. package/src/server/request-log.ts +21 -0
  91. package/src/server/responses/agent-task-recovery.ts +1 -1
  92. package/src/server/responses/compact.ts +30 -1
  93. package/src/server/responses/core.ts +359 -153
  94. package/src/server/responses/empty-completion-guard.ts +35 -6
  95. package/src/server/responses/fetch-helpers.ts +18 -5
  96. package/src/server/responses/v2-native-parent-override.ts +59 -0
  97. package/src/server/responses/ws-upstream.ts +75 -2
  98. package/src/server/responses-undeclared-tool-guard.ts +90 -8
  99. package/src/service.ts +1 -1
  100. package/src/types/config.ts +16 -1
  101. package/src/types/provider.ts +16 -0
  102. package/src/types/request.ts +28 -0
  103. package/src/types/tools.ts +27 -0
  104. package/src/types.ts +6 -0
  105. package/src/web-search/gemini-executor.ts +6 -4
  106. package/src/web-search/loop.ts +42 -6
  107. package/gui/dist/assets/index-BG43zwVe.js +0 -102
  108. package/gui/dist/assets/index-CiSI-jrP.css +0 -1
@@ -41,7 +41,8 @@ import type { FastPolicyAuthority } from "../../providers/fastwire";
41
41
  import { effectiveGoogleMode, getProviderRegistryEntry, providerMatchesRegistryTransport } from "../../providers/registry";
42
42
  import { parseAntigravityAvailableModels, registerAntigravityDiscoveredWireModels } from "../../providers/antigravity-models";
43
43
  import { applyProviderContextCap, providerContextCap, resolveUnknownRoutedContextWindow } from "../../providers/context-cap";
44
- import { routedSlug, slugEquals, slugsEquivalent } from "../../providers/slug-codec";
44
+ import { clampAutoCompactTokenLimit } from "../../providers/auto-compact-budget";
45
+ import { routedSlug, slugEquals, slugEquivalenceKey, slugsEquivalent } from "../../providers/slug-codec";
45
46
  import { CODEX_GPT5_IDENTITY_LINE } from "../../adapters/identity";
46
47
  import { filterCursorConfiguredModelsByLiveDiscovery } from "../../adapters/cursor/discovery";
47
48
  import { fetchCursorUsableModels } from "../../adapters/cursor/live-models";
@@ -60,6 +61,7 @@ import {
60
61
  providerOutboundPost,
61
62
  providerRedirectError,
62
63
  } from "../../lib/provider-outbound";
64
+ import { isAntigravityOAuthProvider } from "../../lib/provider-tls-profile";
63
65
  import { redactSecretString } from "../../lib/redact";
64
66
  import {
65
67
  extractProviderModelItems,
@@ -75,8 +77,7 @@ import { createAdmissionGate, ResourceAdmissionError, type AdmissionMetrics } fr
75
77
 
76
78
  import { CODEX_CUSTOM_MODEL_CATALOG_KIND, JAWCODE_CATALOG_AUGMENT_PROVIDERS, catalogModelSlug, shouldExposeRoutedModel } from "./parsing";
77
79
  import type { CatalogModel } from "./parsing";
78
- import { capsFromProvider, enrichCatalogModelMetadata, persistLiveModelMetadata, type LiveSnapshotRow } from "./model-metadata";
79
- import { disabledNativeSlugs, hasComboTargets, isNativeOpenAiCapabilityAliasModel, NATIVE_GPT56_MAX_INPUT_TOKENS, nativeContextLimits, nativeDefaultReasoningEffort, nativeInputModalities, nativeOpenAiContextWindow, nativeOpenAiMaxInputTokens, nativeOpenAiSlugs, nativeParallelToolCalls, nativeReasoningEfforts } from "./metadata";
80
+ import { disabledNativeSlugs, hasComboTargets, isNativeOpenAiCapabilityAliasModel, NATIVE_GPT56_MAX_INPUT_TOKENS, nativeContextLimits, nativeDefaultReasoningEffort, nativeInputModalities, nativeOpenAiAutoCompactTokenLimit, nativeOpenAiContextWindow, nativeOpenAiMaxInputTokens, nativeOpenAiSlugs, nativeParallelToolCalls, nativeReasoningEfforts } from "./metadata";
80
81
  import { deriveComboCatalogModel, normalizedOpenAiApiSignature, openAiApiCollisionWarnings, replaceLastComboCatalogOmissions, warnUncataloguedComboOnce } from "./aggregation";
81
82
  import type { ComboCatalogOmission } from "./aggregation";
82
83
  import type { CatalogGatherProviderAuthEvidence } from "./filesystem-evidence";
@@ -572,6 +573,7 @@ function providerCatalogFingerprint(name: string, prov: OcxProviderConfig): Reco
572
573
  ctx: prov.contextWindow ?? null,
573
574
  ctxW: prov.modelContextWindows ?? null,
574
575
  maxIn: prov.modelMaxInputTokens ?? null,
576
+ autoCompact: prov.modelAutoCompactTokenLimits ?? null,
575
577
  inMod: prov.modelInputModalities ?? null,
576
578
  re: prov.modelReasoningEfforts ?? null,
577
579
  defRe: prov.modelDefaultReasoningEfforts ?? null,
@@ -626,6 +628,17 @@ export function configuredMaxInputTokens(prov: OcxProviderConfig, id: string): n
626
628
  return typeof configured === "number" && configured > 0 ? configured : undefined;
627
629
  }
628
630
 
631
+ export function configuredAutoCompactTokenLimit(
632
+ prov: OcxProviderConfig | undefined,
633
+ id: string,
634
+ ): number | undefined {
635
+ if (!prov) return undefined;
636
+ const configured = modelRecordValue(prov.modelAutoCompactTokenLimits, id);
637
+ return typeof configured === "number" && Number.isSafeInteger(configured) && configured > 0
638
+ ? configured
639
+ : undefined;
640
+ }
641
+
629
642
  function configuredReasoningSummarySupport(prov: OcxProviderConfig | undefined, id: string): boolean | undefined {
630
643
  if (!prov) return undefined;
631
644
  const explicit = modelRecordValue(prov.modelSupportsReasoningSummaries, id);
@@ -637,6 +650,7 @@ export function applyProviderConfigHints(name: string, prov: OcxProviderConfig,
637
650
  void name;
638
651
  const configuredCap = configuredContextWindow(prov, model.id);
639
652
  const configuredMaxInput = configuredMaxInputTokens(prov, model.id);
653
+ const configuredAutoCompact = configuredAutoCompactTokenLimit(prov, model.id);
640
654
  let inputModalities = configuredInputModalities(prov, model.id);
641
655
  // Vision-sidecar coverage: `noVisionModels` marks models whose images the PROXY describes
642
656
  // (src/vision/index.ts). The catalog must still advertise image input for them — the Codex app
@@ -663,16 +677,9 @@ export function applyProviderConfigHints(name: string, prov: OcxProviderConfig,
663
677
  const hintedWindow = discoveredWindow !== undefined
664
678
  ? (configuredCap !== undefined ? Math.min(discoveredWindow, configuredCap) : discoveredWindow)
665
679
  : (configuredCap ?? (providerCap !== undefined ? resolveUnknownRoutedContextWindow(providerCap) : undefined));
666
- const capFilledMissingWindow = discoveredWindow === undefined && hintedWindow !== undefined;
667
680
  const hinted = {
668
681
  ...modelWithoutServiceTier,
669
682
  ...(hintedWindow !== undefined ? { contextWindow: hintedWindow } : {}),
670
- ...(discoveredWindow !== undefined
671
- ? { detectedContextWindow: model.detectedContextWindow ?? discoveredWindow }
672
- : {}),
673
- ...(capFilledMissingWindow && !model.metadataSource
674
- ? { metadataSource: "config_fallback" as const }
675
- : {}),
676
683
  ...(inputModalities ? { inputModalities } : {}),
677
684
  ...(reasoningEfforts !== undefined ? { reasoningEfforts } : {}),
678
685
  ...(configuredMaxInput !== undefined
@@ -697,26 +704,31 @@ export function applyProviderConfigHints(name: string, prov: OcxProviderConfig,
697
704
  ...(prov.codexToolMode !== undefined ? { codexToolMode: prov.codexToolMode } : {}),
698
705
  };
699
706
  const capped = applyProviderContextCap(hinted.contextWindow, providerCap);
700
- const detectedContextWindow = hinted.detectedContextWindow
701
- ?? discoveredWindow;
702
- const contextCapped = providerCap !== undefined
703
- && typeof capped === "number"
704
- && (
705
- capped !== hinted.contextWindow
706
- || (typeof detectedContextWindow === "number" && detectedContextWindow > capped)
707
- || hinted.contextCapped === true
708
- );
709
- if (providerCap !== undefined && capped !== hinted.contextWindow) {
710
- return {
711
- ...hinted,
712
- contextWindow: capped,
713
- contextCap: providerCap,
714
- contextCapped,
715
- };
716
- }
717
- return providerCap !== undefined
718
- ? { ...hinted, contextCap: providerCap, contextCapped }
707
+ const withCap = providerCap !== undefined
708
+ ? capped !== hinted.contextWindow
709
+ ? { ...hinted, contextWindow: capped, contextCap: providerCap, contextCapped: true }
710
+ : { ...hinted, contextCap: providerCap, contextCapped: false }
719
711
  : hinted;
712
+ const contextWindow = typeof withCap.contextWindow === "number" && withCap.contextWindow > 0
713
+ ? withCap.contextWindow
714
+ : undefined;
715
+ const boundedMaxInput = typeof withCap.maxInputTokens === "number" && withCap.maxInputTokens > 0
716
+ ? (contextWindow !== undefined ? Math.min(withCap.maxInputTokens, contextWindow) : withCap.maxInputTokens)
717
+ : undefined;
718
+ const withHardBounds = boundedMaxInput !== undefined && boundedMaxInput !== withCap.maxInputTokens
719
+ ? { ...withCap, maxInputTokens: boundedMaxInput }
720
+ : withCap;
721
+ const softCandidates = [model.autoCompactTokenLimit, configuredAutoCompact]
722
+ .filter((value): value is number => typeof value === "number" && value > 0);
723
+ if (contextWindow === undefined || softCandidates.length === 0) return withHardBounds;
724
+ return {
725
+ ...withHardBounds,
726
+ autoCompactTokenLimit: clampAutoCompactTokenLimit(
727
+ contextWindow,
728
+ boundedMaxInput,
729
+ Math.min(...softCandidates),
730
+ ),
731
+ };
720
732
  }
721
733
 
722
734
  export function catalogHintsFromProviderConfig(name: string, prov: OcxProviderConfig, id: string, contextCap?: number): Partial<CatalogModel> {
@@ -743,6 +755,7 @@ interface ComboCatalogMemberFallback {
743
755
  readonly contextWindow?: number;
744
756
  /** Input ceiling when it is lower than the window (native GPT-5.6: 922k under 1.05M). */
745
757
  readonly maxInputTokens?: number;
758
+ readonly autoCompactTokenLimit?: number;
746
759
  readonly inputModalities?: readonly string[];
747
760
  readonly reasoningEfforts?: readonly string[];
748
761
  }
@@ -771,26 +784,33 @@ export function resolveComboCatalogMember(
771
784
  if (prov?.disabled === true) return undefined;
772
785
 
773
786
  const withFallbackMetadata = (member: CatalogModel): CatalogModel => {
774
- if (!fallback) return member;
775
787
  const contextWindow = typeof member.contextWindow === "number" && member.contextWindow > 0
776
788
  ? member.contextWindow
777
789
  : undefined;
778
- const addMaxInput = contextWindow !== undefined
790
+ const addMaxInput = fallback !== undefined && contextWindow !== undefined
779
791
  && !(typeof member.maxInputTokens === "number" && member.maxInputTokens > 0);
792
+ const effectiveMaxInput = addMaxInput
793
+ ? Math.min(fallback?.maxInputTokens ?? contextWindow!, contextWindow!)
794
+ : member.maxInputTokens;
795
+ const softCandidates = [member.autoCompactTokenLimit, fallback?.autoCompactTokenLimit]
796
+ .filter((value): value is number => typeof value === "number" && value > 0);
797
+ const autoCompactTokenLimit = contextWindow !== undefined && softCandidates.length > 0
798
+ ? clampAutoCompactTokenLimit(contextWindow, effectiveMaxInput, Math.min(...softCandidates))
799
+ : member.autoCompactTokenLimit;
800
+ const adjustAutoCompact = autoCompactTokenLimit !== member.autoCompactTokenLimit;
780
801
  const addModalities = (!Array.isArray(member.inputModalities) || member.inputModalities.length === 0)
781
- && fallback.inputModalities !== undefined;
802
+ && fallback?.inputModalities !== undefined;
782
803
  const addReasoning = member.reasoningEfforts === undefined
783
- && fallback.reasoningEfforts !== undefined;
784
- if (!addMaxInput && !addModalities && !addReasoning) return member;
804
+ && fallback?.reasoningEfforts !== undefined;
805
+ if (!addMaxInput && !adjustAutoCompact && !addModalities && !addReasoning) return member;
785
806
  return {
786
807
  ...member,
787
808
  // Never claim a larger input budget than the window, and prefer the model's own
788
809
  // measured ceiling when the fallback carries one.
789
- ...(addMaxInput
790
- ? { maxInputTokens: Math.min(fallback.maxInputTokens ?? contextWindow!, contextWindow!) }
791
- : {}),
792
- ...(addModalities ? { inputModalities: [...fallback.inputModalities!] } : {}),
793
- ...(addReasoning ? { reasoningEfforts: [...fallback.reasoningEfforts!] } : {}),
810
+ ...(addMaxInput ? { maxInputTokens: effectiveMaxInput } : {}),
811
+ ...(adjustAutoCompact && autoCompactTokenLimit !== undefined ? { autoCompactTokenLimit } : {}),
812
+ ...(addModalities ? { inputModalities: [...fallback!.inputModalities!] } : {}),
813
+ ...(addReasoning ? { reasoningEfforts: [...fallback!.reasoningEfforts!] } : {}),
794
814
  };
795
815
  };
796
816
 
@@ -871,6 +891,20 @@ export function resolveComboCatalogMember(
871
891
  const maxInputTokens = effectiveMaxInput !== undefined
872
892
  ? Math.min(effectiveMaxInput, contextWindow)
873
893
  : contextWindow;
894
+ const softCandidates = [
895
+ hinted.autoCompactTokenLimit,
896
+ base.autoCompactTokenLimit,
897
+ fallback?.autoCompactTokenLimit,
898
+ configuredAutoCompactTokenLimit(prov, target.model),
899
+ ].filter((value): value is number => typeof value === "number" && value > 0);
900
+ // A generic 128k synthesis is a catalog compatibility fallback, not evidence
901
+ // that a configured soft policy has an authoritative window to clamp against.
902
+ const hasAuthoritativeAutoCompactBasis = hintedContext !== undefined
903
+ || fallbackContext !== undefined
904
+ || contextCap !== undefined;
905
+ const autoCompactTokenLimit = hasAuthoritativeAutoCompactBasis && softCandidates.length > 0
906
+ ? clampAutoCompactTokenLimit(contextWindow, maxInputTokens, Math.min(...softCandidates))
907
+ : undefined;
874
908
 
875
909
  return {
876
910
  ...hinted,
@@ -878,6 +912,7 @@ export function resolveComboCatalogMember(
878
912
  ...(reasoningEfforts !== undefined ? { reasoningEfforts } : {}),
879
913
  contextWindow,
880
914
  maxInputTokens,
915
+ ...(autoCompactTokenLimit !== undefined ? { autoCompactTokenLimit } : {}),
881
916
  ...(fallbackCapped ? { contextCap, contextCapped: true as const } : {}),
882
917
  };
883
918
  }
@@ -1088,9 +1123,6 @@ export function catalogHintsFromModelsApiItem(providerName: string, item: Provid
1088
1123
  item.max_input_length,
1089
1124
  item.max_prompt_tokens,
1090
1125
  );
1091
- // Output ceilings only. A top-level `max_tokens` is ambiguous (some catalogs
1092
- // use it as context) and generated jawcode `maxTokens` is output-only — never
1093
- // treat either as a context window.
1094
1126
  const maxOutputTokens = positiveSafeInteger(
1095
1127
  limits?.max_output_tokens,
1096
1128
  item.max_output_tokens,
@@ -1219,8 +1251,10 @@ async function fetchProviderModelsWithAuth(
1219
1251
  clearProviderDiscoveryStatus(name);
1220
1252
  return observed(configured, "authoritative");
1221
1253
  }
1254
+ const cloudCodeAssist = effectiveGoogleMode(name, prov) === "cloud-code-assist";
1255
+ const antigravityOAuth = isAntigravityOAuthProvider(name, prov);
1222
1256
  const auth: ModelsAuthResolution = captured.observedAuth ?? (resolveAuth.kind === "refreshing"
1223
- ? prov.authMode === "oauth" && effectiveGoogleMode(name, prov) === "cloud-code-assist"
1257
+ ? antigravityOAuth && cloudCodeAssist
1224
1258
  ? await getValidAccessTokenSnapshot(name)
1225
1259
  .then(snapshot => ({
1226
1260
  apiKey: snapshot.accessToken,
@@ -1304,14 +1338,14 @@ async function fetchProviderModelsWithAuth(
1304
1338
  "degraded",
1305
1339
  );
1306
1340
  }
1307
- if (prov.authMode === "oauth" && !apiKey) {
1341
+ if ((prov.authMode === "oauth" || antigravityOAuth) && !apiKey) {
1308
1342
  // No usable token (logged out, or account marked needsReauth). Still surface the
1309
1343
  // configured static catalog so the GUI Models tab / rail counts are not empty —
1310
1344
  // matching Cursor's !apiKey → configured degradation and fetch-failure fallback.
1311
1345
  return observed(configured, "degraded");
1312
1346
  }
1313
- const cloudCodeAssist = effectiveGoogleMode(name, prov) === "cloud-code-assist";
1314
- const project = prov.project ?? auth.oauthProjectId;
1347
+ if (antigravityOAuth && !cloudCodeAssist) return observed(configured, "degraded");
1348
+ const project = antigravityOAuth ? auth.oauthProjectId : prov.project ?? auth.oauthProjectId;
1315
1349
  if (cloudCodeAssist && !project) return observed(configured, "degraded");
1316
1350
  const fresh = getFreshCached(name, ttlMs);
1317
1351
  if (fresh) {
@@ -1328,13 +1362,9 @@ async function fetchProviderModelsWithAuth(
1328
1362
  const stale = getStaleCached(name);
1329
1363
  return observed(
1330
1364
  withConfiguredRetention(
1331
- (stale
1365
+ stale
1332
1366
  ? withVertexDefaultSeed(applyConfigHintsToCachedModels(name, prov, stale, contextCap))
1333
- : failedDiscoveryConfigured
1334
- ).map(model => enrichCatalogModelMetadata(model, {
1335
- liveFresh: false,
1336
- caps: capsFromProvider(prov, model.id, contextCap),
1337
- })),
1367
+ : failedDiscoveryConfigured,
1338
1368
  ),
1339
1369
  "degraded",
1340
1370
  );
@@ -1361,14 +1391,12 @@ async function fetchProviderModelsWithAuth(
1361
1391
  markModelsFetchFailure(name);
1362
1392
  markProviderDiscoveryFailed(name, failure);
1363
1393
  const stale = getStaleCached(name);
1364
- const fallbackModels = stale
1365
- ? withVertexDefaultSeed(applyConfigHintsToCachedModels(name, prov, stale, contextCap))
1366
- : failedDiscoveryConfigured;
1367
1394
  return {
1368
- models: withConfiguredRetention(fallbackModels.map(model => enrichCatalogModelMetadata(model, {
1369
- liveFresh: false,
1370
- caps: capsFromProvider(prov, model.id, contextCap),
1371
- }))),
1395
+ models: withConfiguredRetention(
1396
+ stale
1397
+ ? withVertexDefaultSeed(applyConfigHintsToCachedModels(name, prov, stale, contextCap))
1398
+ : failedDiscoveryConfigured,
1399
+ ),
1372
1400
  fallback: stale ? "stale" : "configured",
1373
1401
  shouldLog,
1374
1402
  };
@@ -1435,40 +1463,19 @@ async function fetchProviderModelsWithAuth(
1435
1463
  return observed(models, "degraded");
1436
1464
  }
1437
1465
  if (antigravity) {
1438
- const observedAt = new Date().toISOString();
1439
- const snapshotRows: LiveSnapshotRow[] = [];
1440
- const live = antigravity.map(model => {
1441
- const hints = {
1442
- // CCA only exposes a numeric thinking budget. Until the adapter owns an exact Codex
1443
- // effort-to-wire mapping for a newly discovered model, do not advertise a false ladder.
1444
- reasoningEfforts: [] as string[],
1445
- ...(model.contextWindow ? { contextWindow: model.contextWindow } : {}),
1446
- ...(model.inputModalities ? { inputModalities: model.inputModalities } : {}),
1447
- };
1448
- if (model.contextWindow || model.inputModalities) {
1449
- snapshotRows.push({
1450
- id: model.id,
1451
- ...(model.contextWindow ? { contextWindow: model.contextWindow } : {}),
1452
- ...(model.inputModalities ? { inputModalities: model.inputModalities } : {}),
1453
- observedAt,
1454
- });
1455
- }
1456
- const enriched = enrichCatalogModelMetadata({
1457
- id: model.id,
1458
- provider: name,
1459
- ...hints,
1460
- ...(model.contextWindow ? { metadataSource: "live" as const, metadataObservedAt: observedAt } : {}),
1461
- }, {
1462
- liveFresh: true,
1463
- caps: capsFromProvider(prov, model.id, contextCap),
1464
- });
1465
- return applyProviderConfigHints(name, prov, enriched, contextCap);
1466
- });
1466
+ const live = antigravity.map(model => applyProviderConfigHints(name, prov, {
1467
+ id: model.id,
1468
+ provider: name,
1469
+ // CCA only exposes a numeric thinking budget. Until the adapter owns an exact Codex
1470
+ // effort-to-wire mapping for a newly discovered model, do not advertise a false ladder.
1471
+ reasoningEfforts: [],
1472
+ ...(model.contextWindow ? { contextWindow: model.contextWindow } : {}),
1473
+ ...(model.inputModalities ? { inputModalities: model.inputModalities } : {}),
1474
+ }, contextCap));
1467
1475
  const forCache = withConfiguredRetention(live, { retainComboTargets: false });
1468
1476
  if (!setCached(name, forCache, Date.now(), cacheGeneration)) {
1469
1477
  return observed(withConfiguredRetention(configured), "degraded");
1470
1478
  }
1471
- persistLiveModelMetadata(name, snapshotRows, { writerGeneration: cacheGeneration, observedAt });
1472
1479
  registerAntigravityDiscoveredWireModels(prov.baseUrl, antigravity, {
1473
1480
  provider: name,
1474
1481
  cacheGeneration,
@@ -1493,30 +1500,22 @@ async function fetchProviderModelsWithAuth(
1493
1500
  return observed(models, "degraded");
1494
1501
  }
1495
1502
  const items = extracted.items;
1496
- const observedAt = new Date().toISOString();
1497
- const snapshotRows: LiveSnapshotRow[] = [];
1498
1503
  const live = items.map(m => {
1499
1504
  const ownedBy = boundedOwnedBy(m.owned_by);
1500
- const hints = catalogHintsFromModelsApiItem(name, m);
1501
- const discovered = Boolean(hints.contextWindow || hints.maxInputTokens || hints.maxOutputTokens
1502
- || hints.inputModalities || hints.capabilities || hints.reasoningEfforts);
1503
- if (discovered) {
1504
- snapshotRows.push({ id: m.id, ...hints, observedAt });
1505
- }
1506
- const enriched = enrichCatalogModelMetadata({
1505
+ return applyProviderConfigHints(name, prov, {
1507
1506
  id: m.id,
1508
1507
  provider: name,
1509
1508
  ...(ownedBy ? { owned_by: ownedBy } : {}),
1510
- ...hints,
1511
- ...(discovered ? { metadataSource: "live" as const, metadataObservedAt: observedAt } : {}),
1512
- }, {
1513
- liveFresh: true,
1514
- caps: capsFromProvider(prov, m.id, contextCap),
1515
- });
1516
- return applyProviderConfigHints(name, prov, enriched, contextCap);
1509
+ ...catalogHintsFromModelsApiItem(name, m),
1510
+ }, contextCap);
1517
1511
  })
1518
1512
  .filter(m => shouldExposeProviderModel(name, m.id));
1513
+ // Capture the count BEFORE the alias/configured augmentation below pushes extra rows into
1514
+ // `live`; otherwise configured entries would be reported as discovered ones.
1519
1515
  const liveModelCount = live.length;
1516
+ // Dated-release aliases + configured retention (compat allow-list, combo targets,
1517
+ // Vertex default). Cache without combo retention so a later gather re-applies the
1518
+ // current capture's retain set on read (warm-cache OCX-111 / #1308).
1520
1519
  const forCache = withConfiguredRetention(live, { retainComboTargets: false });
1521
1520
  const returned = withConfiguredRetention(forCache, { warnDrops: true });
1522
1521
  const droppedConfiguredIds = configured
@@ -1530,7 +1529,6 @@ async function fetchProviderModelsWithAuth(
1530
1529
  if (!setCached(name, forCache, Date.now(), cacheGeneration)) {
1531
1530
  return observed(withConfiguredRetention(configured), "degraded");
1532
1531
  }
1533
- persistLiveModelMetadata(name, snapshotRows, { writerGeneration: cacheGeneration, observedAt });
1534
1532
  markProviderDiscoveryOk(name, liveModelCount);
1535
1533
  return observed(returned, "authoritative");
1536
1534
  } catch (error) {
@@ -1641,7 +1639,24 @@ export function filterCatalogVisibleModels(
1641
1639
  const allowByProvider = new Map<string, Set<string>>();
1642
1640
  for (const [name, prov] of Object.entries(config.providers)) {
1643
1641
  const sel = prov.selectedModels;
1644
- if (Array.isArray(sel) && sel.length > 0) allowByProvider.set(name, new Set(sel));
1642
+ // Keyed the way `sync.ts` keys the same list, so a slash-bearing native id and
1643
+ // the encoded slug the Codex picker displays are one entry rather than two. A
1644
+ // bare `Set(sel)` matched only the native form, so an allowlist written from the
1645
+ // displayed slug — which `ocx models remove` also accepts — hid every model it
1646
+ // was meant to keep.
1647
+ //
1648
+ // The key is deliberately lossy: `p/a/b` and `p/a-b` collapse to one entry, so a
1649
+ // provider publishing both spellings has them selected together. That is a real
1650
+ // limitation, pinned by the tests below and tracked as a follow-up; it is NOT
1651
+ // fixed here. Resolving selections against the current roster instead was tried
1652
+ // and rejected — the roster is an incomplete dictionary (live discovery can omit
1653
+ // a published id), so it produces the same over-grant while additionally
1654
+ // disagreeing with the `slugEquivalenceKey` contract `sync.ts` uses at merge time.
1655
+ // Two catalog stages with different equivalence relations is the exact bug class
1656
+ // this change exists to remove.
1657
+ if (Array.isArray(sel) && sel.length > 0) {
1658
+ allowByProvider.set(name, new Set(sel.map(model => slugEquivalenceKey(routedSlug(name, model)))));
1659
+ }
1645
1660
  }
1646
1661
  return models.filter(m => {
1647
1662
  const nativeAlias = m.provider === COMBO_NAMESPACE && m.nativeAlias === true;
@@ -1653,7 +1668,7 @@ export function filterCatalogVisibleModels(
1653
1668
  if (slugEquals(stored, m.provider, m.id)) return false;
1654
1669
  }
1655
1670
  const allow = allowByProvider.get(m.provider);
1656
- return !allow || allow.has(m.id);
1671
+ return !allow || allow.has(slugEquivalenceKey(routedSlug(m.provider, m.id)));
1657
1672
  });
1658
1673
  }
1659
1674
 
@@ -1838,6 +1853,7 @@ async function gatherRoutedModelsUncached(
1838
1853
  // stay separate fields because routed/API rows of the same family run a wider window.
1839
1854
  // Falls back to the window for slugs with no separate ceiling.
1840
1855
  maxInputTokens: Math.min(nativeOpenAiMaxInputTokens(slug, openaiContextCap) ?? contextWindow, contextWindow),
1856
+ autoCompactTokenLimit: nativeOpenAiAutoCompactTokenLimit(slug, openaiContextCap),
1841
1857
  inputModalities: nativeInputModalities(slug),
1842
1858
  reasoningEfforts: nativeReasoningEfforts(slug),
1843
1859
  ...(nativeParallelToolCalls(slug) ? { parallelToolCalls: true } : {}),
@@ -1854,18 +1870,23 @@ async function gatherRoutedModelsUncached(
1854
1870
  for (const id of listComboIds(config)) {
1855
1871
  const combo = getCombo(config, id);
1856
1872
  if (!combo) continue;
1873
+ const comboNativeLimits = nativeContextLimits(config);
1857
1874
  const nativeContextWindow = combo.nativeAlias && combo.alias
1858
- ? nativeOpenAiContextWindow(combo.alias, nativeContextLimits(config))
1875
+ ? nativeOpenAiContextWindow(combo.alias, comboNativeLimits)
1859
1876
  : undefined;
1860
1877
  const nativeAliasMaxInput = combo.nativeAlias && combo.alias
1861
1878
  ? (combo.alias.startsWith("gpt-5.6-") || combo.alias.includes("daybreak")
1862
1879
  ? NATIVE_GPT56_MAX_INPUT_TOKENS
1863
1880
  : nativeOpenAiMaxInputTokens(combo.alias) ?? nativeOpenAiContextWindow(combo.alias))
1864
1881
  : undefined;
1882
+ const nativeAliasAutoCompact = combo.nativeAlias && combo.alias
1883
+ ? nativeOpenAiAutoCompactTokenLimit(combo.alias, comboNativeLimits)
1884
+ : undefined;
1865
1885
  const nativeAliasFallback = combo.nativeAlias && combo.alias && nativeContextWindow !== undefined
1866
1886
  ? {
1867
1887
  contextWindow: nativeContextWindow,
1868
1888
  ...(nativeAliasMaxInput !== undefined ? { maxInputTokens: nativeAliasMaxInput } : {}),
1889
+ ...(nativeAliasAutoCompact !== undefined ? { autoCompactTokenLimit: nativeAliasAutoCompact } : {}),
1869
1890
  inputModalities: nativeInputModalities(combo.alias),
1870
1891
  reasoningEfforts: nativeReasoningEfforts(combo.alias),
1871
1892
  }
@@ -1928,9 +1949,23 @@ async function gatherRoutedModelsUncached(
1928
1949
  const nativeAliasMaxInputTokens = codexForwardNativeCapabilityAlias
1929
1950
  ? nativeOpenAiMaxInputTokens(cm.modelId, customNativeLimits)
1930
1951
  : undefined;
1931
- const customMaxInputTokens = nativeAliasMaxInputTokens !== undefined && customContextWindow !== undefined
1932
- ? Math.min(nativeAliasMaxInputTokens, customContextWindow)
1933
- : nativeAliasMaxInputTokens;
1952
+ const configuredMaxInput = rawProvider
1953
+ ? configuredMaxInputTokens(rawProvider, cm.modelId)
1954
+ : undefined;
1955
+ const hardMaxCandidates = [nativeAliasMaxInputTokens, configuredMaxInput]
1956
+ .filter((value): value is number => typeof value === "number" && value > 0);
1957
+ const customMaxInputTokens = hardMaxCandidates.length > 0
1958
+ ? Math.min(
1959
+ ...hardMaxCandidates,
1960
+ ...(customContextWindow !== undefined ? [customContextWindow] : []),
1961
+ )
1962
+ : undefined;
1963
+ const configuredAutoCompact = configuredAutoCompactTokenLimit(rawProvider, cm.modelId);
1964
+ const customAutoCompactTokenLimit = codexForwardNativeCapabilityAlias
1965
+ ? nativeOpenAiAutoCompactTokenLimit(cm.modelId, customNativeLimits)
1966
+ : customContextWindow !== undefined && configuredAutoCompact !== undefined
1967
+ ? clampAutoCompactTokenLimit(customContextWindow, customMaxInputTokens, configuredAutoCompact)
1968
+ : undefined;
1934
1969
  const nativeAliasDefaultEffort = codexForwardNativeCapabilityAlias
1935
1970
  ? nativeDefaultReasoningEffort(cm.modelId)
1936
1971
  : undefined;
@@ -1951,6 +1986,7 @@ async function gatherRoutedModelsUncached(
1951
1986
  : codexForwardNativeCapabilityAlias ? { displayName: "Daybreak Blue" } : {}),
1952
1987
  ...(customContextWindow !== undefined ? { contextWindow: customContextWindow } : {}),
1953
1988
  ...(customMaxInputTokens !== undefined ? { maxInputTokens: customMaxInputTokens } : {}),
1989
+ ...(customAutoCompactTokenLimit !== undefined ? { autoCompactTokenLimit: customAutoCompactTokenLimit } : {}),
1954
1990
  ...(cm.inputModalities
1955
1991
  ? { inputModalities: cm.inputModalities }
1956
1992
  : codexForwardNativeCapabilityAlias ? { inputModalities: nativeInputModalities(cm.modelId) } : {}),
@@ -1996,10 +2032,18 @@ async function gatherRoutedModelsUncached(
1996
2032
  // along when it is actually a member — otherwise a provider default like "xhigh" would
1997
2033
  // re-apply onto a narrower custom ladder and override the fallback in applyReasoningLevels.
1998
2034
  const effectiveLadder = base.reasoningEfforts ?? replaced?.reasoningEfforts;
2035
+ const mergedMaxInputCandidates = [base.maxInputTokens, replaced?.maxInputTokens]
2036
+ .filter((value): value is number => typeof value === "number" && value > 0);
2037
+ const mergedMaxInput = mergedMaxInputCandidates.length > 0
2038
+ ? Math.min(...mergedMaxInputCandidates)
2039
+ : undefined;
1999
2040
  const merged: CatalogModel = replaced ? {
2000
2041
  ...base,
2001
2042
  ...(base.contextWindow === undefined && replaced.contextWindow !== undefined ? { contextWindow: replaced.contextWindow } : {}),
2002
- ...(base.maxInputTokens === undefined && replaced.maxInputTokens !== undefined ? { maxInputTokens: replaced.maxInputTokens } : {}),
2043
+ ...(mergedMaxInput !== undefined ? { maxInputTokens: mergedMaxInput } : {}),
2044
+ ...(base.autoCompactTokenLimit === undefined && replaced.autoCompactTokenLimit !== undefined
2045
+ ? { autoCompactTokenLimit: replaced.autoCompactTokenLimit }
2046
+ : {}),
2003
2047
  ...(base.inputModalities === undefined && replaced.inputModalities !== undefined ? { inputModalities: replaced.inputModalities } : {}),
2004
2048
  ...(base.reasoningEfforts === undefined && replaced.reasoningEfforts !== undefined ? { reasoningEfforts: replaced.reasoningEfforts } : {}),
2005
2049
  ...(base.defaultReasoningEffort === undefined && replaced.defaultReasoningEffort !== undefined
@@ -2016,14 +2060,36 @@ async function gatherRoutedModelsUncached(
2016
2060
  // (#349/#344). Deliberately NOT the full applyProviderConfigHints pass — custom rows are a
2017
2061
  // user override, so their explicit contextWindow / inputModalities / reasoning fields must be
2018
2062
  // preserved verbatim (the hint pass would cap context and overwrite modalities from registry).
2063
+ const mergedContext = typeof merged.contextWindow === "number" && merged.contextWindow > 0
2064
+ ? merged.contextWindow
2065
+ : undefined;
2066
+ const boundedMergedMaxInput = typeof merged.maxInputTokens === "number" && merged.maxInputTokens > 0
2067
+ ? (mergedContext !== undefined ? Math.min(merged.maxInputTokens, mergedContext) : merged.maxInputTokens)
2068
+ : undefined;
2069
+ const mergedWithHardBounds = boundedMergedMaxInput !== undefined
2070
+ && boundedMergedMaxInput !== merged.maxInputTokens
2071
+ ? { ...merged, maxInputTokens: boundedMergedMaxInput }
2072
+ : merged;
2073
+ const mergedSoftCandidates = [mergedWithHardBounds.autoCompactTokenLimit, configuredAutoCompact]
2074
+ .filter((value): value is number => typeof value === "number" && value > 0);
2075
+ const mergedWithAutoCompact: CatalogModel = mergedContext !== undefined && mergedSoftCandidates.length > 0
2076
+ ? {
2077
+ ...mergedWithHardBounds,
2078
+ autoCompactTokenLimit: clampAutoCompactTokenLimit(
2079
+ mergedContext,
2080
+ boundedMergedMaxInput,
2081
+ Math.min(...mergedSoftCandidates),
2082
+ ),
2083
+ }
2084
+ : mergedWithHardBounds;
2019
2085
  const enrichedProvider = enrichedByName.get(cm.provider) ?? rawProvider;
2020
- if (enrichedProvider && modelInList(enrichedProvider.noVisionModels, merged.id)) {
2021
- const current = merged.inputModalities ?? ["text"];
2086
+ if (enrichedProvider && modelInList(enrichedProvider.noVisionModels, mergedWithAutoCompact.id)) {
2087
+ const current = mergedWithAutoCompact.inputModalities ?? ["text"];
2022
2088
  if (!current.includes("image")) {
2023
- return { ...merged, inputModalities: [...current, "image"] };
2089
+ return { ...mergedWithAutoCompact, inputModalities: [...current, "image"] };
2024
2090
  }
2025
2091
  }
2026
- return merged;
2092
+ return mergedWithAutoCompact;
2027
2093
  });
2028
2094
  // Custom rows override discovered rows that encode to the same Codex-facing slug.
2029
2095
  const customKeys = new Set(customModels.map(c => routedSlug(c.provider, c.id)));
@@ -2079,7 +2145,15 @@ function augmentRoutedModelsWithCapturedOpenAiApiRows(
2079
2145
  ? Math.min(officialContext, userContext ?? officialContext, providerCap ?? officialContext)
2080
2146
  : undefined;
2081
2147
  const maxInputTokens = typeof officialMaxInput === "number"
2082
- ? Math.min(officialMaxInput, userMaxInput ?? officialMaxInput)
2148
+ ? Math.min(
2149
+ officialMaxInput,
2150
+ userMaxInput ?? officialMaxInput,
2151
+ contextWindow ?? officialMaxInput,
2152
+ )
2153
+ : undefined;
2154
+ const configuredAutoCompact = configuredAutoCompactTokenLimit(configured, id);
2155
+ const autoCompactTokenLimit = contextWindow !== undefined && configuredAutoCompact !== undefined
2156
+ ? clampAutoCompactTokenLimit(contextWindow, maxInputTokens, configuredAutoCompact)
2083
2157
  : undefined;
2084
2158
  return {
2085
2159
  provider: OPENAI_API_PROVIDER_ID,
@@ -2087,6 +2161,7 @@ function augmentRoutedModelsWithCapturedOpenAiApiRows(
2087
2161
  owned_by: OPENAI_API_PROVIDER_ID,
2088
2162
  ...(contextWindow ? { contextWindow } : {}),
2089
2163
  ...(maxInputTokens ? { maxInputTokens } : {}),
2164
+ ...(autoCompactTokenLimit !== undefined ? { autoCompactTokenLimit } : {}),
2090
2165
  ...(policy.modelInputModalities?.[id] ? { inputModalities: [...policy.modelInputModalities[id]!] } : {}),
2091
2166
  ...(policy.modelReasoningEfforts?.[id] ? { reasoningEfforts: [...policy.modelReasoningEfforts[id]!] } : {}),
2092
2167
  };
@@ -2116,14 +2191,7 @@ export function augmentRoutedModelsWithMetadata(
2116
2191
  providers?: Record<string, OcxProviderConfig>,
2117
2192
  caps?: Pick<OcxConfig, "providerContextCaps">,
2118
2193
  ): CatalogModel[] {
2119
- const out = models.map((model) => {
2120
- const provider = providers?.[model.provider];
2121
- const contextCap = caps ? providerContextCap(caps, model.provider) : undefined;
2122
- return enrichCatalogModelMetadata(model, {
2123
- liveFresh: model.metadataSource === "live" && model.metadataStale !== true,
2124
- caps: capsFromProvider(provider, model.id, contextCap),
2125
- });
2126
- });
2194
+ const out = [...models];
2127
2195
  const seen = new Set(out.map(m => `${m.provider}/${m.id}`));
2128
2196
  for (const provider of providerNames) {
2129
2197
  if (!JAWCODE_CATALOG_AUGMENT_PROVIDERS.has(provider)) continue;
@@ -2142,13 +2210,10 @@ export function augmentRoutedModelsWithMetadata(
2142
2210
  ...(typeof meta.contextWindow === "number" && meta.contextWindow > 0 ? { contextWindow: meta.contextWindow } : {}),
2143
2211
  ...(Array.isArray(meta.input) && meta.input.length > 0 ? { inputModalities: [...meta.input] } : {}),
2144
2212
  };
2145
- const hinted = providers?.[provider]
2146
- ? applyProviderConfigHints(provider, providers[provider], model, contextCap)
2147
- : model;
2148
- out.push(enrichCatalogModelMetadata(hinted, {
2149
- liveFresh: false,
2150
- caps: capsFromProvider(providers?.[provider], meta.id, contextCap),
2151
- }));
2213
+ out.push({
2214
+ ...model,
2215
+ ...(providers?.[provider] ? applyProviderConfigHints(provider, providers[provider], model, contextCap) : {}),
2216
+ });
2152
2217
  }
2153
2218
  }
2154
2219
  return out;
@@ -1158,7 +1158,7 @@ export function mergeCatalogEntriesForSync(
1158
1158
  isNativeAliasCatalogEntry(entry) && typeof entry.slug === "string" ? [entry.slug] : []
1159
1159
  )),
1160
1160
  ),
1161
- openaiContextCap?: number,
1161
+ openaiContextCap?: NativeContextLimitsInput,
1162
1162
  keepNativeChatGptOnV1 = false,
1163
1163
  ): RawEntry[] {
1164
1164
  // Retained for source compatibility with the original helper contract. Raw provider ids must
@@ -54,6 +54,7 @@ import {
54
54
  disabledNativeSlugs,
55
55
  desktopAllowlistSuppressedNativeSlugs,
56
56
  NATIVE_OPENAI_MODELS,
57
+ nativeContextLimits,
57
58
  shouldIncludeAccountBoundNativeOpenAi,
58
59
  shouldIncludeNativeOpenAi,
59
60
  } from "./catalog/metadata";
@@ -286,6 +287,7 @@ function prepareCatalog(
286
287
  // selector-qualified rows when a live selector is configured.
287
288
  const observedNativeSlugs: string[] = [];
288
289
  const disabledNative = disabledNativeSlugs(config);
290
+ const openaiContextCap = nativeContextLimits(config);
289
291
  const nativeCatalogModels = mergeCatalogModelsWithNativeRecovery(
290
292
  active?.models ?? catalog.models ?? [],
291
293
  [catalog.models ?? [], ...nativeRecoverySources],
@@ -304,6 +306,7 @@ function prepareCatalog(
304
306
  suppressedBareNativeSlugs,
305
307
  disabledNativeAccountSlugs: new Set(),
306
308
  multiAgentV2Enabled,
309
+ openaiContextCap,
307
310
  });
308
311
  const accountBoundEntries = accountSelectors.length === 0
309
312
  ? []
@@ -320,6 +323,7 @@ function prepareCatalog(
320
323
  disabledNativeAccountSlugs: new Set([...disabledNative].filter(slug => suppressedBareNativeSlugs.has(slug))),
321
324
  multiAgentV2Enabled,
322
325
  keepNativeChatGptOnV1: config.keepNativeChatGptOnV1 === true,
326
+ openaiContextCap,
323
327
  accountNativeSlugs,
324
328
  accountNativeSlugsBySelector,
325
329
  }).filter(entry => trustedAccountBoundNativeCatalogSlug(entry) !== undefined);
@@ -352,6 +356,7 @@ function prepareCatalog(
352
356
  includeNativeOpenAi,
353
357
  accountBoundEntries,
354
358
  suppressedBareNativeSlugs,
359
+ openaiContextCap,
355
360
  policy: {
356
361
  ...CANONICAL_NATIVE_CATALOG_CONTENT_POLICY,
357
362
  nativeBackfillSlugs: [...availableBareNativeSlugs, ...observedNativeSlugs],