@cabane/companion 0.6.18 → 0.6.20
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/dist/cli.js +238 -649
- package/dist/runtime.js +220 -631
- package/package.json +3 -2
package/dist/runtime.js
CHANGED
|
@@ -309,14 +309,14 @@ function localAgentConfig(cfg, agent) {
|
|
|
309
309
|
return map[agent.agentId] ?? map[agent.agentUsername] ?? map[`${agent.workspaceSlug}/${agent.agentUsername}`] ?? {};
|
|
310
310
|
}
|
|
311
311
|
function loadConfig() {
|
|
312
|
-
const
|
|
313
|
-
if (!existsSync(
|
|
312
|
+
const path = configPath();
|
|
313
|
+
if (!existsSync(path)) return null;
|
|
314
314
|
let raw;
|
|
315
315
|
try {
|
|
316
|
-
raw = readFileSync(
|
|
316
|
+
raw = readFileSync(path, "utf8");
|
|
317
317
|
} catch (err) {
|
|
318
318
|
throw new ConfigError(
|
|
319
|
-
`couldn't read ${
|
|
319
|
+
`couldn't read ${path}: ${err instanceof Error ? err.message : String(err)}`
|
|
320
320
|
);
|
|
321
321
|
}
|
|
322
322
|
if (raw.trim().length === 0) return null;
|
|
@@ -325,7 +325,7 @@ function loadConfig() {
|
|
|
325
325
|
parsed = JSON.parse(raw);
|
|
326
326
|
} catch (err) {
|
|
327
327
|
throw new ConfigError(
|
|
328
|
-
`${
|
|
328
|
+
`${path} is not valid JSON: ${err instanceof Error ? err.message : String(err)}`
|
|
329
329
|
);
|
|
330
330
|
}
|
|
331
331
|
const result = companionConfigSchema.safeParse(parsed);
|
|
@@ -333,30 +333,30 @@ function loadConfig() {
|
|
|
333
333
|
const agentIssue = result.error.issues.find((i) => i.path[0] === "agents");
|
|
334
334
|
if (agentIssue) {
|
|
335
335
|
throw new ConfigError(
|
|
336
|
-
`${
|
|
336
|
+
`${path}: invalid "agents" config at \`${agentIssue.path.join(".")}\` \u2014 ${agentIssue.message}. Fix the agents block in the config and retry.`
|
|
337
337
|
);
|
|
338
338
|
}
|
|
339
339
|
throw new ConfigError(
|
|
340
|
-
`${
|
|
340
|
+
`${path} is from an incompatible or older version of the companion, or was hand-edited. Run \`cabane-companion pair\` to re-pair, or \`cabane-companion logout --purge\` to reset.`
|
|
341
341
|
);
|
|
342
342
|
}
|
|
343
343
|
return result.data;
|
|
344
344
|
}
|
|
345
345
|
function saveConfig(cfg) {
|
|
346
|
-
const
|
|
347
|
-
mkdirSync(dirname(
|
|
346
|
+
const path = configPath();
|
|
347
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
348
348
|
try {
|
|
349
349
|
chmodSync(cabaneDir(), 448);
|
|
350
350
|
} catch {
|
|
351
351
|
}
|
|
352
|
-
const tmp = `${
|
|
352
|
+
const tmp = `${path}.${process.pid}.tmp`;
|
|
353
353
|
try {
|
|
354
354
|
writeFileSync(tmp, JSON.stringify(cfg, null, 2) + "\n", { mode: 384 });
|
|
355
355
|
try {
|
|
356
356
|
chmodSync(tmp, 384);
|
|
357
357
|
} catch {
|
|
358
358
|
}
|
|
359
|
-
renameSync(tmp,
|
|
359
|
+
renameSync(tmp, path);
|
|
360
360
|
} catch (err) {
|
|
361
361
|
try {
|
|
362
362
|
rmSync(tmp, { force: true });
|
|
@@ -417,8 +417,8 @@ function consoleMessageFormat(log, messageKey) {
|
|
|
417
417
|
var cached = null;
|
|
418
418
|
function getLogger() {
|
|
419
419
|
if (cached) return cached;
|
|
420
|
-
const
|
|
421
|
-
mkdirSync2(dirname2(
|
|
420
|
+
const path = companionLogPath();
|
|
421
|
+
mkdirSync2(dirname2(path), { recursive: true });
|
|
422
422
|
const streams = [];
|
|
423
423
|
if (process.env.CABANE_COMPANION_DAEMON !== "1") {
|
|
424
424
|
const consoleStream = pretty({
|
|
@@ -428,7 +428,7 @@ function getLogger() {
|
|
|
428
428
|
});
|
|
429
429
|
streams.push({ level: "info", stream: consoleStream });
|
|
430
430
|
}
|
|
431
|
-
streams.push({ level: "debug", stream: createWriteStream(
|
|
431
|
+
streams.push({ level: "debug", stream: createWriteStream(path, { flags: "a" }) });
|
|
432
432
|
cached = pino({ level: "debug" }, pino.multistream(streams));
|
|
433
433
|
return cached;
|
|
434
434
|
}
|
|
@@ -807,12 +807,12 @@ function clampLimit(raw, fallback, max = 200) {
|
|
|
807
807
|
if (!Number.isFinite(n) || n <= 0) return fallback;
|
|
808
808
|
return Math.min(Math.floor(n), max);
|
|
809
809
|
}
|
|
810
|
-
function tailFile(
|
|
811
|
-
if (!existsSync2(
|
|
810
|
+
function tailFile(path, lines) {
|
|
811
|
+
if (!existsSync2(path)) return [];
|
|
812
812
|
const MAX_BYTES = 256 * 1024;
|
|
813
813
|
let fd;
|
|
814
814
|
try {
|
|
815
|
-
fd = openSync(
|
|
815
|
+
fd = openSync(path, "r");
|
|
816
816
|
const size = fstatSync(fd).size;
|
|
817
817
|
const start = Math.max(0, size - MAX_BYTES);
|
|
818
818
|
const len = size - start;
|
|
@@ -1054,9 +1054,9 @@ function serialize(state) {
|
|
|
1054
1054
|
return JSON.stringify(state, null, 2) + "\n";
|
|
1055
1055
|
}
|
|
1056
1056
|
function writeRuntimeState(state) {
|
|
1057
|
-
const
|
|
1057
|
+
const path = runtimePath();
|
|
1058
1058
|
mkdirSync3(cabaneDir(), { recursive: true });
|
|
1059
|
-
writeFileSync2(
|
|
1059
|
+
writeFileSync2(path, serialize(state), "utf8");
|
|
1060
1060
|
}
|
|
1061
1061
|
function acquireRuntimeState(state) {
|
|
1062
1062
|
const live = readLiveRuntimeState();
|
|
@@ -1076,15 +1076,15 @@ function acquireRuntimeState(state) {
|
|
|
1076
1076
|
return { acquired: true };
|
|
1077
1077
|
}
|
|
1078
1078
|
function clearRuntimeState() {
|
|
1079
|
-
const
|
|
1080
|
-
if (existsSync3(
|
|
1079
|
+
const path = runtimePath();
|
|
1080
|
+
if (existsSync3(path)) rmSync2(path, { force: true });
|
|
1081
1081
|
}
|
|
1082
1082
|
function readLiveRuntimeState() {
|
|
1083
|
-
const
|
|
1084
|
-
if (!existsSync3(
|
|
1083
|
+
const path = runtimePath();
|
|
1084
|
+
if (!existsSync3(path)) return null;
|
|
1085
1085
|
let parsed;
|
|
1086
1086
|
try {
|
|
1087
|
-
parsed = JSON.parse(readFileSync2(
|
|
1087
|
+
parsed = JSON.parse(readFileSync2(path, "utf8"));
|
|
1088
1088
|
} catch {
|
|
1089
1089
|
return null;
|
|
1090
1090
|
}
|
|
@@ -1148,8 +1148,8 @@ var CabaneApi = class {
|
|
|
1148
1148
|
// One HTTP attempt — no retry. Throws `ApiError` on a 4xx/5xx response and
|
|
1149
1149
|
// rethrows transport errors (fetch reject) unchanged so the caller's retry
|
|
1150
1150
|
// logic can classify them.
|
|
1151
|
-
async attempt(method,
|
|
1152
|
-
const res = await fetch(`${this.base}${
|
|
1151
|
+
async attempt(method, path, body, signal) {
|
|
1152
|
+
const res = await fetch(`${this.base}${path}`, {
|
|
1153
1153
|
method,
|
|
1154
1154
|
headers: {
|
|
1155
1155
|
Authorization: `Bearer ${this.opts.token}`,
|
|
@@ -1175,12 +1175,12 @@ var CabaneApi = class {
|
|
|
1175
1175
|
}
|
|
1176
1176
|
return parsed;
|
|
1177
1177
|
}
|
|
1178
|
-
async request(method,
|
|
1178
|
+
async request(method, path, body, opts = {}) {
|
|
1179
1179
|
const { signal, retry = false } = opts;
|
|
1180
1180
|
const maxAttempts = retry ? RETRY_BACKOFF_MS.length + 1 : 1;
|
|
1181
1181
|
for (let attempt = 1; ; attempt++) {
|
|
1182
1182
|
try {
|
|
1183
|
-
return await this.attempt(method,
|
|
1183
|
+
return await this.attempt(method, path, body, signal);
|
|
1184
1184
|
} catch (err) {
|
|
1185
1185
|
if (attempt >= maxAttempts || signal?.aborted || !isRetryable(err)) throw err;
|
|
1186
1186
|
await sleep(RETRY_BACKOFF_MS[attempt - 1], signal);
|
|
@@ -1206,9 +1206,9 @@ var CabaneApi = class {
|
|
|
1206
1206
|
// delivers it once the API returns. `(turnId, seq)` is the server's
|
|
1207
1207
|
// idempotency key, so a replay whose original POST's fate is unknown
|
|
1208
1208
|
// converges instead of duplicating.
|
|
1209
|
-
async durableCommit(kind,
|
|
1209
|
+
async durableCommit(kind, path, body, turnId, seq, signal) {
|
|
1210
1210
|
try {
|
|
1211
|
-
await this.request("POST",
|
|
1211
|
+
await this.request("POST", path, body, {
|
|
1212
1212
|
retry: true,
|
|
1213
1213
|
...signal ? { signal } : {}
|
|
1214
1214
|
});
|
|
@@ -1217,7 +1217,7 @@ var CabaneApi = class {
|
|
|
1217
1217
|
if (!outbox) throw err;
|
|
1218
1218
|
if (signal?.aborted || isAbortError(err)) throw err;
|
|
1219
1219
|
if (!isRetryable(err)) throw err;
|
|
1220
|
-
outbox.persist({ enqueuedAt: Date.now(), turnId, seq, method: "POST", path
|
|
1220
|
+
outbox.persist({ enqueuedAt: Date.now(), turnId, seq, method: "POST", path, body, kind });
|
|
1221
1221
|
this.opts.log?.warn(
|
|
1222
1222
|
{ kind, turnId, seq, err: err instanceof Error ? err.message : String(err) },
|
|
1223
1223
|
"companion: commit queued to outbox after transient failure (will drain when the API returns)"
|
|
@@ -1369,12 +1369,12 @@ var CabaneApi = class {
|
|
|
1369
1369
|
// left best-effort: it's lower-stakes and self-heals on the next turn, so it
|
|
1370
1370
|
// stays a single-shot PATCH and is deliberately out of CT93's scope.
|
|
1371
1371
|
setActiveRun(workspaceId, conversationId, agentId, body) {
|
|
1372
|
-
const
|
|
1372
|
+
const path = `/api/workspaces/${workspaceId}/conversations/${conversationId}/participants/agents/${agentId}/active-run`;
|
|
1373
1373
|
const touchesFlag = Object.prototype.hasOwnProperty.call(body, "activeRunStartedAt");
|
|
1374
1374
|
if (touchesFlag && this.opts.outbox) {
|
|
1375
|
-
return this.durableActiveRunWrite(
|
|
1375
|
+
return this.durableActiveRunWrite(path, conversationId, agentId, body);
|
|
1376
1376
|
}
|
|
1377
|
-
return this.request("PATCH",
|
|
1377
|
+
return this.request("PATCH", path, body);
|
|
1378
1378
|
}
|
|
1379
1379
|
// CT93: send-or-enqueue for an active-run flag write, with last-writer-wins
|
|
1380
1380
|
// across the (conversation, agent) pair. Mirrors `durableCommit`, with two
|
|
@@ -1388,11 +1388,11 @@ var CabaneApi = class {
|
|
|
1388
1388
|
// later and clobber the state we just wrote (the cross-turn race: turn N's
|
|
1389
1389
|
// queued clear vs. turn N+1's live set). Combined with persist-overwrites-
|
|
1390
1390
|
// by-key, this is the full last-writer-wins guarantee.
|
|
1391
|
-
async durableActiveRunWrite(
|
|
1391
|
+
async durableActiveRunWrite(path, conversationId, agentId, body) {
|
|
1392
1392
|
const outbox = this.opts.outbox;
|
|
1393
1393
|
const key = activeRunOutboxKey(conversationId, agentId);
|
|
1394
1394
|
try {
|
|
1395
|
-
await this.request("PATCH",
|
|
1395
|
+
await this.request("PATCH", path, body, { retry: true });
|
|
1396
1396
|
outbox?.remove(key, ACTIVE_RUN_OUTBOX_SEQ);
|
|
1397
1397
|
} catch (err) {
|
|
1398
1398
|
if (!outbox) throw err;
|
|
@@ -1405,7 +1405,7 @@ var CabaneApi = class {
|
|
|
1405
1405
|
turnId: key,
|
|
1406
1406
|
seq: ACTIVE_RUN_OUTBOX_SEQ,
|
|
1407
1407
|
method: "PATCH",
|
|
1408
|
-
path
|
|
1408
|
+
path,
|
|
1409
1409
|
body,
|
|
1410
1410
|
kind: "active-run"
|
|
1411
1411
|
});
|
|
@@ -1493,8 +1493,8 @@ var CabaneApi = class {
|
|
|
1493
1493
|
// shared resolver the in-app path uses. Omitting it (older call sites) returns
|
|
1494
1494
|
// the agent default — graceful degradation, no version coupling.
|
|
1495
1495
|
getAgentSelf(conversationId) {
|
|
1496
|
-
const
|
|
1497
|
-
return this.request("GET",
|
|
1496
|
+
const path = conversationId ? `/api/agent/me?conversationId=${encodeURIComponent(conversationId)}` : `/api/agent/me`;
|
|
1497
|
+
return this.request("GET", path);
|
|
1498
1498
|
}
|
|
1499
1499
|
// The companion fetches the triggering message body by listing the
|
|
1500
1500
|
// conversation's messages and finding the one with `id === messageId`.
|
|
@@ -1549,8 +1549,8 @@ var DeviceApi = class {
|
|
|
1549
1549
|
get base() {
|
|
1550
1550
|
return this.opts.baseUrl.endsWith("/") ? this.opts.baseUrl.slice(0, -1) : this.opts.baseUrl;
|
|
1551
1551
|
}
|
|
1552
|
-
async request(method,
|
|
1553
|
-
const res = await fetch(`${this.base}${
|
|
1552
|
+
async request(method, path, body) {
|
|
1553
|
+
const res = await fetch(`${this.base}${path}`, {
|
|
1554
1554
|
method,
|
|
1555
1555
|
headers: {
|
|
1556
1556
|
Authorization: `Bearer ${this.opts.deviceToken}`,
|
|
@@ -1614,11 +1614,11 @@ function credentialsPath() {
|
|
|
1614
1614
|
}
|
|
1615
1615
|
var credentialStoreSchema = z3.record(z3.string(), z3.string());
|
|
1616
1616
|
function load() {
|
|
1617
|
-
const
|
|
1618
|
-
if (!existsSync4(
|
|
1617
|
+
const path = credentialsPath();
|
|
1618
|
+
if (!existsSync4(path)) return {};
|
|
1619
1619
|
let raw;
|
|
1620
1620
|
try {
|
|
1621
|
-
raw = readFileSync3(
|
|
1621
|
+
raw = readFileSync3(path, "utf8");
|
|
1622
1622
|
} catch {
|
|
1623
1623
|
return {};
|
|
1624
1624
|
}
|
|
@@ -1631,20 +1631,20 @@ function load() {
|
|
|
1631
1631
|
}
|
|
1632
1632
|
}
|
|
1633
1633
|
function save(map) {
|
|
1634
|
-
const
|
|
1635
|
-
mkdirSync4(dirname4(
|
|
1634
|
+
const path = credentialsPath();
|
|
1635
|
+
mkdirSync4(dirname4(path), { recursive: true });
|
|
1636
1636
|
try {
|
|
1637
1637
|
chmodSync2(cabaneDir(), 448);
|
|
1638
1638
|
} catch {
|
|
1639
1639
|
}
|
|
1640
|
-
const tmp = `${
|
|
1640
|
+
const tmp = `${path}.${process.pid}.tmp`;
|
|
1641
1641
|
try {
|
|
1642
1642
|
writeFileSync3(tmp, JSON.stringify(map, null, 2) + "\n", { mode: 384 });
|
|
1643
1643
|
try {
|
|
1644
1644
|
chmodSync2(tmp, 384);
|
|
1645
1645
|
} catch {
|
|
1646
1646
|
}
|
|
1647
|
-
renameSync2(tmp,
|
|
1647
|
+
renameSync2(tmp, path);
|
|
1648
1648
|
} catch (err) {
|
|
1649
1649
|
try {
|
|
1650
1650
|
rmSync3(tmp, { force: true });
|
|
@@ -1683,15 +1683,15 @@ function pathFor(workspaceId) {
|
|
|
1683
1683
|
return join7(cabaneDir(), "cursors", encodeURIComponent(workspaceId));
|
|
1684
1684
|
}
|
|
1685
1685
|
function readCursor(workspaceId) {
|
|
1686
|
-
const
|
|
1687
|
-
if (!existsSync5(
|
|
1688
|
-
const raw = readFileSync4(
|
|
1686
|
+
const path = pathFor(workspaceId);
|
|
1687
|
+
if (!existsSync5(path)) return null;
|
|
1688
|
+
const raw = readFileSync4(path, "utf8").trim();
|
|
1689
1689
|
return raw.length > 0 ? raw : null;
|
|
1690
1690
|
}
|
|
1691
1691
|
function writeCursor(workspaceId, eventId) {
|
|
1692
|
-
const
|
|
1692
|
+
const path = pathFor(workspaceId);
|
|
1693
1693
|
mkdirSync5(join7(cabaneDir(), "cursors"), { recursive: true });
|
|
1694
|
-
writeFileSync4(
|
|
1694
|
+
writeFileSync4(path, eventId + "\n", "utf8");
|
|
1695
1695
|
}
|
|
1696
1696
|
|
|
1697
1697
|
// src/cursor-tracker.ts
|
|
@@ -1744,10 +1744,10 @@ function pathFor2(log, workspaceId) {
|
|
|
1744
1744
|
return join8(dir(log), encodeURIComponent(workspaceId));
|
|
1745
1745
|
}
|
|
1746
1746
|
function readIds(log, workspaceId) {
|
|
1747
|
-
const
|
|
1748
|
-
if (!existsSync6(
|
|
1747
|
+
const path = pathFor2(log, workspaceId);
|
|
1748
|
+
if (!existsSync6(path)) return [];
|
|
1749
1749
|
try {
|
|
1750
|
-
return readFileSync5(
|
|
1750
|
+
return readFileSync5(path, "utf8").split("\n").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
1751
1751
|
} catch {
|
|
1752
1752
|
return [];
|
|
1753
1753
|
}
|
|
@@ -1784,10 +1784,10 @@ function resumePathFor(workspaceId) {
|
|
|
1784
1784
|
}
|
|
1785
1785
|
function readResumeCounts(workspaceId) {
|
|
1786
1786
|
const out = /* @__PURE__ */ new Map();
|
|
1787
|
-
const
|
|
1788
|
-
if (!existsSync6(
|
|
1787
|
+
const path = resumePathFor(workspaceId);
|
|
1788
|
+
if (!existsSync6(path)) return out;
|
|
1789
1789
|
try {
|
|
1790
|
-
for (const line of readFileSync5(
|
|
1790
|
+
for (const line of readFileSync5(path, "utf8").split("\n")) {
|
|
1791
1791
|
const trimmed = line.trim();
|
|
1792
1792
|
if (!trimmed) continue;
|
|
1793
1793
|
const tab = trimmed.lastIndexOf(" ");
|
|
@@ -4379,6 +4379,16 @@ function readMcpError(error) {
|
|
|
4379
4379
|
}
|
|
4380
4380
|
|
|
4381
4381
|
// packages/agent-runtime/src/codex/session.ts
|
|
4382
|
+
function fingerprintPrompt(prompt) {
|
|
4383
|
+
let fnv = 2166136261;
|
|
4384
|
+
let djb = 5381;
|
|
4385
|
+
for (let i = 0; i < prompt.length; i += 1) {
|
|
4386
|
+
const code = prompt.charCodeAt(i);
|
|
4387
|
+
fnv = Math.imul(fnv ^ code, 16777619);
|
|
4388
|
+
djb = Math.imul(djb, 33) + code | 0;
|
|
4389
|
+
}
|
|
4390
|
+
return `${prompt.length}.${(fnv >>> 0).toString(36)}.${(djb >>> 0).toString(36)}`;
|
|
4391
|
+
}
|
|
4382
4392
|
function encodeSession3(state) {
|
|
4383
4393
|
return JSON.stringify(state);
|
|
4384
4394
|
}
|
|
@@ -4398,7 +4408,10 @@ function decideResume3(stored, currentCwd) {
|
|
|
4398
4408
|
if (storedCwd !== (currentCwd ?? "")) {
|
|
4399
4409
|
return { fresh: true, reason: "cwd_mismatch" };
|
|
4400
4410
|
}
|
|
4401
|
-
return {
|
|
4411
|
+
return {
|
|
4412
|
+
resume: state.threadId,
|
|
4413
|
+
promptFingerprint: typeof state.promptFingerprint === "string" ? state.promptFingerprint : null
|
|
4414
|
+
};
|
|
4402
4415
|
}
|
|
4403
4416
|
|
|
4404
4417
|
// packages/agent-runtime/src/codex/loop.ts
|
|
@@ -4421,13 +4434,20 @@ async function* decodeCodexStream(events, ctx) {
|
|
|
4421
4434
|
if (ctx.signal.aborted) return;
|
|
4422
4435
|
if (ev.type === "thread.started") {
|
|
4423
4436
|
const threadId = readThreadId(ev);
|
|
4424
|
-
if (
|
|
4425
|
-
|
|
4426
|
-
|
|
4427
|
-
|
|
4428
|
-
|
|
4429
|
-
|
|
4430
|
-
|
|
4437
|
+
if (threadId && !sessionEmitted) {
|
|
4438
|
+
const state = encodeSession3({
|
|
4439
|
+
threadId,
|
|
4440
|
+
cwd: ctx.cwd ?? "",
|
|
4441
|
+
promptFingerprint: ctx.promptFingerprint ?? null
|
|
4442
|
+
});
|
|
4443
|
+
if (threadId !== ctx.resumedThreadId || state !== ctx.storedState) {
|
|
4444
|
+
sessionEmitted = true;
|
|
4445
|
+
yield {
|
|
4446
|
+
type: "session",
|
|
4447
|
+
state,
|
|
4448
|
+
...ctx.degraded ? { degraded: true } : {}
|
|
4449
|
+
};
|
|
4450
|
+
}
|
|
4431
4451
|
}
|
|
4432
4452
|
continue;
|
|
4433
4453
|
}
|
|
@@ -4550,7 +4570,7 @@ function codexToolPolicy(policy) {
|
|
|
4550
4570
|
networkAccessEnabled: policy.web
|
|
4551
4571
|
};
|
|
4552
4572
|
}
|
|
4553
|
-
var CODEX_REASONING_EFFORTS = ["
|
|
4573
|
+
var CODEX_REASONING_EFFORTS = ["low", "medium", "high", "xhigh", "max", "ultra"];
|
|
4554
4574
|
var codexDialectSchema = z11.object({
|
|
4555
4575
|
modelReasoningEffort: z11.enum(CODEX_REASONING_EFFORTS).optional()
|
|
4556
4576
|
}).loose();
|
|
@@ -4572,7 +4592,7 @@ var CABANE_MCP_SERVER3 = "cabane";
|
|
|
4572
4592
|
var TURN_CONTROL_MCP_SERVER2 = "cabane_companion";
|
|
4573
4593
|
var ACTIVE_CONVERSATION_HEADER4 = "x-cabane-active-conversation";
|
|
4574
4594
|
var ENV_ENVELOPE_KEYS = ["CABANE_ENV_TIER", "CABANE_ENV_KEY", "CABANE_ENV_BINDING"];
|
|
4575
|
-
function buildRunSpec2(req, resumeThreadId, instructionsFile = null) {
|
|
4595
|
+
function buildRunSpec2(req, resumeThreadId, instructionsFile = null, threadPromptFingerprint = null) {
|
|
4576
4596
|
const { policy, config } = req;
|
|
4577
4597
|
const directory = req.local.cwd ?? "";
|
|
4578
4598
|
const dialect = readCodexDialect(config.runtimeOptions);
|
|
@@ -4583,6 +4603,9 @@ function buildRunSpec2(req, resumeThreadId, instructionsFile = null) {
|
|
|
4583
4603
|
`[agent-runtime/codex] CT823: dispatching a subscription turn with NO concrete model (config.model=${JSON.stringify(config.model)}) \u2014 Codex will fall back to its own default. This should be unreachable post-CT823; the model resolver should have supplied a concrete house default.`
|
|
4584
4604
|
);
|
|
4585
4605
|
}
|
|
4606
|
+
const promptFingerprint = fingerprintPrompt(req.systemPrompt);
|
|
4607
|
+
const threadHasThisPrompt = resumeThreadId !== null && threadPromptFingerprint !== null && threadPromptFingerprint === promptFingerprint;
|
|
4608
|
+
const promptRidesInput = baseInstructionsFile === null && !threadHasThisPrompt;
|
|
4586
4609
|
return {
|
|
4587
4610
|
resumeThreadId,
|
|
4588
4611
|
directory,
|
|
@@ -4591,14 +4614,16 @@ function buildRunSpec2(req, resumeThreadId, instructionsFile = null) {
|
|
|
4591
4614
|
skipGitRepoCheck: true,
|
|
4592
4615
|
...dialect.modelReasoningEffort ? { modelReasoningEffort: dialect.modelReasoningEffort } : {},
|
|
4593
4616
|
baseInstructionsFile,
|
|
4594
|
-
input: buildInput(req,
|
|
4617
|
+
input: buildInput(req, promptRidesInput),
|
|
4618
|
+
promptRidesInput,
|
|
4619
|
+
promptFingerprint,
|
|
4595
4620
|
config: buildConfig(req, baseInstructionsFile)
|
|
4596
4621
|
};
|
|
4597
4622
|
}
|
|
4598
|
-
function buildInput(req,
|
|
4623
|
+
function buildInput(req, promptRidesInput) {
|
|
4599
4624
|
const userText = req.content.filter((b) => b.type === "text").map((b) => b.text).join("\n\n");
|
|
4600
4625
|
const body = userText.trim().length > 0 ? userText : req.prompt;
|
|
4601
|
-
if (
|
|
4626
|
+
if (!promptRidesInput) return body;
|
|
4602
4627
|
const system = req.systemPrompt.trim();
|
|
4603
4628
|
return system.length > 0 ? `${system}
|
|
4604
4629
|
|
|
@@ -4700,536 +4725,10 @@ function isStringRecord2(v) {
|
|
|
4700
4725
|
return !!v && typeof v === "object" && Object.values(v).every((x) => typeof x === "string");
|
|
4701
4726
|
}
|
|
4702
4727
|
|
|
4703
|
-
// node_modules/.pnpm/@openai+codex-sdk@0.146.0/node_modules/@openai/codex-sdk/dist/index.js
|
|
4704
|
-
import { promises as fs } from "fs";
|
|
4705
|
-
import os from "os";
|
|
4706
|
-
import path from "path";
|
|
4707
|
-
import { spawn as spawn4 } from "child_process";
|
|
4708
|
-
import { statSync } from "fs";
|
|
4709
|
-
import path2 from "path";
|
|
4710
|
-
import readline from "readline";
|
|
4711
|
-
import { createRequire } from "module";
|
|
4712
|
-
async function createOutputSchemaFile(schema) {
|
|
4713
|
-
if (schema === void 0) {
|
|
4714
|
-
return { cleanup: async () => {
|
|
4715
|
-
} };
|
|
4716
|
-
}
|
|
4717
|
-
if (!isJsonObject(schema)) {
|
|
4718
|
-
throw new Error("outputSchema must be a plain JSON object");
|
|
4719
|
-
}
|
|
4720
|
-
const schemaDir = await fs.mkdtemp(path.join(os.tmpdir(), "codex-output-schema-"));
|
|
4721
|
-
const schemaPath = path.join(schemaDir, "schema.json");
|
|
4722
|
-
const cleanup = async () => {
|
|
4723
|
-
try {
|
|
4724
|
-
await fs.rm(schemaDir, { recursive: true, force: true });
|
|
4725
|
-
} catch {
|
|
4726
|
-
}
|
|
4727
|
-
};
|
|
4728
|
-
try {
|
|
4729
|
-
await fs.writeFile(schemaPath, JSON.stringify(schema), "utf8");
|
|
4730
|
-
return { schemaPath, cleanup };
|
|
4731
|
-
} catch (error) {
|
|
4732
|
-
await cleanup();
|
|
4733
|
-
throw error;
|
|
4734
|
-
}
|
|
4735
|
-
}
|
|
4736
|
-
function isJsonObject(value) {
|
|
4737
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4738
|
-
}
|
|
4739
|
-
var Thread = class {
|
|
4740
|
-
_exec;
|
|
4741
|
-
_options;
|
|
4742
|
-
_id;
|
|
4743
|
-
_threadOptions;
|
|
4744
|
-
/** Returns the ID of the thread. Populated after the first turn starts. */
|
|
4745
|
-
get id() {
|
|
4746
|
-
return this._id;
|
|
4747
|
-
}
|
|
4748
|
-
/* @internal */
|
|
4749
|
-
constructor(exec, options, threadOptions, id = null) {
|
|
4750
|
-
this._exec = exec;
|
|
4751
|
-
this._options = options;
|
|
4752
|
-
this._id = id;
|
|
4753
|
-
this._threadOptions = threadOptions;
|
|
4754
|
-
}
|
|
4755
|
-
/** Provides the input to the agent and streams events as they are produced during the turn. */
|
|
4756
|
-
async runStreamed(input, turnOptions = {}) {
|
|
4757
|
-
return { events: this.runStreamedInternal(input, turnOptions) };
|
|
4758
|
-
}
|
|
4759
|
-
async *runStreamedInternal(input, turnOptions = {}) {
|
|
4760
|
-
const { schemaPath, cleanup } = await createOutputSchemaFile(turnOptions.outputSchema);
|
|
4761
|
-
const options = this._threadOptions;
|
|
4762
|
-
const { prompt, images } = normalizeInput(input);
|
|
4763
|
-
const generator = this._exec.run({
|
|
4764
|
-
input: prompt,
|
|
4765
|
-
baseUrl: this._options.baseUrl,
|
|
4766
|
-
apiKey: this._options.apiKey,
|
|
4767
|
-
threadId: this._id,
|
|
4768
|
-
images,
|
|
4769
|
-
model: options?.model,
|
|
4770
|
-
sandboxMode: options?.sandboxMode,
|
|
4771
|
-
workingDirectory: options?.workingDirectory,
|
|
4772
|
-
skipGitRepoCheck: options?.skipGitRepoCheck,
|
|
4773
|
-
outputSchemaFile: schemaPath,
|
|
4774
|
-
modelReasoningEffort: options?.modelReasoningEffort,
|
|
4775
|
-
signal: turnOptions.signal,
|
|
4776
|
-
networkAccessEnabled: options?.networkAccessEnabled,
|
|
4777
|
-
webSearchMode: options?.webSearchMode,
|
|
4778
|
-
webSearchEnabled: options?.webSearchEnabled,
|
|
4779
|
-
approvalPolicy: options?.approvalPolicy,
|
|
4780
|
-
additionalDirectories: options?.additionalDirectories
|
|
4781
|
-
});
|
|
4782
|
-
try {
|
|
4783
|
-
for await (const item of generator) {
|
|
4784
|
-
let parsed;
|
|
4785
|
-
try {
|
|
4786
|
-
parsed = JSON.parse(item);
|
|
4787
|
-
} catch (error) {
|
|
4788
|
-
throw new Error(`Failed to parse item: ${item}`, { cause: error });
|
|
4789
|
-
}
|
|
4790
|
-
if (parsed.type === "thread.started") {
|
|
4791
|
-
this._id = parsed.thread_id;
|
|
4792
|
-
} else if (parsed.type === "turn.completed") {
|
|
4793
|
-
parsed.usage.cache_write_input_tokens ??= 0;
|
|
4794
|
-
}
|
|
4795
|
-
yield parsed;
|
|
4796
|
-
}
|
|
4797
|
-
} finally {
|
|
4798
|
-
await cleanup();
|
|
4799
|
-
}
|
|
4800
|
-
}
|
|
4801
|
-
/** Provides the input to the agent and returns the completed turn. */
|
|
4802
|
-
async run(input, turnOptions = {}) {
|
|
4803
|
-
const generator = this.runStreamedInternal(input, turnOptions);
|
|
4804
|
-
const items = [];
|
|
4805
|
-
let finalResponse = "";
|
|
4806
|
-
let usage = null;
|
|
4807
|
-
let turnFailure = null;
|
|
4808
|
-
for await (const event of generator) {
|
|
4809
|
-
if (event.type === "item.completed") {
|
|
4810
|
-
if (event.item.type === "agent_message") {
|
|
4811
|
-
finalResponse = event.item.text;
|
|
4812
|
-
}
|
|
4813
|
-
items.push(event.item);
|
|
4814
|
-
} else if (event.type === "turn.completed") {
|
|
4815
|
-
usage = event.usage;
|
|
4816
|
-
} else if (event.type === "turn.failed") {
|
|
4817
|
-
turnFailure = event.error;
|
|
4818
|
-
break;
|
|
4819
|
-
}
|
|
4820
|
-
}
|
|
4821
|
-
if (turnFailure) {
|
|
4822
|
-
throw new Error(turnFailure.message);
|
|
4823
|
-
}
|
|
4824
|
-
return { items, finalResponse, usage };
|
|
4825
|
-
}
|
|
4826
|
-
};
|
|
4827
|
-
function normalizeInput(input) {
|
|
4828
|
-
if (typeof input === "string") {
|
|
4829
|
-
return { prompt: input, images: [] };
|
|
4830
|
-
}
|
|
4831
|
-
const promptParts = [];
|
|
4832
|
-
const images = [];
|
|
4833
|
-
for (const item of input) {
|
|
4834
|
-
if (item.type === "text") {
|
|
4835
|
-
promptParts.push(item.text);
|
|
4836
|
-
} else if (item.type === "local_image") {
|
|
4837
|
-
images.push(item.path);
|
|
4838
|
-
}
|
|
4839
|
-
}
|
|
4840
|
-
return { prompt: promptParts.join("\n\n"), images };
|
|
4841
|
-
}
|
|
4842
|
-
var INTERNAL_ORIGINATOR_ENV = "CODEX_INTERNAL_ORIGINATOR_OVERRIDE";
|
|
4843
|
-
var TYPESCRIPT_SDK_ORIGINATOR = "codex_sdk_ts";
|
|
4844
|
-
var CODEX_NPM_NAME = "@openai/codex";
|
|
4845
|
-
var PLATFORM_PACKAGE_BY_TARGET = {
|
|
4846
|
-
"x86_64-unknown-linux-musl": "@openai/codex-linux-x64",
|
|
4847
|
-
"aarch64-unknown-linux-musl": "@openai/codex-linux-arm64",
|
|
4848
|
-
"x86_64-apple-darwin": "@openai/codex-darwin-x64",
|
|
4849
|
-
"aarch64-apple-darwin": "@openai/codex-darwin-arm64",
|
|
4850
|
-
"x86_64-pc-windows-msvc": "@openai/codex-win32-x64",
|
|
4851
|
-
"aarch64-pc-windows-msvc": "@openai/codex-win32-arm64"
|
|
4852
|
-
};
|
|
4853
|
-
var moduleRequire = createRequire(import.meta.url);
|
|
4854
|
-
var CodexExec = class {
|
|
4855
|
-
executablePath;
|
|
4856
|
-
pathDirs;
|
|
4857
|
-
envOverride;
|
|
4858
|
-
configOverrides;
|
|
4859
|
-
constructor(executablePath = null, env, configOverrides) {
|
|
4860
|
-
if (executablePath) {
|
|
4861
|
-
this.executablePath = executablePath;
|
|
4862
|
-
this.pathDirs = [];
|
|
4863
|
-
} else {
|
|
4864
|
-
const resolved = findCodexPath();
|
|
4865
|
-
this.executablePath = resolved.executablePath;
|
|
4866
|
-
this.pathDirs = resolved.pathDirs;
|
|
4867
|
-
}
|
|
4868
|
-
this.envOverride = env;
|
|
4869
|
-
this.configOverrides = configOverrides;
|
|
4870
|
-
}
|
|
4871
|
-
async *run(args) {
|
|
4872
|
-
const commandArgs = ["exec", "--experimental-json"];
|
|
4873
|
-
if (this.configOverrides) {
|
|
4874
|
-
for (const override of serializeConfigOverrides(this.configOverrides)) {
|
|
4875
|
-
commandArgs.push("--config", override);
|
|
4876
|
-
}
|
|
4877
|
-
}
|
|
4878
|
-
if (args.baseUrl) {
|
|
4879
|
-
commandArgs.push(
|
|
4880
|
-
"--config",
|
|
4881
|
-
`openai_base_url=${toTomlValue(args.baseUrl, "openai_base_url")}`
|
|
4882
|
-
);
|
|
4883
|
-
}
|
|
4884
|
-
if (args.model) {
|
|
4885
|
-
commandArgs.push("--model", args.model);
|
|
4886
|
-
}
|
|
4887
|
-
if (args.sandboxMode) {
|
|
4888
|
-
commandArgs.push("--sandbox", args.sandboxMode);
|
|
4889
|
-
}
|
|
4890
|
-
if (args.workingDirectory) {
|
|
4891
|
-
commandArgs.push("--cd", args.workingDirectory);
|
|
4892
|
-
}
|
|
4893
|
-
if (args.additionalDirectories?.length) {
|
|
4894
|
-
for (const dir2 of args.additionalDirectories) {
|
|
4895
|
-
commandArgs.push("--add-dir", dir2);
|
|
4896
|
-
}
|
|
4897
|
-
}
|
|
4898
|
-
if (args.skipGitRepoCheck) {
|
|
4899
|
-
commandArgs.push("--skip-git-repo-check");
|
|
4900
|
-
}
|
|
4901
|
-
if (args.outputSchemaFile) {
|
|
4902
|
-
commandArgs.push("--output-schema", args.outputSchemaFile);
|
|
4903
|
-
}
|
|
4904
|
-
if (args.modelReasoningEffort) {
|
|
4905
|
-
commandArgs.push("--config", `model_reasoning_effort="${args.modelReasoningEffort}"`);
|
|
4906
|
-
}
|
|
4907
|
-
if (args.networkAccessEnabled !== void 0) {
|
|
4908
|
-
commandArgs.push(
|
|
4909
|
-
"--config",
|
|
4910
|
-
`sandbox_workspace_write.network_access=${args.networkAccessEnabled}`
|
|
4911
|
-
);
|
|
4912
|
-
}
|
|
4913
|
-
if (args.webSearchMode) {
|
|
4914
|
-
commandArgs.push("--config", `web_search="${args.webSearchMode}"`);
|
|
4915
|
-
} else if (args.webSearchEnabled === true) {
|
|
4916
|
-
commandArgs.push("--config", `web_search="live"`);
|
|
4917
|
-
} else if (args.webSearchEnabled === false) {
|
|
4918
|
-
commandArgs.push("--config", `web_search="disabled"`);
|
|
4919
|
-
}
|
|
4920
|
-
if (args.approvalPolicy) {
|
|
4921
|
-
commandArgs.push("--config", `approval_policy="${args.approvalPolicy}"`);
|
|
4922
|
-
}
|
|
4923
|
-
if (args.threadId) {
|
|
4924
|
-
commandArgs.push("resume", args.threadId);
|
|
4925
|
-
}
|
|
4926
|
-
if (args.images?.length) {
|
|
4927
|
-
for (const image of args.images) {
|
|
4928
|
-
commandArgs.push("--image", image);
|
|
4929
|
-
}
|
|
4930
|
-
}
|
|
4931
|
-
const env = {};
|
|
4932
|
-
if (this.envOverride) {
|
|
4933
|
-
Object.assign(env, this.envOverride);
|
|
4934
|
-
} else {
|
|
4935
|
-
for (const [key, value] of Object.entries(process.env)) {
|
|
4936
|
-
if (value !== void 0) {
|
|
4937
|
-
env[key] = value;
|
|
4938
|
-
}
|
|
4939
|
-
}
|
|
4940
|
-
}
|
|
4941
|
-
if (!env[INTERNAL_ORIGINATOR_ENV]) {
|
|
4942
|
-
env[INTERNAL_ORIGINATOR_ENV] = TYPESCRIPT_SDK_ORIGINATOR;
|
|
4943
|
-
}
|
|
4944
|
-
if (args.apiKey) {
|
|
4945
|
-
env.CODEX_API_KEY = args.apiKey;
|
|
4946
|
-
}
|
|
4947
|
-
if (this.pathDirs.length > 0) {
|
|
4948
|
-
prependPathDirs(env, this.pathDirs);
|
|
4949
|
-
}
|
|
4950
|
-
const child = spawn4(this.executablePath, commandArgs, {
|
|
4951
|
-
env,
|
|
4952
|
-
signal: args.signal
|
|
4953
|
-
});
|
|
4954
|
-
let spawnError = null;
|
|
4955
|
-
child.once("error", (err) => spawnError = err);
|
|
4956
|
-
if (!child.stdin) {
|
|
4957
|
-
child.kill();
|
|
4958
|
-
throw new Error("Child process has no stdin");
|
|
4959
|
-
}
|
|
4960
|
-
child.stdin.write(args.input);
|
|
4961
|
-
child.stdin.end();
|
|
4962
|
-
if (!child.stdout) {
|
|
4963
|
-
child.kill();
|
|
4964
|
-
throw new Error("Child process has no stdout");
|
|
4965
|
-
}
|
|
4966
|
-
const stderrChunks = [];
|
|
4967
|
-
if (child.stderr) {
|
|
4968
|
-
child.stderr.on("data", (data) => {
|
|
4969
|
-
stderrChunks.push(data);
|
|
4970
|
-
});
|
|
4971
|
-
}
|
|
4972
|
-
const exitPromise = new Promise(
|
|
4973
|
-
(resolve) => {
|
|
4974
|
-
child.once("exit", (code, signal) => {
|
|
4975
|
-
resolve({ code, signal });
|
|
4976
|
-
});
|
|
4977
|
-
}
|
|
4978
|
-
);
|
|
4979
|
-
const rl = readline.createInterface({
|
|
4980
|
-
input: child.stdout,
|
|
4981
|
-
crlfDelay: Infinity
|
|
4982
|
-
});
|
|
4983
|
-
try {
|
|
4984
|
-
for await (const line of rl) {
|
|
4985
|
-
yield line;
|
|
4986
|
-
}
|
|
4987
|
-
if (spawnError) throw spawnError;
|
|
4988
|
-
const { code, signal } = await exitPromise;
|
|
4989
|
-
if (code !== 0 || signal) {
|
|
4990
|
-
const stderrBuffer = Buffer.concat(stderrChunks);
|
|
4991
|
-
const detail = signal ? `signal ${signal}` : `code ${code ?? 1}`;
|
|
4992
|
-
throw new Error(`Codex Exec exited with ${detail}: ${stderrBuffer.toString("utf8")}`);
|
|
4993
|
-
}
|
|
4994
|
-
} finally {
|
|
4995
|
-
rl.close();
|
|
4996
|
-
child.removeAllListeners();
|
|
4997
|
-
try {
|
|
4998
|
-
if (!child.killed) child.kill();
|
|
4999
|
-
} catch {
|
|
5000
|
-
}
|
|
5001
|
-
}
|
|
5002
|
-
}
|
|
5003
|
-
};
|
|
5004
|
-
function serializeConfigOverrides(configOverrides) {
|
|
5005
|
-
const overrides = [];
|
|
5006
|
-
flattenConfigOverrides(configOverrides, "", overrides);
|
|
5007
|
-
return overrides;
|
|
5008
|
-
}
|
|
5009
|
-
function flattenConfigOverrides(value, prefix, overrides) {
|
|
5010
|
-
if (!isPlainObject(value)) {
|
|
5011
|
-
if (prefix) {
|
|
5012
|
-
overrides.push(`${prefix}=${toTomlValue(value, prefix)}`);
|
|
5013
|
-
return;
|
|
5014
|
-
} else {
|
|
5015
|
-
throw new Error("Codex config overrides must be a plain object");
|
|
5016
|
-
}
|
|
5017
|
-
}
|
|
5018
|
-
const entries = Object.entries(value);
|
|
5019
|
-
if (!prefix && entries.length === 0) {
|
|
5020
|
-
return;
|
|
5021
|
-
}
|
|
5022
|
-
if (prefix && entries.length === 0) {
|
|
5023
|
-
overrides.push(`${prefix}={}`);
|
|
5024
|
-
return;
|
|
5025
|
-
}
|
|
5026
|
-
for (const [key, child] of entries) {
|
|
5027
|
-
if (!key) {
|
|
5028
|
-
throw new Error("Codex config override keys must be non-empty strings");
|
|
5029
|
-
}
|
|
5030
|
-
if (child === void 0) {
|
|
5031
|
-
continue;
|
|
5032
|
-
}
|
|
5033
|
-
const path3 = prefix ? `${prefix}.${key}` : key;
|
|
5034
|
-
if (isPlainObject(child)) {
|
|
5035
|
-
flattenConfigOverrides(child, path3, overrides);
|
|
5036
|
-
} else {
|
|
5037
|
-
overrides.push(`${path3}=${toTomlValue(child, path3)}`);
|
|
5038
|
-
}
|
|
5039
|
-
}
|
|
5040
|
-
}
|
|
5041
|
-
function toTomlValue(value, path3) {
|
|
5042
|
-
if (typeof value === "string") {
|
|
5043
|
-
return JSON.stringify(value);
|
|
5044
|
-
} else if (typeof value === "number") {
|
|
5045
|
-
if (!Number.isFinite(value)) {
|
|
5046
|
-
throw new Error(`Codex config override at ${path3} must be a finite number`);
|
|
5047
|
-
}
|
|
5048
|
-
return `${value}`;
|
|
5049
|
-
} else if (typeof value === "boolean") {
|
|
5050
|
-
return value ? "true" : "false";
|
|
5051
|
-
} else if (Array.isArray(value)) {
|
|
5052
|
-
const rendered = value.map((item, index) => toTomlValue(item, `${path3}[${index}]`));
|
|
5053
|
-
return `[${rendered.join(", ")}]`;
|
|
5054
|
-
} else if (isPlainObject(value)) {
|
|
5055
|
-
const parts = [];
|
|
5056
|
-
for (const [key, child] of Object.entries(value)) {
|
|
5057
|
-
if (!key) {
|
|
5058
|
-
throw new Error("Codex config override keys must be non-empty strings");
|
|
5059
|
-
}
|
|
5060
|
-
if (child === void 0) {
|
|
5061
|
-
continue;
|
|
5062
|
-
}
|
|
5063
|
-
parts.push(`${formatTomlKey(key)} = ${toTomlValue(child, `${path3}.${key}`)}`);
|
|
5064
|
-
}
|
|
5065
|
-
return `{${parts.join(", ")}}`;
|
|
5066
|
-
} else if (value === null) {
|
|
5067
|
-
throw new Error(`Codex config override at ${path3} cannot be null`);
|
|
5068
|
-
} else {
|
|
5069
|
-
const typeName = typeof value;
|
|
5070
|
-
throw new Error(`Unsupported Codex config override value at ${path3}: ${typeName}`);
|
|
5071
|
-
}
|
|
5072
|
-
}
|
|
5073
|
-
var TOML_BARE_KEY = /^[A-Za-z0-9_-]+$/;
|
|
5074
|
-
function formatTomlKey(key) {
|
|
5075
|
-
return TOML_BARE_KEY.test(key) ? key : JSON.stringify(key);
|
|
5076
|
-
}
|
|
5077
|
-
function isPlainObject(value) {
|
|
5078
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5079
|
-
}
|
|
5080
|
-
function findCodexPath() {
|
|
5081
|
-
const { platform, arch } = process;
|
|
5082
|
-
let targetTriple = null;
|
|
5083
|
-
switch (platform) {
|
|
5084
|
-
case "linux":
|
|
5085
|
-
case "android":
|
|
5086
|
-
switch (arch) {
|
|
5087
|
-
case "x64":
|
|
5088
|
-
targetTriple = "x86_64-unknown-linux-musl";
|
|
5089
|
-
break;
|
|
5090
|
-
case "arm64":
|
|
5091
|
-
targetTriple = "aarch64-unknown-linux-musl";
|
|
5092
|
-
break;
|
|
5093
|
-
default:
|
|
5094
|
-
break;
|
|
5095
|
-
}
|
|
5096
|
-
break;
|
|
5097
|
-
case "darwin":
|
|
5098
|
-
switch (arch) {
|
|
5099
|
-
case "x64":
|
|
5100
|
-
targetTriple = "x86_64-apple-darwin";
|
|
5101
|
-
break;
|
|
5102
|
-
case "arm64":
|
|
5103
|
-
targetTriple = "aarch64-apple-darwin";
|
|
5104
|
-
break;
|
|
5105
|
-
default:
|
|
5106
|
-
break;
|
|
5107
|
-
}
|
|
5108
|
-
break;
|
|
5109
|
-
case "win32":
|
|
5110
|
-
switch (arch) {
|
|
5111
|
-
case "x64":
|
|
5112
|
-
targetTriple = "x86_64-pc-windows-msvc";
|
|
5113
|
-
break;
|
|
5114
|
-
case "arm64":
|
|
5115
|
-
targetTriple = "aarch64-pc-windows-msvc";
|
|
5116
|
-
break;
|
|
5117
|
-
default:
|
|
5118
|
-
break;
|
|
5119
|
-
}
|
|
5120
|
-
break;
|
|
5121
|
-
default:
|
|
5122
|
-
break;
|
|
5123
|
-
}
|
|
5124
|
-
if (!targetTriple) {
|
|
5125
|
-
throw new Error(`Unsupported platform: ${platform} (${arch})`);
|
|
5126
|
-
}
|
|
5127
|
-
const platformPackage = PLATFORM_PACKAGE_BY_TARGET[targetTriple];
|
|
5128
|
-
if (!platformPackage) {
|
|
5129
|
-
throw new Error(`Unsupported target triple: ${targetTriple}`);
|
|
5130
|
-
}
|
|
5131
|
-
let vendorRoot;
|
|
5132
|
-
try {
|
|
5133
|
-
const codexPackageJsonPath = moduleRequire.resolve(`${CODEX_NPM_NAME}/package.json`);
|
|
5134
|
-
const codexRequire = createRequire(codexPackageJsonPath);
|
|
5135
|
-
const platformPackageJsonPath = codexRequire.resolve(`${platformPackage}/package.json`);
|
|
5136
|
-
vendorRoot = path2.join(path2.dirname(platformPackageJsonPath), "vendor");
|
|
5137
|
-
} catch {
|
|
5138
|
-
throw new Error(
|
|
5139
|
-
`Unable to locate Codex CLI binaries. Ensure ${CODEX_NPM_NAME} is installed with optional dependencies.`
|
|
5140
|
-
);
|
|
5141
|
-
}
|
|
5142
|
-
const codexBinaryName = process.platform === "win32" ? "codex.exe" : "codex";
|
|
5143
|
-
const nativePackage = resolveNativePackage(vendorRoot, targetTriple, codexBinaryName);
|
|
5144
|
-
if (!nativePackage) {
|
|
5145
|
-
throw new Error(
|
|
5146
|
-
`Unable to locate Codex CLI binaries for ${targetTriple}. Ensure ${CODEX_NPM_NAME} is installed with optional dependencies.`
|
|
5147
|
-
);
|
|
5148
|
-
}
|
|
5149
|
-
return nativePackage;
|
|
5150
|
-
}
|
|
5151
|
-
function resolveNativePackage(vendorRoot, targetTriple, codexBinaryName) {
|
|
5152
|
-
const packageRoot = path2.join(vendorRoot, targetTriple);
|
|
5153
|
-
const packageBinaryPath = path2.join(packageRoot, "bin", codexBinaryName);
|
|
5154
|
-
if (isFile(packageBinaryPath) && isFile(path2.join(packageRoot, "codex-package.json"))) {
|
|
5155
|
-
return {
|
|
5156
|
-
executablePath: packageBinaryPath,
|
|
5157
|
-
pathDirs: existingDirs(path2.join(packageRoot, "codex-path"))
|
|
5158
|
-
};
|
|
5159
|
-
}
|
|
5160
|
-
const legacyBinaryPath = path2.join(packageRoot, "codex", codexBinaryName);
|
|
5161
|
-
if (isFile(legacyBinaryPath)) {
|
|
5162
|
-
return {
|
|
5163
|
-
executablePath: legacyBinaryPath,
|
|
5164
|
-
pathDirs: existingDirs(path2.join(packageRoot, "path"))
|
|
5165
|
-
};
|
|
5166
|
-
}
|
|
5167
|
-
return null;
|
|
5168
|
-
}
|
|
5169
|
-
function existingDirs(...dirs) {
|
|
5170
|
-
return dirs.filter(isDirectory);
|
|
5171
|
-
}
|
|
5172
|
-
function prependPathDirs(env, pathDirs, platform = process.platform) {
|
|
5173
|
-
const pathKey = pathEnvKey(env, platform);
|
|
5174
|
-
if (platform === "win32") {
|
|
5175
|
-
for (const key of Object.keys(env)) {
|
|
5176
|
-
if (key.toLowerCase() === "path" && key !== pathKey) {
|
|
5177
|
-
delete env[key];
|
|
5178
|
-
}
|
|
5179
|
-
}
|
|
5180
|
-
}
|
|
5181
|
-
const existingEntries = (env[pathKey] ?? "").split(path2.delimiter).filter((entry) => entry.length > 0 && !pathDirs.includes(entry));
|
|
5182
|
-
env[pathKey] = [...pathDirs, ...existingEntries].join(path2.delimiter);
|
|
5183
|
-
}
|
|
5184
|
-
function pathEnvKey(env, platform) {
|
|
5185
|
-
if (platform !== "win32") {
|
|
5186
|
-
return "PATH";
|
|
5187
|
-
}
|
|
5188
|
-
const matchingKeys = Object.keys(env).filter((key) => key.toLowerCase() === "path");
|
|
5189
|
-
return matchingKeys.includes("Path") ? "Path" : matchingKeys.at(-1) ?? "PATH";
|
|
5190
|
-
}
|
|
5191
|
-
function isFile(filePath) {
|
|
5192
|
-
try {
|
|
5193
|
-
return statSync(filePath).isFile();
|
|
5194
|
-
} catch {
|
|
5195
|
-
return false;
|
|
5196
|
-
}
|
|
5197
|
-
}
|
|
5198
|
-
function isDirectory(filePath) {
|
|
5199
|
-
try {
|
|
5200
|
-
return statSync(filePath).isDirectory();
|
|
5201
|
-
} catch {
|
|
5202
|
-
return false;
|
|
5203
|
-
}
|
|
5204
|
-
}
|
|
5205
|
-
var Codex = class {
|
|
5206
|
-
exec;
|
|
5207
|
-
options;
|
|
5208
|
-
constructor(options = {}) {
|
|
5209
|
-
const { codexPathOverride, env, config } = options;
|
|
5210
|
-
this.exec = new CodexExec(codexPathOverride, env, config);
|
|
5211
|
-
this.options = options;
|
|
5212
|
-
}
|
|
5213
|
-
/**
|
|
5214
|
-
* Starts a new conversation with an agent.
|
|
5215
|
-
* @returns A new thread instance.
|
|
5216
|
-
*/
|
|
5217
|
-
startThread(options = {}) {
|
|
5218
|
-
return new Thread(this.exec, this.options, options);
|
|
5219
|
-
}
|
|
5220
|
-
/**
|
|
5221
|
-
* Resumes a conversation with an agent based on the thread id.
|
|
5222
|
-
* Threads are persisted in ~/.codex/sessions.
|
|
5223
|
-
*
|
|
5224
|
-
* @param id The id of the thread to resume.
|
|
5225
|
-
* @returns A new thread instance.
|
|
5226
|
-
*/
|
|
5227
|
-
resumeThread(id, options = {}) {
|
|
5228
|
-
return new Thread(this.exec, this.options, options, id);
|
|
5229
|
-
}
|
|
5230
|
-
};
|
|
5231
|
-
|
|
5232
4728
|
// packages/agent-runtime/src/codex/transport.ts
|
|
4729
|
+
import {
|
|
4730
|
+
Codex
|
|
4731
|
+
} from "@openai/codex-sdk";
|
|
5233
4732
|
function buildSdkThreadOptions(spec) {
|
|
5234
4733
|
return {
|
|
5235
4734
|
...spec.model ? { model: spec.model } : {},
|
|
@@ -5283,6 +4782,7 @@ function createCodexAdapter(deps = {}) {
|
|
|
5283
4782
|
const directory = req.local.cwd ?? "";
|
|
5284
4783
|
const decision = decideResume3(req.session, directory);
|
|
5285
4784
|
const resumeThreadId = "resume" in decision ? decision.resume : null;
|
|
4785
|
+
const threadPromptFingerprint = "resume" in decision ? decision.promptFingerprint : null;
|
|
5286
4786
|
if ("fresh" in decision && decision.reason === "cwd_mismatch") {
|
|
5287
4787
|
deps.onWarn?.(
|
|
5288
4788
|
"codex adapter: stored session directory no longer matches the current environment \u2014 starting a fresh thread (SJ527-analogue guard)",
|
|
@@ -5308,7 +4808,12 @@ function createCodexAdapter(deps = {}) {
|
|
|
5308
4808
|
}
|
|
5309
4809
|
}
|
|
5310
4810
|
try {
|
|
5311
|
-
const spec = buildRunSpec2(
|
|
4811
|
+
const spec = buildRunSpec2(
|
|
4812
|
+
req,
|
|
4813
|
+
resumeThreadId,
|
|
4814
|
+
instructions?.path ?? null,
|
|
4815
|
+
threadPromptFingerprint
|
|
4816
|
+
);
|
|
5312
4817
|
const result = await transport.run(spec, signal);
|
|
5313
4818
|
if (signal.aborted) return;
|
|
5314
4819
|
yield* decodeCodexStream(result.events, {
|
|
@@ -5316,6 +4821,14 @@ function createCodexAdapter(deps = {}) {
|
|
|
5316
4821
|
cwd: req.local.cwd,
|
|
5317
4822
|
resumedThreadId: resumeThreadId,
|
|
5318
4823
|
degraded,
|
|
4824
|
+
// CT1075: which prompt the thread carries once this turn lands. If we sent
|
|
4825
|
+
// one, it's that one — a later copy is what the model reads, so a changed
|
|
4826
|
+
// prompt supersedes the older copy still sitting above it. If we withheld
|
|
4827
|
+
// it, the thread still carries whatever it did before. Read off the spec
|
|
4828
|
+
// rather than recomputed, so the recorded fact can't disagree with what
|
|
4829
|
+
// was actually sent.
|
|
4830
|
+
promptFingerprint: spec.promptRidesInput ? spec.promptFingerprint : threadPromptFingerprint,
|
|
4831
|
+
storedState: req.session,
|
|
5319
4832
|
// CT601: Codex reports no model in-stream, so record what the thread was
|
|
5320
4833
|
// started with (null on the default token). Same for the reasoning effort.
|
|
5321
4834
|
resolvedModel: spec.model,
|
|
@@ -5350,9 +4863,10 @@ var COMPANION_POLICY2 = {
|
|
|
5350
4863
|
uiPrompts: "never"
|
|
5351
4864
|
};
|
|
5352
4865
|
var DIR2 = "/env/here";
|
|
4866
|
+
var PROMPT = "system";
|
|
5353
4867
|
function makeRequest3(overrides = {}) {
|
|
5354
4868
|
return {
|
|
5355
|
-
systemPrompt:
|
|
4869
|
+
systemPrompt: PROMPT,
|
|
5356
4870
|
prompt: "hi there",
|
|
5357
4871
|
content: [{ type: "text", text: "hi there" }],
|
|
5358
4872
|
// CT601: the resolved model + reasoning effort ride the terminal `result` off
|
|
@@ -5395,9 +4909,9 @@ var errorItem = (id, message) => ({
|
|
|
5395
4909
|
type: "item.completed",
|
|
5396
4910
|
item: { id, type: "error", message }
|
|
5397
4911
|
});
|
|
5398
|
-
var sessionEvent3 = (threadId, cwd = DIR2, degraded = false) => ({
|
|
4912
|
+
var sessionEvent3 = (threadId, cwd = DIR2, degraded = false, promptFingerprint = fingerprintPrompt(PROMPT)) => ({
|
|
5399
4913
|
type: "session",
|
|
5400
|
-
state: encodeSession3({ threadId, cwd }),
|
|
4914
|
+
state: encodeSession3({ threadId, cwd, promptFingerprint }),
|
|
5401
4915
|
...degraded ? { degraded: true } : {}
|
|
5402
4916
|
});
|
|
5403
4917
|
var CODEX_CONFORMANCE_FIXTURES = [
|
|
@@ -5855,23 +5369,91 @@ var CODEX_CONFORMANCE_FIXTURES = [
|
|
|
5855
5369
|
{
|
|
5856
5370
|
// Session resume: the stored state matches the current directory, so the adapter
|
|
5857
5371
|
// resumes its thread id and `thread.started` echoes the SAME id — NO session
|
|
5858
|
-
// event fires (the id didn't change). This is what
|
|
5372
|
+
// event fires (the id didn't change AND the state is unchanged). This is what
|
|
5373
|
+
// carries cross-turn memory.
|
|
5374
|
+
//
|
|
5375
|
+
// CT1075: the stored state also says the thread already holds the composed
|
|
5376
|
+
// prompt, so this turn withholds the preamble and the state it would write is
|
|
5377
|
+
// byte-identical to the stored one. The silence is now proof of BOTH: an id that
|
|
5378
|
+
// didn't change and a prompt that didn't need re-sending.
|
|
5859
5379
|
name: "session resume (no re-emit on echo)",
|
|
5860
|
-
request: makeRequest3({
|
|
5380
|
+
request: makeRequest3({
|
|
5381
|
+
session: encodeSession3({
|
|
5382
|
+
threadId: "th1",
|
|
5383
|
+
cwd: DIR2,
|
|
5384
|
+
promptFingerprint: fingerprintPrompt(PROMPT)
|
|
5385
|
+
})
|
|
5386
|
+
}),
|
|
5861
5387
|
nativeStream: [threadStarted("th1"), agentMessage("m1", "Back again."), turnCompleted()],
|
|
5862
5388
|
expected: [
|
|
5863
5389
|
{ type: "text", body: "Back again.", terminal: true },
|
|
5864
5390
|
{ type: "result", ok: true }
|
|
5865
5391
|
]
|
|
5866
5392
|
},
|
|
5393
|
+
{
|
|
5394
|
+
// CT1075: a resumed thread the platform's state does NOT vouch for — a state
|
|
5395
|
+
// written before CT1075 existed, so it carries no `promptFingerprint` field. The
|
|
5396
|
+
// adapter reads the absence as "we can't say which prompt this thread holds",
|
|
5397
|
+
// sends the full prompt exactly as it did pre-CT1075, and re-emits the state with
|
|
5398
|
+
// the fingerprint recorded — on the SAME thread id, which pre-CT1075 emitted
|
|
5399
|
+
// nothing at all. That re-emit is the upgrade path: without it a long-lived
|
|
5400
|
+
// thread would re-send the whole prompt forever.
|
|
5401
|
+
//
|
|
5402
|
+
// This is the fixture that fails if the static half is withheld on a thread we
|
|
5403
|
+
// have no evidence about.
|
|
5404
|
+
name: "session resume with a pre-CT1075 state re-emits with the prompt fingerprint",
|
|
5405
|
+
request: makeRequest3({
|
|
5406
|
+
session: JSON.stringify({ threadId: "th1", cwd: DIR2 })
|
|
5407
|
+
}),
|
|
5408
|
+
nativeStream: [threadStarted("th1"), agentMessage("m1", "Back again."), turnCompleted()],
|
|
5409
|
+
expected: [
|
|
5410
|
+
sessionEvent3("th1"),
|
|
5411
|
+
{ type: "text", body: "Back again.", terminal: true },
|
|
5412
|
+
{ type: "result", ok: true }
|
|
5413
|
+
]
|
|
5414
|
+
},
|
|
5415
|
+
{
|
|
5416
|
+
// CT1075 (review finding): the thread carries a DIFFERENT prompt than the one
|
|
5417
|
+
// this turn composed — a charter edit, a renamed agent, a redeployed prompt
|
|
5418
|
+
// body. The stored fingerprint doesn't match, so the new prompt is sent exactly
|
|
5419
|
+
// as on a fresh thread and the state is re-recorded against it.
|
|
5420
|
+
//
|
|
5421
|
+
// This is the fixture that fails if a stale prompt is treated as good enough. A
|
|
5422
|
+
// boolean flag would have passed the resume fixture above and silently withheld
|
|
5423
|
+
// every future charter edit from this thread.
|
|
5424
|
+
name: "session resume with a stale prompt fingerprint re-sends the prompt",
|
|
5425
|
+
request: makeRequest3({
|
|
5426
|
+
session: encodeSession3({
|
|
5427
|
+
threadId: "th1",
|
|
5428
|
+
cwd: DIR2,
|
|
5429
|
+
promptFingerprint: fingerprintPrompt("an older composed prompt")
|
|
5430
|
+
})
|
|
5431
|
+
}),
|
|
5432
|
+
nativeStream: [threadStarted("th1"), agentMessage("m1", "Back again."), turnCompleted()],
|
|
5433
|
+
expected: [
|
|
5434
|
+
sessionEvent3("th1"),
|
|
5435
|
+
{ type: "text", body: "Back again.", terminal: true },
|
|
5436
|
+
{ type: "result", ok: true }
|
|
5437
|
+
]
|
|
5438
|
+
},
|
|
5867
5439
|
{
|
|
5868
5440
|
// Directory-mismatch degrade (the SJ527 analogue): the stored state was created
|
|
5869
5441
|
// under a DIFFERENT directory, so the adapter refuses to resume and starts
|
|
5870
5442
|
// fresh — `thread.started` reports a NEW id, and the emitted session event
|
|
5871
5443
|
// records the CURRENT directory with `degraded: true` so the host rewinds the
|
|
5872
5444
|
// catch-up mark.
|
|
5445
|
+
//
|
|
5446
|
+
// CT1075: the refused resume means the new thread is EMPTY, so the full prompt
|
|
5447
|
+
// goes in even though the stored state vouched for a copy — of a thread we're
|
|
5448
|
+
// no longer in. The emitted state records the fresh thread's own answer.
|
|
5873
5449
|
name: "session directory-mismatch degrades to fresh",
|
|
5874
|
-
request: makeRequest3({
|
|
5450
|
+
request: makeRequest3({
|
|
5451
|
+
session: encodeSession3({
|
|
5452
|
+
threadId: "old",
|
|
5453
|
+
cwd: "/env/gone",
|
|
5454
|
+
promptFingerprint: fingerprintPrompt(PROMPT)
|
|
5455
|
+
})
|
|
5456
|
+
}),
|
|
5875
5457
|
nativeStream: [
|
|
5876
5458
|
threadStarted(NEW_THREAD_ID),
|
|
5877
5459
|
agentMessage("m1", "Fresh start."),
|
|
@@ -5938,7 +5520,7 @@ var ConnectorHealthStore = class {
|
|
|
5938
5520
|
|
|
5939
5521
|
// src/dispatcher.ts
|
|
5940
5522
|
import { randomUUID } from "crypto";
|
|
5941
|
-
import { appendFileSync as appendFileSync2, existsSync as existsSync9, mkdirSync as mkdirSync9, statSync
|
|
5523
|
+
import { appendFileSync as appendFileSync2, existsSync as existsSync9, mkdirSync as mkdirSync9, statSync } from "fs";
|
|
5942
5524
|
import { join as join13 } from "path";
|
|
5943
5525
|
|
|
5944
5526
|
// src/summon.ts
|
|
@@ -6237,9 +5819,16 @@ function buildCompanionTurnRequest(params) {
|
|
|
6237
5819
|
...t.workspaceToolSurface ? { workspaceToolSurface: t.workspaceToolSurface } : {},
|
|
6238
5820
|
// CT714: mount the turn-control surface ONLY when a real turn token backs
|
|
6239
5821
|
// this turn — the surface admits `turn_token` auth exclusively, so a
|
|
6240
|
-
// PAT-fallback bearer
|
|
6241
|
-
//
|
|
6242
|
-
//
|
|
5822
|
+
// PAT-fallback bearer would be rejected there. Absent it, external adapters
|
|
5823
|
+
// simply don't mount it that turn.
|
|
5824
|
+
//
|
|
5825
|
+
// CT1074: that fallback is now an OLD-API case only. The server mints a token
|
|
5826
|
+
// for every turn it dispatched, so "no token" no longer means "no human behind
|
|
5827
|
+
// the turn" — it means the API predates this change. The condition is right as
|
|
5828
|
+
// it stands and stays where the failure is honest (a rejected bearer is worse
|
|
5829
|
+
// than an unmounted server); what was wrong was upstream, where an undelegated
|
|
5830
|
+
// turn minted nothing and this silently dropped all five verbs on Codex and
|
|
5831
|
+
// opencode with no refusal anyone could see.
|
|
6243
5832
|
...params.turnToken ? { turnControlUrl: turnControlMcpUrl(params.baseUrl) } : {}
|
|
6244
5833
|
},
|
|
6245
5834
|
local: {
|
|
@@ -6269,10 +5858,10 @@ import { join as join9 } from "path";
|
|
|
6269
5858
|
var PREFIX = "cabane-codex-instructions-";
|
|
6270
5859
|
async function writeCodexInstructionsFile(contents) {
|
|
6271
5860
|
const dir2 = await mkdtemp(join9(tmpdir(), PREFIX));
|
|
6272
|
-
const
|
|
6273
|
-
await writeFile(
|
|
5861
|
+
const path = join9(dir2, "instructions.md");
|
|
5862
|
+
await writeFile(path, contents, { encoding: "utf8", mode: 384 });
|
|
6274
5863
|
return {
|
|
6275
|
-
path
|
|
5864
|
+
path,
|
|
6276
5865
|
cleanup: async () => {
|
|
6277
5866
|
await rm(dir2, { recursive: true, force: true });
|
|
6278
5867
|
}
|
|
@@ -6292,10 +5881,10 @@ function pathFor3(workspaceId, conversationId, agentId) {
|
|
|
6292
5881
|
return join10(conversationDir(workspaceId, conversationId), `${encodeURIComponent(agentId)}.json`);
|
|
6293
5882
|
}
|
|
6294
5883
|
function readPrepared(workspaceId, conversationId, agentId) {
|
|
6295
|
-
const
|
|
6296
|
-
if (!existsSync7(
|
|
5884
|
+
const path = pathFor3(workspaceId, conversationId, agentId);
|
|
5885
|
+
if (!existsSync7(path)) return null;
|
|
6297
5886
|
try {
|
|
6298
|
-
const parsed = JSON.parse(readFileSync6(
|
|
5887
|
+
const parsed = JSON.parse(readFileSync6(path, "utf8"));
|
|
6299
5888
|
if (parsed && typeof parsed.cwd === "string" && parsed.cwd.length > 0) {
|
|
6300
5889
|
return {
|
|
6301
5890
|
cwd: parsed.cwd,
|
|
@@ -6329,14 +5918,14 @@ function secretsPath() {
|
|
|
6329
5918
|
}
|
|
6330
5919
|
var secretStoreSchema = z13.record(z13.string(), z13.string());
|
|
6331
5920
|
function loadSecretStore() {
|
|
6332
|
-
const
|
|
6333
|
-
if (!existsSync8(
|
|
5921
|
+
const path = secretsPath();
|
|
5922
|
+
if (!existsSync8(path)) return makeStore({});
|
|
6334
5923
|
let raw;
|
|
6335
5924
|
try {
|
|
6336
|
-
raw = readFileSync7(
|
|
5925
|
+
raw = readFileSync7(path, "utf8");
|
|
6337
5926
|
} catch (err) {
|
|
6338
5927
|
throw new ConfigError(
|
|
6339
|
-
`couldn't read ${
|
|
5928
|
+
`couldn't read ${path}: ${err instanceof Error ? err.message : String(err)}`
|
|
6340
5929
|
);
|
|
6341
5930
|
}
|
|
6342
5931
|
if (raw.trim().length === 0) return makeStore({});
|
|
@@ -6345,13 +5934,13 @@ function loadSecretStore() {
|
|
|
6345
5934
|
parsed = JSON.parse(raw);
|
|
6346
5935
|
} catch (err) {
|
|
6347
5936
|
throw new ConfigError(
|
|
6348
|
-
`${
|
|
5937
|
+
`${path} is not valid JSON: ${err instanceof Error ? err.message : String(err)}. It must be a flat object of "NAME": "value" secret pairs.`
|
|
6349
5938
|
);
|
|
6350
5939
|
}
|
|
6351
5940
|
const result = secretStoreSchema.safeParse(parsed);
|
|
6352
5941
|
if (!result.success) {
|
|
6353
5942
|
throw new ConfigError(
|
|
6354
|
-
`${
|
|
5943
|
+
`${path} must be a flat object mapping secret names to string values (e.g. { "GITHUB_TOKEN": "ghp_\u2026" }).`
|
|
6355
5944
|
);
|
|
6356
5945
|
}
|
|
6357
5946
|
return makeStore(result.data);
|
|
@@ -6787,7 +6376,7 @@ function checkoutState(cwd) {
|
|
|
6787
6376
|
if (!existsSync9(gitPath)) return { ok: false, reason: `${cwd} holds no git metadata` };
|
|
6788
6377
|
let stat;
|
|
6789
6378
|
try {
|
|
6790
|
-
stat =
|
|
6379
|
+
stat = statSync(gitPath);
|
|
6791
6380
|
} catch (error) {
|
|
6792
6381
|
return { ok: false, reason: `${gitPath} is unreadable (${error.message})` };
|
|
6793
6382
|
}
|
|
@@ -8218,8 +7807,8 @@ function sleep2(ms) {
|
|
|
8218
7807
|
}
|
|
8219
7808
|
|
|
8220
7809
|
// src/version.ts
|
|
8221
|
-
import { createRequire
|
|
8222
|
-
var pkg =
|
|
7810
|
+
import { createRequire } from "module";
|
|
7811
|
+
var pkg = createRequire(import.meta.url)("../package.json");
|
|
8223
7812
|
var COMPANION_VERSION = pkg.version;
|
|
8224
7813
|
|
|
8225
7814
|
// src/supervisor.ts
|
|
@@ -9023,9 +8612,9 @@ var CompanionSupervisor = class {
|
|
|
9023
8612
|
};
|
|
9024
8613
|
function defaultReexec() {
|
|
9025
8614
|
clearRuntimeState();
|
|
9026
|
-
void import("child_process").then(({ spawn:
|
|
8615
|
+
void import("child_process").then(({ spawn: spawn4 }) => {
|
|
9027
8616
|
try {
|
|
9028
|
-
const child =
|
|
8617
|
+
const child = spawn4(process.execPath, process.argv.slice(1), {
|
|
9029
8618
|
stdio: "inherit",
|
|
9030
8619
|
detached: false
|
|
9031
8620
|
});
|
|
@@ -9109,8 +8698,8 @@ function recordCrash(rec) {
|
|
|
9109
8698
|
}
|
|
9110
8699
|
function clearCrash() {
|
|
9111
8700
|
try {
|
|
9112
|
-
const
|
|
9113
|
-
if (existsSync11(
|
|
8701
|
+
const path = crashMarkerPath();
|
|
8702
|
+
if (existsSync11(path)) rmSync7(path, { force: true });
|
|
9114
8703
|
} catch {
|
|
9115
8704
|
}
|
|
9116
8705
|
}
|