@bitkyc08/opencodex 2.24.1 → 2.25.0-preview.20260818

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 (58) hide show
  1. package/gui/dist/assets/{index-C3FiAveG.js → index-TFd4xi1L.js} +8 -8
  2. package/gui/dist/index.html +1 -1
  3. package/package.json +1 -1
  4. package/src/adapters/anthropic.ts +42 -0
  5. package/src/adapters/client-fingerprint.ts +9 -5
  6. package/src/adapters/cline-pass-deepseek-v4-tool-replay.ts +69 -0
  7. package/src/adapters/command-code.ts +17 -0
  8. package/src/adapters/cursor/cursor-errors.ts +49 -0
  9. package/src/adapters/cursor/live-models.ts +36 -2
  10. package/src/adapters/cursor/live-transport.ts +55 -4
  11. package/src/adapters/cursor/native-exec.ts +9 -0
  12. package/src/adapters/cursor/protobuf-request.ts +160 -9
  13. package/src/adapters/cursor/request-builder.ts +9 -1
  14. package/src/adapters/cursor/tool-definitions.ts +7 -2
  15. package/src/adapters/google-antigravity-wire.ts +1 -1
  16. package/src/adapters/google.ts +30 -12
  17. package/src/adapters/openai-responses-url.ts +5 -3
  18. package/src/adapters/registry.ts +3 -1
  19. package/src/adapters/tool-catalog-nudge.ts +76 -9
  20. package/src/bridge.ts +53 -9
  21. package/src/claude/context-windows.ts +2 -2
  22. package/src/claude/desktop-3p.ts +6 -6
  23. package/src/claude/model-info.ts +2 -2
  24. package/src/cli/claude-desktop.ts +2 -3
  25. package/src/codex/app-server-processes.ts +69 -35
  26. package/src/codex/catalog/metadata.ts +29 -10
  27. package/src/codex/catalog/provider-fetch.ts +21 -11
  28. package/src/codex/catalog.ts +1 -1
  29. package/src/codex/injected-marker.ts +9 -3
  30. package/src/codex/user-identity.ts +88 -6
  31. package/src/config.ts +1 -0
  32. package/src/generated/compatibility-version.json +61 -53
  33. package/src/grok/sync.ts +2 -4
  34. package/src/lab/projection/rebuild.ts +36 -18
  35. package/src/lib/windows-elevation.ts +18 -3
  36. package/src/lib/windows-secret-acl.ts +49 -19
  37. package/src/oauth/google-antigravity.ts +7 -2
  38. package/src/providers/antigravity-models.ts +126 -17
  39. package/src/providers/derive.ts +11 -1
  40. package/src/responses/parser.ts +4 -0
  41. package/src/responses/reasoning-replay-cache.ts +16 -1
  42. package/src/responses/thought-signature-replay.ts +17 -1
  43. package/src/responses/truncated-stop-reason.ts +60 -0
  44. package/src/router.ts +2 -10
  45. package/src/routing/capability.ts +5 -6
  46. package/src/server/index.ts +3 -4
  47. package/src/server/management/agent-settings-routes.ts +5 -5
  48. package/src/server/management/config-routes.ts +2 -2
  49. package/src/server/management/context.ts +2 -0
  50. package/src/server/management/native-integration-routes.ts +3 -3
  51. package/src/server/management/provider-routes.ts +22 -0
  52. package/src/server/management/shared.ts +4 -4
  53. package/src/server/management-api.ts +2 -2
  54. package/src/server/request-log.ts +11 -3
  55. package/src/server/responses/core.ts +4 -1
  56. package/src/server/responses/input-admission.ts +13 -10
  57. package/src/server/system-env.ts +3 -3
  58. package/src/types.ts +13 -1
package/src/bridge.ts CHANGED
@@ -9,6 +9,7 @@ import type {
9
9
  import { coerceIntegerToolArguments } from "./lib/tool-argument-integers";
10
10
  import { adapterFailureFromMessage, classifyError, CYBER_POLICY_ERROR_CODE, isCyberPolicyCode, type OcxErrorPayload } from "./lib/errors";
11
11
  import { encodeCompactionSummary } from "./responses/compaction";
12
+ import { isTruncatedStopReason, truncationReasonFor } from "./responses/truncated-stop-reason";
12
13
  import { encodeReasoningEnvelope, type ReasoningEnvelope } from "./responses/reasoning-envelope";
13
14
  import { rememberReasoningForCall } from "./responses/reasoning-replay-cache";
14
15
  import {
@@ -1154,7 +1155,11 @@ export function bridgeToResponsesSSE(
1154
1155
  // After every close above, so the blob lands AFTER the assistant message it belongs
1155
1156
  // to and the parser's backwards pairing finds it.
1156
1157
  flushKiroRedactedReasoning();
1157
- if (options?.compaction) {
1158
+ // Truncated turns must never install replacement history (#422). The buffered path
1159
+ // has always checked this; streaming emitted the item BEFORE reading stopReason, so
1160
+ // a max_tokens/content_filter turn shipped a half-written summary and then declared
1161
+ // itself incomplete — the same hazard, one branch over.
1162
+ if (options?.compaction && !isTruncatedStopReason(event.stopReason)) {
1158
1163
  // Exactly one compaction item per turn; codex-rs takes the first and fatals on 0.
1159
1164
  const item = {
1160
1165
  type: "compaction", id: `cmp_${uuid()}`,
@@ -1164,14 +1169,18 @@ export function bridgeToResponsesSSE(
1164
1169
  retainFinishedItem(item as OutputItem, compactionTextBytes);
1165
1170
  outputIndex++;
1166
1171
  }
1167
- if (event.stopReason === "max_tokens" || event.stopReason === "content_filter") {
1172
+ // Recognize every adapter's truncation vocabulary, not just the canonical pair.
1173
+ // Suppression and terminal status must agree: withholding the compaction item while
1174
+ // still reporting success hands codex-rs a completed response with zero compaction
1175
+ // items, which it treats as fatal.
1176
+ if (truncationReasonFor(event.stopReason)) {
1168
1177
  // Upstream stopped before a normal completion. Surface as incomplete so the
1169
1178
  // client can distinguish a truncated/filtered turn from a finished one.
1170
1179
  const response = {
1171
1180
  ...responseSnapshot("incomplete", finishedItems, event.endTurn),
1172
1181
  usage: responsesUsage(event.usage),
1173
1182
  incomplete_details: {
1174
- reason: event.stopReason === "max_tokens" ? "max_output_tokens" : "content_filter",
1183
+ reason: truncationReasonFor(event.stopReason) ?? "content_filter",
1175
1184
  },
1176
1185
  };
1177
1186
  // Cache max-output partials so previous_response_id replay can continue them;
@@ -1467,7 +1476,15 @@ function buildResponseJSONWithBudget(
1467
1476
  let incompleteEvent: Extract<AdapterEvent, { type: "incomplete" }> | undefined;
1468
1477
  let endTurn: boolean | undefined;
1469
1478
  let stopReason: string | undefined;
1479
+ // The adapter's stop reason exactly as it arrived. `stopReason` above is deliberately narrowed
1480
+ // to the two reasons that map onto a Responses `incomplete_details`; the raw value is what the
1481
+ // truncation guard needs, because adapters disagree on vocabulary (`length`, `refusal`, ...).
1482
+ let rawStopReason: string | undefined;
1470
1483
  let cleanDone = false;
1484
+ // Whether the adapter emitted ANY terminal (done/error/incomplete). Distinct from `cleanDone`,
1485
+ // which is only true for a `done` without a stop reason. A buffered turn whose adapter simply
1486
+ // stopped emitting has no terminal at all, and must not be reported as a success.
1487
+ let sawTerminal = false;
1471
1488
  let compactionText = "";
1472
1489
  let compactionTextBytes = 0;
1473
1490
 
@@ -1782,20 +1799,30 @@ function buildResponseJSONWithBudget(
1782
1799
  break;
1783
1800
  case "error":
1784
1801
  errorEvent = e;
1802
+ sawTerminal = true;
1785
1803
  usage = e.usage ?? usage;
1786
1804
  break;
1787
1805
  case "incomplete":
1788
1806
  incompleteEvent = e;
1807
+ sawTerminal = true;
1789
1808
  endTurn = e.endTurn;
1790
1809
  if (e.providerState) options?.onProviderState?.(e.providerState);
1791
1810
  break;
1792
1811
  case "done":
1793
1812
  usage = e.usage;
1813
+ sawTerminal = true;
1794
1814
  endTurn = e.endTurn;
1795
1815
  cleanDone = e.stopReason === undefined;
1816
+ rawStopReason = e.stopReason;
1796
1817
  if (e.providerState) options?.onProviderState?.(e.providerState);
1797
1818
  // Match streaming: max_tokens and content_filter both terminate as incomplete.
1798
- if (e.stopReason === "max_tokens" || e.stopReason === "content_filter") stopReason = e.stopReason;
1819
+ // Normalize every adapter's truncation vocabulary to the canonical pair, so a raw
1820
+ // `length` or `refusal` reaches the status/incomplete_details logic below instead of
1821
+ // silently reading as a clean stop.
1822
+ {
1823
+ const truncation = truncationReasonFor(e.stopReason);
1824
+ if (truncation) stopReason = truncation === "max_output_tokens" ? "max_tokens" : "content_filter";
1825
+ }
1799
1826
  break;
1800
1827
  }
1801
1828
  if (budget) releaseTranslatedEvent(e, budget);
@@ -1803,8 +1830,11 @@ function buildResponseJSONWithBudget(
1803
1830
  flushText(cleanDone && !errorEvent && !incompleteEvent ? "final_answer" : undefined);
1804
1831
  flushSummaryReasoning();
1805
1832
  flushRawReasoning();
1806
- // Open tool call on a failed/incomplete turn must not land as status:"completed".
1807
- if (currentToolCallId) flushToolCall(errorEvent || incompleteEvent ? "incomplete" : "completed");
1833
+ // Open tool call on a failed/incomplete turn must not land as status:"completed" — and neither
1834
+ // must one left open by a stream that stopped without any terminal at all. That case previously
1835
+ // fell through to "completed", handing back a function_call whose arguments were half-written
1836
+ // JSON, inside a turn also marked completed.
1837
+ if (currentToolCallId) flushToolCall(errorEvent || incompleteEvent || !sawTerminal ? "incomplete" : "completed");
1808
1838
  if (batchKiroRedacted) {
1809
1839
  // pushOutput reserves the item itself and releases the retained raw blob it replaces.
1810
1840
  pushOutput({
@@ -1820,8 +1850,12 @@ function buildResponseJSONWithBudget(
1820
1850
  options?.compaction
1821
1851
  && !errorEvent
1822
1852
  && !incompleteEvent
1823
- && stopReason !== "max_tokens"
1824
- && stopReason !== "content_filter"
1853
+ // A stream that stopped without any terminal did not complete either. The original guard
1854
+ // could only see explicit failure events, so an adapter EOF slipped past it and installed a
1855
+ // truncated summary as replacement history — the exact #422 hazard, reached by a route that
1856
+ // did not exist when the guard was written.
1857
+ && sawTerminal
1858
+ && !isTruncatedStopReason(rawStopReason)
1825
1859
  ) {
1826
1860
  pushOutput({ type: "compaction", id: `cmp_${uuid()}`, encrypted_content: encodeCompactionSummary(compactionText) }, compactionTextBytes);
1827
1861
  }
@@ -1831,7 +1865,13 @@ function buildResponseJSONWithBudget(
1831
1865
  ? "failed"
1832
1866
  : incompleteEvent || stopReason === "max_tokens" || stopReason === "content_filter"
1833
1867
  ? "incomplete"
1834
- : "completed";
1868
+ : sawTerminal
1869
+ ? "completed"
1870
+ // The adapter stopped emitting without any terminal, so the turn was cut short. Streaming
1871
+ // already reports this as response.incomplete / adapter_eof (see the !terminated branch);
1872
+ // defaulting the buffered path to "completed" handed callers a truncated turn — including
1873
+ // one carrying a never-closed tool call with half-written JSON arguments — as a success.
1874
+ : "incomplete";
1835
1875
  options?.onUsage?.(incompleteEvent?.usage ?? usage);
1836
1876
  return {
1837
1877
  id: responseId, object: "response",
@@ -1851,6 +1891,10 @@ function buildResponseJSONWithBudget(
1851
1891
  incomplete_details: { reason: "max_output_tokens" },
1852
1892
  } : stopReason === "content_filter" ? {
1853
1893
  incomplete_details: { reason: "content_filter" },
1894
+ } : !sawTerminal ? {
1895
+ // Same reason string the streaming path uses, so a caller sees one signal for one condition
1896
+ // regardless of which surface it asked for.
1897
+ incomplete_details: { reason: "adapter_eof" },
1854
1898
  } : {}),
1855
1899
  usage: responsesUsage(incompleteEvent?.usage ?? usage),
1856
1900
  };
@@ -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
  }
@@ -348,8 +348,34 @@ function listDarwinSnapshots(uid: number | undefined): ProcessSnapshot[] {
348
348
  * Exported for the Windows integration regression that exercises the real
349
349
  * PowerShell enumeration.
350
350
  */
351
- export function listWindowsSnapshots(): ProcessSnapshot[] {
351
+ /**
352
+ * Turn one PowerShell enumeration's stdout into snapshots.
353
+ *
354
+ * Split out from the spawn so the failure contract is testable off-Windows: the
355
+ * sentinel path is the difference between "no Codex process is running" and "we could
356
+ * not read the process list", and only one of those is safe to act on.
357
+ */
358
+ export function parseWindowsSnapshotOutput(output: string): ProcessSnapshot[] {
352
359
  const out: ProcessSnapshot[] = [];
360
+ for (const line of output.split(/\r?\n/)) {
361
+ // A candidate whose owner could not be verified — or a top-level query that
362
+ // failed outright — makes the whole enumeration incomplete. The staleness
363
+ // collector must not read the partial result as "nothing running".
364
+ if (line.trim() === "__OCX_ENUM_INCOMPLETE__") throw new Error("windows_enum_incomplete");
365
+ const tab = line.indexOf("\t");
366
+ if (tab <= 0) continue;
367
+ const tab2 = line.indexOf("\t", tab + 1);
368
+ if (tab2 <= tab) continue;
369
+ const pid = Number(line.slice(0, tab));
370
+ const commandLine = line.slice(tab + 1, tab2).trim();
371
+ const owner = line.slice(tab2 + 1).trim();
372
+ if (!Number.isSafeInteger(pid) || pid <= 1 || !commandLine || !owner) continue;
373
+ out.push({ pid, commandLine, owner });
374
+ }
375
+ return out;
376
+ }
377
+
378
+ export function listWindowsSnapshots(runPowerShell?: (psCommand: string) => string): ProcessSnapshot[] {
353
379
  // Newlines keep -Command as a real script (space-joined statements need ';').
354
380
  // Double-quoted format string so `t expands to a real tab.
355
381
  // Codex candidates only: basename token codex / codex.exe / codex.cmd /
@@ -361,7 +387,15 @@ export function listWindowsSnapshots(): ProcessSnapshot[] {
361
387
  const psCommand = [
362
388
  "$ErrorActionPreference='SilentlyContinue'",
363
389
  "$me=[System.Security.Principal.WindowsIdentity]::GetCurrent().Name",
364
- "Get-CimInstance Win32_Process | Where-Object {",
390
+ // -ErrorAction Stop plus the outer try is what makes a TOP-LEVEL query failure
391
+ // observable. Under SilentlyContinue alone, a failing Get-CimInstance emits nothing
392
+ // and the enumeration is indistinguishable from "no Codex process is running" —
393
+ // the parse loop finds no rows, no sentinel is produced, and the staleness collector
394
+ // reports not_running for a machine whose process list it never actually read.
395
+ // The per-process catch below cannot cover this: it only runs once the pipeline has
396
+ // objects to iterate.
397
+ "try {",
398
+ "Get-CimInstance Win32_Process -ErrorAction Stop | Where-Object {",
365
399
  " -not [string]::IsNullOrWhiteSpace($_.CommandLine) -and (",
366
400
  ` $_.CommandLine -match ${basenameMatch} -or`,
367
401
  ` $_.CommandLine -match ${codeModeMatch}`,
@@ -376,31 +410,19 @@ export function listWindowsSnapshots(): ProcessSnapshot[] {
376
410
  " \"{0}`t{1}`t{2}\" -f $_.ProcessId, $cmd, $owner",
377
411
  " } catch { \"__OCX_ENUM_INCOMPLETE__\" }",
378
412
  "}",
413
+ "} catch { \"__OCX_ENUM_INCOMPLETE__\" }",
379
414
  ].join("\n");
380
415
  // Top-level exec failure propagates (see listDarwinSnapshots note). The
381
416
  // executable resolves from the trusted System32 directory (never PATH), and
382
417
  // windowsHide keeps the enumeration console-less on desktop sessions (#1278).
383
- const output = execFileSync(resolveTrustedWindowsPowerShellExe(), [
384
- "-NoProfile", "-NoLogo", "-NonInteractive",
385
- "-Command",
386
- psCommand,
387
- ], { encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"], timeout: 8_000, windowsHide: true });
388
- for (const line of output.split(/\r?\n/)) {
389
- // A candidate whose owner could not be verified makes the whole
390
- // enumeration incomplete — the staleness collector must not read the
391
- // partial result as "nothing running".
392
- if (line.trim() === "__OCX_ENUM_INCOMPLETE__") throw new Error("windows_enum_incomplete");
393
- const tab = line.indexOf("\t");
394
- if (tab <= 0) continue;
395
- const tab2 = line.indexOf("\t", tab + 1);
396
- if (tab2 <= tab) continue;
397
- const pid = Number(line.slice(0, tab));
398
- const commandLine = line.slice(tab + 1, tab2).trim();
399
- const owner = line.slice(tab2 + 1).trim();
400
- if (!Number.isSafeInteger(pid) || pid <= 1 || !commandLine || !owner) continue;
401
- out.push({ pid, commandLine, owner });
402
- }
403
- return out;
418
+ const output = runPowerShell
419
+ ? runPowerShell(psCommand)
420
+ : execFileSync(resolveTrustedWindowsPowerShellExe(), [
421
+ "-NoProfile", "-NoLogo", "-NonInteractive",
422
+ "-Command",
423
+ psCommand,
424
+ ], { encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"], timeout: 8_000, windowsHide: true });
425
+ return parseWindowsSnapshotOutput(output);
404
426
  }
405
427
 
406
428
  function defaultListSnapshots(platform: NodeJS.Platform, getuid: () => number | undefined): ProcessSnapshot[] {
@@ -592,6 +614,18 @@ function defaultCatalogMtimeMs(): number | null {
592
614
  // guidance calls (#857).
593
615
  let catalogStateCache: { atMs: number; status: CodexAppServerCatalogStatus } | null = null;
594
616
  const CATALOG_STATE_TTL_MS = 5_000;
617
+ /**
618
+ * `unknown` is a failure to observe, not an observation, so it gets a much shorter
619
+ * window than a real reading. At the full 5s a single transient enumeration failure
620
+ * suppresses guidance for every call in that window, and the retry that would have
621
+ * succeeded never runs. Keeping a brief window still collapses a burst of per-turn
622
+ * calls into one probe, which is what the cache is for.
623
+ */
624
+ const CATALOG_STATE_UNKNOWN_TTL_MS = 250;
625
+
626
+ export function catalogStateTtlMs(state: CodexAppServerCatalogState): number {
627
+ return state === "unknown" ? CATALOG_STATE_UNKNOWN_TTL_MS : CATALOG_STATE_TTL_MS;
628
+ }
595
629
 
596
630
  /**
597
631
  * Compare the on-disk catalog mtime against the start time of running Codex
@@ -616,7 +650,8 @@ export function collectCodexAppServerCatalogState(
616
650
  const fullyDefault = !io.listSnapshots && !io.readStartMs && !io.catalogMtimeMs
617
651
  && !io.platform && !io.getuid && !io.now;
618
652
  if (fullyDefault
619
- && catalogStateCache && now - catalogStateCache.atMs < CATALOG_STATE_TTL_MS) {
653
+ && catalogStateCache
654
+ && now - catalogStateCache.atMs < catalogStateTtlMs(catalogStateCache.status.state)) {
620
655
  return catalogStateCache.status;
621
656
  }
622
657
  const compute = (): CodexAppServerCatalogStatus => {
@@ -630,17 +665,16 @@ export function collectCodexAppServerCatalogState(
630
665
  });
631
666
  let snapshots: ProcessSnapshot[];
632
667
  let enumerationFailed = false;
633
- if (io.listSnapshots) {
634
- snapshots = io.listSnapshots();
635
- } else {
636
- try {
637
- snapshots = defaultListSnapshots(platform, getuid);
638
- } catch {
639
- // Enumeration failure must never read as "nothing running" that
640
- // would let positive model guidance through on guesswork (#857).
641
- snapshots = [];
642
- enumerationFailed = true;
643
- }
668
+ const enumerate = io.listSnapshots ?? (() => defaultListSnapshots(platform, getuid));
669
+ try {
670
+ snapshots = enumerate();
671
+ } catch {
672
+ // Enumeration failure must never read as "nothing running" — that would let
673
+ // positive model guidance through on guesswork (#857). The injected seam gets
674
+ // the same contract as the default path: whoever enumerates, a failure to read
675
+ // the process list is unknown, not an empty machine.
676
+ snapshots = [];
677
+ enumerationFailed = true;
644
678
  }
645
679
  const processes: CodexAppServerProcess[] = [];
646
680
  const seen = new Set<number>();
@@ -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 {
@@ -38,7 +38,7 @@ import {
38
38
  type CapturedServiceTierAdapterAuthority,
39
39
  } from "../../providers/service-tier";
40
40
  import { effectiveGoogleMode, getProviderRegistryEntry, providerMatchesRegistryTransport } from "../../providers/registry";
41
- import { parseAntigravityAvailableModels } from "../../providers/antigravity-models";
41
+ import { parseAntigravityAvailableModels, registerAntigravityDiscoveredWireModels } from "../../providers/antigravity-models";
42
42
  import { applyProviderContextCap, providerContextCap } from "../../providers/context-cap";
43
43
  import { routedSlug, slugEquals, slugsEquivalent } from "../../providers/slug-codec";
44
44
  import { CODEX_GPT5_IDENTITY_LINE } from "../../adapters/identity";
@@ -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";
@@ -1374,6 +1374,10 @@ async function fetchProviderModelsWithAuth(
1374
1374
  if (!setCached(name, forCache, Date.now(), cacheGeneration)) {
1375
1375
  return observed(withConfiguredRetention(configured), "degraded");
1376
1376
  }
1377
+ registerAntigravityDiscoveredWireModels(prov.baseUrl, antigravity, {
1378
+ provider: name,
1379
+ cacheGeneration,
1380
+ });
1377
1381
  markProviderDiscoveryOk(name, live.length);
1378
1382
  return observed(withConfiguredRetention(forCache, { warnDrops: true }), "authoritative");
1379
1383
  }
@@ -1707,7 +1711,7 @@ async function gatherRoutedModelsUncached(
1707
1711
  // configs that will never need it.
1708
1712
  } else {
1709
1713
  const disabled = disabledNativeSlugs(config);
1710
- const openaiContextCap = providerContextCap(config, OPENAI_CODEX_PROVIDER_ID);
1714
+ const openaiContextCap = nativeContextLimits(config);
1711
1715
  const requiredNativeComboTargets = new Set(listComboIds(config).flatMap(id => {
1712
1716
  const combo = getCombo(config, id);
1713
1717
  return combo?.targets.flatMap(target => (
@@ -1747,15 +1751,17 @@ async function gatherRoutedModelsUncached(
1747
1751
  const combo = getCombo(config, id);
1748
1752
  if (!combo) continue;
1749
1753
  const nativeContextWindow = combo.nativeAlias && combo.alias
1750
- ? nativeOpenAiContextWindow(combo.alias, providerContextCap(config, OPENAI_CODEX_PROVIDER_ID))
1754
+ ? nativeOpenAiContextWindow(combo.alias, nativeContextLimits(config))
1751
1755
  : undefined;
1752
1756
  const nativeAliasMaxInput = combo.nativeAlias && combo.alias
1753
- ? nativeOpenAiMaxInputTokens(combo.alias, providerContextCap(config, OPENAI_CODEX_PROVIDER_ID))
1757
+ ? (combo.alias.startsWith("gpt-5.6-") || combo.alias.includes("daybreak")
1758
+ ? NATIVE_GPT56_MAX_INPUT_TOKENS
1759
+ : nativeOpenAiMaxInputTokens(combo.alias) ?? nativeOpenAiContextWindow(combo.alias))
1754
1760
  : undefined;
1755
1761
  const nativeAliasFallback = combo.nativeAlias && combo.alias && nativeContextWindow !== undefined
1756
1762
  ? {
1757
1763
  contextWindow: nativeContextWindow,
1758
- ...(nativeAliasMaxInput !== undefined ? { maxInputTokens: Math.min(nativeAliasMaxInput, nativeContextWindow) } : {}),
1764
+ ...(nativeAliasMaxInput !== undefined ? { maxInputTokens: nativeAliasMaxInput } : {}),
1759
1765
  inputModalities: nativeInputModalities(combo.alias),
1760
1766
  reasoningEfforts: nativeReasoningEfforts(combo.alias),
1761
1767
  }
@@ -1801,18 +1807,22 @@ async function gatherRoutedModelsUncached(
1801
1807
  && providerForCanonicalCheck !== undefined
1802
1808
  && isCanonicalOpenAiForwardProvider(providerForCanonicalCheck)
1803
1809
  && isNativeOpenAiCapabilityAliasModel(cm.modelId);
1810
+ const customNativeLimits = {
1811
+ ...nativeContextLimits(config),
1812
+ ...(typeof cm.contextWindow === "number" && cm.contextWindow > 0
1813
+ ? { modelWindows: { ...(nativeContextLimits(config).modelWindows ?? {}), [cm.modelId]: cm.contextWindow } }
1814
+ : {}),
1815
+ };
1804
1816
  const nativeAliasContextWindow = codexForwardNativeCapabilityAlias
1805
- ? nativeOpenAiContextWindow(cm.modelId, providerContextCap(config, OPENAI_CODEX_PROVIDER_ID))
1817
+ ? nativeOpenAiContextWindow(cm.modelId, customNativeLimits)
1806
1818
  : undefined;
1807
1819
  const customContextWindow = cm.contextWindow
1808
1820
  ? nativeAliasContextWindow !== undefined
1809
- ? Math.min(cm.contextWindow, nativeAliasContextWindow)
1821
+ ? nativeAliasContextWindow
1810
1822
  : cm.contextWindow
1811
1823
  : 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
1824
  const nativeAliasMaxInputTokens = codexForwardNativeCapabilityAlias
1815
- ? nativeOpenAiMaxInputTokens(cm.modelId, providerContextCap(config, OPENAI_CODEX_PROVIDER_ID))
1825
+ ? nativeOpenAiMaxInputTokens(cm.modelId, customNativeLimits)
1816
1826
  : undefined;
1817
1827
  const customMaxInputTokens = nativeAliasMaxInputTokens !== undefined && customContextWindow !== undefined
1818
1828
  ? Math.min(nativeAliasMaxInputTokens, customContextWindow)
@@ -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
  }