sessionpipe 0.12.0 → 0.13.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/README.md +48 -6
- package/dist/ask.d.ts +40 -0
- package/dist/ask.d.ts.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +3098 -1166
- package/dist/cli.js.map +4 -4
- package/dist/control/channel.d.ts +43 -0
- package/dist/control/channel.d.ts.map +1 -0
- package/dist/control/cli.d.ts +13 -1
- package/dist/control/cli.d.ts.map +1 -1
- package/dist/control/daemon.d.ts +189 -2
- package/dist/control/daemon.d.ts.map +1 -1
- package/dist/control/inbox.d.ts +119 -0
- package/dist/control/inbox.d.ts.map +1 -0
- package/dist/control/local.d.ts +69 -4
- package/dist/control/local.d.ts.map +1 -1
- package/dist/control/pair.d.ts.map +1 -1
- package/dist/control/reply.d.ts +25 -1
- package/dist/control/reply.d.ts.map +1 -1
- package/dist/control/route.d.ts +50 -2
- package/dist/control/route.d.ts.map +1 -1
- package/dist/control/sdk.d.ts.map +1 -1
- package/dist/control/store.d.ts +49 -4
- package/dist/control/store.d.ts.map +1 -1
- package/dist/help.d.ts.map +1 -1
- package/dist/hook.d.ts.map +1 -1
- package/dist/hook.js +14 -4
- package/dist/hook.js.map +2 -2
- package/dist/index.js +819 -138
- package/dist/index.js.map +4 -4
- package/dist/run.d.ts +1 -0
- package/dist/run.d.ts.map +1 -1
- package/dist/worker.js +828 -147
- package/dist/worker.js.map +4 -4
- package/package.json +1 -1
package/dist/worker.js
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// sessionpipe 0.
|
|
2
|
+
// sessionpipe 0.13.0 · Apache-2.0 · https://sessionpipe.org
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __export = (target, all) => {
|
|
5
|
+
for (var name in all)
|
|
6
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
7
|
+
};
|
|
3
8
|
|
|
4
9
|
// src/worker.ts
|
|
5
|
-
import { spawn } from "node:child_process";
|
|
10
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
6
11
|
import { realpathSync as realpathSync2 } from "node:fs";
|
|
7
12
|
import { pathToFileURL } from "node:url";
|
|
8
13
|
|
|
@@ -184,7 +189,14 @@ ${m.text}`;
|
|
|
184
189
|
continue;
|
|
185
190
|
}
|
|
186
191
|
if (cur) turns.push(cur);
|
|
187
|
-
cur = {
|
|
192
|
+
cur = {
|
|
193
|
+
user: m.text,
|
|
194
|
+
assistant: "",
|
|
195
|
+
at: m.at,
|
|
196
|
+
startLine: m.line,
|
|
197
|
+
endLine: m.line,
|
|
198
|
+
...m.injected ? { injected: true } : {}
|
|
199
|
+
};
|
|
188
200
|
} else {
|
|
189
201
|
if (!cur) cur = { user: "", assistant: "", at: m.at, startLine: m.line, endLine: m.line };
|
|
190
202
|
cur.assistant += (cur.assistant ? "\n\n" : "") + m.text;
|
|
@@ -194,10 +206,17 @@ ${m.text}`;
|
|
|
194
206
|
if (cur?.assistant) turns.push(cur);
|
|
195
207
|
const line = cur && !cur.assistant ? cur.startLine : endLine;
|
|
196
208
|
const out = {
|
|
197
|
-
turns: turns.map((t) => ({
|
|
209
|
+
turns: turns.map((t) => ({
|
|
210
|
+
user: t.user,
|
|
211
|
+
assistant: t.assistant,
|
|
212
|
+
at: t.at,
|
|
213
|
+
endLine: t.endLine,
|
|
214
|
+
...t.injected ? { injected: true } : {}
|
|
215
|
+
})),
|
|
198
216
|
line
|
|
199
217
|
};
|
|
200
|
-
if (cur && !cur.assistant && cur.user)
|
|
218
|
+
if (cur && !cur.assistant && cur.user)
|
|
219
|
+
out.open = { user: cur.user, at: cur.at, ...cur.injected ? { injected: true } : {} };
|
|
201
220
|
return out;
|
|
202
221
|
}
|
|
203
222
|
var isInjected = (text) => /^<[a-z_-]+(?:\s[^>]*)?>/i.test(text) && /<\/[a-z_-]+>\s*$/i.test(text) || /^<!--\s/.test(text) || /^<command-/.test(text);
|
|
@@ -220,27 +239,51 @@ var textParts = (c, kinds) => {
|
|
|
220
239
|
(b) => b && typeof b === "object" && kinds.includes(b.type ?? "") && typeof b.text === "string"
|
|
221
240
|
).map((b) => b.text).join("\n");
|
|
222
241
|
};
|
|
242
|
+
var SPOKEN = /* @__PURE__ */ new Set(["human", "peer"]);
|
|
243
|
+
var PEER_INTRO = /sent a message[^\n]*:$/;
|
|
244
|
+
var PEER_OUTRO = "\n\nThis came from another ";
|
|
245
|
+
var ENVELOPE = /^<cross-session-message\b[^>]*>\n([\s\S]*)\n<\/cross-session-message>$/;
|
|
246
|
+
function peerAsk(origin, text) {
|
|
247
|
+
const body = origin?.body;
|
|
248
|
+
let t = typeof body === "string" && body.trim() ? body : text;
|
|
249
|
+
if (t !== body) {
|
|
250
|
+
const nl = t.indexOf("\n");
|
|
251
|
+
if (nl > 0 && PEER_INTRO.test(t.slice(0, nl))) t = t.slice(nl + 1);
|
|
252
|
+
const outro = t.indexOf(PEER_OUTRO);
|
|
253
|
+
if (outro > 0) t = t.slice(0, outro);
|
|
254
|
+
}
|
|
255
|
+
return (ENVELOPE.exec(t.trim())?.[1] ?? t).trim();
|
|
256
|
+
}
|
|
223
257
|
var queuedPrompt = (a) => {
|
|
224
258
|
if (!a || typeof a !== "object") return "";
|
|
225
259
|
const q = a;
|
|
226
260
|
if (q.type !== "queued_command" || (q.commandMode ?? "prompt") !== "prompt") return "";
|
|
227
|
-
|
|
228
|
-
|
|
261
|
+
const kind = q.origin?.kind ?? "human";
|
|
262
|
+
if (!SPOKEN.has(kind)) return "";
|
|
263
|
+
const text = textParts(q.prompt, ["text"]).trim();
|
|
264
|
+
return kind === "peer" ? peerAsk(q.origin, text) : text;
|
|
229
265
|
};
|
|
266
|
+
var channelText = (text) => /^<channel\b[^>]*>([\s\S]*)<\/channel>$/.exec(text)?.[1]?.trim() ?? "";
|
|
230
267
|
function readClaude(lines, from) {
|
|
231
268
|
const msgs = [];
|
|
232
269
|
for (let i = from; i < lines.length; i++) {
|
|
233
270
|
const j = lines[i] ? parse(lines[i]) : null;
|
|
234
271
|
if (!j || j.isSidechain) continue;
|
|
235
272
|
const origin = j.origin;
|
|
236
|
-
|
|
237
|
-
|
|
273
|
+
const channel = j.type === "user" && origin?.kind === "channel";
|
|
274
|
+
const peer = origin?.kind === "peer";
|
|
275
|
+
if (j.type === "user" && (j.isCompactSummary || j.repeatsCompactSummary)) continue;
|
|
276
|
+
if (j.type === "user" && !channel && !peer && (j.isMeta || origin?.kind && origin.kind !== "human")) continue;
|
|
238
277
|
const at = Date.parse(String(j.timestamp ?? "")) || Date.now();
|
|
239
278
|
const c = j.message?.content;
|
|
240
|
-
if (
|
|
241
|
-
const text = textParts(c, ["text"]).trim();
|
|
279
|
+
if (channel) {
|
|
280
|
+
const text = channelText(textParts(c, ["text"]).trim());
|
|
281
|
+
if (text) msgs.push({ role: "user", text, at, line: i, injected: true });
|
|
282
|
+
} else if (j.type === "user") {
|
|
283
|
+
const raw = textParts(c, ["text"]).trim();
|
|
284
|
+
const text = peer ? peerAsk(origin, raw) : raw;
|
|
242
285
|
if (!text || isInjected(text)) continue;
|
|
243
|
-
msgs.push({ role: "user", text, at, line: i });
|
|
286
|
+
msgs.push({ role: "user", text, at, line: i, ...peer ? { injected: true } : {} });
|
|
244
287
|
} else if (j.type === "assistant" && Array.isArray(c)) {
|
|
245
288
|
const text = textParts(c, ["text"]).trim();
|
|
246
289
|
if (text) msgs.push({ role: "assistant", text, at, line: i });
|
|
@@ -1154,8 +1197,44 @@ async function claudeAiTitle(bridge, token) {
|
|
|
1154
1197
|
}
|
|
1155
1198
|
|
|
1156
1199
|
// ../core/src/adapters/headless.ts
|
|
1200
|
+
import { spawn } from "node:child_process";
|
|
1157
1201
|
import { accessSync, constants, readFileSync as readFileSync3, statSync as statSync3 } from "node:fs";
|
|
1158
1202
|
import path5 from "node:path";
|
|
1203
|
+
|
|
1204
|
+
// ../core/src/launched-by.ts
|
|
1205
|
+
var LAUNCHED_BY_ENV = "SESSIONPIPE_LAUNCHED_BY";
|
|
1206
|
+
var DELEGATED_BY_ENV = "SESSIONPIPE_ASKED_BY";
|
|
1207
|
+
var ASK_CHAIN_ENV = "SESSIONPIPE_ASK_CHAIN";
|
|
1208
|
+
var LINEAGE_ENV = [LAUNCHED_BY_ENV, DELEGATED_BY_ENV, ASK_CHAIN_ENV];
|
|
1209
|
+
var SESSION_REF = /^[a-z][a-z0-9-]{0,39}:[A-Za-z0-9._:-]{1,128}$/;
|
|
1210
|
+
function withoutLineage(env) {
|
|
1211
|
+
const e = { ...env };
|
|
1212
|
+
for (const k of LINEAGE_ENV) delete e[k];
|
|
1213
|
+
return e;
|
|
1214
|
+
}
|
|
1215
|
+
function refIn(env, name) {
|
|
1216
|
+
const v = env[name]?.trim();
|
|
1217
|
+
return v && SESSION_REF.test(v) ? v : void 0;
|
|
1218
|
+
}
|
|
1219
|
+
function launchedBy(env) {
|
|
1220
|
+
return refIn(env, LAUNCHED_BY_ENV);
|
|
1221
|
+
}
|
|
1222
|
+
function delegatedBy(env) {
|
|
1223
|
+
return refIn(env, DELEGATED_BY_ENV);
|
|
1224
|
+
}
|
|
1225
|
+
function readHook(adapter, input) {
|
|
1226
|
+
const r = adapter.fromHook(input);
|
|
1227
|
+
if (r && !r.session.parent_id) {
|
|
1228
|
+
const self = `${adapter.name}:${r.session.id}`;
|
|
1229
|
+
const launcher = launchedBy(input.env);
|
|
1230
|
+
if (launcher && launcher !== self) r.session.launched_by = launcher;
|
|
1231
|
+
const asker = delegatedBy(input.env);
|
|
1232
|
+
if (asker && asker !== self) r.session.delegated_by = asker;
|
|
1233
|
+
}
|
|
1234
|
+
return r;
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
// ../core/src/adapters/headless.ts
|
|
1159
1238
|
var JOB_TIMEOUT_MS = 30 * 6e4;
|
|
1160
1239
|
var OUT_CAP = 1024 * 1024;
|
|
1161
1240
|
function isExe(p) {
|
|
@@ -1197,10 +1276,116 @@ function spawnable(bin, args) {
|
|
|
1197
1276
|
const target = shimTarget(bin);
|
|
1198
1277
|
return target ? { file: process.execPath, args: [target, ...args] } : null;
|
|
1199
1278
|
}
|
|
1279
|
+
var shimNote = (bin) => `${path5.basename(bin)} is a launcher script that names no program to run; point the harness's override (SESSIONPIPE_CLAUDE, SESSIONPIPE_CODEX, \u2026) at the program itself`;
|
|
1200
1280
|
function probe(bin, args) {
|
|
1201
1281
|
const s = spawnable(bin, args);
|
|
1202
1282
|
return s ? { ...s, shell: false } : { file: [`"${bin}"`, ...args].join(" "), args: [], shell: true };
|
|
1203
1283
|
}
|
|
1284
|
+
function runHeadless(bin, args, o) {
|
|
1285
|
+
return new Promise((resolve) => {
|
|
1286
|
+
let out = [];
|
|
1287
|
+
let outLen = 0;
|
|
1288
|
+
let tooBig = false;
|
|
1289
|
+
let errTail = "";
|
|
1290
|
+
let timedOut = false;
|
|
1291
|
+
let settled = false;
|
|
1292
|
+
let child;
|
|
1293
|
+
const c = spawnable(bin, args);
|
|
1294
|
+
if (!c) {
|
|
1295
|
+
resolve({ error: shimNote(bin) });
|
|
1296
|
+
return;
|
|
1297
|
+
}
|
|
1298
|
+
try {
|
|
1299
|
+
child = spawn(c.file, c.args, {
|
|
1300
|
+
cwd: o.cwd,
|
|
1301
|
+
env: o.env,
|
|
1302
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
1303
|
+
detached: process.platform !== "win32",
|
|
1304
|
+
windowsHide: true
|
|
1305
|
+
});
|
|
1306
|
+
} catch (e) {
|
|
1307
|
+
resolve({ error: e.message });
|
|
1308
|
+
return;
|
|
1309
|
+
}
|
|
1310
|
+
const kill = (sig) => {
|
|
1311
|
+
try {
|
|
1312
|
+
if (process.platform !== "win32" && child.pid) process.kill(-child.pid, sig);
|
|
1313
|
+
else child.kill(sig);
|
|
1314
|
+
} catch {
|
|
1315
|
+
try {
|
|
1316
|
+
child.kill(sig);
|
|
1317
|
+
} catch {
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
};
|
|
1321
|
+
const keep = (line) => {
|
|
1322
|
+
if (!o.keep?.(line)) return;
|
|
1323
|
+
out = [Buffer.from(line)];
|
|
1324
|
+
outLen = line.length;
|
|
1325
|
+
};
|
|
1326
|
+
let partial = "";
|
|
1327
|
+
child.stdout?.on("data", (b) => {
|
|
1328
|
+
if (o.onLine) {
|
|
1329
|
+
const chunk = partial + b.toString("utf8");
|
|
1330
|
+
const lines = chunk.split("\n");
|
|
1331
|
+
partial = lines.pop();
|
|
1332
|
+
for (const line of lines) {
|
|
1333
|
+
if (!line.trim()) continue;
|
|
1334
|
+
try {
|
|
1335
|
+
o.onLine(line);
|
|
1336
|
+
} catch {
|
|
1337
|
+
}
|
|
1338
|
+
keep(line);
|
|
1339
|
+
}
|
|
1340
|
+
if (partial.length > OUT_CAP) partial = "";
|
|
1341
|
+
return;
|
|
1342
|
+
}
|
|
1343
|
+
if (tooBig) return;
|
|
1344
|
+
if (outLen + b.length > OUT_CAP) {
|
|
1345
|
+
tooBig = true;
|
|
1346
|
+
out = [];
|
|
1347
|
+
return;
|
|
1348
|
+
}
|
|
1349
|
+
out.push(b);
|
|
1350
|
+
outLen += b.length;
|
|
1351
|
+
});
|
|
1352
|
+
child.stderr?.on("data", (b) => {
|
|
1353
|
+
errTail = (errTail + b.toString("utf8")).slice(-4e3);
|
|
1354
|
+
});
|
|
1355
|
+
const timer = setTimeout(() => {
|
|
1356
|
+
timedOut = true;
|
|
1357
|
+
kill("SIGTERM");
|
|
1358
|
+
setTimeout(() => kill("SIGKILL"), 1e4).unref();
|
|
1359
|
+
}, o.timeoutMs ?? JOB_TIMEOUT_MS);
|
|
1360
|
+
const result = (code, signal) => ({
|
|
1361
|
+
code,
|
|
1362
|
+
signal,
|
|
1363
|
+
stdout: tooBig ? null : Buffer.concat(out).toString("utf8"),
|
|
1364
|
+
tooBig,
|
|
1365
|
+
stderr: errTail,
|
|
1366
|
+
timedOut
|
|
1367
|
+
});
|
|
1368
|
+
const finish = (r) => {
|
|
1369
|
+
if (settled) return;
|
|
1370
|
+
settled = true;
|
|
1371
|
+
clearTimeout(timer);
|
|
1372
|
+
resolve(r);
|
|
1373
|
+
};
|
|
1374
|
+
child.on("error", (e) => finish({ error: e.message }));
|
|
1375
|
+
child.on("exit", (code, signal) => setTimeout(() => finish(result(code, signal)), 2e3).unref());
|
|
1376
|
+
child.on("close", (code, signal) => {
|
|
1377
|
+
if (o.onLine && partial.trim()) {
|
|
1378
|
+
try {
|
|
1379
|
+
o.onLine(partial);
|
|
1380
|
+
} catch {
|
|
1381
|
+
}
|
|
1382
|
+
keep(partial);
|
|
1383
|
+
partial = "";
|
|
1384
|
+
}
|
|
1385
|
+
finish(result(code, signal));
|
|
1386
|
+
});
|
|
1387
|
+
});
|
|
1388
|
+
}
|
|
1204
1389
|
|
|
1205
1390
|
// ../core/src/adapters/antigravity.ts
|
|
1206
1391
|
import {
|
|
@@ -1490,6 +1675,50 @@ function antigravityFacts(file, opts) {
|
|
|
1490
1675
|
return out;
|
|
1491
1676
|
}
|
|
1492
1677
|
|
|
1678
|
+
// ../core/src/adapters/claude-code-control.ts
|
|
1679
|
+
var claude_code_control_exports = {};
|
|
1680
|
+
__export(claude_code_control_exports, {
|
|
1681
|
+
CLAUDE_MODES: () => CLAUDE_MODES,
|
|
1682
|
+
JOB_TIMEOUT_MS: () => JOB_TIMEOUT_MS,
|
|
1683
|
+
MODE_RANK: () => MODE_RANK,
|
|
1684
|
+
NO_HOME_SETTINGS: () => NO_HOME_SETTINGS,
|
|
1685
|
+
ceilingLevel: () => ceilingLevel,
|
|
1686
|
+
claudeArgs: () => claudeArgs,
|
|
1687
|
+
claudeEnv: () => claudeEnv,
|
|
1688
|
+
claudeLogin: () => claudeLogin2,
|
|
1689
|
+
cmpVersion: () => cmpVersion,
|
|
1690
|
+
defaultLevel: () => defaultLevel,
|
|
1691
|
+
detectCaps: () => detectCaps,
|
|
1692
|
+
findClaude: () => findClaude,
|
|
1693
|
+
findTranscript: () => findTranscript,
|
|
1694
|
+
findTranscripts: () => findTranscripts,
|
|
1695
|
+
homeSettings: () => homeSettings,
|
|
1696
|
+
hookMode: () => hookMode,
|
|
1697
|
+
isUuid: () => isUuid,
|
|
1698
|
+
liveProcess: () => liveProcess,
|
|
1699
|
+
liveRecord: () => liveRecord,
|
|
1700
|
+
liveRecords: () => liveRecords,
|
|
1701
|
+
loginCommand: () => loginCommand,
|
|
1702
|
+
loginHint: () => loginHint,
|
|
1703
|
+
modeFlags: () => modeFlags,
|
|
1704
|
+
modeRank: () => modeRank,
|
|
1705
|
+
offeredLevels: () => offeredLevels,
|
|
1706
|
+
outputArgs: () => outputArgs,
|
|
1707
|
+
parseAuthStatus: () => parseAuthStatus,
|
|
1708
|
+
parseCaps: () => parseCaps,
|
|
1709
|
+
parseResult: () => parseResult,
|
|
1710
|
+
peerClass: () => peerClass,
|
|
1711
|
+
recentlyWritten: () => recentlyWritten,
|
|
1712
|
+
relaunchMode: () => relaunchMode,
|
|
1713
|
+
resolveHome: () => resolveHome,
|
|
1714
|
+
runClaude: () => runClaude,
|
|
1715
|
+
sessionFolder: () => sessionFolder
|
|
1716
|
+
});
|
|
1717
|
+
import { execFile, execFileSync as execFileSync3 } from "node:child_process";
|
|
1718
|
+
import { closeSync as closeSync2, openSync as openSync2, readdirSync as readdirSync4, readFileSync as readFileSync5, readSync as readSync2, statSync as statSync5 } from "node:fs";
|
|
1719
|
+
import os2 from "node:os";
|
|
1720
|
+
import path7 from "node:path";
|
|
1721
|
+
|
|
1493
1722
|
// ../core/src/control/verify.ts
|
|
1494
1723
|
var GRANT_MAX_MS = 24 * 36e5;
|
|
1495
1724
|
var SKEW_MS = 5 * 6e4;
|
|
@@ -1500,10 +1729,10 @@ var MODEL_RE = /^[A-Za-z0-9][A-Za-z0-9._:/@[\]-]*$/;
|
|
|
1500
1729
|
var START_PERMISSIONS = ["read", "safe", "edits", "auto"];
|
|
1501
1730
|
var levelRank = (l) => START_PERMISSIONS.indexOf(l);
|
|
1502
1731
|
var FIELDS = {
|
|
1503
|
-
prompt: ["text", "files"],
|
|
1732
|
+
prompt: ["text", "files", "reply_to"],
|
|
1504
1733
|
"permission.answer": ["for", "decision"],
|
|
1505
1734
|
cancel: [],
|
|
1506
|
-
start: ["text", "cwd", "files", "permission", "model"],
|
|
1735
|
+
start: ["text", "cwd", "files", "permission", "model", "reply_to"],
|
|
1507
1736
|
"key.add": ["key", "name"],
|
|
1508
1737
|
pool: ["pool_action", "pool_account", "pool_order", "pool_code"]
|
|
1509
1738
|
};
|
|
@@ -1511,9 +1740,454 @@ var POOL_FIELDS = FIELDS.pool;
|
|
|
1511
1740
|
var enc = new TextEncoder();
|
|
1512
1741
|
var dec = new TextDecoder();
|
|
1513
1742
|
|
|
1743
|
+
// ../core/src/adapters/claude-code-control.ts
|
|
1744
|
+
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
1745
|
+
var LIVE_WRITE_MS = 9e4;
|
|
1746
|
+
var cmpVersion = (a, b) => {
|
|
1747
|
+
const pa = a.split(".").map(Number);
|
|
1748
|
+
const pb = b.split(".").map(Number);
|
|
1749
|
+
for (let i = 0; i < Math.max(pa.length, pb.length); i++)
|
|
1750
|
+
if ((pa[i] ?? 0) !== (pb[i] ?? 0)) return (pa[i] ?? 0) - (pb[i] ?? 0);
|
|
1751
|
+
return 0;
|
|
1752
|
+
};
|
|
1753
|
+
function findClaude(env = process.env, home = os2.homedir()) {
|
|
1754
|
+
if (env.SESSIONPIPE_CLAUDE && isExe(env.SESSIONPIPE_CLAUDE)) return env.SESSIONPIPE_CLAUDE;
|
|
1755
|
+
const names = process.platform === "win32" ? ["claude.exe", "claude.cmd"] : ["claude"];
|
|
1756
|
+
for (const d of String(env.PATH || "").split(path7.delimiter)) {
|
|
1757
|
+
if (!d || !path7.isAbsolute(d)) continue;
|
|
1758
|
+
for (const n of names) if (isExe(path7.join(d, n))) return path7.join(d, n);
|
|
1759
|
+
}
|
|
1760
|
+
for (const p of [
|
|
1761
|
+
path7.join(home, ".claude", "local", "claude"),
|
|
1762
|
+
path7.join(home, ".local", "bin", "claude"),
|
|
1763
|
+
"/opt/homebrew/bin/claude",
|
|
1764
|
+
"/usr/local/bin/claude"
|
|
1765
|
+
])
|
|
1766
|
+
if (isExe(p)) return p;
|
|
1767
|
+
const app = path7.join(home, "Library", "Application Support", "Claude", "claude-code");
|
|
1768
|
+
let versions = [];
|
|
1769
|
+
try {
|
|
1770
|
+
versions = readdirSync4(app).filter((n) => /^\d+(\.\d+)*$/.test(n)).sort(cmpVersion).reverse();
|
|
1771
|
+
} catch {
|
|
1772
|
+
}
|
|
1773
|
+
for (const v of versions) {
|
|
1774
|
+
const p = path7.join(app, v, "claude.app", "Contents", "MacOS", "claude");
|
|
1775
|
+
if (isExe(p)) return p;
|
|
1776
|
+
}
|
|
1777
|
+
return null;
|
|
1778
|
+
}
|
|
1779
|
+
function optionBlock(help, flag) {
|
|
1780
|
+
const lines = help.split("\n");
|
|
1781
|
+
const re = new RegExp(`^\\s{0,4}(?:-\\w, )?${flag.replace(/-/g, "\\-")}(?![\\w-])`);
|
|
1782
|
+
const start = lines.findIndex((l) => re.test(l));
|
|
1783
|
+
if (start < 0) return null;
|
|
1784
|
+
const out = [lines[start]];
|
|
1785
|
+
for (let i = start + 1; i < lines.length && lines[i]?.trim() && !/^\s{0,4}-/.test(lines[i]); i++)
|
|
1786
|
+
out.push(lines[i]);
|
|
1787
|
+
return out.join(" ");
|
|
1788
|
+
}
|
|
1789
|
+
function parseCaps(help) {
|
|
1790
|
+
const pm = optionBlock(help, "--permission-mode");
|
|
1791
|
+
const pp = optionBlock(help, "--permission-prompts");
|
|
1792
|
+
return {
|
|
1793
|
+
hasMode: !!pm,
|
|
1794
|
+
modes: pm ? [...pm.matchAll(/"([A-Za-z]+)"/g)].map((m) => m[1]) : [],
|
|
1795
|
+
promptsNone: !!pp && /"none"/.test(pp),
|
|
1796
|
+
fork: !!optionBlock(help, "--fork-session"),
|
|
1797
|
+
stream: !!optionBlock(help, "--include-partial-messages")
|
|
1798
|
+
};
|
|
1799
|
+
}
|
|
1800
|
+
var LEVEL_MODE = {
|
|
1801
|
+
read: "plan",
|
|
1802
|
+
edits: "acceptEdits",
|
|
1803
|
+
auto: "auto"
|
|
1804
|
+
};
|
|
1805
|
+
function modeFlags(level, caps) {
|
|
1806
|
+
if (level === "inherit") return { args: [] };
|
|
1807
|
+
if (caps.unread)
|
|
1808
|
+
return { error: `this machine couldn't read its Claude Code's options (${caps.unread}); try again in a minute` };
|
|
1809
|
+
if (level === "auto" && !caps.modes.includes("auto"))
|
|
1810
|
+
return { error: "this machine is in auto mode, but its Claude Code has no auto permission mode" };
|
|
1811
|
+
if (level !== "safe") {
|
|
1812
|
+
const m = LEVEL_MODE[level];
|
|
1813
|
+
if (!caps.modes.includes(m)) return { error: `this machine's Claude Code has no ${m} permission mode` };
|
|
1814
|
+
return { args: ["--permission-mode", m] };
|
|
1815
|
+
}
|
|
1816
|
+
if (caps.modes.includes("dontAsk")) return { args: ["--permission-mode", "dontAsk"] };
|
|
1817
|
+
const args = [];
|
|
1818
|
+
if (caps.hasMode)
|
|
1819
|
+
args.push(
|
|
1820
|
+
"--permission-mode",
|
|
1821
|
+
caps.modes.includes("manual") && !caps.modes.includes("default") ? "manual" : "default"
|
|
1822
|
+
);
|
|
1823
|
+
if (caps.promptsNone) args.push("--permission-prompts", "none");
|
|
1824
|
+
return { args };
|
|
1825
|
+
}
|
|
1826
|
+
var ceilingLevel = (mode) => mode === "inherit" ? "safe" : mode;
|
|
1827
|
+
function offeredLevels(mode, caps) {
|
|
1828
|
+
if (caps.unread) return [];
|
|
1829
|
+
return START_PERMISSIONS.filter(
|
|
1830
|
+
(l) => levelRank(l) <= levelRank(ceilingLevel(mode)) && (l === "safe" || caps.modes.includes(LEVEL_MODE[l]))
|
|
1831
|
+
);
|
|
1832
|
+
}
|
|
1833
|
+
var defaultLevel = (mode) => mode;
|
|
1834
|
+
function outputArgs(caps) {
|
|
1835
|
+
return caps.stream ? ["--output-format", "stream-json", "--verbose", "--include-partial-messages"] : ["--output-format", "json"];
|
|
1836
|
+
}
|
|
1837
|
+
function claudeArgs(o) {
|
|
1838
|
+
const mf = modeFlags(o.level, o.caps);
|
|
1839
|
+
if ("error" in mf) return mf;
|
|
1840
|
+
const where = o.resume ? ["--resume", o.resume, ...o.fork ? ["--fork-session"] : []] : ["--session-id", o.session];
|
|
1841
|
+
return {
|
|
1842
|
+
args: [
|
|
1843
|
+
"-p",
|
|
1844
|
+
...outputArgs(o.caps),
|
|
1845
|
+
...mf.args,
|
|
1846
|
+
...o.model ? ["--model", o.model] : [],
|
|
1847
|
+
...o.allowedTools?.length ? [`--allowedTools=${o.allowedTools.join(",")}`] : [],
|
|
1848
|
+
...where,
|
|
1849
|
+
o.prompt.startsWith("-") ? ` ${o.prompt}` : o.prompt
|
|
1850
|
+
]
|
|
1851
|
+
};
|
|
1852
|
+
}
|
|
1853
|
+
function claudeEnv(configDir, env = process.env) {
|
|
1854
|
+
const e = withoutLineage(env);
|
|
1855
|
+
for (const k of [
|
|
1856
|
+
"CLAUDECODE",
|
|
1857
|
+
"CLAUDE_CODE_SESSION_ID",
|
|
1858
|
+
"CLAUDE_CODE_HOST_SESSION_ID",
|
|
1859
|
+
"CLAUDE_CODE_ENTRYPOINT",
|
|
1860
|
+
"CLAUDE_CODE_SESSION_ATTENDED",
|
|
1861
|
+
"CLAUDE_CODE_SSE_PORT"
|
|
1862
|
+
])
|
|
1863
|
+
delete e[k];
|
|
1864
|
+
e.SESSIONPIPE_CONTROL_JOB = "1";
|
|
1865
|
+
if (configDir !== void 0) {
|
|
1866
|
+
if (path7.resolve(configDir) === path7.resolve(os2.homedir(), ".claude")) delete e.CLAUDE_CONFIG_DIR;
|
|
1867
|
+
else e.CLAUDE_CONFIG_DIR = configDir;
|
|
1868
|
+
}
|
|
1869
|
+
return e;
|
|
1870
|
+
}
|
|
1871
|
+
function parseAuthStatus(stdout) {
|
|
1872
|
+
const text = String(stdout || "").trim();
|
|
1873
|
+
const start = text.indexOf("{");
|
|
1874
|
+
if (start < 0) return { loggedIn: null };
|
|
1875
|
+
try {
|
|
1876
|
+
const j = JSON.parse(text.slice(start));
|
|
1877
|
+
if (typeof j.loggedIn !== "boolean") return { loggedIn: null };
|
|
1878
|
+
return {
|
|
1879
|
+
loggedIn: j.loggedIn,
|
|
1880
|
+
...typeof j.authMethod === "string" ? { method: j.authMethod } : {},
|
|
1881
|
+
...typeof j.subscriptionType === "string" ? { subscription: j.subscriptionType } : {}
|
|
1882
|
+
};
|
|
1883
|
+
} catch {
|
|
1884
|
+
return { loggedIn: null };
|
|
1885
|
+
}
|
|
1886
|
+
}
|
|
1887
|
+
function claudeLogin2(bin, configDir, env = process.env) {
|
|
1888
|
+
const c = probe(bin, ["auth", "status", "--json"]);
|
|
1889
|
+
return new Promise((resolve) => {
|
|
1890
|
+
execFile(
|
|
1891
|
+
c.file,
|
|
1892
|
+
c.args,
|
|
1893
|
+
{ encoding: "utf8", timeout: 2e4, env: claudeEnv(configDir, env), maxBuffer: 256 * 1024, shell: c.shell },
|
|
1894
|
+
// `auth status` may exit non-zero when signed out; its JSON still says so.
|
|
1895
|
+
(_err, stdout) => resolve(parseAuthStatus(stdout))
|
|
1896
|
+
);
|
|
1897
|
+
});
|
|
1898
|
+
}
|
|
1899
|
+
function loginCommand(configDir) {
|
|
1900
|
+
const home = os2.homedir();
|
|
1901
|
+
const isDefault = !configDir || path7.resolve(configDir) === path7.resolve(home, ".claude");
|
|
1902
|
+
return isDefault ? "`claude auth login`" : `\`CLAUDE_CONFIG_DIR=${configDir.replace(home, "~")} claude auth login\``;
|
|
1903
|
+
}
|
|
1904
|
+
function loginHint(configDir, platform = process.platform) {
|
|
1905
|
+
const shown = configDir ? configDir.replace(os2.homedir(), "~") : "~/.claude";
|
|
1906
|
+
const login = loginCommand(configDir);
|
|
1907
|
+
if (platform === "darwin")
|
|
1908
|
+
return `Claude Code (${shown}) isn't signed in as this machine's background service sees it. On a Mac its login lives in the login keychain, which a background service can read only while someone is logged in at the screen: log in at the Mac, or run ${login} there.`;
|
|
1909
|
+
return `Claude Code (${shown}) isn't signed in on this machine: run ${login} there.`;
|
|
1910
|
+
}
|
|
1911
|
+
function detectCaps(bin, timeoutMs = 3e4) {
|
|
1912
|
+
const c = probe(bin, ["--help"]);
|
|
1913
|
+
try {
|
|
1914
|
+
return parseCaps(
|
|
1915
|
+
execFileSync3(c.file, c.args, {
|
|
1916
|
+
encoding: "utf8",
|
|
1917
|
+
timeout: timeoutMs,
|
|
1918
|
+
env: claudeEnv(),
|
|
1919
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
1920
|
+
maxBuffer: 4 * 1024 * 1024,
|
|
1921
|
+
shell: c.shell
|
|
1922
|
+
})
|
|
1923
|
+
);
|
|
1924
|
+
} catch (e) {
|
|
1925
|
+
const err = e;
|
|
1926
|
+
const caps = parseCaps(String(err?.stdout || ""));
|
|
1927
|
+
if (caps.hasMode) return caps;
|
|
1928
|
+
const why = err?.code === "ETIMEDOUT" ? `timed out after ${timeoutMs / 1e3} s` : "failed";
|
|
1929
|
+
return { ...caps, unread: `\`${path7.basename(bin)} --help\` ${why}` };
|
|
1930
|
+
}
|
|
1931
|
+
}
|
|
1932
|
+
function transcriptIn(session, dir) {
|
|
1933
|
+
let projects = [];
|
|
1934
|
+
try {
|
|
1935
|
+
projects = readdirSync4(path7.join(dir, "projects"), { withFileTypes: true });
|
|
1936
|
+
} catch {
|
|
1937
|
+
return null;
|
|
1938
|
+
}
|
|
1939
|
+
for (const p of projects) {
|
|
1940
|
+
if (!p.isDirectory()) continue;
|
|
1941
|
+
const file = path7.join(dir, "projects", p.name, `${session}.jsonl`);
|
|
1942
|
+
try {
|
|
1943
|
+
if (statSync5(file).isFile()) return { file, configDir: dir, project: p.name };
|
|
1944
|
+
} catch {
|
|
1945
|
+
}
|
|
1946
|
+
}
|
|
1947
|
+
return null;
|
|
1948
|
+
}
|
|
1949
|
+
function findTranscript(session, dirs) {
|
|
1950
|
+
if (!UUID_RE.test(session)) return null;
|
|
1951
|
+
for (const dir of dirs) {
|
|
1952
|
+
const t = transcriptIn(session, dir);
|
|
1953
|
+
if (t) return t;
|
|
1954
|
+
}
|
|
1955
|
+
return null;
|
|
1956
|
+
}
|
|
1957
|
+
function findTranscripts(session, dirs) {
|
|
1958
|
+
if (!UUID_RE.test(session)) return [];
|
|
1959
|
+
const out = [];
|
|
1960
|
+
for (const dir of dirs) {
|
|
1961
|
+
const t = transcriptIn(session, dir);
|
|
1962
|
+
if (t) out.push(t);
|
|
1963
|
+
}
|
|
1964
|
+
return out;
|
|
1965
|
+
}
|
|
1966
|
+
var homeName = (t) => path7.basename(t.configDir);
|
|
1967
|
+
function resolveHome(session, dirs) {
|
|
1968
|
+
const all = findTranscripts(session, dirs);
|
|
1969
|
+
if (!all.length) return null;
|
|
1970
|
+
if (all.length === 1) return { transcript: all[0] };
|
|
1971
|
+
const live = all.filter((t) => liveRecords(session, t.configDir).length > 0);
|
|
1972
|
+
if (live.length === 1) return { transcript: live[0] };
|
|
1973
|
+
return {
|
|
1974
|
+
ambiguous: [...new Set(all.map(homeName))].sort(),
|
|
1975
|
+
live: [...new Set(live.map(homeName))].sort()
|
|
1976
|
+
};
|
|
1977
|
+
}
|
|
1978
|
+
var projectName = (cwd) => cwd.replace(/[^a-zA-Z0-9]/g, "-");
|
|
1979
|
+
function sessionFolder(t, cap2 = 8 * 1024 * 1024) {
|
|
1980
|
+
let first = null;
|
|
1981
|
+
let count = 0;
|
|
1982
|
+
let fd;
|
|
1983
|
+
const look = (line) => {
|
|
1984
|
+
if (line.indexOf('"cwd"') < 0) return null;
|
|
1985
|
+
let j;
|
|
1986
|
+
try {
|
|
1987
|
+
j = JSON.parse(line.toString("utf8"));
|
|
1988
|
+
} catch {
|
|
1989
|
+
return null;
|
|
1990
|
+
}
|
|
1991
|
+
if (!j || typeof j.cwd !== "string" || !path7.isAbsolute(j.cwd)) return null;
|
|
1992
|
+
if (projectName(j.cwd) === t.project) return j.cwd;
|
|
1993
|
+
first ??= j.cwd;
|
|
1994
|
+
count++;
|
|
1995
|
+
return null;
|
|
1996
|
+
};
|
|
1997
|
+
try {
|
|
1998
|
+
fd = openSync2(t.file, "r");
|
|
1999
|
+
const buf = Buffer.alloc(256 * 1024);
|
|
2000
|
+
let pos = 0;
|
|
2001
|
+
let rest = Buffer.alloc(0);
|
|
2002
|
+
let skipping = false;
|
|
2003
|
+
while (pos < cap2 && count < 50) {
|
|
2004
|
+
const n = readSync2(fd, buf, 0, buf.length, pos);
|
|
2005
|
+
if (!n) {
|
|
2006
|
+
if (!skipping && rest.length) {
|
|
2007
|
+
const hit = look(rest);
|
|
2008
|
+
if (hit) return hit;
|
|
2009
|
+
}
|
|
2010
|
+
break;
|
|
2011
|
+
}
|
|
2012
|
+
pos += n;
|
|
2013
|
+
let chunk = rest.length ? Buffer.concat([rest, buf.subarray(0, n)]) : buf.subarray(0, n);
|
|
2014
|
+
let nl = chunk.indexOf(10);
|
|
2015
|
+
while (nl >= 0) {
|
|
2016
|
+
const line = chunk.subarray(0, nl);
|
|
2017
|
+
chunk = chunk.subarray(nl + 1);
|
|
2018
|
+
if (skipping) skipping = false;
|
|
2019
|
+
else {
|
|
2020
|
+
const hit = look(line);
|
|
2021
|
+
if (hit) return hit;
|
|
2022
|
+
}
|
|
2023
|
+
nl = chunk.indexOf(10);
|
|
2024
|
+
}
|
|
2025
|
+
if (chunk.length > 2 * 1024 * 1024) {
|
|
2026
|
+
skipping = true;
|
|
2027
|
+
rest = Buffer.alloc(0);
|
|
2028
|
+
} else rest = skipping ? Buffer.alloc(0) : Buffer.from(chunk);
|
|
2029
|
+
}
|
|
2030
|
+
} catch {
|
|
2031
|
+
} finally {
|
|
2032
|
+
if (fd !== void 0)
|
|
2033
|
+
try {
|
|
2034
|
+
closeSync2(fd);
|
|
2035
|
+
} catch {
|
|
2036
|
+
}
|
|
2037
|
+
}
|
|
2038
|
+
return first;
|
|
2039
|
+
}
|
|
2040
|
+
function pidAlive(pid) {
|
|
2041
|
+
if (!Number.isSafeInteger(pid) || pid <= 1) return false;
|
|
2042
|
+
try {
|
|
2043
|
+
process.kill(pid, 0);
|
|
2044
|
+
return true;
|
|
2045
|
+
} catch (e) {
|
|
2046
|
+
return e.code === "EPERM";
|
|
2047
|
+
}
|
|
2048
|
+
}
|
|
2049
|
+
var registryRow = (file, configDir, j) => {
|
|
2050
|
+
const pid = Number(j.pid ?? path7.basename(file).slice(0, -5));
|
|
2051
|
+
const str = (v) => typeof v === "string" && v ? v : null;
|
|
2052
|
+
let keyFile = null;
|
|
2053
|
+
try {
|
|
2054
|
+
const want = new RegExp(`^${pid}\\.[0-9a-f]{64}\\.key$`);
|
|
2055
|
+
const hit = readdirSync4(path7.dirname(file)).find((n) => want.test(n));
|
|
2056
|
+
if (hit) keyFile = path7.join(path7.dirname(file), hit);
|
|
2057
|
+
} catch {
|
|
2058
|
+
}
|
|
2059
|
+
return {
|
|
2060
|
+
configDir,
|
|
2061
|
+
file,
|
|
2062
|
+
pid,
|
|
2063
|
+
socket: str(j.messagingSocketPath),
|
|
2064
|
+
peerProtocol: typeof j.peerProtocol === "number" ? j.peerProtocol : null,
|
|
2065
|
+
procStart: str(j.procStart),
|
|
2066
|
+
status: str(j.status),
|
|
2067
|
+
kind: str(j.kind),
|
|
2068
|
+
entrypoint: str(j.entrypoint),
|
|
2069
|
+
keyFile
|
|
2070
|
+
};
|
|
2071
|
+
};
|
|
2072
|
+
function liveRecords(session, dirs) {
|
|
2073
|
+
const out = [];
|
|
2074
|
+
for (const dir of typeof dirs === "string" ? [dirs] : dirs) {
|
|
2075
|
+
let names = [];
|
|
2076
|
+
try {
|
|
2077
|
+
names = readdirSync4(path7.join(dir, "sessions")).filter((n) => /^\d+\.json$/.test(n));
|
|
2078
|
+
} catch {
|
|
2079
|
+
continue;
|
|
2080
|
+
}
|
|
2081
|
+
for (const n of names) {
|
|
2082
|
+
const file = path7.join(dir, "sessions", n);
|
|
2083
|
+
let j;
|
|
2084
|
+
try {
|
|
2085
|
+
j = JSON.parse(readFileSync5(file, "utf8"));
|
|
2086
|
+
} catch {
|
|
2087
|
+
continue;
|
|
2088
|
+
}
|
|
2089
|
+
if (String(j.sessionId) !== session) continue;
|
|
2090
|
+
const row = registryRow(file, dir, j);
|
|
2091
|
+
if (pidAlive(row.pid)) out.push(row);
|
|
2092
|
+
}
|
|
2093
|
+
}
|
|
2094
|
+
return out;
|
|
2095
|
+
}
|
|
2096
|
+
function liveRecord(session, dirs) {
|
|
2097
|
+
const all = liveRecords(session, dirs);
|
|
2098
|
+
return all.length === 1 ? all[0] : null;
|
|
2099
|
+
}
|
|
2100
|
+
function liveProcess(session, configDir) {
|
|
2101
|
+
return liveRecords(session, configDir).length > 0;
|
|
2102
|
+
}
|
|
2103
|
+
var MODE_RANK = {
|
|
2104
|
+
plan: 0,
|
|
2105
|
+
default: 1,
|
|
2106
|
+
manual: 1,
|
|
2107
|
+
dontAsk: 1,
|
|
2108
|
+
acceptEdits: 2,
|
|
2109
|
+
auto: 3,
|
|
2110
|
+
bypassPermissions: 4
|
|
2111
|
+
};
|
|
2112
|
+
var CLAUDE_MODES = Object.keys(MODE_RANK);
|
|
2113
|
+
var modeRank = (m) => m && m in MODE_RANK ? MODE_RANK[m] : -1;
|
|
2114
|
+
function hookMode(payload) {
|
|
2115
|
+
if (!payload || typeof payload !== "object") return null;
|
|
2116
|
+
const p = payload;
|
|
2117
|
+
const v = typeof p.permission_mode === "string" ? p.permission_mode : p.permissionMode;
|
|
2118
|
+
return typeof v === "string" && CLAUDE_MODES.includes(v) ? v : null;
|
|
2119
|
+
}
|
|
2120
|
+
function peerClass(mode) {
|
|
2121
|
+
if (mode === "bypassPermissions") return "bypass";
|
|
2122
|
+
if (mode === "default" || mode === "manual" || mode === "dontAsk" || mode === "acceptEdits") return "prompting";
|
|
2123
|
+
return null;
|
|
2124
|
+
}
|
|
2125
|
+
var NO_HOME_SETTINGS = { defaultMode: null, disableAutoMode: false, disableBypass: false };
|
|
2126
|
+
function homeSettings(configDir, home = os2.homedir()) {
|
|
2127
|
+
const file = path7.join(configDir ?? path7.join(home, ".claude"), "settings.json");
|
|
2128
|
+
let j;
|
|
2129
|
+
try {
|
|
2130
|
+
j = JSON.parse(readFileSync5(file, "utf8"));
|
|
2131
|
+
} catch {
|
|
2132
|
+
return { ...NO_HOME_SETTINGS };
|
|
2133
|
+
}
|
|
2134
|
+
if (!j || typeof j !== "object" || Array.isArray(j)) return { ...NO_HOME_SETTINGS };
|
|
2135
|
+
const p = j.permissions && typeof j.permissions === "object" ? j.permissions : {};
|
|
2136
|
+
const off = (k) => j[k] === "disable" || p[k] === "disable";
|
|
2137
|
+
return {
|
|
2138
|
+
defaultMode: typeof p.defaultMode === "string" && CLAUDE_MODES.includes(p.defaultMode) ? p.defaultMode : null,
|
|
2139
|
+
disableAutoMode: off("disableAutoMode"),
|
|
2140
|
+
disableBypass: off("disableBypassPermissionsMode")
|
|
2141
|
+
};
|
|
2142
|
+
}
|
|
2143
|
+
function relaunchMode(recorded, home) {
|
|
2144
|
+
if (recorded === "plan") return recorded;
|
|
2145
|
+
if ((recorded === "dontAsk" || recorded === "acceptEdits") && modeRank(home.defaultMode) >= modeRank(recorded))
|
|
2146
|
+
return recorded;
|
|
2147
|
+
return null;
|
|
2148
|
+
}
|
|
2149
|
+
function recentlyWritten(file, ourLastEnd, now = Date.now()) {
|
|
2150
|
+
let m;
|
|
2151
|
+
try {
|
|
2152
|
+
m = statSync5(file).mtimeMs;
|
|
2153
|
+
} catch {
|
|
2154
|
+
return false;
|
|
2155
|
+
}
|
|
2156
|
+
if (now - m > LIVE_WRITE_MS) return false;
|
|
2157
|
+
return !(ourLastEnd && m <= ourLastEnd + 2e3);
|
|
2158
|
+
}
|
|
2159
|
+
function runClaude(bin, args, o) {
|
|
2160
|
+
return runHeadless(bin, args, { ...o, keep: o.keep ?? ((line) => line.includes('"type":"result"')) });
|
|
2161
|
+
}
|
|
2162
|
+
function parseResult(stdout) {
|
|
2163
|
+
const s = String(stdout || "").trim();
|
|
2164
|
+
if (!s) return null;
|
|
2165
|
+
const pick = (j) => {
|
|
2166
|
+
if (Array.isArray(j)) {
|
|
2167
|
+
for (let i = j.length - 1; i >= 0; i--) if (j[i]?.type === "result") return j[i];
|
|
2168
|
+
return null;
|
|
2169
|
+
}
|
|
2170
|
+
return j && typeof j === "object" ? j : null;
|
|
2171
|
+
};
|
|
2172
|
+
try {
|
|
2173
|
+
return pick(JSON.parse(s));
|
|
2174
|
+
} catch {
|
|
2175
|
+
}
|
|
2176
|
+
const lines = s.split("\n");
|
|
2177
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
2178
|
+
try {
|
|
2179
|
+
const j = pick(JSON.parse(lines[i]));
|
|
2180
|
+
if (j && (j.type === "result" || "result" in j)) return j;
|
|
2181
|
+
} catch {
|
|
2182
|
+
}
|
|
2183
|
+
}
|
|
2184
|
+
return null;
|
|
2185
|
+
}
|
|
2186
|
+
var isUuid = (s) => typeof s === "string" && UUID_RE.test(s);
|
|
2187
|
+
|
|
1514
2188
|
// ../core/src/adapters/codex.ts
|
|
1515
|
-
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync as
|
|
1516
|
-
import
|
|
2189
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync as readdirSync5, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "node:fs";
|
|
2190
|
+
import path8 from "node:path";
|
|
1517
2191
|
|
|
1518
2192
|
// ../core/src/adapters/codex-config.ts
|
|
1519
2193
|
var NOTIFY_LINE = /^[ \t]*notify[ \t]*=[ \t]*(\[.*\])[ \t]*(?:#.*)?(?=\r?$)/m;
|
|
@@ -1659,24 +2333,24 @@ var CODEX_EVENTS = [
|
|
|
1659
2333
|
];
|
|
1660
2334
|
var OPTS2 = { matcherFor: (e) => /Tool|Permission/.test(e) ? "" : void 0, timeout: 5 };
|
|
1661
2335
|
var eventsFor2 = (lean) => lean ? CODEX_EVENTS.filter((e) => e !== "PreToolUse") : [...CODEX_EVENTS];
|
|
1662
|
-
var codexHome = (env) => env.CODEX_HOME ||
|
|
1663
|
-
var hooksFile2 = (env) =>
|
|
1664
|
-
var configFile2 = (env) =>
|
|
1665
|
-
var sessionsDir = (env) =>
|
|
2336
|
+
var codexHome = (env) => env.CODEX_HOME || path8.join(homeDir(), ".codex");
|
|
2337
|
+
var hooksFile2 = (env) => path8.join(codexHome(env), "hooks.json");
|
|
2338
|
+
var configFile2 = (env) => path8.join(codexHome(env), "config.toml");
|
|
2339
|
+
var sessionsDir = (env) => path8.join(codexHome(env), "sessions");
|
|
1666
2340
|
var validTool3 = (n) => typeof n === "string" && /^[A-Za-z0-9_.:-]{1,120}$/.test(n);
|
|
1667
2341
|
var endReason2 = (r) => r === "clear" || r === "logout" || r === "exit" ? r : "other";
|
|
1668
2342
|
function installNotify(cmd, env) {
|
|
1669
2343
|
const file = configFile2(env);
|
|
1670
2344
|
let text = "";
|
|
1671
2345
|
try {
|
|
1672
|
-
text =
|
|
2346
|
+
text = readFileSync6(file, "utf8");
|
|
1673
2347
|
} catch {
|
|
1674
2348
|
}
|
|
1675
2349
|
const argv = cmd([NAME3, "notify"]).argv;
|
|
1676
2350
|
const line = `notify = ${JSON.stringify(argv)}`;
|
|
1677
2351
|
const n = readNotify(text);
|
|
1678
2352
|
const write = (next) => {
|
|
1679
|
-
mkdirSync3(
|
|
2353
|
+
mkdirSync3(path8.dirname(file), { recursive: true });
|
|
1680
2354
|
writeFileSync4(file, next);
|
|
1681
2355
|
};
|
|
1682
2356
|
if (n && !n.other) {
|
|
@@ -1715,7 +2389,7 @@ function uninstallNotify(env) {
|
|
|
1715
2389
|
const file = configFile2(env);
|
|
1716
2390
|
let text;
|
|
1717
2391
|
try {
|
|
1718
|
-
text =
|
|
2392
|
+
text = readFileSync6(file, "utf8");
|
|
1719
2393
|
} catch {
|
|
1720
2394
|
return { file, changed: false };
|
|
1721
2395
|
}
|
|
@@ -1738,12 +2412,12 @@ function findRollout(threadId, env = process.env) {
|
|
|
1738
2412
|
const walk = (dir, depth) => {
|
|
1739
2413
|
let names = [];
|
|
1740
2414
|
try {
|
|
1741
|
-
names =
|
|
2415
|
+
names = readdirSync5(dir);
|
|
1742
2416
|
} catch {
|
|
1743
2417
|
return;
|
|
1744
2418
|
}
|
|
1745
2419
|
for (const n of names.sort().reverse()) {
|
|
1746
|
-
const p =
|
|
2420
|
+
const p = path8.join(dir, n);
|
|
1747
2421
|
if (n.endsWith(`-${threadId}.jsonl`)) {
|
|
1748
2422
|
best = p;
|
|
1749
2423
|
return;
|
|
@@ -1787,7 +2461,7 @@ var codex = {
|
|
|
1787
2461
|
];
|
|
1788
2462
|
let text = "";
|
|
1789
2463
|
try {
|
|
1790
|
-
text =
|
|
2464
|
+
text = readFileSync6(configFile2(env), "utf8");
|
|
1791
2465
|
} catch {
|
|
1792
2466
|
}
|
|
1793
2467
|
const n = readNotify(text);
|
|
@@ -1914,7 +2588,7 @@ var codex = {
|
|
|
1914
2588
|
readTranscript(file, fromLine) {
|
|
1915
2589
|
let text = "";
|
|
1916
2590
|
try {
|
|
1917
|
-
text =
|
|
2591
|
+
text = readFileSync6(file, "utf8");
|
|
1918
2592
|
} catch {
|
|
1919
2593
|
return { turns: [], line: fromLine };
|
|
1920
2594
|
}
|
|
@@ -1928,7 +2602,7 @@ var codex = {
|
|
|
1928
2602
|
backfill(sinceMs, env = process.env) {
|
|
1929
2603
|
const rows2 = [];
|
|
1930
2604
|
for (const f of recentFiles(sessionsDir(env), 3, sinceMs, (n) => /^rollout-.*\.jsonl$/.test(n))) {
|
|
1931
|
-
const m = /([0-9a-f]{8}-[0-9a-f-]{27,})\.jsonl$/i.exec(
|
|
2605
|
+
const m = /([0-9a-f]{8}-[0-9a-f-]{27,})\.jsonl$/i.exec(path8.basename(f));
|
|
1932
2606
|
if (!m?.[1]) continue;
|
|
1933
2607
|
if (codexSubagent(rolloutMeta(f))) continue;
|
|
1934
2608
|
const facts = codexFacts(f);
|
|
@@ -2015,10 +2689,10 @@ function codexFacts(file) {
|
|
|
2015
2689
|
// ../core/src/adapters/devin.ts
|
|
2016
2690
|
import { createHash as createHash2 } from "node:crypto";
|
|
2017
2691
|
import { existsSync as existsSync6, readlinkSync } from "node:fs";
|
|
2018
|
-
import
|
|
2692
|
+
import path9 from "node:path";
|
|
2019
2693
|
|
|
2020
2694
|
// ../core/src/readers/sqlite.ts
|
|
2021
|
-
import { execFileSync as
|
|
2695
|
+
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
2022
2696
|
import { existsSync as existsSync5 } from "node:fs";
|
|
2023
2697
|
import { createRequire } from "node:module";
|
|
2024
2698
|
function quiet(fn) {
|
|
@@ -2098,7 +2772,7 @@ db.close();
|
|
|
2098
2772
|
process.stdout.write(JSON.stringify(out));`;
|
|
2099
2773
|
function viaChild(file, sql, params, o) {
|
|
2100
2774
|
try {
|
|
2101
|
-
const out =
|
|
2775
|
+
const out = execFileSync4(
|
|
2102
2776
|
process.execPath,
|
|
2103
2777
|
["--experimental-sqlite", "--no-warnings", "-e", CHILD, file, sql, JSON.stringify(params), String(o.timeoutMs)],
|
|
2104
2778
|
{
|
|
@@ -2142,7 +2816,7 @@ function viaCli(file, sql, params, o) {
|
|
|
2142
2816
|
const text = inlineParams(sql, params);
|
|
2143
2817
|
if (!text) return null;
|
|
2144
2818
|
try {
|
|
2145
|
-
const out =
|
|
2819
|
+
const out = execFileSync4("sqlite3", ["-readonly", "-json", file, text], {
|
|
2146
2820
|
encoding: "utf8",
|
|
2147
2821
|
timeout: o.timeoutMs + 3e3,
|
|
2148
2822
|
stdio: ["ignore", "pipe", "ignore"],
|
|
@@ -2199,11 +2873,11 @@ function foreignEvent(file) {
|
|
|
2199
2873
|
return Object.keys(hooks).find((k) => !ok.includes(k));
|
|
2200
2874
|
}
|
|
2201
2875
|
var homeOf = (env) => env.HOME || homeDir();
|
|
2202
|
-
var appData = (env) => env.APPDATA ||
|
|
2203
|
-
var devinConfigDir = (env = process.env) => process.platform === "win32" ?
|
|
2204
|
-
var configFile3 = (env) =>
|
|
2205
|
-
var devinDataDir = (env = process.env) => process.platform === "win32" ?
|
|
2206
|
-
var sessionsDb = (env) =>
|
|
2876
|
+
var appData = (env) => env.APPDATA || path9.join(homeOf(env), "AppData", "Roaming");
|
|
2877
|
+
var devinConfigDir = (env = process.env) => process.platform === "win32" ? path9.join(appData(env), "devin") : path9.join(env.XDG_CONFIG_HOME || path9.join(homeOf(env), ".config"), "devin");
|
|
2878
|
+
var configFile3 = (env) => path9.join(devinConfigDir(env), "config.json");
|
|
2879
|
+
var devinDataDir = (env = process.env) => process.platform === "win32" ? path9.join(appData(env), "devin", "cli") : path9.join(env.XDG_DATA_HOME || path9.join(homeOf(env), ".local", "share"), "devin", "cli");
|
|
2880
|
+
var sessionsDb = (env) => path9.join(devinDataDir(env), "sessions.db");
|
|
2207
2881
|
var SESSION_ID = /^[A-Za-z0-9._:-]{1,128}$/;
|
|
2208
2882
|
var validTool4 = (n) => typeof n === "string" && /^[A-Za-z0-9_.:-]{1,120}$/.test(n);
|
|
2209
2883
|
var endReason3 = (r) => r === "clear" || r === "logout" || r === "exit" ? r : "other";
|
|
@@ -2375,7 +3049,7 @@ var BACKFILL_SQL = `SELECT s.id, ${SESSION_FIELDS} FROM sessions s
|
|
|
2375
3049
|
function sessionFacts(row) {
|
|
2376
3050
|
const out = {};
|
|
2377
3051
|
if (typeof row.model === "string" && row.model) out.model = row.model;
|
|
2378
|
-
if (typeof row.working_directory === "string" &&
|
|
3052
|
+
if (typeof row.working_directory === "string" && path9.isAbsolute(row.working_directory))
|
|
2379
3053
|
out.cwd = row.working_directory;
|
|
2380
3054
|
const started = Number(row.created_at);
|
|
2381
3055
|
const last = Number(row.last_activity_at);
|
|
@@ -2420,8 +3094,8 @@ function readDevinChain(db, session, fromLine) {
|
|
|
2420
3094
|
}
|
|
2421
3095
|
function devinVersion(env = process.env) {
|
|
2422
3096
|
try {
|
|
2423
|
-
const target = readlinkSync(
|
|
2424
|
-
const name =
|
|
3097
|
+
const target = readlinkSync(path9.join(devinDataDir(env), "_versions", "current"));
|
|
3098
|
+
const name = path9.basename(target);
|
|
2425
3099
|
return /^[0-9][0-9A-Za-z.+-]{0,63}$/.test(name) ? name : void 0;
|
|
2426
3100
|
} catch {
|
|
2427
3101
|
return void 0;
|
|
@@ -2434,9 +3108,9 @@ function devinAccount(env = process.env) {
|
|
|
2434
3108
|
}
|
|
2435
3109
|
|
|
2436
3110
|
// ../core/src/adapters/gemini-cli.ts
|
|
2437
|
-
import { execFileSync as
|
|
2438
|
-
import { existsSync as existsSync7, readFileSync as
|
|
2439
|
-
import
|
|
3111
|
+
import { execFileSync as execFileSync5 } from "node:child_process";
|
|
3112
|
+
import { existsSync as existsSync7, readFileSync as readFileSync7 } from "node:fs";
|
|
3113
|
+
import path10 from "node:path";
|
|
2440
3114
|
var NAME5 = "gemini-cli";
|
|
2441
3115
|
var GEMINI_EVENTS = [
|
|
2442
3116
|
"SessionStart",
|
|
@@ -2449,16 +3123,16 @@ var GEMINI_EVENTS = [
|
|
|
2449
3123
|
"SessionEnd"
|
|
2450
3124
|
];
|
|
2451
3125
|
var OPTS4 = { matcherFor: (e) => /Tool/.test(e) ? ".*" : void 0, timeout: 5e3 };
|
|
2452
|
-
var geminiDir = (env) => env.GEMINI_CLI_HOME ||
|
|
2453
|
-
var settings = (env) =>
|
|
3126
|
+
var geminiDir = (env) => env.GEMINI_CLI_HOME || path10.join(homeDir(), ".gemini");
|
|
3127
|
+
var settings = (env) => path10.join(geminiDir(env), "settings.json");
|
|
2454
3128
|
var validTool5 = (n) => typeof n === "string" && /^[A-Za-z0-9_.:-]{1,120}$/.test(n);
|
|
2455
3129
|
var eventsFor4 = (lean) => lean ? GEMINI_EVENTS.filter((e) => e !== "BeforeTool") : [...GEMINI_EVENTS];
|
|
2456
3130
|
function geminiVersion(platform = process.platform, env = process.env) {
|
|
2457
3131
|
const win = platform === "win32";
|
|
2458
|
-
const bin = win ? findBinary({ names: ["gemini.exe", "gemini.cmd"], dirs: [
|
|
3132
|
+
const bin = win ? findBinary({ names: ["gemini.exe", "gemini.cmd"], dirs: [path10.dirname(process.execPath)], env }) : "gemini";
|
|
2459
3133
|
if (!bin) throw new Error("no gemini on PATH");
|
|
2460
3134
|
const c = probe(bin, ["--version"]);
|
|
2461
|
-
return
|
|
3135
|
+
return execFileSync5(c.file, c.args, {
|
|
2462
3136
|
encoding: "utf8",
|
|
2463
3137
|
timeout: 4e3,
|
|
2464
3138
|
stdio: ["ignore", "pipe", "ignore"],
|
|
@@ -2491,7 +3165,7 @@ var geminiCli = {
|
|
|
2491
3165
|
name: NAME5,
|
|
2492
3166
|
events: GEMINI_EVENTS,
|
|
2493
3167
|
// ~/.gemini also belongs to Antigravity; Gemini CLI is here when its settings or its chat logs exist.
|
|
2494
|
-
detect: (env = process.env) => existsSync7(settings(env)) || existsSync7(
|
|
3168
|
+
detect: (env = process.env) => existsSync7(settings(env)) || existsSync7(path10.join(geminiDir(env), "tmp")),
|
|
2495
3169
|
configFiles: (env = process.env) => [settings(env)],
|
|
2496
3170
|
install: (cmd, env = process.env, opts = {}) => {
|
|
2497
3171
|
const r = installClaudeShaped(settings(env), NAME5, eventsFor4(opts.lean), cmd, OPTS4);
|
|
@@ -2589,7 +3263,7 @@ var geminiCli = {
|
|
|
2589
3263
|
readTranscript(file, fromLine) {
|
|
2590
3264
|
let text = "";
|
|
2591
3265
|
try {
|
|
2592
|
-
text =
|
|
3266
|
+
text = readFileSync7(file, "utf8");
|
|
2593
3267
|
} catch {
|
|
2594
3268
|
return { turns: [], line: fromLine };
|
|
2595
3269
|
}
|
|
@@ -2629,12 +3303,12 @@ var ADAPTERS = [claudeCode, codex, geminiCli, antigravity, devin];
|
|
|
2629
3303
|
var adapterByName = (name) => ADAPTERS.find((a) => a.name === name);
|
|
2630
3304
|
|
|
2631
3305
|
// ../core/src/config.ts
|
|
2632
|
-
import { chmodSync, existsSync as existsSync8, mkdirSync as mkdirSync4, readFileSync as
|
|
2633
|
-
import
|
|
3306
|
+
import { chmodSync, existsSync as existsSync8, mkdirSync as mkdirSync4, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "node:fs";
|
|
3307
|
+
import path11 from "node:path";
|
|
2634
3308
|
var DEFAULT_CONFIG = { sinks: [], harnesses: {}, update_check: true, keep_days: 30 };
|
|
2635
3309
|
function readConfig(file = configFile()) {
|
|
2636
3310
|
try {
|
|
2637
|
-
const j = JSON.parse(
|
|
3311
|
+
const j = JSON.parse(readFileSync8(file, "utf8"));
|
|
2638
3312
|
return {
|
|
2639
3313
|
...DEFAULT_CONFIG,
|
|
2640
3314
|
...j,
|
|
@@ -2646,7 +3320,7 @@ function readConfig(file = configFile()) {
|
|
|
2646
3320
|
}
|
|
2647
3321
|
}
|
|
2648
3322
|
function writeConfig(c, file = configFile()) {
|
|
2649
|
-
mkdirSync4(
|
|
3323
|
+
mkdirSync4(path11.dirname(file), { recursive: true, mode: 448 });
|
|
2650
3324
|
writeFileSync5(file, `${JSON.stringify(c, null, 2)}
|
|
2651
3325
|
`, { mode: 384 });
|
|
2652
3326
|
if (process.platform !== "win32" && existsSync8(file)) {
|
|
@@ -2710,38 +3384,22 @@ function makeEvent(a, ctx) {
|
|
|
2710
3384
|
};
|
|
2711
3385
|
}
|
|
2712
3386
|
|
|
2713
|
-
// ../core/src/launched-by.ts
|
|
2714
|
-
var LAUNCHED_BY_ENV = "SESSIONPIPE_LAUNCHED_BY";
|
|
2715
|
-
var LAUNCHED_BY = /^[a-z][a-z0-9-]{0,39}:[A-Za-z0-9._:-]{1,128}$/;
|
|
2716
|
-
function launchedBy(env) {
|
|
2717
|
-
const v = env[LAUNCHED_BY_ENV]?.trim();
|
|
2718
|
-
return v && LAUNCHED_BY.test(v) ? v : void 0;
|
|
2719
|
-
}
|
|
2720
|
-
function readHook(adapter, input) {
|
|
2721
|
-
const r = adapter.fromHook(input);
|
|
2722
|
-
if (r && !r.session.parent_id) {
|
|
2723
|
-
const launcher = launchedBy(input.env);
|
|
2724
|
-
if (launcher && launcher !== `${adapter.name}:${r.session.id}`) r.session.launched_by = launcher;
|
|
2725
|
-
}
|
|
2726
|
-
return r;
|
|
2727
|
-
}
|
|
2728
|
-
|
|
2729
3387
|
// ../core/src/outbox.ts
|
|
2730
3388
|
import {
|
|
2731
3389
|
appendFileSync,
|
|
2732
|
-
closeSync as
|
|
3390
|
+
closeSync as closeSync3,
|
|
2733
3391
|
existsSync as existsSync9,
|
|
2734
3392
|
mkdirSync as mkdirSync5,
|
|
2735
|
-
openSync as
|
|
2736
|
-
readdirSync as
|
|
2737
|
-
readFileSync as
|
|
2738
|
-
readSync as
|
|
3393
|
+
openSync as openSync3,
|
|
3394
|
+
readdirSync as readdirSync6,
|
|
3395
|
+
readFileSync as readFileSync9,
|
|
3396
|
+
readSync as readSync3,
|
|
2739
3397
|
renameSync,
|
|
2740
|
-
statSync as
|
|
3398
|
+
statSync as statSync6,
|
|
2741
3399
|
unlinkSync as unlinkSync3,
|
|
2742
3400
|
writeFileSync as writeFileSync6
|
|
2743
3401
|
} from "node:fs";
|
|
2744
|
-
import
|
|
3402
|
+
import path12 from "node:path";
|
|
2745
3403
|
var READ_CHUNK = 1024 * 1024;
|
|
2746
3404
|
var READ_MAX = 8 * 1024 * 1024;
|
|
2747
3405
|
var Outbox = class {
|
|
@@ -2750,13 +3408,13 @@ var Outbox = class {
|
|
|
2750
3408
|
}
|
|
2751
3409
|
root;
|
|
2752
3410
|
dir(harness) {
|
|
2753
|
-
return
|
|
3411
|
+
return path12.join(this.root, "outbox", safeId(harness));
|
|
2754
3412
|
}
|
|
2755
3413
|
file(ref) {
|
|
2756
|
-
return
|
|
3414
|
+
return path12.join(this.dir(ref.harness), `${safeId(ref.session)}.jsonl`);
|
|
2757
3415
|
}
|
|
2758
3416
|
seqFile(ref) {
|
|
2759
|
-
return
|
|
3417
|
+
return path12.join(this.dir(ref.harness), `${safeId(ref.session)}.seq`);
|
|
2760
3418
|
}
|
|
2761
3419
|
/** The next seq for a session, persisted (survives a hibernating machine and a
|
|
2762
3420
|
* restart; an in-memory counter would restart at 0 and collide). */
|
|
@@ -2764,19 +3422,19 @@ var Outbox = class {
|
|
|
2764
3422
|
const f = this.seqFile(ref);
|
|
2765
3423
|
let n = -1;
|
|
2766
3424
|
try {
|
|
2767
|
-
const t =
|
|
3425
|
+
const t = readFileSync9(f, "utf8").trim();
|
|
2768
3426
|
if (/^\d+$/.test(t)) n = Number(t);
|
|
2769
3427
|
} catch {
|
|
2770
3428
|
}
|
|
2771
3429
|
if (n < 0) n = this.lastSeqInFile(ref) + 1;
|
|
2772
|
-
mkdirSync5(
|
|
3430
|
+
mkdirSync5(path12.dirname(f), { recursive: true, mode: 448 });
|
|
2773
3431
|
writeFileSync6(f, String(n + 1), { mode: 384 });
|
|
2774
3432
|
return n;
|
|
2775
3433
|
}
|
|
2776
3434
|
lastSeqInFile(ref) {
|
|
2777
3435
|
const f = this.file(ref);
|
|
2778
3436
|
if (!existsSync9(f)) return -1;
|
|
2779
|
-
const lines =
|
|
3437
|
+
const lines = readFileSync9(f, "utf8").trim().split("\n");
|
|
2780
3438
|
for (let i = lines.length - 1; i >= 0; i--) {
|
|
2781
3439
|
try {
|
|
2782
3440
|
const j = JSON.parse(lines[i]);
|
|
@@ -2795,19 +3453,19 @@ var Outbox = class {
|
|
|
2795
3453
|
/** Every session file, newest first. */
|
|
2796
3454
|
sessions() {
|
|
2797
3455
|
const out = [];
|
|
2798
|
-
const base =
|
|
3456
|
+
const base = path12.join(this.root, "outbox");
|
|
2799
3457
|
if (!existsSync9(base)) return [];
|
|
2800
|
-
for (const h of
|
|
2801
|
-
const d =
|
|
3458
|
+
for (const h of readdirSync6(base)) {
|
|
3459
|
+
const d = path12.join(base, h);
|
|
2802
3460
|
let names = [];
|
|
2803
3461
|
try {
|
|
2804
|
-
names =
|
|
3462
|
+
names = readdirSync6(d).filter((n) => n.endsWith(".jsonl"));
|
|
2805
3463
|
} catch {
|
|
2806
3464
|
continue;
|
|
2807
3465
|
}
|
|
2808
3466
|
for (const n of names) {
|
|
2809
3467
|
try {
|
|
2810
|
-
out.push({ ref: { harness: h, session: n.slice(0, -6) }, mtime:
|
|
3468
|
+
out.push({ ref: { harness: h, session: n.slice(0, -6) }, mtime: statSync6(path12.join(d, n)).mtimeMs });
|
|
2811
3469
|
} catch {
|
|
2812
3470
|
}
|
|
2813
3471
|
}
|
|
@@ -2823,16 +3481,16 @@ var Outbox = class {
|
|
|
2823
3481
|
read(ref, from, maxEvents = 500) {
|
|
2824
3482
|
const f = this.file(ref);
|
|
2825
3483
|
if (!existsSync9(f)) return { events: [], offset: from };
|
|
2826
|
-
const size =
|
|
3484
|
+
const size = statSync6(f).size;
|
|
2827
3485
|
if (from >= size) return { events: [], offset: from };
|
|
2828
|
-
const fd =
|
|
3486
|
+
const fd = openSync3(f, "r");
|
|
2829
3487
|
try {
|
|
2830
3488
|
const events = [];
|
|
2831
3489
|
let offset = from;
|
|
2832
3490
|
let chunk = READ_CHUNK;
|
|
2833
3491
|
while (events.length < maxEvents && offset < size) {
|
|
2834
3492
|
const buf = Buffer.alloc(Math.min(size - offset, chunk));
|
|
2835
|
-
const n =
|
|
3493
|
+
const n = readSync3(fd, buf, 0, buf.length, offset);
|
|
2836
3494
|
const text = buf.subarray(0, n).toString("utf8");
|
|
2837
3495
|
let pos = 0;
|
|
2838
3496
|
let used = 0;
|
|
@@ -2855,7 +3513,7 @@ var Outbox = class {
|
|
|
2855
3513
|
}
|
|
2856
3514
|
return { events, offset };
|
|
2857
3515
|
} finally {
|
|
2858
|
-
|
|
3516
|
+
closeSync3(fd);
|
|
2859
3517
|
}
|
|
2860
3518
|
}
|
|
2861
3519
|
/** Files older than `days` are pruned; the cursor files with them. */
|
|
@@ -2865,7 +3523,7 @@ var Outbox = class {
|
|
|
2865
3523
|
for (const ref of this.sessions()) {
|
|
2866
3524
|
const f = this.file(ref);
|
|
2867
3525
|
try {
|
|
2868
|
-
if (
|
|
3526
|
+
if (statSync6(f).mtimeMs < cutoff) {
|
|
2869
3527
|
unlinkSync3(f);
|
|
2870
3528
|
try {
|
|
2871
3529
|
unlinkSync3(this.seqFile(ref));
|
|
@@ -2895,18 +3553,18 @@ var Cursors = class {
|
|
|
2895
3553
|
root;
|
|
2896
3554
|
sink;
|
|
2897
3555
|
file() {
|
|
2898
|
-
return
|
|
3556
|
+
return path12.join(this.root, "cursors", `${safeId(this.sink)}.json`);
|
|
2899
3557
|
}
|
|
2900
3558
|
read() {
|
|
2901
3559
|
try {
|
|
2902
|
-
return JSON.parse(
|
|
3560
|
+
return JSON.parse(readFileSync9(this.file(), "utf8"));
|
|
2903
3561
|
} catch {
|
|
2904
3562
|
return {};
|
|
2905
3563
|
}
|
|
2906
3564
|
}
|
|
2907
3565
|
write(c) {
|
|
2908
3566
|
const f = this.file();
|
|
2909
|
-
mkdirSync5(
|
|
3567
|
+
mkdirSync5(path12.dirname(f), { recursive: true, mode: 448 });
|
|
2910
3568
|
const tmp = `${f}.${process.pid}.tmp`;
|
|
2911
3569
|
writeFileSync6(tmp, JSON.stringify(c), { mode: 384 });
|
|
2912
3570
|
renameSync(tmp, f);
|
|
@@ -2919,11 +3577,11 @@ function takeLock(root, ref, staleMs = 5 * 6e4) {
|
|
|
2919
3577
|
return takeNamedLock(root, `${safeId(ref.harness)}-${safeId(ref.session)}`, staleMs);
|
|
2920
3578
|
}
|
|
2921
3579
|
function takeNamedLock(root, name, staleMs = 5 * 6e4) {
|
|
2922
|
-
const dir =
|
|
3580
|
+
const dir = path12.join(root, "locks");
|
|
2923
3581
|
mkdirSync5(dir, { recursive: true, mode: 448 });
|
|
2924
|
-
const f =
|
|
3582
|
+
const f = path12.join(dir, `${name}.lock`);
|
|
2925
3583
|
try {
|
|
2926
|
-
if (Date.now() -
|
|
3584
|
+
if (Date.now() - statSync6(f).mtimeMs > staleMs || !processAlive(Number(readFileSync9(f, "utf8")))) unlinkSync3(f);
|
|
2927
3585
|
else return null;
|
|
2928
3586
|
} catch {
|
|
2929
3587
|
}
|
|
@@ -3161,6 +3819,8 @@ var TYPE_TIER = {
|
|
|
3161
3819
|
"subagent.ended": 1,
|
|
3162
3820
|
"context.compacted": 1,
|
|
3163
3821
|
"limits.observed": 1,
|
|
3822
|
+
"delegation.started": 0,
|
|
3823
|
+
"delegation.ended": 0,
|
|
3164
3824
|
native: 3
|
|
3165
3825
|
};
|
|
3166
3826
|
var DATA_ALLOW = {
|
|
@@ -3171,8 +3831,8 @@ var DATA_ALLOW = {
|
|
|
3171
3831
|
"session.forgotten": { 0: [] },
|
|
3172
3832
|
"turn.started": { 0: ["turn_id", "submitted_by", "injected"], 1: ["prompt_chars"] },
|
|
3173
3833
|
"turn.ended": { 0: ["turn_id", "reason", "ms", "background"], 1: ["error"] },
|
|
3174
|
-
"turn.transcript": { 2: ["turn", "user", "assistant", "at"] },
|
|
3175
|
-
"turn.progress": { 2: ["turn", "user", "assistant", "at"] },
|
|
3834
|
+
"turn.transcript": { 2: ["turn", "user", "assistant", "at", "injected"] },
|
|
3835
|
+
"turn.progress": { 2: ["turn", "user", "assistant", "at", "injected"] },
|
|
3176
3836
|
"tool.started": { 1: ["tool", "call_id", "turn_id", "agent_id"], 3: ["input"] },
|
|
3177
3837
|
"tool.ended": { 1: ["tool", "call_id", "turn_id", "agent_id", "ms", "ok", "error"], 3: ["input", "output"] },
|
|
3178
3838
|
"files.changed": { 1: ["paths"] },
|
|
@@ -3181,7 +3841,9 @@ var DATA_ALLOW = {
|
|
|
3181
3841
|
"subagent.started": { 1: ["agent_id", "agent_type"] },
|
|
3182
3842
|
"subagent.ended": { 1: ["agent_id", "agent_type"] },
|
|
3183
3843
|
"context.compacted": { 1: ["trigger"] },
|
|
3184
|
-
"limits.observed": { 1: ["windows", "plan", "account_id"] }
|
|
3844
|
+
"limits.observed": { 1: ["windows", "plan", "account_id"] },
|
|
3845
|
+
"delegation.started": { 0: ["ask_id", "via", "harness", "to"], 1: ["text"] },
|
|
3846
|
+
"delegation.ended": { 0: ["ask_id", "to", "ok", "ms", "error", "relayed"], 2: ["reply"] }
|
|
3185
3847
|
};
|
|
3186
3848
|
var SESSION_ALLOW = [
|
|
3187
3849
|
"id",
|
|
@@ -3197,7 +3859,8 @@ var SESSION_ALLOW = [
|
|
|
3197
3859
|
"account_id",
|
|
3198
3860
|
"attended",
|
|
3199
3861
|
"parent_id",
|
|
3200
|
-
"launched_by"
|
|
3862
|
+
"launched_by",
|
|
3863
|
+
"delegated_by"
|
|
3201
3864
|
];
|
|
3202
3865
|
var MAX_SIDE = 2e4;
|
|
3203
3866
|
var MAX_IO = 64 * 1024;
|
|
@@ -3236,6 +3899,8 @@ function filterEvent(event, tier, pii, opts = {}) {
|
|
|
3236
3899
|
if (data.input !== void 0) data.input = cap(data.input, MAX_IO);
|
|
3237
3900
|
if (data.output !== void 0) data.output = cap(data.output, MAX_IO);
|
|
3238
3901
|
if (typeof data.message === "string") data.message = cap(data.message, 200);
|
|
3902
|
+
if (event.type === "delegation.started" && typeof data.text === "string") data.text = cap(data.text, 200);
|
|
3903
|
+
if (event.type === "delegation.ended" && typeof data.reply === "string") data.reply = cap(data.reply, MAX_SIDE);
|
|
3239
3904
|
const rulesets = [];
|
|
3240
3905
|
let out = { ...event, tier, session, data, privacy: { rulesets, pii } };
|
|
3241
3906
|
if (tier > 0) {
|
|
@@ -3263,7 +3928,7 @@ function shortId(harness, id) {
|
|
|
3263
3928
|
|
|
3264
3929
|
// ../core/src/sinks/file.ts
|
|
3265
3930
|
import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync6 } from "node:fs";
|
|
3266
|
-
import
|
|
3931
|
+
import path13 from "node:path";
|
|
3267
3932
|
var FileSink = class {
|
|
3268
3933
|
constructor(name, file, tier, pii) {
|
|
3269
3934
|
this.name = name;
|
|
@@ -3277,7 +3942,7 @@ var FileSink = class {
|
|
|
3277
3942
|
pii;
|
|
3278
3943
|
async send(events) {
|
|
3279
3944
|
try {
|
|
3280
|
-
mkdirSync6(
|
|
3945
|
+
mkdirSync6(path13.dirname(this.file), { recursive: true });
|
|
3281
3946
|
appendFileSync2(this.file, `${events.map((e) => JSON.stringify(e)).join("\n")}
|
|
3282
3947
|
`);
|
|
3283
3948
|
return { ok: true, acknowledged: events.length, accepted: events.length, duplicates: 0, rejected: [] };
|
|
@@ -3315,19 +3980,19 @@ import {
|
|
|
3315
3980
|
appendFileSync as appendFileSync3,
|
|
3316
3981
|
existsSync as existsSync10,
|
|
3317
3982
|
mkdirSync as mkdirSync8,
|
|
3318
|
-
readdirSync as
|
|
3319
|
-
readFileSync as
|
|
3983
|
+
readdirSync as readdirSync7,
|
|
3984
|
+
readFileSync as readFileSync11,
|
|
3320
3985
|
renameSync as renameSync3,
|
|
3321
|
-
statSync as
|
|
3986
|
+
statSync as statSync7,
|
|
3322
3987
|
unlinkSync as unlinkSync4,
|
|
3323
3988
|
writeFileSync as writeFileSync8
|
|
3324
3989
|
} from "node:fs";
|
|
3325
|
-
import
|
|
3326
|
-
import
|
|
3990
|
+
import os4 from "node:os";
|
|
3991
|
+
import path16 from "node:path";
|
|
3327
3992
|
|
|
3328
3993
|
// src/control/aliases.ts
|
|
3329
|
-
import { chmodSync as chmodSync2, mkdirSync as mkdirSync7, readFileSync as
|
|
3330
|
-
import
|
|
3994
|
+
import { chmodSync as chmodSync2, mkdirSync as mkdirSync7, readFileSync as readFileSync10, renameSync as renameSync2, writeFileSync as writeFileSync7 } from "node:fs";
|
|
3995
|
+
import path14 from "node:path";
|
|
3331
3996
|
|
|
3332
3997
|
// src/secrets.ts
|
|
3333
3998
|
import { spawnSync } from "node:child_process";
|
|
@@ -3371,10 +4036,10 @@ function startOptionsOf(v) {
|
|
|
3371
4036
|
}
|
|
3372
4037
|
|
|
3373
4038
|
// src/control/aliases.ts
|
|
3374
|
-
var aliasFile = (controlDir) =>
|
|
4039
|
+
var aliasFile = (controlDir) => path14.join(controlDir, "aliases.json");
|
|
3375
4040
|
function readAliases(file) {
|
|
3376
4041
|
try {
|
|
3377
|
-
const j = JSON.parse(
|
|
4042
|
+
const j = JSON.parse(readFileSync10(file, "utf8"));
|
|
3378
4043
|
if (!j || typeof j !== "object" || Array.isArray(j)) return {};
|
|
3379
4044
|
const out = {};
|
|
3380
4045
|
for (const [k, v] of Object.entries(j)) {
|
|
@@ -3395,12 +4060,12 @@ function receiverIdOf(all, harness, id) {
|
|
|
3395
4060
|
|
|
3396
4061
|
// src/control/local.ts
|
|
3397
4062
|
import net from "node:net";
|
|
3398
|
-
import
|
|
3399
|
-
import
|
|
4063
|
+
import os3 from "node:os";
|
|
4064
|
+
import path15 from "node:path";
|
|
3400
4065
|
function socketPath(state, env = process.env) {
|
|
3401
4066
|
if (env.SESSIONPIPE_SOCKET) return env.SESSIONPIPE_SOCKET;
|
|
3402
|
-
if (process.platform === "win32") return `\\\\.\\pipe\\sessionpipe-control-${
|
|
3403
|
-
return
|
|
4067
|
+
if (process.platform === "win32") return `\\\\.\\pipe\\sessionpipe-control-${os3.userInfo().username}`;
|
|
4068
|
+
return path15.join(state, "control.sock");
|
|
3404
4069
|
}
|
|
3405
4070
|
function ask(sock, req, timeoutMs) {
|
|
3406
4071
|
return new Promise((resolve) => {
|
|
@@ -3531,6 +4196,11 @@ var HttpSink = class {
|
|
|
3531
4196
|
async function tellDaemon(state, job, session, cwd, transcript) {
|
|
3532
4197
|
const sock = socketPath(state);
|
|
3533
4198
|
if (process.platform !== "win32" && !existsSync10(sock)) return;
|
|
4199
|
+
let mode = null;
|
|
4200
|
+
try {
|
|
4201
|
+
mode = claude_code_control_exports.hookMode(JSON.parse(job.stdin));
|
|
4202
|
+
} catch {
|
|
4203
|
+
}
|
|
3534
4204
|
await ask(
|
|
3535
4205
|
sock,
|
|
3536
4206
|
{
|
|
@@ -3538,7 +4208,8 @@ async function tellDaemon(state, job, session, cwd, transcript) {
|
|
|
3538
4208
|
session: `${job.harness}:${session}`,
|
|
3539
4209
|
event: job.event,
|
|
3540
4210
|
...cwd ? { cwd } : {},
|
|
3541
|
-
...transcript ? { transcript } : {}
|
|
4211
|
+
...transcript ? { transcript } : {},
|
|
4212
|
+
...mode ? { mode } : {}
|
|
3542
4213
|
},
|
|
3543
4214
|
300
|
|
3544
4215
|
);
|
|
@@ -3547,7 +4218,7 @@ var MAX_TRIES = 10;
|
|
|
3547
4218
|
function enqueueJob(state, job) {
|
|
3548
4219
|
const jobs = jobsDir(state);
|
|
3549
4220
|
mkdirSync8(jobs, { recursive: true, mode: 448 });
|
|
3550
|
-
const f =
|
|
4221
|
+
const f = path16.join(jobs, `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}.json`);
|
|
3551
4222
|
writeFileSync8(f, JSON.stringify(job), { mode: 384 });
|
|
3552
4223
|
return f;
|
|
3553
4224
|
}
|
|
@@ -3559,7 +4230,7 @@ function claimJob(file) {
|
|
|
3559
4230
|
return null;
|
|
3560
4231
|
}
|
|
3561
4232
|
try {
|
|
3562
|
-
return { job: JSON.parse(
|
|
4233
|
+
return { job: JSON.parse(readFileSync11(claimed, "utf8")), file: claimed };
|
|
3563
4234
|
} catch {
|
|
3564
4235
|
releaseJob(claimed);
|
|
3565
4236
|
return null;
|
|
@@ -3576,7 +4247,7 @@ async function sweepJobs(state, olderThanMs = 3e3, limit = 20, abandonedMs = 6e4
|
|
|
3576
4247
|
const jobs = jobsDir(state);
|
|
3577
4248
|
let names = [];
|
|
3578
4249
|
try {
|
|
3579
|
-
names =
|
|
4250
|
+
names = readdirSync7(jobs);
|
|
3580
4251
|
} catch {
|
|
3581
4252
|
return 0;
|
|
3582
4253
|
}
|
|
@@ -3584,19 +4255,19 @@ async function sweepJobs(state, olderThanMs = 3e3, limit = 20, abandonedMs = 6e4
|
|
|
3584
4255
|
for (const n of names) {
|
|
3585
4256
|
const m = /^(.*\.json)\.(\d+)\.running$/.exec(n);
|
|
3586
4257
|
if (!m) continue;
|
|
3587
|
-
const f =
|
|
4258
|
+
const f = path16.join(jobs, n);
|
|
3588
4259
|
try {
|
|
3589
|
-
if (processAlive(Number(m[2])) && Date.now() -
|
|
3590
|
-
renameSync3(f,
|
|
4260
|
+
if (processAlive(Number(m[2])) && Date.now() - statSync7(f).mtimeMs < abandonedMs) continue;
|
|
4261
|
+
renameSync3(f, path16.join(jobs, m[1]));
|
|
3591
4262
|
queued.push(m[1]);
|
|
3592
4263
|
} catch {
|
|
3593
4264
|
}
|
|
3594
4265
|
}
|
|
3595
4266
|
let ran = 0;
|
|
3596
4267
|
for (const n of queued.sort().slice(0, limit)) {
|
|
3597
|
-
const f =
|
|
4268
|
+
const f = path16.join(jobs, n);
|
|
3598
4269
|
try {
|
|
3599
|
-
if (Date.now() -
|
|
4270
|
+
if (Date.now() - statSync7(f).mtimeMs < olderThanMs) continue;
|
|
3600
4271
|
} catch {
|
|
3601
4272
|
continue;
|
|
3602
4273
|
}
|
|
@@ -3615,9 +4286,9 @@ async function sweepJobs(state, olderThanMs = 3e3, limit = 20, abandonedMs = 6e4
|
|
|
3615
4286
|
function log(state, line) {
|
|
3616
4287
|
try {
|
|
3617
4288
|
mkdirSync8(state, { recursive: true, mode: 448 });
|
|
3618
|
-
const f =
|
|
4289
|
+
const f = path16.join(state, "log");
|
|
3619
4290
|
try {
|
|
3620
|
-
if (
|
|
4291
|
+
if (statSync7(f).size > 1024 * 1024) renameSync3(f, `${f}.1`);
|
|
3621
4292
|
} catch {
|
|
3622
4293
|
}
|
|
3623
4294
|
appendFileSync3(f, `${(/* @__PURE__ */ new Date()).toISOString()} ${line}
|
|
@@ -3626,11 +4297,11 @@ function log(state, line) {
|
|
|
3626
4297
|
}
|
|
3627
4298
|
}
|
|
3628
4299
|
function factsState(state) {
|
|
3629
|
-
const file = (h, s) =>
|
|
4300
|
+
const file = (h, s) => path16.join(state, "facts", safeId(h), `${safeId(s)}.json`);
|
|
3630
4301
|
return {
|
|
3631
4302
|
get(h, s) {
|
|
3632
4303
|
try {
|
|
3633
|
-
return JSON.parse(
|
|
4304
|
+
return JSON.parse(readFileSync11(file(h, s), "utf8"));
|
|
3634
4305
|
} catch {
|
|
3635
4306
|
return {};
|
|
3636
4307
|
}
|
|
@@ -3639,7 +4310,7 @@ function factsState(state) {
|
|
|
3639
4310
|
const cur = this.get(h, s);
|
|
3640
4311
|
const next = { ...cur, ...patch };
|
|
3641
4312
|
if (JSON.stringify(next) === JSON.stringify(cur)) return;
|
|
3642
|
-
mkdirSync8(
|
|
4313
|
+
mkdirSync8(path16.dirname(file(h, s)), { recursive: true, mode: 448 });
|
|
3643
4314
|
writeFileSync8(file(h, s), JSON.stringify(next), { mode: 384 });
|
|
3644
4315
|
}
|
|
3645
4316
|
};
|
|
@@ -3656,7 +4327,7 @@ function buildSinks(sinks, o = {}) {
|
|
|
3656
4327
|
const tier = Math.min(s.tier, s.max_tier ?? 3);
|
|
3657
4328
|
if (s.url === "stdout") out.push(new StdoutSink(s.name, tier, !!s.pii));
|
|
3658
4329
|
else if (s.url.startsWith("file:"))
|
|
3659
|
-
out.push(new FileSink(s.name, s.url.slice(5).replace(/^~(?=\/|$)/,
|
|
4330
|
+
out.push(new FileSink(s.name, s.url.slice(5).replace(/^~(?=\/|$)/, os4.homedir()), tier, !!s.pii));
|
|
3660
4331
|
else if (/^https?:\/\//.test(s.url)) out.push(new HttpSink(s, tier));
|
|
3661
4332
|
}
|
|
3662
4333
|
return out;
|
|
@@ -3664,13 +4335,14 @@ function buildSinks(sinks, o = {}) {
|
|
|
3664
4335
|
var PROGRESS_MS = 2e4;
|
|
3665
4336
|
function openTurn(read, o) {
|
|
3666
4337
|
const last = read.turns[read.turns.length - 1];
|
|
3667
|
-
const fresh = (user, at) => ({
|
|
4338
|
+
const fresh = (user, at, injected) => ({
|
|
3668
4339
|
turn: o.base + read.turns.length,
|
|
3669
4340
|
user: user.slice(0, MAX_TURN_SIDE),
|
|
3670
4341
|
assistant: "",
|
|
3671
|
-
at
|
|
4342
|
+
at,
|
|
4343
|
+
...injected ? { injected: true } : {}
|
|
3672
4344
|
});
|
|
3673
|
-
if (read.open) return fresh(read.open.user, read.open.at);
|
|
4345
|
+
if (read.open) return fresh(read.open.user, read.open.at, read.open.injected);
|
|
3674
4346
|
const prompt = o.prompt?.trim();
|
|
3675
4347
|
if (prompt && !isInjected(prompt) && !(last && last.user.includes(prompt.slice(0, 200)))) return fresh(prompt, o.at);
|
|
3676
4348
|
if (!last) return null;
|
|
@@ -3678,6 +4350,7 @@ function openTurn(read, o) {
|
|
|
3678
4350
|
turn: o.base + read.turns.length - 1,
|
|
3679
4351
|
user: last.user.slice(0, MAX_TURN_SIDE),
|
|
3680
4352
|
assistant: capTail(last.assistant, MAX_TURN_SIDE),
|
|
4353
|
+
...last.injected ? { injected: true } : {},
|
|
3681
4354
|
at: last.at
|
|
3682
4355
|
};
|
|
3683
4356
|
}
|
|
@@ -3685,6 +4358,7 @@ var MAX_TURN_SIDE = 2e4;
|
|
|
3685
4358
|
var JOB_OWN_ENV = [
|
|
3686
4359
|
"CLAUDE_CODE_SESSION_ATTENDED",
|
|
3687
4360
|
"CLAUDE_CODE_HOST_SESSION_ID",
|
|
4361
|
+
"SESSIONPIPE_ASKED_BY",
|
|
3688
4362
|
"SESSIONPIPE_CONTROL_JOB",
|
|
3689
4363
|
"SESSIONPIPE_LAUNCHED_BY"
|
|
3690
4364
|
];
|
|
@@ -3712,7 +4386,7 @@ async function runJob(job, opts = {}) {
|
|
|
3712
4386
|
);
|
|
3713
4387
|
return { events: [], delivered: {} };
|
|
3714
4388
|
}
|
|
3715
|
-
const id = receiverIdOf(readAliases(aliasFile(
|
|
4389
|
+
const id = receiverIdOf(readAliases(aliasFile(path16.join(state, "control"))), job.harness, r.session.id) ?? r.session.id;
|
|
3716
4390
|
const claudeEnd = job.harness === "claude-code" && r.events.some((e) => e.type === "session.ended");
|
|
3717
4391
|
if (!claudeEnd) await tellDaemon(state, job, id, r.session.cwd, r.transcript);
|
|
3718
4392
|
const outbox = new Outbox(state);
|
|
@@ -3749,7 +4423,7 @@ async function runJob(job, opts = {}) {
|
|
|
3749
4423
|
learned = rest;
|
|
3750
4424
|
if (harness_version) facts.save(job.harness, id, { harness_version });
|
|
3751
4425
|
facts.save(job.harness, id, { session: { ...mem.session, ...learned } });
|
|
3752
|
-
if (CWD_FROM_HOOKS.has(job.harness) && r.session.cwd &&
|
|
4426
|
+
if (CWD_FROM_HOOKS.has(job.harness) && r.session.cwd && path16.isAbsolute(r.session.cwd))
|
|
3753
4427
|
facts.save(job.harness, id, { cwd: r.session.cwd });
|
|
3754
4428
|
}
|
|
3755
4429
|
const known = mem.session ?? {};
|
|
@@ -3762,10 +4436,11 @@ async function runJob(job, opts = {}) {
|
|
|
3762
4436
|
...learned.branch ? { branch: learned.branch } : {},
|
|
3763
4437
|
id,
|
|
3764
4438
|
seq: 0,
|
|
3765
|
-
machine: machineName(cfg,
|
|
4439
|
+
machine: machineName(cfg, os4.hostname()),
|
|
3766
4440
|
...cwdRaw ? { cwd: tilde(cwdRaw) } : {},
|
|
3767
4441
|
...r.session.model ? { model: r.session.model } : {},
|
|
3768
|
-
...r.session.launched_by ? { launched_by: r.session.launched_by } : {}
|
|
4442
|
+
...r.session.launched_by ? { launched_by: r.session.launched_by } : {},
|
|
4443
|
+
...r.session.delegated_by ? { delegated_by: r.session.delegated_by } : {}
|
|
3769
4444
|
};
|
|
3770
4445
|
const version = facts.get(job.harness, id).harness_version ?? void 0;
|
|
3771
4446
|
for (const a of r.events) {
|
|
@@ -3819,7 +4494,13 @@ async function runJob(job, opts = {}) {
|
|
|
3819
4494
|
{
|
|
3820
4495
|
type: "turn.transcript",
|
|
3821
4496
|
harnessEvent: job.event,
|
|
3822
|
-
data: {
|
|
4497
|
+
data: {
|
|
4498
|
+
turn: turnNo++,
|
|
4499
|
+
user: t.user,
|
|
4500
|
+
assistant: t.assistant,
|
|
4501
|
+
at: new Date(t.at).toISOString(),
|
|
4502
|
+
...t.injected ? { injected: true } : {}
|
|
4503
|
+
}
|
|
3823
4504
|
},
|
|
3824
4505
|
{
|
|
3825
4506
|
harness: { name: job.harness, ...version ? { version } : {} },
|
|
@@ -3865,7 +4546,7 @@ async function runJob(job, opts = {}) {
|
|
|
3865
4546
|
return { events, delivered };
|
|
3866
4547
|
}
|
|
3867
4548
|
async function drain(state, sinkConfigs, outbox, first) {
|
|
3868
|
-
const more =
|
|
4549
|
+
const more = path16.join(state, "locks", "flush.more");
|
|
3869
4550
|
let release = takeNamedLock(state, "flush");
|
|
3870
4551
|
if (!release) {
|
|
3871
4552
|
try {
|
|
@@ -3893,10 +4574,10 @@ async function drain(state, sinkConfigs, outbox, first) {
|
|
|
3893
4574
|
}
|
|
3894
4575
|
}
|
|
3895
4576
|
var BACKOFF_MS = [2e3, 1e4, 6e4, 5 * 6e4, 30 * 6e4];
|
|
3896
|
-
var backoffFile = (state, sink) =>
|
|
4577
|
+
var backoffFile = (state, sink) => path16.join(state, "backoff", `${safeId(sink)}.json`);
|
|
3897
4578
|
function readBackoff(state, sink) {
|
|
3898
4579
|
try {
|
|
3899
|
-
const b = JSON.parse(
|
|
4580
|
+
const b = JSON.parse(readFileSync11(backoffFile(state, sink), "utf8"));
|
|
3900
4581
|
return typeof b.until === "number" ? b : null;
|
|
3901
4582
|
} catch {
|
|
3902
4583
|
return null;
|
|
@@ -3909,7 +4590,7 @@ function noteFailure(state, sink, res, prev) {
|
|
|
3909
4590
|
const b = { until: Date.now() + wait, failures, why: `${res.status ?? ""} ${res.error ?? ""}`.trim() };
|
|
3910
4591
|
const f = backoffFile(state, sink);
|
|
3911
4592
|
try {
|
|
3912
|
-
mkdirSync8(
|
|
4593
|
+
mkdirSync8(path16.dirname(f), { recursive: true, mode: 448 });
|
|
3913
4594
|
writeFileSync8(f, JSON.stringify(b), { mode: 384 });
|
|
3914
4595
|
} catch {
|
|
3915
4596
|
}
|
|
@@ -4009,7 +4690,7 @@ async function flush(state, sinkConfigs, outbox, first, o = {}) {
|
|
|
4009
4690
|
const { events, offset: next } = outbox.read(ref, offset, room);
|
|
4010
4691
|
if (!events.length) break;
|
|
4011
4692
|
for (const e of events) {
|
|
4012
|
-
const f = filterEvent(e, sink.tier, sink.pii, { home:
|
|
4693
|
+
const f = filterEvent(e, sink.tier, sink.pii, { home: os4.homedir() });
|
|
4013
4694
|
if (f) batch.push(f);
|
|
4014
4695
|
}
|
|
4015
4696
|
offset = next;
|
|
@@ -4042,7 +4723,7 @@ function applySinkVerdict(name, res) {
|
|
|
4042
4723
|
} catch {
|
|
4043
4724
|
}
|
|
4044
4725
|
}
|
|
4045
|
-
var jobsDir = (state) =>
|
|
4726
|
+
var jobsDir = (state) => path16.join(state, "jobs");
|
|
4046
4727
|
|
|
4047
4728
|
// src/worker.ts
|
|
4048
4729
|
async function workerMain(argv) {
|
|
@@ -4054,7 +4735,7 @@ async function workerMain(argv) {
|
|
|
4054
4735
|
deadline.unref();
|
|
4055
4736
|
await runJob(claimed.job, { claimed: claimed.file }).then((r) => {
|
|
4056
4737
|
if (!r.requeued || (claimed.job.tries ?? 0) + 1 >= MAX_TRIES) return;
|
|
4057
|
-
const p =
|
|
4738
|
+
const p = spawn2(process.execPath, [argv[1], r.requeued], {
|
|
4058
4739
|
detached: true,
|
|
4059
4740
|
stdio: "ignore",
|
|
4060
4741
|
windowsHide: true
|