@bitkyc08/opencodex 2.29.0 → 2.31.0-preview.20260822
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/README.md +5 -5
- package/gui/dist/assets/index-DyWYnr-t.js +102 -0
- package/gui/dist/index.html +1 -1
- package/package.json +3 -3
- package/src/adapters/cursor/cursor-errors.ts +65 -6
- package/src/adapters/cursor/discovery.ts +29 -2
- package/src/adapters/cursor/effort-map.ts +6 -0
- package/src/adapters/cursor/h2-pool.ts +123 -0
- package/src/adapters/cursor/images.ts +704 -0
- package/src/adapters/cursor/live-models.ts +21 -26
- package/src/adapters/cursor/live-transport.ts +239 -8
- package/src/adapters/cursor/native-exec-common.ts +17 -0
- package/src/adapters/cursor/native-exec.ts +9 -4
- package/src/adapters/cursor/protobuf-events.ts +5 -1
- package/src/adapters/cursor/protobuf-request.ts +46 -9
- package/src/adapters/cursor/request-builder.ts +29 -14
- package/src/adapters/cursor/tool-definitions.ts +20 -0
- package/src/adapters/cursor/transport.ts +10 -0
- package/src/adapters/cursor/types.ts +8 -1
- package/src/adapters/cursor.ts +23 -5
- package/src/adapters/google.ts +16 -3
- package/src/adapters/openai-responses.ts +66 -20
- package/src/adapters/xai-web-search.ts +185 -0
- package/src/cli/agent.ts +2 -1
- package/src/cli/dispatch.ts +2 -2
- package/src/cli/doctor.ts +89 -0
- package/src/cli/help.ts +2 -0
- package/src/cli/registry.ts +7 -2
- package/src/codex/auth-context.ts +41 -2
- package/src/codex/catalog/effort.ts +1 -1
- package/src/codex/catalog/parsing.ts +2 -0
- package/src/codex/catalog/provider-fetch.ts +20 -5
- package/src/codex/coordinator-doctor.ts +332 -0
- package/src/codex/features.ts +58 -0
- package/src/codex/inject-coordination.ts +39 -6
- package/src/codex/transition-state.ts +12 -12
- package/src/generated/compatibility-version.json +86 -58
- package/src/lib/bun-stream-caps.ts +7 -4
- package/src/lib/errors.ts +8 -2
- package/src/oauth/cursor.ts +21 -0
- package/src/providers/command-code-efforts.ts +7 -0
- package/src/providers/cursor-pool.ts +72 -0
- package/src/providers/derive.ts +3 -0
- package/src/providers/fastwire.ts +12 -1
- package/src/providers/openai-sidecar.ts +1 -0
- package/src/providers/quota.ts +98 -25
- package/src/providers/registry.ts +115 -10
- package/src/providers/service-tier.ts +22 -7
- package/src/responses/custom-tool-compat.ts +24 -8
- package/src/responses/namespace-tool-compat.ts +2 -3
- package/src/router.ts +3 -0
- package/src/server/chat-completions.ts +4 -0
- package/src/server/chat-native.ts +20 -0
- package/src/server/management/agent-settings-routes.ts +16 -5
- package/src/server/management/config-routes.ts +25 -5
- package/src/server/management/vision-sidecar-options.ts +54 -19
- package/src/server/responses/compact.ts +1 -2
- package/src/server/responses/core.ts +54 -13
- package/src/service.ts +122 -14
- package/src/types/config.ts +9 -3
- package/src/types/provider.ts +6 -0
- package/src/usage/cost.ts +52 -38
- package/src/usage/expected-prices.ts +79 -9
- package/src/vision/backends.ts +97 -0
- package/src/vision/eligibility.ts +43 -22
- package/src/vision/index.ts +73 -5
- package/src/vision/routed-describe.ts +175 -0
- package/gui/dist/assets/index-BNESwCzn.js +0 -102
package/src/codex/features.ts
CHANGED
|
@@ -155,6 +155,29 @@ export function multiAgentV2EnabledFromConfigText(content: string | null): boole
|
|
|
155
155
|
return false;
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
+
// Bun 1.4 enforces TOML's "value must begin on the assignment line" rule that
|
|
159
|
+
// 1.3.14 did not, so `hint =` followed by `[` on the next line now fails the
|
|
160
|
+
// real parse and reaches the line-based fallback below — which reads that `[`
|
|
161
|
+
// as a table header and truncates the table before `enabled`. Codex's own
|
|
162
|
+
// parser accepts the document, so answering "disabled" would report a parser
|
|
163
|
+
// disagreement as a feature state (#1295, #1691).
|
|
164
|
+
//
|
|
165
|
+
// Joining a dangling `=` to the line that follows is the smallest repair that
|
|
166
|
+
// keeps the scanner untouched: widening `tomlTableBody` to be string-aware is
|
|
167
|
+
// what previously broke `getAgentsEnabled`, `getAgentsMaxDepth`, and
|
|
168
|
+
// `getMaxConcurrentThreads` (see its comment). If the joined document parses,
|
|
169
|
+
// that answer is authoritative; if it does not, nothing is lost.
|
|
170
|
+
const joined = joinDanglingTomlAssignments(content);
|
|
171
|
+
if (joined !== content) {
|
|
172
|
+
const reparsed = parsedTomlTable(joined, "features");
|
|
173
|
+
if (reparsed !== null) {
|
|
174
|
+
const table = plainTomlRecord(reparsed.multi_agent_v2);
|
|
175
|
+
if (table !== null) return table.enabled === true;
|
|
176
|
+
if (typeof reparsed.multi_agent_v2 === "boolean") return reparsed.multi_agent_v2;
|
|
177
|
+
return false;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
158
181
|
const table = tomlTableBody(content, "features.multi_agent_v2");
|
|
159
182
|
if (table !== null) {
|
|
160
183
|
const enabled = tomlBoolInBody(table, "enabled");
|
|
@@ -184,6 +207,41 @@ function plainTomlRecord(value: unknown): Record<string, unknown> | null {
|
|
|
184
207
|
: null;
|
|
185
208
|
}
|
|
186
209
|
|
|
210
|
+
/**
|
|
211
|
+
* Join `key =` to the following line when the value was written on the next
|
|
212
|
+
* line, so a parser enforcing TOML's same-line rule can read the document.
|
|
213
|
+
*
|
|
214
|
+
* Bun 1.3.14 accepted this shape; Bun 1.4 rejects it, correctly — TOML requires
|
|
215
|
+
* the value to begin on the assignment line. Codex's parser still accepts it, so
|
|
216
|
+
* this exists to keep the two readers agreeing rather than to endorse the shape.
|
|
217
|
+
*
|
|
218
|
+
* Deliberately narrow: it only acts on a line whose LAST non-comment character
|
|
219
|
+
* is `=`, which cannot occur in a valid assignment. Lines inside multi-line
|
|
220
|
+
* strings are left alone — a `"""` body line ending in `=` would be rewritten,
|
|
221
|
+
* but the result is only used when it PARSES, and the unmodified document is
|
|
222
|
+
* always tried first, so a wrong join cannot displace a correct read.
|
|
223
|
+
*/
|
|
224
|
+
function joinDanglingTomlAssignments(content: string): string {
|
|
225
|
+
const lines = content.split("\n");
|
|
226
|
+
const out: string[] = [];
|
|
227
|
+
for (let i = 0; i < lines.length; i++) {
|
|
228
|
+
const line = lines[i]!;
|
|
229
|
+
// A dangling assignment: trailing `=` with nothing after it on this line.
|
|
230
|
+
if (/^[^#]*[^=!<>]=\s*$/.test(line) && i + 1 < lines.length) {
|
|
231
|
+
let j = i + 1;
|
|
232
|
+
// Skip blank and comment-only lines between the `=` and its value.
|
|
233
|
+
while (j < lines.length && /^\s*(?:#.*)?$/.test(lines[j]!)) j++;
|
|
234
|
+
if (j < lines.length) {
|
|
235
|
+
out.push(`${line.replace(/\s*$/, "")} ${lines[j]!.replace(/^\s*/, "")}`);
|
|
236
|
+
i = j;
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
out.push(line);
|
|
241
|
+
}
|
|
242
|
+
return out.join("\n");
|
|
243
|
+
}
|
|
244
|
+
|
|
187
245
|
/**
|
|
188
246
|
* A top-level table from a full TOML parse, or null when the document does not
|
|
189
247
|
* parse. A parsed document with no such table yields `{}` rather than null: that
|
|
@@ -5,10 +5,11 @@
|
|
|
5
5
|
* sequence it is, rather than doubling in length around the lock.
|
|
6
6
|
*/
|
|
7
7
|
import { createHash } from "node:crypto";
|
|
8
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
8
|
+
import { existsSync, lstatSync, readFileSync } from "node:fs";
|
|
9
9
|
|
|
10
10
|
import { atomicWriteFile } from "../config";
|
|
11
11
|
import type { CodexWriteLockResult } from "./codex-write-lock";
|
|
12
|
+
import { inspectCodexCoordinatorPath } from "./coordinator-doctor";
|
|
12
13
|
import { JOURNAL_PATH } from "./journal";
|
|
13
14
|
import { CODEX_CONFIG_PATH, CODEX_PROFILE_PATH } from "./paths";
|
|
14
15
|
import {
|
|
@@ -43,21 +44,51 @@ export type CodexWriteCoordinationEligibility =
|
|
|
43
44
|
| { kind: "legacy-uncoordinated"; reason: string }
|
|
44
45
|
| { kind: "refused"; reason: string };
|
|
45
46
|
|
|
47
|
+
/**
|
|
48
|
+
* A live SQLite creator exposes a zero-byte pathname before BEGIN IMMEDIATE.
|
|
49
|
+
* Requiring a settled filesystem age makes that scheduling window remain on
|
|
50
|
+
* the coordinated path while old crash remnants can use the legacy boundary.
|
|
51
|
+
*/
|
|
52
|
+
export const STABLE_ZERO_BYTE_COORDINATOR_AGE_MS = 1_000;
|
|
53
|
+
|
|
46
54
|
export function codexWriteCoordinationEligibility(deps: {
|
|
47
55
|
coordinatorPath: () => string;
|
|
48
56
|
residue: () => { kind: string };
|
|
49
57
|
integrationRecord: () => { kind: string };
|
|
58
|
+
nowMs?: () => number;
|
|
50
59
|
}): CodexWriteCoordinationEligibility {
|
|
51
60
|
let coordinatorExists: boolean;
|
|
61
|
+
let coordinatorIsStableZeroByte = false;
|
|
52
62
|
try {
|
|
53
|
-
|
|
63
|
+
const path = deps.coordinatorPath();
|
|
64
|
+
coordinatorExists = existsSync(path);
|
|
65
|
+
if (coordinatorExists) {
|
|
66
|
+
const entry = lstatSync(path);
|
|
67
|
+
if (entry.isFile() && !entry.isSymbolicLink() && entry.size === 0) {
|
|
68
|
+
const diagnostic = inspectCodexCoordinatorPath(path);
|
|
69
|
+
if (diagnostic.kind === "zero-byte") {
|
|
70
|
+
const lastIdentityChange = Math.max(diagnostic.identity.mtimeMs, diagnostic.identity.ctimeMs);
|
|
71
|
+
coordinatorIsStableZeroByte = (deps.nowMs?.() ?? Date.now()) - lastIdentityChange
|
|
72
|
+
>= STABLE_ZERO_BYTE_COORDINATOR_AGE_MS;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
54
76
|
} catch (error) {
|
|
55
77
|
return { kind: "refused", reason: `the coordinator path could not be resolved: ${String(error)}` };
|
|
56
78
|
}
|
|
57
79
|
|
|
58
|
-
//
|
|
59
|
-
//
|
|
60
|
-
|
|
80
|
+
// Every existing coordinator remains authoritative unless it is proven to be
|
|
81
|
+
// an old, immutable SQLite-empty remnant. The age gate is part of that proof:
|
|
82
|
+
// a live creator exposes the same zero-byte pathname briefly before taking N,
|
|
83
|
+
// and sending that fresh file down the legacy path would bypass its lock.
|
|
84
|
+
// Non-empty, fresh, unsafe, changed, unversioned, and rowless files therefore
|
|
85
|
+
// stay coordinated and are validated/refused by the transaction owner.
|
|
86
|
+
//
|
|
87
|
+
// We do NOT initialize or adopt it here. Clean homes still enter the
|
|
88
|
+
// coordinated path, whose SQLite transaction safely initializes it. Routed
|
|
89
|
+
// or indeterminate legacy homes keep the same uncoordinated compatibility
|
|
90
|
+
// boundary they would have had if the remnant pathname were absent.
|
|
91
|
+
if (coordinatorExists && !coordinatorIsStableZeroByte) return { kind: "coordinated" };
|
|
61
92
|
|
|
62
93
|
const record = deps.integrationRecord();
|
|
63
94
|
if (record.kind === "invalid") {
|
|
@@ -83,7 +114,9 @@ export function codexWriteCoordinationEligibility(deps: {
|
|
|
83
114
|
*/
|
|
84
115
|
return {
|
|
85
116
|
kind: "legacy-uncoordinated",
|
|
86
|
-
reason:
|
|
117
|
+
reason: coordinatorIsStableZeroByte
|
|
118
|
+
? "the coordinator is a zero-byte non-authoritative remnant and this routed home has not been adopted yet"
|
|
119
|
+
: residue.kind === "residue"
|
|
87
120
|
? "this home was routed before write coordination existed and has not been adopted yet"
|
|
88
121
|
: "the existing native Codex state could not be classified, so it cannot seed a coordinator row",
|
|
89
122
|
};
|
|
@@ -37,7 +37,7 @@ import {
|
|
|
37
37
|
samePathIdentity,
|
|
38
38
|
} from "./user-identity";
|
|
39
39
|
|
|
40
|
-
const
|
|
40
|
+
export const CODEX_COORDINATOR_SCHEMA_VERSION = 1;
|
|
41
41
|
const DURABLE_HISTORY_STATUSES = new Set(["converged", "pending", "running", "blocked", "unknown"]);
|
|
42
42
|
const DURABLE_HISTORY_REASONS = new Set([
|
|
43
43
|
"db-busy",
|
|
@@ -241,7 +241,7 @@ function rowToState(row: TransitionRow | null): CodexTransitionState {
|
|
|
241
241
|
};
|
|
242
242
|
}
|
|
243
243
|
|
|
244
|
-
function
|
|
244
|
+
export function readCodexCoordinatorState(database: Database): CodexTransitionState {
|
|
245
245
|
const row = database.query<TransitionRow, []>(SELECT_TRANSITION_ROW).get();
|
|
246
246
|
return rowToState(row);
|
|
247
247
|
}
|
|
@@ -282,7 +282,7 @@ function assertInitialStateCanBeCreated(): void {
|
|
|
282
282
|
|
|
283
283
|
function initialize(database: Database, databaseWasAbsent: boolean): void {
|
|
284
284
|
const version = database.query<{ user_version: number }, []>("PRAGMA user_version").get()?.user_version;
|
|
285
|
-
if (version !== 0 && version !==
|
|
285
|
+
if (version !== 0 && version !== CODEX_COORDINATOR_SCHEMA_VERSION) {
|
|
286
286
|
throw new CodexCoordinatorTransactionError("The coordinator database schema version is unsupported.");
|
|
287
287
|
}
|
|
288
288
|
if (!databaseWasAbsent && version === 0) {
|
|
@@ -301,8 +301,8 @@ function initialize(database: Database, databaseWasAbsent: boolean): void {
|
|
|
301
301
|
assertInitialStateCanBeCreated();
|
|
302
302
|
database.query(INITIALIZE_TRANSITION_ROW).run(new Date().toISOString());
|
|
303
303
|
}
|
|
304
|
-
if (version === 0) database.exec(`PRAGMA user_version = ${
|
|
305
|
-
|
|
304
|
+
if (version === 0) database.exec(`PRAGMA user_version = ${CODEX_COORDINATOR_SCHEMA_VERSION}`);
|
|
305
|
+
readCodexCoordinatorState(database);
|
|
306
306
|
}
|
|
307
307
|
|
|
308
308
|
function createCapability(
|
|
@@ -336,7 +336,7 @@ function createCapability(
|
|
|
336
336
|
expected.nativeGeneration,
|
|
337
337
|
expected.currentTxId,
|
|
338
338
|
);
|
|
339
|
-
const state =
|
|
339
|
+
const state = readCodexCoordinatorState(database);
|
|
340
340
|
const update: TransitionStateUpdate = result.changes === 1
|
|
341
341
|
? { kind: "updated", state }
|
|
342
342
|
: { kind: "conflict", current: state };
|
|
@@ -451,7 +451,7 @@ export function openCodexCoordinatorTransaction(finalDatabasePath: string): Code
|
|
|
451
451
|
capability,
|
|
452
452
|
expectation() {
|
|
453
453
|
requireOpen();
|
|
454
|
-
const state =
|
|
454
|
+
const state = readCodexCoordinatorState(db);
|
|
455
455
|
return {
|
|
456
456
|
nativeBefore: state.nativeGeneration,
|
|
457
457
|
nativeAfter: state.nativeGeneration + 1,
|
|
@@ -460,7 +460,7 @@ export function openCodexCoordinatorTransaction(finalDatabasePath: string): Code
|
|
|
460
460
|
},
|
|
461
461
|
version() {
|
|
462
462
|
requireOpen();
|
|
463
|
-
const state =
|
|
463
|
+
const state = readCodexCoordinatorState(db);
|
|
464
464
|
return { nativeGeneration: state.nativeGeneration, currentTxId: state.currentTxId };
|
|
465
465
|
},
|
|
466
466
|
assertPublished(expectation) {
|
|
@@ -468,7 +468,7 @@ export function openCodexCoordinatorTransaction(finalDatabasePath: string): Code
|
|
|
468
468
|
if (lastResult?.kind !== "updated") {
|
|
469
469
|
throw new CodexCoordinatorTransactionError("The coordinator transition was not published.");
|
|
470
470
|
}
|
|
471
|
-
const state =
|
|
471
|
+
const state = readCodexCoordinatorState(db);
|
|
472
472
|
if (state.nativeGeneration !== expectation.nativeAfter || state.currentTxId !== expectation.txId) {
|
|
473
473
|
throw new CodexCoordinatorTransactionError("The coordinator published a different transition.");
|
|
474
474
|
}
|
|
@@ -540,7 +540,7 @@ function readCommittedState(): TransitionStateRead {
|
|
|
540
540
|
try {
|
|
541
541
|
database = new Database(path, { readonly: true });
|
|
542
542
|
database.exec("PRAGMA busy_timeout = 0");
|
|
543
|
-
return { kind: "ready", state:
|
|
543
|
+
return { kind: "ready", state: readCodexCoordinatorState(database) };
|
|
544
544
|
} catch (error) {
|
|
545
545
|
return mapUnavailable(error);
|
|
546
546
|
} finally {
|
|
@@ -577,7 +577,7 @@ export const updateCodexHistoryTransition: UpdateCodexHistoryTransition = (expec
|
|
|
577
577
|
database = new Database(currentCoordinatorDatabasePath(), { readwrite: true, create: false });
|
|
578
578
|
database.exec("PRAGMA busy_timeout = 0; BEGIN IMMEDIATE");
|
|
579
579
|
transactionOpen = true;
|
|
580
|
-
const current =
|
|
580
|
+
const current = readCodexCoordinatorState(database);
|
|
581
581
|
if (current.nativeGeneration > 0 && current.historySchedule === null) {
|
|
582
582
|
throw new CodexCoordinatorTransactionError("A positive transition cannot lose its direction.");
|
|
583
583
|
}
|
|
@@ -594,7 +594,7 @@ export const updateCodexHistoryTransition: UpdateCodexHistoryTransition = (expec
|
|
|
594
594
|
expected.currentTxId,
|
|
595
595
|
expected.currentTxId,
|
|
596
596
|
);
|
|
597
|
-
const state =
|
|
597
|
+
const state = readCodexCoordinatorState(database);
|
|
598
598
|
database.exec("COMMIT");
|
|
599
599
|
transactionOpen = false;
|
|
600
600
|
return result.changes === 1
|