@bitkyc08/opencodex 2.24.0 → 2.24.2

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.
@@ -16,7 +16,7 @@
16
16
  } catch (e) {}
17
17
  })();
18
18
  </script>
19
- <script type="module" crossorigin src="/assets/index-CFqJKF2L.js"></script>
19
+ <script type="module" crossorigin src="/assets/index-DW-DYWmz.js"></script>
20
20
  <link rel="stylesheet" crossorigin href="/assets/index-DQsMZzI5.css">
21
21
  </head>
22
22
  <body>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bitkyc08/opencodex",
3
- "version": "2.24.0",
3
+ "version": "2.24.2",
4
4
  "description": "Universal provider proxy for OpenAI Codex & Claude Code — use any LLM with Codex CLI/App/SDK and Claude Code",
5
5
  "type": "module",
6
6
  "main": "./bin/package-main.mjs",
@@ -10,7 +10,7 @@
10
10
  */
11
11
  import { aliasForNative, aliasForRoute } from "./alias";
12
12
  import { desktop3pAlias } from "./desktop-3p";
13
- import { nativeOpenAiContextWindow, type CatalogModel } from "../codex/catalog";
13
+ import { nativeOpenAiContextWindow, type CatalogModel, type NativeContextLimitsInput } from "../codex/catalog";
14
14
 
15
15
  const ONE_MILLION = 1_000_000;
16
16
 
@@ -104,7 +104,7 @@ export function buildClaudeContextWindows(
104
104
  // A configured providerContextCaps.openai has to reach the native rows here too. Without
105
105
  // it the Claude surface keeps advertising the uncapped authoritative window while the
106
106
  // Codex catalog advertises the capped one, and the two disagree about the same model.
107
- nativeContextCap?: number,
107
+ nativeContextCap?: NativeContextLimitsInput,
108
108
  ): Record<string, number> {
109
109
  const out: Record<string, number> = {};
110
110
  const put = (key: string | null, value: number) => {
@@ -10,7 +10,7 @@ import {
10
10
  renderDesktopProfile,
11
11
  type DesktopProfileModel,
12
12
  } from "./desktop-profile";
13
- import { nativeOpenAiContextWindow } from "../codex/catalog";
13
+ import { nativeOpenAiContextWindow, type NativeContextLimitsInput } from "../codex/catalog";
14
14
  import { assertDesktop3pModelsValid } from "./desktop-3p-guard";
15
15
 
16
16
  export interface Desktop3pModelEntry {
@@ -191,7 +191,7 @@ function collectDesktop3pModels(
191
191
  nativeSlugs: string[],
192
192
  routedModels: Array<Desktop3pRoutedModel>,
193
193
  profile?: OcxClaudeDesktopProfile,
194
- nativeContextCap?: number,
194
+ nativeContextCap?: NativeContextLimitsInput,
195
195
  ): { models: Desktop3pModelEntry[]; registry: Map<string, string> } {
196
196
  const registry = new Map<string, string>();
197
197
  const models: Desktop3pModelEntry[] = [];
@@ -293,7 +293,7 @@ export function buildDesktop3pRegistry(
293
293
  nativeSlugs: string[],
294
294
  routedModels: Array<Desktop3pRoutedModel>,
295
295
  profile?: OcxClaudeDesktopProfile,
296
- nativeContextCap?: number,
296
+ nativeContextCap?: NativeContextLimitsInput,
297
297
  ): Map<string, string> {
298
298
  const { registry } = collectDesktop3pModels(nativeSlugs, routedModels, profile, nativeContextCap);
299
299
  desktop3pRegistry = registry;
@@ -305,7 +305,7 @@ export function generateDesktop3pModels(
305
305
  nativeSlugs: string[],
306
306
  routedModels: Array<Desktop3pRoutedModel>,
307
307
  profile?: OcxClaudeDesktopProfile,
308
- nativeContextCap?: number,
308
+ nativeContextCap?: NativeContextLimitsInput,
309
309
  ): Desktop3pModelEntry[] {
310
310
  const { models, registry } = collectDesktop3pModels(nativeSlugs, routedModels, profile, nativeContextCap);
311
311
  desktop3pRegistry = registry;
@@ -337,7 +337,7 @@ export function generateDesktop3pConfig(
337
337
  apiKey = "ocx",
338
338
  mode: Desktop3pConfigMode = "static",
339
339
  profile?: OcxClaudeDesktopProfile,
340
- nativeContextCap?: number,
340
+ nativeContextCap?: NativeContextLimitsInput,
341
341
  ): object {
342
342
  const base = {
343
343
  inferenceProvider: "gateway",
@@ -558,7 +558,7 @@ export function writeDesktop3pConfig(
558
558
  apiKey?: string,
559
559
  mode: Desktop3pConfigMode = "static",
560
560
  profile?: OcxClaudeDesktopProfile,
561
- nativeContextCap?: number,
561
+ nativeContextCap?: NativeContextLimitsInput,
562
562
  ): { written: boolean; path: string; reason?: string; fingerprint?: string } {
563
563
  const libraryPath = resolveDesktop3pConfigLibraryPath();
564
564
  const metadataPath = join(libraryPath, "_meta.json");
@@ -15,7 +15,7 @@
15
15
  * - created_at is a fixed constant; max_input_tokens is authoritative-or-null;
16
16
  * max_tokens is always null (no authoritative output limit exists proxy-side).
17
17
  */
18
- import { catalogModelEfforts, nativeEffortClamp, nativeOpenAiContextWindow, nativeOpenAiMaxInputTokens, type CatalogModel } from "../codex/catalog";
18
+ import { catalogModelEfforts, nativeEffortClamp, nativeOpenAiContextWindow, nativeOpenAiMaxInputTokens, type CatalogModel, type NativeContextLimitsInput } from "../codex/catalog";
19
19
  import { claudeCodeAlias, claudeCodeNativeAlias } from "./alias";
20
20
  import { desktop3pAlias } from "./desktop-3p";
21
21
  import { AUTO_CONTEXT_OFF, type AutoContextMode } from "./context-windows";
@@ -108,7 +108,7 @@ export function buildAnthropicModelInfos(
108
108
  auto: AutoContextMode = AUTO_CONTEXT_OFF,
109
109
  idStyle: AnthropicIdStyle = "desktop3p",
110
110
  aliasForRoute: (provider: string, modelId: string) => string = desktop3pAlias,
111
- nativeContextCap?: number,
111
+ nativeContextCap?: NativeContextLimitsInput,
112
112
  ): AnthropicModelInfo[] {
113
113
  const out: AnthropicModelInfo[] = [];
114
114
  const seen = new Set<string>();
@@ -11,11 +11,10 @@ import {
11
11
  type DesktopProfile,
12
12
  } from "../claude/desktop-profile";
13
13
  import { writeDesktop3pConfig, type Desktop3pConfigMode, parseDesktop3pModeArgs } from "../claude/desktop-3p";
14
- import { filterCatalogVisibleModels, desktopVisibleNativeSlugs } from "../codex/catalog";
14
+ import { filterCatalogVisibleModels, desktopVisibleNativeSlugs, nativeContextLimits } from "../codex/catalog";
15
15
  import { buildClaudeDesktopState, fetchAllModels } from "../server/management-api";
16
16
  import { findLiveProxy } from "../server/proxy-liveness";
17
17
  import { runtimeRequest } from "./runtime-api";
18
- import { providerContextCap } from "../providers/context-cap";
19
18
  import { OPENAI_CODEX_PROVIDER_ID } from "../providers/openai-tiers";
20
19
 
21
20
  function isFamily(value: string | undefined): value is DesktopFamily {
@@ -100,7 +99,7 @@ export async function applyProfile(
100
99
  config.apiKeys?.[0]?.key,
101
100
  mode,
102
101
  state.profile,
103
- providerContextCap(config, OPENAI_CODEX_PROVIDER_ID),
102
+ nativeContextLimits(config),
104
103
  );
105
104
  return { ok: result.written, path: result.path, reason: result.reason };
106
105
  }
@@ -123,7 +123,7 @@ export function isUnsupportedOpenAiNativeSlug(slug: string): boolean {
123
123
  * Evidence: devlog/_plan/260817_native_gpt56_1m_context/001_measurement_evidence.md
124
124
  * and 014_final_922k_with_margin.md.
125
125
  */
126
- export const NATIVE_GPT56_CONTEXT_WINDOW = 922_000;
126
+ export const NATIVE_GPT56_CONTEXT_WINDOW = 272_000;
127
127
 
128
128
  /**
129
129
  * Hard ceiling: the largest input the native GPT-5.6 family actually accepts (measured).
@@ -134,19 +134,29 @@ export const NATIVE_GPT56_CONTEXT_WINDOW = 922_000;
134
134
  */
135
135
  export const NATIVE_GPT56_MAX_INPUT_TOKENS = 922_000;
136
136
 
137
+ /** User-facing 1M opt-in: the largest window the native 5.6 family may advertise. */
138
+ export const NATIVE_GPT56_OPT_IN_CONTEXT_WINDOW = NATIVE_GPT56_MAX_INPUT_TOKENS;
139
+
140
+ const NATIVE_GPT56_FAMILY = new Set<string>([
141
+ "gpt-5.6-sol",
142
+ "gpt-5.6-terra",
143
+ "gpt-5.6-luna",
144
+ NATIVE_DAYBREAK_BLUE_MODEL,
145
+ ]);
146
+
137
147
  export const NATIVE_OPENAI_CONTEXT_OVERRIDES: Record<string, { contextWindow?: number; maxContextWindow?: number; maxInputTokens?: number }> = {
138
148
  "gpt-5.5": { contextWindow: 272_000, maxContextWindow: 272_000 },
139
149
  "gpt-5.4": { contextWindow: 1_000_000, maxContextWindow: 1_000_000 },
140
150
  "gpt-5.3-codex-spark": { contextWindow: 100_000, maxContextWindow: 100_000 },
141
- "gpt-5.6-sol": { contextWindow: NATIVE_GPT56_CONTEXT_WINDOW, maxContextWindow: NATIVE_GPT56_CONTEXT_WINDOW, maxInputTokens: NATIVE_GPT56_MAX_INPUT_TOKENS },
142
- "gpt-5.6-terra": { contextWindow: NATIVE_GPT56_CONTEXT_WINDOW, maxContextWindow: NATIVE_GPT56_CONTEXT_WINDOW, maxInputTokens: NATIVE_GPT56_MAX_INPUT_TOKENS },
143
- "gpt-5.6-luna": { contextWindow: NATIVE_GPT56_CONTEXT_WINDOW, maxContextWindow: NATIVE_GPT56_CONTEXT_WINDOW, maxInputTokens: NATIVE_GPT56_MAX_INPUT_TOKENS },
151
+ "gpt-5.6-sol": { contextWindow: NATIVE_GPT56_CONTEXT_WINDOW, maxContextWindow: NATIVE_GPT56_MAX_INPUT_TOKENS, maxInputTokens: NATIVE_GPT56_MAX_INPUT_TOKENS },
152
+ "gpt-5.6-terra": { contextWindow: NATIVE_GPT56_CONTEXT_WINDOW, maxContextWindow: NATIVE_GPT56_MAX_INPUT_TOKENS, maxInputTokens: NATIVE_GPT56_MAX_INPUT_TOKENS },
153
+ "gpt-5.6-luna": { contextWindow: NATIVE_GPT56_CONTEXT_WINDOW, maxContextWindow: NATIVE_GPT56_MAX_INPUT_TOKENS, maxInputTokens: NATIVE_GPT56_MAX_INPUT_TOKENS },
144
154
  // Daybreak Blue borrows Sol's capability metadata and rides the same family contract.
145
155
  // Unlike sol/terra/luna its window was NOT measured here: this account cannot reach it
146
156
  // (`400 "The 'gpt-daybreak-blue-latest' model is not supported when using Codex with a
147
157
  // ChatGPT account."`), so the promotion rests on a report from an account that has
148
158
  // access rather than on a probe. Treat it as the weaker evidence of the four.
149
- [NATIVE_DAYBREAK_BLUE_MODEL]: { contextWindow: NATIVE_GPT56_CONTEXT_WINDOW, maxContextWindow: NATIVE_GPT56_CONTEXT_WINDOW, maxInputTokens: NATIVE_GPT56_MAX_INPUT_TOKENS },
159
+ [NATIVE_DAYBREAK_BLUE_MODEL]: { contextWindow: NATIVE_GPT56_CONTEXT_WINDOW, maxContextWindow: NATIVE_GPT56_MAX_INPUT_TOKENS, maxInputTokens: NATIVE_GPT56_MAX_INPUT_TOKENS },
150
160
  };
151
161
 
152
162
  const PINNED_UPSTREAM_MODELS: Map<string, RawEntry> = new Map(
@@ -171,10 +181,10 @@ const PINNED_NATIVE_CAPABILITY_ENTRIES: Map<string, RawEntry> = new Map(
171
181
  );
172
182
 
173
183
  /**
174
- * The user-owned levers that narrow a native window, carried together.
184
+ * The user-owned levers that set a native window, carried together.
175
185
  *
176
- * Both only ever lower: the authoritative window is measured against what the upstream
177
- * accepts, so a user value above it would re-create the over-advertising this unit fixed.
186
+ * For the GPT-5.6 family these may raise the Codex 272k default up to the measured
187
+ * 922k ceiling. Other native slugs still only ever lower.
178
188
  *
179
189
  * This travels as an ARGUMENT rather than module state on purpose. `grok/sync.ts` runs in
180
190
  * the `ocx ensure` parent process, outside the server, so an injected global would never
@@ -223,13 +233,22 @@ export function nativeContextLimits(
223
233
  };
224
234
  }
225
235
 
226
- /** Apply the user levers to an authoritative value. Lowering only, in a fixed order. */
236
+ /** Apply the user levers to an authoritative value. */
227
237
  function narrowToLimits(raw: number | undefined, slug: string, input: NativeContextLimitsInput): number | undefined {
228
238
  if (raw === undefined) return undefined;
229
239
  const limits = asLimits(input);
230
240
  const overlay = positiveInt(limits.modelWindows?.[slug]) ?? positiveInt(limits.providerWindow);
241
+ const cap = positiveInt(limits.cap);
242
+ if (NATIVE_GPT56_FAMILY.has(slug)) {
243
+ const ceiling = NATIVE_GPT56_MAX_INPUT_TOKENS;
244
+ const chosen = overlay ?? cap ?? raw;
245
+ const window = Math.min(chosen, ceiling);
246
+ return overlay !== undefined && cap !== undefined ? Math.min(window, cap) : window;
247
+ }
231
248
  const narrowed = overlay === undefined ? raw : Math.min(raw, overlay);
232
- return applyProviderContextCap(narrowed, limits.cap) ?? narrowed;
249
+ // 922k is the GPT-5.6 1M opt-in, not a request to shrink gpt-5.4's 1M window.
250
+ if (cap === NATIVE_GPT56_MAX_INPUT_TOKENS) return narrowed;
251
+ return applyProviderContextCap(narrowed, cap) ?? narrowed;
233
252
  }
234
253
 
235
254
  export function nativeOpenAiContextWindow(slug: string, limits?: NativeContextLimitsInput): number | undefined {
@@ -74,7 +74,7 @@ import { createAdmissionGate, ResourceAdmissionError, type AdmissionMetrics } fr
74
74
 
75
75
  import { CODEX_CUSTOM_MODEL_CATALOG_KIND, JAWCODE_CATALOG_AUGMENT_PROVIDERS, catalogModelSlug, shouldExposeRoutedModel } from "./parsing";
76
76
  import type { CatalogModel } from "./parsing";
77
- import { disabledNativeSlugs, hasComboTargets, isNativeOpenAiCapabilityAliasModel, nativeDefaultReasoningEffort, nativeInputModalities, nativeOpenAiContextWindow, nativeOpenAiMaxInputTokens, nativeOpenAiSlugs, nativeParallelToolCalls, nativeReasoningEfforts } from "./metadata";
77
+ import { disabledNativeSlugs, hasComboTargets, isNativeOpenAiCapabilityAliasModel, NATIVE_GPT56_MAX_INPUT_TOKENS, nativeContextLimits, nativeDefaultReasoningEffort, nativeInputModalities, nativeOpenAiContextWindow, nativeOpenAiMaxInputTokens, nativeOpenAiSlugs, nativeParallelToolCalls, nativeReasoningEfforts } from "./metadata";
78
78
  import { deriveComboCatalogModel, normalizedOpenAiApiSignature, openAiApiCollisionWarnings, replaceLastComboCatalogOmissions, warnUncataloguedComboOnce } from "./aggregation";
79
79
  import type { ComboCatalogOmission } from "./aggregation";
80
80
  import type { CatalogGatherProviderAuthEvidence } from "./filesystem-evidence";
@@ -1707,7 +1707,7 @@ async function gatherRoutedModelsUncached(
1707
1707
  // configs that will never need it.
1708
1708
  } else {
1709
1709
  const disabled = disabledNativeSlugs(config);
1710
- const openaiContextCap = providerContextCap(config, OPENAI_CODEX_PROVIDER_ID);
1710
+ const openaiContextCap = nativeContextLimits(config);
1711
1711
  const requiredNativeComboTargets = new Set(listComboIds(config).flatMap(id => {
1712
1712
  const combo = getCombo(config, id);
1713
1713
  return combo?.targets.flatMap(target => (
@@ -1747,15 +1747,17 @@ async function gatherRoutedModelsUncached(
1747
1747
  const combo = getCombo(config, id);
1748
1748
  if (!combo) continue;
1749
1749
  const nativeContextWindow = combo.nativeAlias && combo.alias
1750
- ? nativeOpenAiContextWindow(combo.alias, providerContextCap(config, OPENAI_CODEX_PROVIDER_ID))
1750
+ ? nativeOpenAiContextWindow(combo.alias, nativeContextLimits(config))
1751
1751
  : undefined;
1752
1752
  const nativeAliasMaxInput = combo.nativeAlias && combo.alias
1753
- ? nativeOpenAiMaxInputTokens(combo.alias, providerContextCap(config, OPENAI_CODEX_PROVIDER_ID))
1753
+ ? (combo.alias.startsWith("gpt-5.6-") || combo.alias.includes("daybreak")
1754
+ ? NATIVE_GPT56_MAX_INPUT_TOKENS
1755
+ : nativeOpenAiMaxInputTokens(combo.alias) ?? nativeOpenAiContextWindow(combo.alias))
1754
1756
  : undefined;
1755
1757
  const nativeAliasFallback = combo.nativeAlias && combo.alias && nativeContextWindow !== undefined
1756
1758
  ? {
1757
1759
  contextWindow: nativeContextWindow,
1758
- ...(nativeAliasMaxInput !== undefined ? { maxInputTokens: Math.min(nativeAliasMaxInput, nativeContextWindow) } : {}),
1760
+ ...(nativeAliasMaxInput !== undefined ? { maxInputTokens: nativeAliasMaxInput } : {}),
1759
1761
  inputModalities: nativeInputModalities(combo.alias),
1760
1762
  reasoningEfforts: nativeReasoningEfforts(combo.alias),
1761
1763
  }
@@ -1801,18 +1803,22 @@ async function gatherRoutedModelsUncached(
1801
1803
  && providerForCanonicalCheck !== undefined
1802
1804
  && isCanonicalOpenAiForwardProvider(providerForCanonicalCheck)
1803
1805
  && isNativeOpenAiCapabilityAliasModel(cm.modelId);
1806
+ const customNativeLimits = {
1807
+ ...nativeContextLimits(config),
1808
+ ...(typeof cm.contextWindow === "number" && cm.contextWindow > 0
1809
+ ? { modelWindows: { ...(nativeContextLimits(config).modelWindows ?? {}), [cm.modelId]: cm.contextWindow } }
1810
+ : {}),
1811
+ };
1804
1812
  const nativeAliasContextWindow = codexForwardNativeCapabilityAlias
1805
- ? nativeOpenAiContextWindow(cm.modelId, providerContextCap(config, OPENAI_CODEX_PROVIDER_ID))
1813
+ ? nativeOpenAiContextWindow(cm.modelId, customNativeLimits)
1806
1814
  : undefined;
1807
1815
  const customContextWindow = cm.contextWindow
1808
1816
  ? nativeAliasContextWindow !== undefined
1809
- ? Math.min(cm.contextWindow, nativeAliasContextWindow)
1817
+ ? nativeAliasContextWindow
1810
1818
  : cm.contextWindow
1811
1819
  : nativeAliasContextWindow;
1812
- // Input ceiling for a native capability alias, clamped to whatever window we settled on
1813
- // above. A custom row that lowered the window must not keep the full native input budget.
1814
1820
  const nativeAliasMaxInputTokens = codexForwardNativeCapabilityAlias
1815
- ? nativeOpenAiMaxInputTokens(cm.modelId, providerContextCap(config, OPENAI_CODEX_PROVIDER_ID))
1821
+ ? nativeOpenAiMaxInputTokens(cm.modelId, customNativeLimits)
1816
1822
  : undefined;
1817
1823
  const customMaxInputTokens = nativeAliasMaxInputTokens !== undefined && customContextWindow !== undefined
1818
1824
  ? Math.min(nativeAliasMaxInputTokens, customContextWindow)
@@ -79,9 +79,11 @@ function canonicalConfigEncoding(value: unknown, ancestors = new Set<object>()):
79
79
  }
80
80
 
81
81
  if (ancestors.has(value)) throw new TypeError("Catalog config identity cannot encode a cyclic graph.");
82
- if (Object.getOwnPropertySymbols(value).length > 0) {
83
- throw new TypeError("Catalog config identity cannot encode symbol keys.");
84
- }
82
+ // Symbol keys are process-local metadata (e.g. the user-cost-overlay
83
+ // preservation-owner tag) that JSON.stringify omits and no persist path
84
+ // writes. They must not change the durable config identity, so encoding
85
+ // proceeds over the enumerable string-keyed properties below (Object.keys
86
+ // already ignores symbols). Symbol VALUES are still refused above.
85
87
  ancestors.add(value);
86
88
  try {
87
89
  if (Array.isArray(value)) {
@@ -2,7 +2,7 @@
2
2
  // Public surface preserved exactly; importers keep using "src/codex/catalog".
3
3
  export { isMediaGenerationModelId, shouldExposeRoutedModel, readCodexCatalogPath, readCatalog, normalizeRoutedCatalogEntry, catalogModelSlug, filterSupportedNativeSlugs, catalogModelSupportsReasoningSummaries } from "./catalog/parsing";
4
4
  export type { CatalogModel, MultiAgentMode } from "./catalog/parsing";
5
- export { accountBoundNativeOpenAiSlugs, accountBoundNativeOpenAiSlugsBySelector, CODEX_NATIVE_ALIAS_CATALOG_KIND, NATIVE_DAYBREAK_BLUE_MODEL, NATIVE_GPT56_MAX_INPUT_TOKENS, NATIVE_OPENAI_CAPABILITY_ALIAS_MODELS, NATIVE_OPENAI_MODELS, configuredNativeAliasSlugs, desktopAllowlistSuppressedNativeSlugs, isNativeAliasCatalogEntry, isNativeOpenAiCapabilityAliasModel, nativeContextLimits, nativeOpenAiCapabilitySourceSlug, nativeOpenAiContextWindow, nativeOpenAiMaxInputTokens, disabledNativeSlugs, visibleNativeSlugs, desktopVisibleNativeSlugs, nativeModelRows, applyNativeVisibility, observedAccountBoundNativeEntries, observedAccountBoundNativeOpenAiSlugs, upstreamNativeEntry, nativeOpenAiSlugs, listCatalogNativeSlugs, nativeInputModalities, nativeReasoningEfforts, nativeDefaultReasoningEffort, shouldIncludeAccountBoundNativeOpenAi, shouldIncludeNativeOpenAi, type NativeContextLimits, type NativeContextLimitsInput } from "./catalog/metadata";
5
+ export { accountBoundNativeOpenAiSlugs, accountBoundNativeOpenAiSlugsBySelector, CODEX_NATIVE_ALIAS_CATALOG_KIND, NATIVE_DAYBREAK_BLUE_MODEL, NATIVE_GPT56_CONTEXT_WINDOW, NATIVE_GPT56_MAX_INPUT_TOKENS, NATIVE_GPT56_OPT_IN_CONTEXT_WINDOW, NATIVE_OPENAI_CAPABILITY_ALIAS_MODELS, NATIVE_OPENAI_MODELS, configuredNativeAliasSlugs, desktopAllowlistSuppressedNativeSlugs, isNativeAliasCatalogEntry, isNativeOpenAiCapabilityAliasModel, nativeContextLimits, nativeOpenAiCapabilitySourceSlug, nativeOpenAiContextWindow, nativeOpenAiMaxInputTokens, disabledNativeSlugs, visibleNativeSlugs, desktopVisibleNativeSlugs, nativeModelRows, applyNativeVisibility, observedAccountBoundNativeEntries, observedAccountBoundNativeOpenAiSlugs, upstreamNativeEntry, nativeOpenAiSlugs, listCatalogNativeSlugs, nativeInputModalities, nativeReasoningEfforts, nativeDefaultReasoningEffort, shouldIncludeAccountBoundNativeOpenAi, shouldIncludeNativeOpenAi, type NativeContextLimits, type NativeContextLimitsInput } from "./catalog/metadata";
6
6
  export { isSpawnableCodexCandidate, codexExecInvocation, loadBundledCodexCatalog, materializeBundledCodexCatalog, loadCatalogTemplate } from "./catalog/bundled";
7
7
  export { nativeEffortClamp, shouldApplyNativeEffortClamp, catalogModelEfforts, codexSupportedReasoningEfforts, clampedDefaultEffort, clampEntryToCodexSupportedEfforts, clampCatalogModelsToCodexSupport } from "./catalog/effort";
8
8
  export { applyProviderConfigHints, isDatedVariantId, filterCatalogVisibleModels, gatherRoutedModels, clearGatherRoutedModelsInflight, augmentRoutedModelsWithRegistryOpenAiApiRows, augmentRoutedModelsWithMetadata, resolveComboCatalogMember, configuredComboTargetModelsByProvider } from "./catalog/provider-fetch";
@@ -7,6 +7,8 @@
7
7
  * them here breaks that cycle. `inject.ts` imports them back and re-exports the
8
8
  * two public predicates, so external callers see no change.
9
9
  */
10
+ import { parseTomlString } from "./paths";
11
+
10
12
  export const OCX_SECTION_MARKER = "# Auto-injected by opencodex";
11
13
 
12
14
  export function isRootOpenaiBaseUrlLine(line: string): boolean {
@@ -16,7 +18,11 @@ export function isRootOpenaiBaseUrlLine(line: string): boolean {
16
18
  export function tomlStringPattern(key: string): RegExp {
17
19
  const escaped = key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
18
20
  const keyToken = `(?:${escaped}|"${escaped}"|'${escaped}')`;
19
- return new RegExp(`^\\s*${keyToken}\\s*=\\s*["']([^"']+)["']\\s*(?:#.*)?$`);
21
+ // The quoted value is captured WITH its quotes so callers can decode it as TOML.
22
+ // A basic string escapes backslashes, so a Windows path is stored doubled; reading
23
+ // the raw bytes back returned a path that matched nothing on disk and made the
24
+ // journal's recorded catalog path un-restorable (#1798).
25
+ return new RegExp(`^\\s*${keyToken}\\s*=\\s*("(?:\\\\.|[^"])*"|'[^']*')\\s*(?:#.*)?$`);
20
26
  }
21
27
 
22
28
  export function rootTomlString(content: string, key: string): string | null {
@@ -26,7 +32,7 @@ export function rootTomlString(content: string, key: string): string | null {
26
32
  const pattern = tomlStringPattern(key);
27
33
  for (const line of rootLines) {
28
34
  const match = pattern.exec(line);
29
- if (match?.[1]) return match[1].trim();
35
+ if (match?.[1]) return parseTomlString(match[1]).trim();
30
36
  }
31
37
  return null;
32
38
  }
@@ -45,7 +51,7 @@ export function providerTableString(content: string, provider: string, key: stri
45
51
  const pattern = tomlStringPattern(key);
46
52
  for (let index = start + 1; index < lines.length && !/^\s*\[/.test(lines[index]); index += 1) {
47
53
  const match = pattern.exec(lines[index]);
48
- if (match?.[1]) return match[1].trim();
54
+ if (match?.[1]) return parseTomlString(match[1]).trim();
49
55
  }
50
56
  return null;
51
57
  }
@@ -39,6 +39,31 @@ const SID_PATTERN = /^S-1-(?:\d+-)+\d+$/i;
39
39
  */
40
40
  const WINDOWS_POWERSHELL_LOOKUP_TIMEOUT_MS = 8_000;
41
41
 
42
+ /**
43
+ * FOLDERID_LocalAppData, and the flag that makes the lookup ignore the caller's
44
+ * environment.
45
+ *
46
+ * The obvious spelling, .NET's
47
+ * `GetFolderPath(SpecialFolder.LocalApplicationData)`, is unusable here: on
48
+ * Windows it resolves through `USERPROFILE`, and when the profile named there has
49
+ * no local AppData directory on disk it returns an EMPTY STRING rather than an
50
+ * error. Any
51
+ * caller that redirected `USERPROFILE` (the test sandbox does, and so does a
52
+ * service account whose profile has not been materialized) therefore refused every
53
+ * coordinator lookup with "returned an empty value", which is the exact
54
+ * environment dependence this module exists to eliminate.
55
+ *
56
+ * `SHGetKnownFolderPath` with a null token and `KF_FLAG_DEFAULT_PATH` reads the
57
+ * known-folder registration for the effective token instead: it returns the real
58
+ * per-user path whether or not the directory exists, and it is unaffected by
59
+ * `USERPROFILE`, `LOCALAPPDATA`, `HOMEDRIVE`, or `HOMEPATH`. A non-null token argument
60
+ * is NOT equivalent: passing (HANDLE)-1 resolves the DEFAULT USER profile
61
+ * (the built-in "Default" profile), which would key coordination to a namespace
62
+ * no real account writes to.
63
+ */
64
+ const WINDOWS_LOCAL_APPDATA_FOLDER_ID = "F1B32785-6FBA-4FCF-9D55-7B8E7F157091";
65
+ const WINDOWS_KF_FLAG_DEFAULT_PATH = "0x00000400";
66
+
42
67
  export class CodexUserIdentityRefusal extends Error {
43
68
  readonly code = "CODEX_USER_IDENTITY_REFUSED";
44
69
 
@@ -93,6 +118,42 @@ export function windowsIdentityPowerShellCommandForTests(expression: string): st
93
118
  return windowsIdentityPowerShellCommand(expression);
94
119
  }
95
120
 
121
+ /**
122
+ * PowerShell expression yielding the effective account's local AppData path.
123
+ *
124
+ * P/Invoke rather than a .NET convenience wrapper, for the reason recorded on
125
+ * WINDOWS_LOCAL_APPDATA_FOLDER_ID: the wrapper follows `USERPROFILE` and answers
126
+ * an empty string for a profile whose directory is absent, which is precisely
127
+ * the environment dependence this module refuses to inherit. The type is added
128
+ * under a unique name per process because `Add-Type` cannot redefine one.
129
+ *
130
+ * The whole sequence is wrapped in one `$(...)` subexpression because the caller
131
+ * substitutes this text into `[string](<expression>)`; several statements
132
+ * spliced in bare would close that cast's parenthesis early and fail to parse.
133
+ */
134
+ function windowsLocalAppDataExpression(): string {
135
+ const signature =
136
+ '[DllImport("shell32.dll", CharSet = CharSet.Unicode)] public static extern int '
137
+ + 'SHGetKnownFolderPath(ref System.Guid id, uint flags, System.IntPtr token, out System.IntPtr path);';
138
+ const statements = [
139
+ `$ocxShell = Add-Type -MemberDefinition '${signature}'`
140
+ + " -Name OcxKnownFolder -Namespace OcxIdentity -PassThru",
141
+ `$ocxFolderId = [System.Guid]'${WINDOWS_LOCAL_APPDATA_FOLDER_ID}'`,
142
+ "$ocxPathPtr = [System.IntPtr]::Zero",
143
+ "$ocxHr = $ocxShell::SHGetKnownFolderPath([ref]$ocxFolderId, "
144
+ + `${WINDOWS_KF_FLAG_DEFAULT_PATH}, [System.IntPtr]::Zero, [ref]$ocxPathPtr)`,
145
+ "if ($ocxHr -ne 0) { throw 'SHGetKnownFolderPath failed' }",
146
+ "try { [System.Runtime.InteropServices.Marshal]::PtrToStringUni($ocxPathPtr) }"
147
+ + " finally { [System.Runtime.InteropServices.Marshal]::FreeCoTaskMem($ocxPathPtr) }",
148
+ ];
149
+ return `$(${statements.join("; ")})`;
150
+ }
151
+
152
+ /** Test-only readback of the environment-independent known-folder expression. */
153
+ export function windowsLocalAppDataExpressionForTests(): string {
154
+ return windowsLocalAppDataExpression();
155
+ }
156
+
96
157
  /** Test-only readback of the spawn options shared by the identity lookups (#1278). */
97
158
  export function windowsIdentityPowerShellSpawnOptionsForTests(): ReturnType<
98
159
  typeof windowsIdentityPowerShellSpawnOptions
@@ -122,7 +183,31 @@ export function decodeWindowsIdentityPowerShellOutputForTests(output: Uint8Array
122
183
  return decodeWindowsIdentityPowerShellOutput(output);
123
184
  }
124
185
 
186
+ /**
187
+ * Per-process memo for the Windows lookups.
188
+ *
189
+ * Both values -- the effective token's SID and its known-folder local AppData --
190
+ * are fixed for the lifetime of a process: neither can change without a new logon
191
+ * token, and the lookups deliberately ignore the environment, so nothing a caller
192
+ * does between two calls can alter the answer. Each call otherwise spawns a fresh
193
+ * PowerShell, roughly 150ms for the SID and 310ms for the folder, and the
194
+ * coordinator asks for both on every config write and lock acquisition. That cost
195
+ * pushed real multi-process injection tests past their budget while proving
196
+ * nothing: the second spawn re-derives what the first already established.
197
+ *
198
+ * Only successful lookups are memoized, so a transient failure cannot pin the
199
+ * process into a permanently refusing state.
200
+ */
201
+ const windowsIdentityValueCache = new Map<string, string>();
202
+
203
+ /** Test-only reset so a suite can force a fresh lookup. */
204
+ export function resetWindowsIdentityValueCacheForTests(): void {
205
+ windowsIdentityValueCache.clear();
206
+ }
207
+
125
208
  function powershellValue(expression: string): string {
209
+ const memoized = windowsIdentityValueCache.get(expression);
210
+ if (memoized !== undefined) return memoized;
126
211
  let command: string[];
127
212
  try {
128
213
  command = windowsIdentityPowerShellCommand(expression);
@@ -145,6 +230,7 @@ function powershellValue(expression: string): string {
145
230
  if (result.exitCode !== 0) refuse("Windows effective-account lookup failed.");
146
231
  const value = decodeWindowsIdentityPowerShellOutput(result.stdout ?? Buffer.alloc(0));
147
232
  if (!value) refuse("Windows effective-account lookup returned an empty value.");
233
+ windowsIdentityValueCache.set(expression, value);
148
234
  return value;
149
235
  }
150
236
 
@@ -265,9 +351,7 @@ export function probeCodexCoordinatorNamespace(identity: UserIdentity): Coordina
265
351
  }
266
352
 
267
353
  if (!SID_PATTERN.test(identity.sid)) refuse("The coordinator identity contains an invalid SID.");
268
- const localAppData = powershellValue(
269
- "[Environment]::GetFolderPath([Environment+SpecialFolder]::LocalApplicationData)",
270
- );
354
+ const localAppData = powershellValue(windowsLocalAppDataExpression());
271
355
  if (!isAbsolute(localAppData)) refuse("Windows LocalAppData resolution returned a relative path.");
272
356
  const root = resolve(localAppData, "OpenCodex", "Runtime", "v1", identity.sid.toUpperCase());
273
357
  let entry;
@@ -297,9 +381,7 @@ export function probeCodexCoordinatorNamespace(identity: UserIdentity): Coordina
297
381
 
298
382
  function resolveWindowsRuntimeRoot(identity: Extract<UserIdentity, { platform: "win32" }>): string {
299
383
  if (!SID_PATTERN.test(identity.sid)) refuse("The coordinator identity contains an invalid SID.");
300
- const localAppData = powershellValue(
301
- "[Environment]::GetFolderPath([Environment+SpecialFolder]::LocalApplicationData)",
302
- );
384
+ const localAppData = powershellValue(windowsLocalAppDataExpression());
303
385
  if (!isAbsolute(localAppData)) refuse("Windows LocalAppData resolution returned a relative path.");
304
386
 
305
387
  // The SID and known-folder values come from the effective token/.NET OS APIs,