@bitkyc08/opencodex 2.62.0-preview.20260923 → 2.63.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/gui/dist/assets/App-BqrsSrIR.js +50 -0
- package/gui/dist/assets/{Tray-DdvPBVLi.js → Tray-CncKDBTp.js} +1 -1
- package/gui/dist/assets/index-C6SJrh0N.js +86 -0
- package/gui/dist/assets/index-DdDunwDb.css +1 -0
- package/gui/dist/assets/{usage-companion-chart-BIhvlbQW.js → usage-companion-chart-CzAAAB1o.js} +1 -1
- package/gui/dist/index.html +2 -2
- package/package.json +1 -1
- package/src/adapters/cursor/catalog.ts +15 -0
- package/src/adapters/cursor/effort-map.ts +7 -0
- package/src/adapters/cursor/envelope-echo.ts +51 -25
- package/src/adapters/cursor/protobuf-request.ts +39 -13
- package/src/adapters/cursor.ts +52 -4
- package/src/adapters/devin/cloud-direct/chat.ts +50 -16
- package/src/adapters/devin/cloud-direct/stated-reset-retry.ts +4 -1
- package/src/adapters/devin/live-models.ts +7 -0
- package/src/adapters/devin.ts +7 -5
- package/src/adapters/kiro/reasoning.ts +5 -0
- package/src/adapters/openai-responses/passthrough.ts +4 -4
- package/src/adapters/openai-responses/tool-output-recovery.ts +5 -3
- package/src/claude/desktop-gateway-state.ts +7 -2
- package/src/claude/desktop-policy.ts +105 -1
- package/src/cli/config-command.ts +22 -7
- package/src/cli/doctor.ts +14 -4
- package/src/codex/catalog/effort.ts +35 -4
- package/src/codex/catalog/metadata.ts +33 -5
- package/src/codex/catalog/native-models.ts +43 -2
- package/src/codex/catalog/pinned-models.ts +37 -0
- package/src/codex/catalog-auto-refresh.ts +6 -0
- package/src/codex/data/roster-pinned-models.json +359 -0
- package/src/codex/data/upstream-models.json +365 -271
- package/src/codex/inject/provider-table.ts +108 -0
- package/src/codex/inject/remove.ts +2 -114
- package/src/codex/model-entitlements.ts +14 -10
- package/src/codex/subagent-defaults.ts +2 -109
- package/src/codex/toml-source-lines.ts +112 -0
- package/src/config/live-reconcile.ts +145 -27
- package/src/config/load-degrade.ts +3 -4
- package/src/config.ts +2 -2
- package/src/generated/compatibility-version.json +83 -67
- package/src/generated/model-metadata.ts +5 -5
- package/src/lab/artifacts/sanitize.ts +60 -19
- package/src/lib/app-owned-memory-stores.ts +7 -0
- package/src/lib/app-owned-memory.ts +13 -2
- package/src/lib/errors.ts +6 -4
- package/src/lib/upstream-retry.ts +35 -5
- package/src/oauth/devin.ts +43 -37
- package/src/providers/codebuddy-models.ts +11 -0
- package/src/providers/kiro-models.ts +9 -0
- package/src/providers/quota/vendor-probes-oauth.ts +9 -2
- package/src/providers/registry/entries-core.ts +19 -8
- package/src/providers/registry/entries-extended.ts +4 -1
- package/src/providers/registry/model-seeds.ts +22 -2
- package/src/responses/bridge-search-replay-cache.ts +20 -10
- package/src/responses/plaintext-v2-agent-messages.ts +10 -1
- package/src/routing/identity-domains.ts +22 -15
- package/src/server/index/websocket-handler.ts +22 -2
- package/src/server/management/agent-settings-routes.ts +19 -10
- package/src/server/management/context.ts +4 -2
- package/src/server/responses/codex-ws-exchange.ts +24 -8
- package/src/server/responses/core-codex-account.ts +4 -0
- package/src/server/responses/core-combo-failure.ts +16 -9
- package/src/server/responses/core-combo.ts +7 -3
- package/src/server/responses/core-options.ts +6 -0
- package/src/server/responses/native-injection-replay.ts +13 -1
- package/src/server/responses/native-injection.ts +66 -7
- package/src/server/responses/native-response-control.ts +6 -2
- package/src/server/responses/native-steering-replay.ts +60 -0
- package/src/server/responses/native-steering.ts +9 -1
- package/src/server/responses/passthrough-delivery.ts +3 -4
- package/src/server/responses/passthrough-dispatch.ts +7 -0
- package/src/server/responses/request-prepare.ts +12 -0
- package/src/server/responses/request-transport.ts +5 -2
- package/src/server/responses/ws-upstream.ts +1 -1
- package/src/server/ws-bridge.ts +17 -1
- package/src/types/request.ts +5 -0
- package/src/usage/expected-prices.ts +38 -8
- package/src/web-search/executor.ts +38 -13
- package/gui/dist/assets/App-CANWata-.js +0 -50
- package/gui/dist/assets/index-BIS5HXEP.js +0 -86
- package/gui/dist/assets/index-_bpvxJu0.css +0 -1
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { isDeepStrictEqual } from "node:util";
|
|
2
|
+
import { OCX_SECTION_MARKER } from "../injected-marker";
|
|
3
|
+
import { splitSourceLines, type SourceLine } from "../toml-source-lines";
|
|
4
|
+
|
|
5
|
+
function record(value: unknown): Record<string, unknown> | null {
|
|
6
|
+
return value !== null && typeof value === "object" && !Array.isArray(value)
|
|
7
|
+
? value as Record<string, unknown> : null;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function providerValue(text: string): Record<string, unknown> {
|
|
11
|
+
let parsed: unknown;
|
|
12
|
+
try { parsed = Bun.TOML.parse(text); }
|
|
13
|
+
catch { throw new Error("Codex provider table could not be read safely."); }
|
|
14
|
+
const root = record(parsed);
|
|
15
|
+
const providers = record(root?.model_providers);
|
|
16
|
+
const provider = record(providers?.opencodex);
|
|
17
|
+
if (!provider || Object.keys(root!).length !== 1 || Object.keys(providers!).length !== 1) {
|
|
18
|
+
throw new Error("Codex provider table could not be read safely.");
|
|
19
|
+
}
|
|
20
|
+
return provider;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const KEY = String.raw`(?:[A-Za-z0-9_-]+|"(?:[^"\\]|\\.)*"|'[^']*')`;
|
|
24
|
+
const ASSIGNMENT = new RegExp(`^\\s*(${KEY}(?:\\s*\\.\\s*${KEY})*)\\s*=`);
|
|
25
|
+
|
|
26
|
+
function unsupportedProviderAssignment(text: string, section: string): boolean {
|
|
27
|
+
if (section !== "root" && section !== "providers") return false;
|
|
28
|
+
const match = ASSIGNMENT.exec(text);
|
|
29
|
+
if (!match) return false;
|
|
30
|
+
// Parsing only the key path handles quoted/escaped components without reading
|
|
31
|
+
// unrelated large integer values or allowing strings to impersonate a header.
|
|
32
|
+
const parsed = record(Bun.TOML.parse(`${match[1]} = {}\n`))!;
|
|
33
|
+
if (section === "providers") return Object.hasOwn(parsed, "opencodex");
|
|
34
|
+
const providers = record(parsed.model_providers);
|
|
35
|
+
return providers !== null && (Object.keys(providers).length === 0 || Object.hasOwn(providers, "opencodex"));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Recognize real table headers, including quoted keys, without parsing unrelated values. */
|
|
39
|
+
function headerKind(text: string): "provider" | "providers" | "foreign" {
|
|
40
|
+
let parsed: Record<string, unknown> | null;
|
|
41
|
+
try { parsed = record(Bun.TOML.parse(text + "\n")); }
|
|
42
|
+
catch { throw new Error("Codex table header could not be read safely."); }
|
|
43
|
+
const providers = record(parsed?.model_providers);
|
|
44
|
+
if (providers && Object.hasOwn(providers, "opencodex")) {
|
|
45
|
+
if (!record(providers.opencodex)) throw new Error("Codex provider array tables cannot be retained safely.");
|
|
46
|
+
return "provider";
|
|
47
|
+
}
|
|
48
|
+
return providers && Object.keys(providers).length === 0 ? "providers" : "foreign";
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Physical spans are shared by capture and removal so neither sees headers inside values. */
|
|
52
|
+
function providerLines(content: string): { bom: string; lines: SourceLine[]; owned: Set<number> } {
|
|
53
|
+
const bom = content.startsWith("\uFEFF") ? "\uFEFF" : "";
|
|
54
|
+
const lines = splitSourceLines(content.slice(bom.length));
|
|
55
|
+
const owned = new Set<number>();
|
|
56
|
+
let section: "root" | "provider" | "providers" | "foreign" = "root";
|
|
57
|
+
for (let index = 0; index < lines.length; index++) {
|
|
58
|
+
const line = lines[index]!;
|
|
59
|
+
const text = line.text;
|
|
60
|
+
if (line.structural && /^\s*\[/.test(text)) {
|
|
61
|
+
section = headerKind(text);
|
|
62
|
+
if (section === "provider") {
|
|
63
|
+
const previous = lines[index - 1];
|
|
64
|
+
if (previous?.structural && previous.text.trim() === OCX_SECTION_MARKER) owned.add(index - 1);
|
|
65
|
+
}
|
|
66
|
+
} else if (line.structural && unsupportedProviderAssignment(text, section)) {
|
|
67
|
+
// These forms have no independently removable table span. Do not append a
|
|
68
|
+
// competing definition or silently treat a user-owned inline value as absent.
|
|
69
|
+
throw new Error("Codex provider retention requires an explicit provider table.");
|
|
70
|
+
}
|
|
71
|
+
if (section === "provider") owned.add(index);
|
|
72
|
+
}
|
|
73
|
+
return { bom, lines, owned };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function hasOcxProviderTable(content: string): boolean {
|
|
77
|
+
return providerLines(content).owned.size > 0;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Preserve value bytes, including multiline newlines and separated child tables. */
|
|
81
|
+
export function extractOcxProviderTableBlock(content: string): string | null {
|
|
82
|
+
const { lines, owned } = providerLines(content);
|
|
83
|
+
if (owned.size === 0) return null;
|
|
84
|
+
const captured = lines.filter((_, index) => owned.has(index));
|
|
85
|
+
while (captured.length && captured.at(-1)!.structural && !captured.at(-1)!.text.trim()) captured.pop();
|
|
86
|
+
const text = captured.map(line => line.text + line.eol).join("");
|
|
87
|
+
providerValue(text); // Ambiguous or incomplete owned spans never become a retained block.
|
|
88
|
+
return text.endsWith("\n") ? text : text + "\n";
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function removeOcxSection(content: string): string {
|
|
92
|
+
const { bom, lines, owned } = providerLines(content);
|
|
93
|
+
if (owned.size) providerValue(lines.filter((_, index) => owned.has(index)).map(line => line.text + line.eol).join(""));
|
|
94
|
+
return bom + lines.filter((_, index) => !owned.has(index)).map(line => line.text + line.eol).join("");
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** Compare decoded values; cosmetic layout never rewrites the existing file. */
|
|
98
|
+
export function appendOcxProviderTableBlock(content: string, block: string): string {
|
|
99
|
+
const captured = providerValue(block);
|
|
100
|
+
const existing = extractOcxProviderTableBlock(content);
|
|
101
|
+
if (existing !== null) {
|
|
102
|
+
if (!isDeepStrictEqual(providerValue(existing), captured)) {
|
|
103
|
+
throw new Error("Codex restore refused: the native config already defines a different [model_providers.opencodex] table.");
|
|
104
|
+
}
|
|
105
|
+
return content;
|
|
106
|
+
}
|
|
107
|
+
return `${content}${content.endsWith("\n") ? "\n" : "\n\n"}${block}${block.endsWith("\n") ? "" : "\n"}`;
|
|
108
|
+
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { existsSync, readFileSync, unlinkSync } from "node:fs";
|
|
2
2
|
import { atomicWriteFile } from "../../config";
|
|
3
3
|
import {
|
|
4
|
-
OCX_SECTION_MARKER,
|
|
5
4
|
REALTIME_WS_BASE_URL_KEY,
|
|
6
5
|
hasInjectedOpenaiBaseUrl,
|
|
7
6
|
rootTomlString,
|
|
@@ -23,119 +22,8 @@ import {
|
|
|
23
22
|
stripRootRoutedModel,
|
|
24
23
|
} from "./config-toml";
|
|
25
24
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
* config rewrite re-serializes the provider's inline `env_http_headers` table. They define the
|
|
29
|
-
* same `model_providers.opencodex` provider, so cleanup must remove them too — otherwise the
|
|
30
|
-
* provider survives with no `name` and Codex rejects the whole config
|
|
31
|
-
* ("provider name must not be empty"). The dot terminator keeps a user's
|
|
32
|
-
* `[model_providers.opencodex_backup]`-style tables out of scope.
|
|
33
|
-
*/
|
|
34
|
-
function isOcxProviderHeaderLine(trimmedLine: string): boolean {
|
|
35
|
-
// Root form matched by regex, not equality: TOML v1.0 allows a trailing comment
|
|
36
|
-
// (`[model_providers.opencodex] # comment`), and an exact compare would miss that form.
|
|
37
|
-
// The sub-table prefix check already tolerates trailing comments by construction.
|
|
38
|
-
return (
|
|
39
|
-
/^\[model_providers\.opencodex\]\s*(?:#.*)?$/.test(trimmedLine) ||
|
|
40
|
-
trimmedLine.startsWith("[model_providers.opencodex.")
|
|
41
|
-
);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export function hasOcxProviderTable(content: string): boolean {
|
|
45
|
-
return content
|
|
46
|
-
.split("\n")
|
|
47
|
-
.some((line) => isOcxProviderHeaderLine(line.trim()));
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export function removeOcxSection(content: string): string {
|
|
51
|
-
const lines = content.split("\n");
|
|
52
|
-
const filtered: string[] = [];
|
|
53
|
-
let inOcxSection = false;
|
|
54
|
-
for (const line of lines) {
|
|
55
|
-
if (
|
|
56
|
-
line.includes(OCX_SECTION_MARKER) ||
|
|
57
|
-
isOcxProviderHeaderLine(line.trim())
|
|
58
|
-
) {
|
|
59
|
-
inOcxSection = true;
|
|
60
|
-
continue;
|
|
61
|
-
}
|
|
62
|
-
if (inOcxSection) {
|
|
63
|
-
// End the injected section at the next table header that ISN'T our own. Exact match on the
|
|
64
|
-
// provider name (plus our own sub-tables) so a user's
|
|
65
|
-
// "[model_providers.opencodex_backup]" (or similar) is preserved, not swallowed.
|
|
66
|
-
if (/^\s*\[/.test(line) && !isOcxProviderHeaderLine(line.trim())) {
|
|
67
|
-
inOcxSection = false;
|
|
68
|
-
filtered.push(line);
|
|
69
|
-
}
|
|
70
|
-
continue;
|
|
71
|
-
}
|
|
72
|
-
filtered.push(line);
|
|
73
|
-
}
|
|
74
|
-
return (
|
|
75
|
-
filtered
|
|
76
|
-
.join("\n")
|
|
77
|
-
.replace(/\n{3,}/g, "\n\n")
|
|
78
|
-
.trimEnd() + "\n"
|
|
79
|
-
);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Capture `[model_providers.opencodex]` verbatim so it can survive a restore that only
|
|
84
|
-
* takes routing down (#4812).
|
|
85
|
-
*
|
|
86
|
-
* This is deliberately NOT a mirror of `removeOcxSection`'s scan. That one opens a
|
|
87
|
-
* section on any line containing `OCX_SECTION_MARKER`, which is safe there only because
|
|
88
|
-
* `stripInjectedOpenaiBaseUrl` has already consumed the identical marker that annotates
|
|
89
|
-
* the root `openai_base_url`. Capture runs against the untouched file, so the same rule
|
|
90
|
-
* would collect that marker and the routing line under it — and re-appending the result
|
|
91
|
-
* would restore the exact base-url override the caller just removed.
|
|
92
|
-
*
|
|
93
|
-
* So the anchor is the provider header itself, via the shared `isOcxProviderHeaderLine`,
|
|
94
|
-
* with an immediately preceding marker line pulled in as its comment. Sharing that
|
|
95
|
-
* predicate is what keeps capture and removal from disagreeing about what our table is.
|
|
96
|
-
*/
|
|
97
|
-
export function extractOcxProviderTableBlock(content: string): string | null {
|
|
98
|
-
const lines = content.split("\n");
|
|
99
|
-
const collected: string[] = [];
|
|
100
|
-
let capturing = false;
|
|
101
|
-
for (let index = 0; index < lines.length; index++) {
|
|
102
|
-
const line = lines[index]!;
|
|
103
|
-
if (isOcxProviderHeaderLine(line.trim())) {
|
|
104
|
-
if (!capturing) {
|
|
105
|
-
const previous = lines[index - 1];
|
|
106
|
-
if (previous !== undefined && previous.includes(OCX_SECTION_MARKER)) collected.push(previous);
|
|
107
|
-
capturing = true;
|
|
108
|
-
}
|
|
109
|
-
collected.push(line);
|
|
110
|
-
continue;
|
|
111
|
-
}
|
|
112
|
-
if (!capturing) continue;
|
|
113
|
-
// A foreign table header closes ours, exactly as in `removeOcxSection`. A later
|
|
114
|
-
// `[model_providers.opencodex.*]` sub-table reopens capture on the next iteration,
|
|
115
|
-
// which is why the two are separate passes over the same predicate.
|
|
116
|
-
if (/^\s*\[/.test(line)) {
|
|
117
|
-
capturing = false;
|
|
118
|
-
continue;
|
|
119
|
-
}
|
|
120
|
-
collected.push(line);
|
|
121
|
-
}
|
|
122
|
-
if (collected.length === 0) return null;
|
|
123
|
-
return collected.join("\n").replace(/\n{3,}/g, "\n\n").trimEnd() + "\n";
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* Append a captured provider table to stripped content, as one buffer.
|
|
128
|
-
*
|
|
129
|
-
* Pure on purpose. Upstream resolves `model_provider` against the merged provider map and
|
|
130
|
-
* fails the WHOLE config load on a miss — not the one thread — so a config carrying root
|
|
131
|
-
* `model_provider = "opencodex"` without this table breaks every `codex` invocation. The
|
|
132
|
-
* strip and the re-append therefore have to reach disk in a single write, which they can
|
|
133
|
-
* only do if the append is a transform rather than a second file operation.
|
|
134
|
-
*/
|
|
135
|
-
export function appendOcxProviderTableBlock(content: string, block: string): string {
|
|
136
|
-
if (hasOcxProviderTable(content)) return content;
|
|
137
|
-
return `${content.replace(/\n+$/, "")}\n\n${block.replace(/\n+$/, "")}\n`;
|
|
138
|
-
}
|
|
25
|
+
import { appendOcxProviderTableBlock, extractOcxProviderTableBlock, hasOcxProviderTable, removeOcxSection } from "./provider-table";
|
|
26
|
+
export { appendOcxProviderTableBlock, extractOcxProviderTableBlock, hasOcxProviderTable, removeOcxSection } from "./provider-table";
|
|
139
27
|
|
|
140
28
|
/** Read the provider table straight off disk, before anything has transformed it. */
|
|
141
29
|
export function readOcxProviderTableBlock(): string | null {
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
} from "./catalog/native-models";
|
|
21
21
|
import { loadPersistedCodexRuntime } from "./runtime";
|
|
22
22
|
import { codexRuntimeStateEpoch } from "./runtime";
|
|
23
|
-
import
|
|
23
|
+
import { pinnedNativeModelRows } from "./catalog/pinned-models";
|
|
24
24
|
import { codexCredentialMutationEpoch } from "./credential-mutation-epoch";
|
|
25
25
|
import {
|
|
26
26
|
clearObservedCodexModelDenial,
|
|
@@ -90,9 +90,10 @@ export function deriveGatedClientVersionFloor(
|
|
|
90
90
|
/**
|
|
91
91
|
* Lowest `client_version` MEASURED to actually return the account-gated rows.
|
|
92
92
|
*
|
|
93
|
-
* The bundled snapshot
|
|
94
|
-
*
|
|
95
|
-
* gpt-5.6; `0.144.0` and above answer
|
|
93
|
+
* The bundled snapshot was not sufficient on its own. Until the 2026-09-23 re-pin it recorded
|
|
94
|
+
* `0.142.2` for the gpt-5.6 rows (upstream now records `0.144.0`), and `0.142.2` is a version
|
|
95
|
+
* upstream answers with 200 and five models, none of them gpt-5.6; `0.144.0` and above answer
|
|
96
|
+
* with the gated rows present
|
|
96
97
|
* (devlog/_fin/260817_native_gpt56_1m_context/001_measurement_evidence.md, independently
|
|
97
98
|
* reproduced by the #2886 and #3022 reporters). So a floor derived from the snapshot alone
|
|
98
99
|
* asks a question whose honest answer is an empty gated set — and the fail-closed gate then
|
|
@@ -107,9 +108,9 @@ const MEASURED_GATED_CLIENT_VERSION_MINIMUM = "0.144.0";
|
|
|
107
108
|
/**
|
|
108
109
|
* Lowest versions measured to return each account-gated model when the account owns it.
|
|
109
110
|
*
|
|
110
|
-
* This is deliberately independent of the bundled upstream snapshot
|
|
111
|
-
*
|
|
112
|
-
*
|
|
111
|
+
* This is deliberately independent of the bundled upstream snapshot, which can lag or lead the
|
|
112
|
+
* live roster: live measurements show that upstream omits sol/terra/luna below 0.144.0.
|
|
113
|
+
* Daybreak's shipped row records 0.142.2 and has no independent measured minimum, so its omission remains
|
|
113
114
|
* authoritative instead of inheriting a guessed floor from another model.
|
|
114
115
|
*/
|
|
115
116
|
export const ACCOUNT_GATED_NATIVE_MODEL_MINIMUM_CLIENT_VERSIONS: ReadonlyMap<string, string> = new Map([
|
|
@@ -121,8 +122,8 @@ export const ACCOUNT_GATED_NATIVE_MODEL_MINIMUM_CLIENT_VERSIONS: ReadonlyMap<str
|
|
|
121
122
|
/**
|
|
122
123
|
* Fallback when the snapshot records no usable gated floor.
|
|
123
124
|
*
|
|
124
|
-
* Not every gated slug
|
|
125
|
-
* row
|
|
125
|
+
* Not every gated slug is guaranteed a `minimal_client_version` — `gpt-daybreak-blue-latest` had
|
|
126
|
+
* no row until the 2026-09-23 re-pin — so the derivation can legitimately come back empty as the
|
|
126
127
|
* gated set changes. This is the last resort behind it, and it is still a version upstream can
|
|
127
128
|
* filter on rather than the placeholder that caused #2886.
|
|
128
129
|
*/
|
|
@@ -147,8 +148,11 @@ function composeGatedClientVersionFloor(
|
|
|
147
148
|
: MEASURED_GATED_CLIENT_VERSION_MINIMUM;
|
|
148
149
|
}
|
|
149
150
|
|
|
151
|
+
// Reads every pinned row (codex-rs snapshot plus roster-captured rows), so a gated slug whose row
|
|
152
|
+
// arrives through roster-pinned-models.json still contributes its floor. None does today:
|
|
153
|
+
// gpt-6-sol/luna are ungated, and gpt-6-astra-minor has no row and no measured minimum.
|
|
150
154
|
export const GATED_MODEL_CLIENT_VERSION_FLOOR: string = composeGatedClientVersionFloor(
|
|
151
|
-
(
|
|
155
|
+
pinnedNativeModelRows(),
|
|
152
156
|
);
|
|
153
157
|
|
|
154
158
|
/** Test-only seam: the composition on synthetic rows, so both directions can be proven. */
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { splitSourceLines, type SourceLine } from "./toml-source-lines";
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Pure, ownership-aware edits for Codex's native `[agents]` defaults.
|
|
3
5
|
*
|
|
@@ -40,12 +42,6 @@ export type ManagedSubagentDefaultsTransformResult =
|
|
|
40
42
|
error: string;
|
|
41
43
|
};
|
|
42
44
|
|
|
43
|
-
interface SourceLine {
|
|
44
|
-
text: string;
|
|
45
|
-
eol: "\r\n" | "\n" | "";
|
|
46
|
-
/** False when this physical line began inside a TOML multiline string. */
|
|
47
|
-
structural: boolean;
|
|
48
|
-
}
|
|
49
45
|
|
|
50
46
|
interface TargetDefinition {
|
|
51
47
|
key: ManagedSubagentDefaultKey;
|
|
@@ -65,109 +61,6 @@ const TARGET_KEYS: readonly ManagedSubagentDefaultKey[] = [
|
|
|
65
61
|
"default_subagent_reasoning_effort",
|
|
66
62
|
];
|
|
67
63
|
|
|
68
|
-
function splitSourceLines(content: string): SourceLine[] {
|
|
69
|
-
const lines: SourceLine[] = [];
|
|
70
|
-
let offset = 0;
|
|
71
|
-
while (offset < content.length) {
|
|
72
|
-
const lf = content.indexOf("\n", offset);
|
|
73
|
-
if (lf === -1) {
|
|
74
|
-
lines.push({ text: content.slice(offset), eol: "", structural: true });
|
|
75
|
-
break;
|
|
76
|
-
}
|
|
77
|
-
const crlf = lf > offset && content[lf - 1] === "\r";
|
|
78
|
-
lines.push({
|
|
79
|
-
text: content.slice(offset, crlf ? lf - 1 : lf),
|
|
80
|
-
eol: crlf ? "\r\n" : "\n",
|
|
81
|
-
structural: true,
|
|
82
|
-
});
|
|
83
|
-
offset = lf + 1;
|
|
84
|
-
}
|
|
85
|
-
markStructuralLines(lines);
|
|
86
|
-
return lines;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
type MultilineStringKind = "basic" | "literal" | null;
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* TOML table-looking text inside a multiline string is data, not syntax. Keep
|
|
93
|
-
* a deliberately small lexical state machine so the format-preserving editor
|
|
94
|
-
* never treats those physical lines as headers, keys, or ownership markers.
|
|
95
|
-
*/
|
|
96
|
-
function markStructuralLines(lines: SourceLine[]): void {
|
|
97
|
-
let multiline: MultilineStringKind = null;
|
|
98
|
-
let squareDepth = 0;
|
|
99
|
-
let curlyDepth = 0;
|
|
100
|
-
|
|
101
|
-
for (const line of lines) {
|
|
102
|
-
line.structural = multiline === null && squareDepth === 0 && curlyDepth === 0;
|
|
103
|
-
let single: "basic" | "literal" | null = null;
|
|
104
|
-
|
|
105
|
-
for (let index = 0; index < line.text.length;) {
|
|
106
|
-
if (multiline === "basic") {
|
|
107
|
-
if (line.text.startsWith('"""', index)) {
|
|
108
|
-
multiline = null;
|
|
109
|
-
index += 3;
|
|
110
|
-
} else if (line.text[index] === "\\") {
|
|
111
|
-
index += 2;
|
|
112
|
-
} else {
|
|
113
|
-
index += 1;
|
|
114
|
-
}
|
|
115
|
-
continue;
|
|
116
|
-
}
|
|
117
|
-
if (multiline === "literal") {
|
|
118
|
-
if (line.text.startsWith("'''", index)) {
|
|
119
|
-
multiline = null;
|
|
120
|
-
index += 3;
|
|
121
|
-
} else {
|
|
122
|
-
index += 1;
|
|
123
|
-
}
|
|
124
|
-
continue;
|
|
125
|
-
}
|
|
126
|
-
if (single === "basic") {
|
|
127
|
-
if (line.text[index] === "\\") index += 2;
|
|
128
|
-
else if (line.text[index] === '"') {
|
|
129
|
-
single = null;
|
|
130
|
-
index += 1;
|
|
131
|
-
} else index += 1;
|
|
132
|
-
continue;
|
|
133
|
-
}
|
|
134
|
-
if (single === "literal") {
|
|
135
|
-
if (line.text[index] === "'") single = null;
|
|
136
|
-
index += 1;
|
|
137
|
-
continue;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
if (line.text[index] === "#") break;
|
|
141
|
-
if (line.text.startsWith('"""', index)) {
|
|
142
|
-
multiline = "basic";
|
|
143
|
-
index += 3;
|
|
144
|
-
} else if (line.text.startsWith("'''", index)) {
|
|
145
|
-
multiline = "literal";
|
|
146
|
-
index += 3;
|
|
147
|
-
} else if (line.text[index] === '"') {
|
|
148
|
-
single = "basic";
|
|
149
|
-
index += 1;
|
|
150
|
-
} else if (line.text[index] === "'") {
|
|
151
|
-
single = "literal";
|
|
152
|
-
index += 1;
|
|
153
|
-
} else if (line.text[index] === "[") {
|
|
154
|
-
squareDepth += 1;
|
|
155
|
-
index += 1;
|
|
156
|
-
} else if (line.text[index] === "]") {
|
|
157
|
-
squareDepth = Math.max(0, squareDepth - 1);
|
|
158
|
-
index += 1;
|
|
159
|
-
} else if (line.text[index] === "{") {
|
|
160
|
-
curlyDepth += 1;
|
|
161
|
-
index += 1;
|
|
162
|
-
} else if (line.text[index] === "}") {
|
|
163
|
-
curlyDepth = Math.max(0, curlyDepth - 1);
|
|
164
|
-
index += 1;
|
|
165
|
-
} else {
|
|
166
|
-
index += 1;
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
64
|
|
|
172
65
|
function joinSourceLines(lines: readonly SourceLine[]): string {
|
|
173
66
|
return lines.map(line => `${line.text}${line.eol}`).join("");
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/** Lossless physical TOML lines with lexical structural boundaries. */
|
|
2
|
+
export interface SourceLine {
|
|
3
|
+
text: string;
|
|
4
|
+
eol: "\r\n" | "\n" | "";
|
|
5
|
+
/** False when this physical line began inside a TOML multiline string. */
|
|
6
|
+
structural: boolean;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
export function splitSourceLines(content: string): SourceLine[] {
|
|
11
|
+
const lines: SourceLine[] = [];
|
|
12
|
+
let offset = 0;
|
|
13
|
+
while (offset < content.length) {
|
|
14
|
+
const lf = content.indexOf("\n", offset);
|
|
15
|
+
if (lf === -1) {
|
|
16
|
+
lines.push({ text: content.slice(offset), eol: "", structural: true });
|
|
17
|
+
break;
|
|
18
|
+
}
|
|
19
|
+
const crlf = lf > offset && content[lf - 1] === "\r";
|
|
20
|
+
lines.push({
|
|
21
|
+
text: content.slice(offset, crlf ? lf - 1 : lf),
|
|
22
|
+
eol: crlf ? "\r\n" : "\n",
|
|
23
|
+
structural: true,
|
|
24
|
+
});
|
|
25
|
+
offset = lf + 1;
|
|
26
|
+
}
|
|
27
|
+
markStructuralLines(lines);
|
|
28
|
+
return lines;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
type MultilineStringKind = "basic" | "literal" | null;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* TOML table-looking text inside a multiline string is data, not syntax. Keep
|
|
35
|
+
* a deliberately small lexical state machine so the format-preserving editor
|
|
36
|
+
* never treats those physical lines as headers, keys, or ownership markers.
|
|
37
|
+
*/
|
|
38
|
+
function markStructuralLines(lines: SourceLine[]): void {
|
|
39
|
+
let multiline: MultilineStringKind = null;
|
|
40
|
+
let squareDepth = 0;
|
|
41
|
+
let curlyDepth = 0;
|
|
42
|
+
|
|
43
|
+
for (const line of lines) {
|
|
44
|
+
line.structural = multiline === null && squareDepth === 0 && curlyDepth === 0;
|
|
45
|
+
let single: "basic" | "literal" | null = null;
|
|
46
|
+
|
|
47
|
+
for (let index = 0; index < line.text.length;) {
|
|
48
|
+
if (multiline === "basic") {
|
|
49
|
+
if (line.text.startsWith('"""', index)) {
|
|
50
|
+
multiline = null;
|
|
51
|
+
index += 3;
|
|
52
|
+
} else if (line.text[index] === "\\") {
|
|
53
|
+
index += 2;
|
|
54
|
+
} else {
|
|
55
|
+
index += 1;
|
|
56
|
+
}
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
if (multiline === "literal") {
|
|
60
|
+
if (line.text.startsWith("'''", index)) {
|
|
61
|
+
multiline = null;
|
|
62
|
+
index += 3;
|
|
63
|
+
} else {
|
|
64
|
+
index += 1;
|
|
65
|
+
}
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
if (single === "basic") {
|
|
69
|
+
if (line.text[index] === "\\") index += 2;
|
|
70
|
+
else if (line.text[index] === '"') {
|
|
71
|
+
single = null;
|
|
72
|
+
index += 1;
|
|
73
|
+
} else index += 1;
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
if (single === "literal") {
|
|
77
|
+
if (line.text[index] === "'") single = null;
|
|
78
|
+
index += 1;
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (line.text[index] === "#") break;
|
|
83
|
+
if (line.text.startsWith('"""', index)) {
|
|
84
|
+
multiline = "basic";
|
|
85
|
+
index += 3;
|
|
86
|
+
} else if (line.text.startsWith("'''", index)) {
|
|
87
|
+
multiline = "literal";
|
|
88
|
+
index += 3;
|
|
89
|
+
} else if (line.text[index] === '"') {
|
|
90
|
+
single = "basic";
|
|
91
|
+
index += 1;
|
|
92
|
+
} else if (line.text[index] === "'") {
|
|
93
|
+
single = "literal";
|
|
94
|
+
index += 1;
|
|
95
|
+
} else if (line.text[index] === "[") {
|
|
96
|
+
squareDepth += 1;
|
|
97
|
+
index += 1;
|
|
98
|
+
} else if (line.text[index] === "]") {
|
|
99
|
+
squareDepth = Math.max(0, squareDepth - 1);
|
|
100
|
+
index += 1;
|
|
101
|
+
} else if (line.text[index] === "{") {
|
|
102
|
+
curlyDepth += 1;
|
|
103
|
+
index += 1;
|
|
104
|
+
} else if (line.text[index] === "}") {
|
|
105
|
+
curlyDepth = Math.max(0, curlyDepth - 1);
|
|
106
|
+
index += 1;
|
|
107
|
+
} else {
|
|
108
|
+
index += 1;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|