@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.
- package/gui/dist/assets/{index-C3FiAveG.js → index-TFd4xi1L.js} +8 -8
- package/gui/dist/index.html +1 -1
- package/package.json +1 -1
- package/src/adapters/anthropic.ts +42 -0
- package/src/adapters/client-fingerprint.ts +9 -5
- package/src/adapters/cline-pass-deepseek-v4-tool-replay.ts +69 -0
- package/src/adapters/command-code.ts +17 -0
- package/src/adapters/cursor/cursor-errors.ts +49 -0
- package/src/adapters/cursor/live-models.ts +36 -2
- package/src/adapters/cursor/live-transport.ts +55 -4
- package/src/adapters/cursor/native-exec.ts +9 -0
- package/src/adapters/cursor/protobuf-request.ts +160 -9
- package/src/adapters/cursor/request-builder.ts +9 -1
- package/src/adapters/cursor/tool-definitions.ts +7 -2
- package/src/adapters/google-antigravity-wire.ts +1 -1
- package/src/adapters/google.ts +30 -12
- package/src/adapters/openai-responses-url.ts +5 -3
- package/src/adapters/registry.ts +3 -1
- package/src/adapters/tool-catalog-nudge.ts +76 -9
- package/src/bridge.ts +53 -9
- package/src/claude/context-windows.ts +2 -2
- package/src/claude/desktop-3p.ts +6 -6
- package/src/claude/model-info.ts +2 -2
- package/src/cli/claude-desktop.ts +2 -3
- package/src/codex/app-server-processes.ts +69 -35
- package/src/codex/catalog/metadata.ts +29 -10
- package/src/codex/catalog/provider-fetch.ts +21 -11
- package/src/codex/catalog.ts +1 -1
- package/src/codex/injected-marker.ts +9 -3
- package/src/codex/user-identity.ts +88 -6
- package/src/config.ts +1 -0
- package/src/generated/compatibility-version.json +61 -53
- package/src/grok/sync.ts +2 -4
- package/src/lab/projection/rebuild.ts +36 -18
- package/src/lib/windows-elevation.ts +18 -3
- package/src/lib/windows-secret-acl.ts +49 -19
- package/src/oauth/google-antigravity.ts +7 -2
- package/src/providers/antigravity-models.ts +126 -17
- package/src/providers/derive.ts +11 -1
- package/src/responses/parser.ts +4 -0
- package/src/responses/reasoning-replay-cache.ts +16 -1
- package/src/responses/thought-signature-replay.ts +17 -1
- package/src/responses/truncated-stop-reason.ts +60 -0
- package/src/router.ts +2 -10
- package/src/routing/capability.ts +5 -6
- package/src/server/index.ts +3 -4
- package/src/server/management/agent-settings-routes.ts +5 -5
- package/src/server/management/config-routes.ts +2 -2
- package/src/server/management/context.ts +2 -0
- package/src/server/management/native-integration-routes.ts +3 -3
- package/src/server/management/provider-routes.ts +22 -0
- package/src/server/management/shared.ts +4 -4
- package/src/server/management-api.ts +2 -2
- package/src/server/request-log.ts +11 -3
- package/src/server/responses/core.ts +4 -1
- package/src/server/responses/input-admission.ts +13 -10
- package/src/server/system-env.ts +3 -3
- 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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1824
|
-
|
|
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
|
-
:
|
|
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?:
|
|
107
|
+
nativeContextCap?: NativeContextLimitsInput,
|
|
108
108
|
): Record<string, number> {
|
|
109
109
|
const out: Record<string, number> = {};
|
|
110
110
|
const put = (key: string | null, value: number) => {
|
package/src/claude/desktop-3p.ts
CHANGED
|
@@ -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?:
|
|
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?:
|
|
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?:
|
|
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?:
|
|
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?:
|
|
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");
|
package/src/claude/model-info.ts
CHANGED
|
@@ -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?:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
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
|
|
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
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
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 =
|
|
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:
|
|
142
|
-
"gpt-5.6-terra": { contextWindow: NATIVE_GPT56_CONTEXT_WINDOW, maxContextWindow:
|
|
143
|
-
"gpt-5.6-luna": { contextWindow: NATIVE_GPT56_CONTEXT_WINDOW, maxContextWindow:
|
|
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:
|
|
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
|
|
184
|
+
* The user-owned levers that set a native window, carried together.
|
|
175
185
|
*
|
|
176
|
-
*
|
|
177
|
-
*
|
|
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.
|
|
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
|
-
|
|
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 =
|
|
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,
|
|
1754
|
+
? nativeOpenAiContextWindow(combo.alias, nativeContextLimits(config))
|
|
1751
1755
|
: undefined;
|
|
1752
1756
|
const nativeAliasMaxInput = combo.nativeAlias && combo.alias
|
|
1753
|
-
?
|
|
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:
|
|
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,
|
|
1817
|
+
? nativeOpenAiContextWindow(cm.modelId, customNativeLimits)
|
|
1806
1818
|
: undefined;
|
|
1807
1819
|
const customContextWindow = cm.contextWindow
|
|
1808
1820
|
? nativeAliasContextWindow !== undefined
|
|
1809
|
-
?
|
|
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,
|
|
1825
|
+
? nativeOpenAiMaxInputTokens(cm.modelId, customNativeLimits)
|
|
1816
1826
|
: undefined;
|
|
1817
1827
|
const customMaxInputTokens = nativeAliasMaxInputTokens !== undefined && customContextWindow !== undefined
|
|
1818
1828
|
? Math.min(nativeAliasMaxInputTokens, customContextWindow)
|
package/src/codex/catalog.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
}
|