sessionpipe 0.11.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 +3193 -1220
- 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 +15 -5
- package/dist/hook.js.map +2 -2
- package/dist/index.js +865 -155
- 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 +874 -164
- package/dist/worker.js.map +4 -4
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
// sessionpipe 0.
|
|
1
|
+
// sessionpipe 0.13.0 · Apache-2.0 · https://sessionpipe.org
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __export = (target, all) => {
|
|
4
|
+
for (var name in all)
|
|
5
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
6
|
+
};
|
|
2
7
|
|
|
3
8
|
// src/http-sink.ts
|
|
4
9
|
import { createHash, createHmac } from "node:crypto";
|
|
@@ -100,15 +105,15 @@ import {
|
|
|
100
105
|
appendFileSync as appendFileSync3,
|
|
101
106
|
existsSync as existsSync10,
|
|
102
107
|
mkdirSync as mkdirSync8,
|
|
103
|
-
readdirSync as
|
|
104
|
-
readFileSync as
|
|
108
|
+
readdirSync as readdirSync7,
|
|
109
|
+
readFileSync as readFileSync11,
|
|
105
110
|
renameSync as renameSync3,
|
|
106
|
-
statSync as
|
|
111
|
+
statSync as statSync7,
|
|
107
112
|
unlinkSync as unlinkSync4,
|
|
108
113
|
writeFileSync as writeFileSync8
|
|
109
114
|
} from "node:fs";
|
|
110
|
-
import
|
|
111
|
-
import
|
|
115
|
+
import os4 from "node:os";
|
|
116
|
+
import path16 from "node:path";
|
|
112
117
|
|
|
113
118
|
// ../core/src/paths.ts
|
|
114
119
|
import os from "node:os";
|
|
@@ -288,7 +293,14 @@ ${m.text}`;
|
|
|
288
293
|
continue;
|
|
289
294
|
}
|
|
290
295
|
if (cur) turns.push(cur);
|
|
291
|
-
cur = {
|
|
296
|
+
cur = {
|
|
297
|
+
user: m.text,
|
|
298
|
+
assistant: "",
|
|
299
|
+
at: m.at,
|
|
300
|
+
startLine: m.line,
|
|
301
|
+
endLine: m.line,
|
|
302
|
+
...m.injected ? { injected: true } : {}
|
|
303
|
+
};
|
|
292
304
|
} else {
|
|
293
305
|
if (!cur) cur = { user: "", assistant: "", at: m.at, startLine: m.line, endLine: m.line };
|
|
294
306
|
cur.assistant += (cur.assistant ? "\n\n" : "") + m.text;
|
|
@@ -298,10 +310,17 @@ ${m.text}`;
|
|
|
298
310
|
if (cur?.assistant) turns.push(cur);
|
|
299
311
|
const line = cur && !cur.assistant ? cur.startLine : endLine;
|
|
300
312
|
const out = {
|
|
301
|
-
turns: turns.map((t) => ({
|
|
313
|
+
turns: turns.map((t) => ({
|
|
314
|
+
user: t.user,
|
|
315
|
+
assistant: t.assistant,
|
|
316
|
+
at: t.at,
|
|
317
|
+
endLine: t.endLine,
|
|
318
|
+
...t.injected ? { injected: true } : {}
|
|
319
|
+
})),
|
|
302
320
|
line
|
|
303
321
|
};
|
|
304
|
-
if (cur && !cur.assistant && cur.user)
|
|
322
|
+
if (cur && !cur.assistant && cur.user)
|
|
323
|
+
out.open = { user: cur.user, at: cur.at, ...cur.injected ? { injected: true } : {} };
|
|
305
324
|
return out;
|
|
306
325
|
}
|
|
307
326
|
var isInjected = (text) => /^<[a-z_-]+(?:\s[^>]*)?>/i.test(text) && /<\/[a-z_-]+>\s*$/i.test(text) || /^<!--\s/.test(text) || /^<command-/.test(text);
|
|
@@ -324,27 +343,51 @@ var textParts = (c, kinds) => {
|
|
|
324
343
|
(b) => b && typeof b === "object" && kinds.includes(b.type ?? "") && typeof b.text === "string"
|
|
325
344
|
).map((b) => b.text).join("\n");
|
|
326
345
|
};
|
|
346
|
+
var SPOKEN = /* @__PURE__ */ new Set(["human", "peer"]);
|
|
347
|
+
var PEER_INTRO = /sent a message[^\n]*:$/;
|
|
348
|
+
var PEER_OUTRO = "\n\nThis came from another ";
|
|
349
|
+
var ENVELOPE = /^<cross-session-message\b[^>]*>\n([\s\S]*)\n<\/cross-session-message>$/;
|
|
350
|
+
function peerAsk(origin, text) {
|
|
351
|
+
const body = origin?.body;
|
|
352
|
+
let t = typeof body === "string" && body.trim() ? body : text;
|
|
353
|
+
if (t !== body) {
|
|
354
|
+
const nl = t.indexOf("\n");
|
|
355
|
+
if (nl > 0 && PEER_INTRO.test(t.slice(0, nl))) t = t.slice(nl + 1);
|
|
356
|
+
const outro = t.indexOf(PEER_OUTRO);
|
|
357
|
+
if (outro > 0) t = t.slice(0, outro);
|
|
358
|
+
}
|
|
359
|
+
return (ENVELOPE.exec(t.trim())?.[1] ?? t).trim();
|
|
360
|
+
}
|
|
327
361
|
var queuedPrompt = (a) => {
|
|
328
362
|
if (!a || typeof a !== "object") return "";
|
|
329
363
|
const q = a;
|
|
330
364
|
if (q.type !== "queued_command" || (q.commandMode ?? "prompt") !== "prompt") return "";
|
|
331
|
-
|
|
332
|
-
|
|
365
|
+
const kind = q.origin?.kind ?? "human";
|
|
366
|
+
if (!SPOKEN.has(kind)) return "";
|
|
367
|
+
const text = textParts(q.prompt, ["text"]).trim();
|
|
368
|
+
return kind === "peer" ? peerAsk(q.origin, text) : text;
|
|
333
369
|
};
|
|
370
|
+
var channelText = (text) => /^<channel\b[^>]*>([\s\S]*)<\/channel>$/.exec(text)?.[1]?.trim() ?? "";
|
|
334
371
|
function readClaude(lines, from) {
|
|
335
372
|
const msgs = [];
|
|
336
373
|
for (let i = from; i < lines.length; i++) {
|
|
337
374
|
const j = lines[i] ? parse(lines[i]) : null;
|
|
338
375
|
if (!j || j.isSidechain) continue;
|
|
339
376
|
const origin = j.origin;
|
|
340
|
-
|
|
341
|
-
|
|
377
|
+
const channel = j.type === "user" && origin?.kind === "channel";
|
|
378
|
+
const peer = origin?.kind === "peer";
|
|
379
|
+
if (j.type === "user" && (j.isCompactSummary || j.repeatsCompactSummary)) continue;
|
|
380
|
+
if (j.type === "user" && !channel && !peer && (j.isMeta || origin?.kind && origin.kind !== "human")) continue;
|
|
342
381
|
const at = Date.parse(String(j.timestamp ?? "")) || Date.now();
|
|
343
382
|
const c = j.message?.content;
|
|
344
|
-
if (
|
|
345
|
-
const text = textParts(c, ["text"]).trim();
|
|
383
|
+
if (channel) {
|
|
384
|
+
const text = channelText(textParts(c, ["text"]).trim());
|
|
385
|
+
if (text) msgs.push({ role: "user", text, at, line: i, injected: true });
|
|
386
|
+
} else if (j.type === "user") {
|
|
387
|
+
const raw = textParts(c, ["text"]).trim();
|
|
388
|
+
const text = peer ? peerAsk(origin, raw) : raw;
|
|
346
389
|
if (!text || isInjected(text)) continue;
|
|
347
|
-
msgs.push({ role: "user", text, at, line: i });
|
|
390
|
+
msgs.push({ role: "user", text, at, line: i, ...peer ? { injected: true } : {} });
|
|
348
391
|
} else if (j.type === "assistant" && Array.isArray(c)) {
|
|
349
392
|
const text = textParts(c, ["text"]).trim();
|
|
350
393
|
if (text) msgs.push({ role: "assistant", text, at, line: i });
|
|
@@ -722,13 +765,7 @@ var claudeCode = {
|
|
|
722
765
|
);
|
|
723
766
|
break;
|
|
724
767
|
case "Stop":
|
|
725
|
-
events.push(
|
|
726
|
-
ev("turn.ended", {
|
|
727
|
-
turn_id,
|
|
728
|
-
reason: "stop",
|
|
729
|
-
background: Array.isArray(s.background_tasks) ? s.background_tasks.length : void 0
|
|
730
|
-
})
|
|
731
|
-
);
|
|
768
|
+
events.push(ev("turn.ended", { turn_id, reason: "stop", background: backgroundCount(s.background_tasks) }));
|
|
732
769
|
break;
|
|
733
770
|
case "StopFailure":
|
|
734
771
|
events.push(
|
|
@@ -751,7 +788,8 @@ var claudeCode = {
|
|
|
751
788
|
events.push(
|
|
752
789
|
ev("subagent.ended", {
|
|
753
790
|
agent_id: String(s.agent_id ?? ""),
|
|
754
|
-
agent_type: typeof s.agent_type === "string" ? s.agent_type : void 0
|
|
791
|
+
agent_type: typeof s.agent_type === "string" ? s.agent_type : void 0,
|
|
792
|
+
background: backgroundCount(s.background_tasks, String(s.agent_id ?? ""))
|
|
755
793
|
})
|
|
756
794
|
);
|
|
757
795
|
break;
|
|
@@ -826,6 +864,15 @@ var claudeCode = {
|
|
|
826
864
|
return rows2;
|
|
827
865
|
}
|
|
828
866
|
};
|
|
867
|
+
var WAITER_RE = /(?:^|[\s"'`/\\])sessionpipe(?:\.\w+)?["']?\s+wait(?:\s|$)/;
|
|
868
|
+
function backgroundCount(tasks, ending) {
|
|
869
|
+
if (!Array.isArray(tasks)) return void 0;
|
|
870
|
+
return tasks.filter((t) => {
|
|
871
|
+
const task = t;
|
|
872
|
+
if (ending !== void 0 && task?.id === ending) return false;
|
|
873
|
+
return !(typeof task?.command === "string" && WAITER_RE.test(task.command));
|
|
874
|
+
}).length;
|
|
875
|
+
}
|
|
829
876
|
var LINE_REFRESH_S = 60;
|
|
830
877
|
function statusLine(file, want, cmd) {
|
|
831
878
|
const f = readJsonFile(file);
|
|
@@ -1254,8 +1301,44 @@ async function claudeAiTitle(bridge, token) {
|
|
|
1254
1301
|
}
|
|
1255
1302
|
|
|
1256
1303
|
// ../core/src/adapters/headless.ts
|
|
1257
|
-
import {
|
|
1304
|
+
import { spawn } from "node:child_process";
|
|
1305
|
+
import { accessSync, constants, readFileSync as readFileSync3, statSync as statSync3 } from "node:fs";
|
|
1258
1306
|
import path5 from "node:path";
|
|
1307
|
+
|
|
1308
|
+
// ../core/src/launched-by.ts
|
|
1309
|
+
var LAUNCHED_BY_ENV = "SESSIONPIPE_LAUNCHED_BY";
|
|
1310
|
+
var DELEGATED_BY_ENV = "SESSIONPIPE_ASKED_BY";
|
|
1311
|
+
var ASK_CHAIN_ENV = "SESSIONPIPE_ASK_CHAIN";
|
|
1312
|
+
var LINEAGE_ENV = [LAUNCHED_BY_ENV, DELEGATED_BY_ENV, ASK_CHAIN_ENV];
|
|
1313
|
+
var SESSION_REF = /^[a-z][a-z0-9-]{0,39}:[A-Za-z0-9._:-]{1,128}$/;
|
|
1314
|
+
function withoutLineage(env) {
|
|
1315
|
+
const e = { ...env };
|
|
1316
|
+
for (const k of LINEAGE_ENV) delete e[k];
|
|
1317
|
+
return e;
|
|
1318
|
+
}
|
|
1319
|
+
function refIn(env, name) {
|
|
1320
|
+
const v = env[name]?.trim();
|
|
1321
|
+
return v && SESSION_REF.test(v) ? v : void 0;
|
|
1322
|
+
}
|
|
1323
|
+
function launchedBy(env) {
|
|
1324
|
+
return refIn(env, LAUNCHED_BY_ENV);
|
|
1325
|
+
}
|
|
1326
|
+
function delegatedBy(env) {
|
|
1327
|
+
return refIn(env, DELEGATED_BY_ENV);
|
|
1328
|
+
}
|
|
1329
|
+
function readHook(adapter, input) {
|
|
1330
|
+
const r = adapter.fromHook(input);
|
|
1331
|
+
if (r && !r.session.parent_id) {
|
|
1332
|
+
const self = `${adapter.name}:${r.session.id}`;
|
|
1333
|
+
const launcher = launchedBy(input.env);
|
|
1334
|
+
if (launcher && launcher !== self) r.session.launched_by = launcher;
|
|
1335
|
+
const asker = delegatedBy(input.env);
|
|
1336
|
+
if (asker && asker !== self) r.session.delegated_by = asker;
|
|
1337
|
+
}
|
|
1338
|
+
return r;
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1341
|
+
// ../core/src/adapters/headless.ts
|
|
1259
1342
|
var JOB_TIMEOUT_MS = 30 * 6e4;
|
|
1260
1343
|
var OUT_CAP = 1024 * 1024;
|
|
1261
1344
|
function isExe(p) {
|
|
@@ -1276,13 +1359,144 @@ function findBinary(o) {
|
|
|
1276
1359
|
for (const d of o.dirs) for (const n of o.names) if (isExe(path5.join(d, n))) return path5.join(d, n);
|
|
1277
1360
|
return null;
|
|
1278
1361
|
}
|
|
1362
|
+
function shimTarget(shim) {
|
|
1363
|
+
let text = "";
|
|
1364
|
+
try {
|
|
1365
|
+
text = readFileSync3(shim, "utf8");
|
|
1366
|
+
} catch {
|
|
1367
|
+
return null;
|
|
1368
|
+
}
|
|
1369
|
+
const m = /"%(?:~dp0|dp0%)\\([^"]+)"\s+%\*/.exec(text);
|
|
1370
|
+
if (!m) return null;
|
|
1371
|
+
const target = path5.resolve(path5.dirname(shim), m[1].split("\\").join(path5.sep));
|
|
1372
|
+
try {
|
|
1373
|
+
return statSync3(target).isFile() ? target : null;
|
|
1374
|
+
} catch {
|
|
1375
|
+
return null;
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
function spawnable(bin, args) {
|
|
1379
|
+
if (!/\.(cmd|bat)$/i.test(bin)) return { file: bin, args };
|
|
1380
|
+
const target = shimTarget(bin);
|
|
1381
|
+
return target ? { file: process.execPath, args: [target, ...args] } : null;
|
|
1382
|
+
}
|
|
1383
|
+
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`;
|
|
1384
|
+
function probe(bin, args) {
|
|
1385
|
+
const s = spawnable(bin, args);
|
|
1386
|
+
return s ? { ...s, shell: false } : { file: [`"${bin}"`, ...args].join(" "), args: [], shell: true };
|
|
1387
|
+
}
|
|
1388
|
+
function runHeadless(bin, args, o) {
|
|
1389
|
+
return new Promise((resolve) => {
|
|
1390
|
+
let out = [];
|
|
1391
|
+
let outLen = 0;
|
|
1392
|
+
let tooBig = false;
|
|
1393
|
+
let errTail = "";
|
|
1394
|
+
let timedOut = false;
|
|
1395
|
+
let settled = false;
|
|
1396
|
+
let child;
|
|
1397
|
+
const c = spawnable(bin, args);
|
|
1398
|
+
if (!c) {
|
|
1399
|
+
resolve({ error: shimNote(bin) });
|
|
1400
|
+
return;
|
|
1401
|
+
}
|
|
1402
|
+
try {
|
|
1403
|
+
child = spawn(c.file, c.args, {
|
|
1404
|
+
cwd: o.cwd,
|
|
1405
|
+
env: o.env,
|
|
1406
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
1407
|
+
detached: process.platform !== "win32",
|
|
1408
|
+
windowsHide: true
|
|
1409
|
+
});
|
|
1410
|
+
} catch (e) {
|
|
1411
|
+
resolve({ error: e.message });
|
|
1412
|
+
return;
|
|
1413
|
+
}
|
|
1414
|
+
const kill = (sig) => {
|
|
1415
|
+
try {
|
|
1416
|
+
if (process.platform !== "win32" && child.pid) process.kill(-child.pid, sig);
|
|
1417
|
+
else child.kill(sig);
|
|
1418
|
+
} catch {
|
|
1419
|
+
try {
|
|
1420
|
+
child.kill(sig);
|
|
1421
|
+
} catch {
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
};
|
|
1425
|
+
const keep = (line) => {
|
|
1426
|
+
if (!o.keep?.(line)) return;
|
|
1427
|
+
out = [Buffer.from(line)];
|
|
1428
|
+
outLen = line.length;
|
|
1429
|
+
};
|
|
1430
|
+
let partial = "";
|
|
1431
|
+
child.stdout?.on("data", (b) => {
|
|
1432
|
+
if (o.onLine) {
|
|
1433
|
+
const chunk = partial + b.toString("utf8");
|
|
1434
|
+
const lines = chunk.split("\n");
|
|
1435
|
+
partial = lines.pop();
|
|
1436
|
+
for (const line of lines) {
|
|
1437
|
+
if (!line.trim()) continue;
|
|
1438
|
+
try {
|
|
1439
|
+
o.onLine(line);
|
|
1440
|
+
} catch {
|
|
1441
|
+
}
|
|
1442
|
+
keep(line);
|
|
1443
|
+
}
|
|
1444
|
+
if (partial.length > OUT_CAP) partial = "";
|
|
1445
|
+
return;
|
|
1446
|
+
}
|
|
1447
|
+
if (tooBig) return;
|
|
1448
|
+
if (outLen + b.length > OUT_CAP) {
|
|
1449
|
+
tooBig = true;
|
|
1450
|
+
out = [];
|
|
1451
|
+
return;
|
|
1452
|
+
}
|
|
1453
|
+
out.push(b);
|
|
1454
|
+
outLen += b.length;
|
|
1455
|
+
});
|
|
1456
|
+
child.stderr?.on("data", (b) => {
|
|
1457
|
+
errTail = (errTail + b.toString("utf8")).slice(-4e3);
|
|
1458
|
+
});
|
|
1459
|
+
const timer = setTimeout(() => {
|
|
1460
|
+
timedOut = true;
|
|
1461
|
+
kill("SIGTERM");
|
|
1462
|
+
setTimeout(() => kill("SIGKILL"), 1e4).unref();
|
|
1463
|
+
}, o.timeoutMs ?? JOB_TIMEOUT_MS);
|
|
1464
|
+
const result = (code, signal) => ({
|
|
1465
|
+
code,
|
|
1466
|
+
signal,
|
|
1467
|
+
stdout: tooBig ? null : Buffer.concat(out).toString("utf8"),
|
|
1468
|
+
tooBig,
|
|
1469
|
+
stderr: errTail,
|
|
1470
|
+
timedOut
|
|
1471
|
+
});
|
|
1472
|
+
const finish = (r) => {
|
|
1473
|
+
if (settled) return;
|
|
1474
|
+
settled = true;
|
|
1475
|
+
clearTimeout(timer);
|
|
1476
|
+
resolve(r);
|
|
1477
|
+
};
|
|
1478
|
+
child.on("error", (e) => finish({ error: e.message }));
|
|
1479
|
+
child.on("exit", (code, signal) => setTimeout(() => finish(result(code, signal)), 2e3).unref());
|
|
1480
|
+
child.on("close", (code, signal) => {
|
|
1481
|
+
if (o.onLine && partial.trim()) {
|
|
1482
|
+
try {
|
|
1483
|
+
o.onLine(partial);
|
|
1484
|
+
} catch {
|
|
1485
|
+
}
|
|
1486
|
+
keep(partial);
|
|
1487
|
+
partial = "";
|
|
1488
|
+
}
|
|
1489
|
+
finish(result(code, signal));
|
|
1490
|
+
});
|
|
1491
|
+
});
|
|
1492
|
+
}
|
|
1279
1493
|
|
|
1280
1494
|
// ../core/src/adapters/antigravity.ts
|
|
1281
1495
|
import {
|
|
1282
1496
|
existsSync as existsSync3,
|
|
1283
1497
|
mkdirSync as mkdirSync2,
|
|
1284
1498
|
readdirSync as readdirSync3,
|
|
1285
|
-
readFileSync as
|
|
1499
|
+
readFileSync as readFileSync4,
|
|
1286
1500
|
realpathSync,
|
|
1287
1501
|
statSync as statSync4,
|
|
1288
1502
|
unlinkSync as unlinkSync2,
|
|
@@ -1411,7 +1625,7 @@ var antigravity = {
|
|
|
1411
1625
|
readTranscript(file, fromLine) {
|
|
1412
1626
|
let text = "";
|
|
1413
1627
|
try {
|
|
1414
|
-
text =
|
|
1628
|
+
text = readFileSync4(file, "utf8");
|
|
1415
1629
|
} catch {
|
|
1416
1630
|
return { turns: [], line: fromLine };
|
|
1417
1631
|
}
|
|
@@ -1490,7 +1704,7 @@ function agProjects(dir) {
|
|
|
1490
1704
|
if (!ID_RE.test(id)) continue;
|
|
1491
1705
|
let j;
|
|
1492
1706
|
try {
|
|
1493
|
-
j = JSON.parse(
|
|
1707
|
+
j = JSON.parse(readFileSync4(path6.join(dir, n), "utf8"));
|
|
1494
1708
|
} catch {
|
|
1495
1709
|
continue;
|
|
1496
1710
|
}
|
|
@@ -1527,7 +1741,7 @@ function antigravityRemoteUrl(transcript, opts) {
|
|
|
1527
1741
|
for (const d of dirs) {
|
|
1528
1742
|
try {
|
|
1529
1743
|
const m = /installation_uuid:\s*"([0-9a-f-]{36})"/i.exec(
|
|
1530
|
-
|
|
1744
|
+
readFileSync4(path6.join(d, "antigravity_state.pbtxt"), "utf8")
|
|
1531
1745
|
);
|
|
1532
1746
|
if (!m) continue;
|
|
1533
1747
|
const base = `https://antigravity.google.com/r/${m[1]}-v2`;
|
|
@@ -1565,6 +1779,50 @@ function antigravityFacts(file, opts) {
|
|
|
1565
1779
|
return out;
|
|
1566
1780
|
}
|
|
1567
1781
|
|
|
1782
|
+
// ../core/src/adapters/claude-code-control.ts
|
|
1783
|
+
var claude_code_control_exports = {};
|
|
1784
|
+
__export(claude_code_control_exports, {
|
|
1785
|
+
CLAUDE_MODES: () => CLAUDE_MODES,
|
|
1786
|
+
JOB_TIMEOUT_MS: () => JOB_TIMEOUT_MS,
|
|
1787
|
+
MODE_RANK: () => MODE_RANK,
|
|
1788
|
+
NO_HOME_SETTINGS: () => NO_HOME_SETTINGS,
|
|
1789
|
+
ceilingLevel: () => ceilingLevel,
|
|
1790
|
+
claudeArgs: () => claudeArgs,
|
|
1791
|
+
claudeEnv: () => claudeEnv,
|
|
1792
|
+
claudeLogin: () => claudeLogin2,
|
|
1793
|
+
cmpVersion: () => cmpVersion,
|
|
1794
|
+
defaultLevel: () => defaultLevel,
|
|
1795
|
+
detectCaps: () => detectCaps,
|
|
1796
|
+
findClaude: () => findClaude,
|
|
1797
|
+
findTranscript: () => findTranscript,
|
|
1798
|
+
findTranscripts: () => findTranscripts,
|
|
1799
|
+
homeSettings: () => homeSettings,
|
|
1800
|
+
hookMode: () => hookMode,
|
|
1801
|
+
isUuid: () => isUuid,
|
|
1802
|
+
liveProcess: () => liveProcess,
|
|
1803
|
+
liveRecord: () => liveRecord,
|
|
1804
|
+
liveRecords: () => liveRecords,
|
|
1805
|
+
loginCommand: () => loginCommand,
|
|
1806
|
+
loginHint: () => loginHint,
|
|
1807
|
+
modeFlags: () => modeFlags,
|
|
1808
|
+
modeRank: () => modeRank,
|
|
1809
|
+
offeredLevels: () => offeredLevels,
|
|
1810
|
+
outputArgs: () => outputArgs,
|
|
1811
|
+
parseAuthStatus: () => parseAuthStatus,
|
|
1812
|
+
parseCaps: () => parseCaps,
|
|
1813
|
+
parseResult: () => parseResult,
|
|
1814
|
+
peerClass: () => peerClass,
|
|
1815
|
+
recentlyWritten: () => recentlyWritten,
|
|
1816
|
+
relaunchMode: () => relaunchMode,
|
|
1817
|
+
resolveHome: () => resolveHome,
|
|
1818
|
+
runClaude: () => runClaude,
|
|
1819
|
+
sessionFolder: () => sessionFolder
|
|
1820
|
+
});
|
|
1821
|
+
import { execFile, execFileSync as execFileSync3 } from "node:child_process";
|
|
1822
|
+
import { closeSync as closeSync2, openSync as openSync2, readdirSync as readdirSync4, readFileSync as readFileSync5, readSync as readSync2, statSync as statSync5 } from "node:fs";
|
|
1823
|
+
import os2 from "node:os";
|
|
1824
|
+
import path7 from "node:path";
|
|
1825
|
+
|
|
1568
1826
|
// ../core/src/control/verify.ts
|
|
1569
1827
|
var GRANT_MAX_MS = 24 * 36e5;
|
|
1570
1828
|
var SKEW_MS = 5 * 6e4;
|
|
@@ -1575,10 +1833,10 @@ var MODEL_RE = /^[A-Za-z0-9][A-Za-z0-9._:/@[\]-]*$/;
|
|
|
1575
1833
|
var START_PERMISSIONS = ["read", "safe", "edits", "auto"];
|
|
1576
1834
|
var levelRank = (l) => START_PERMISSIONS.indexOf(l);
|
|
1577
1835
|
var FIELDS = {
|
|
1578
|
-
prompt: ["text", "files"],
|
|
1836
|
+
prompt: ["text", "files", "reply_to"],
|
|
1579
1837
|
"permission.answer": ["for", "decision"],
|
|
1580
1838
|
cancel: [],
|
|
1581
|
-
start: ["text", "cwd", "files", "permission", "model"],
|
|
1839
|
+
start: ["text", "cwd", "files", "permission", "model", "reply_to"],
|
|
1582
1840
|
"key.add": ["key", "name"],
|
|
1583
1841
|
pool: ["pool_action", "pool_account", "pool_order", "pool_code"]
|
|
1584
1842
|
};
|
|
@@ -1586,9 +1844,454 @@ var POOL_FIELDS = FIELDS.pool;
|
|
|
1586
1844
|
var enc = new TextEncoder();
|
|
1587
1845
|
var dec = new TextDecoder();
|
|
1588
1846
|
|
|
1847
|
+
// ../core/src/adapters/claude-code-control.ts
|
|
1848
|
+
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
1849
|
+
var LIVE_WRITE_MS = 9e4;
|
|
1850
|
+
var cmpVersion = (a, b) => {
|
|
1851
|
+
const pa = a.split(".").map(Number);
|
|
1852
|
+
const pb = b.split(".").map(Number);
|
|
1853
|
+
for (let i = 0; i < Math.max(pa.length, pb.length); i++)
|
|
1854
|
+
if ((pa[i] ?? 0) !== (pb[i] ?? 0)) return (pa[i] ?? 0) - (pb[i] ?? 0);
|
|
1855
|
+
return 0;
|
|
1856
|
+
};
|
|
1857
|
+
function findClaude(env = process.env, home = os2.homedir()) {
|
|
1858
|
+
if (env.SESSIONPIPE_CLAUDE && isExe(env.SESSIONPIPE_CLAUDE)) return env.SESSIONPIPE_CLAUDE;
|
|
1859
|
+
const names = process.platform === "win32" ? ["claude.exe", "claude.cmd"] : ["claude"];
|
|
1860
|
+
for (const d of String(env.PATH || "").split(path7.delimiter)) {
|
|
1861
|
+
if (!d || !path7.isAbsolute(d)) continue;
|
|
1862
|
+
for (const n of names) if (isExe(path7.join(d, n))) return path7.join(d, n);
|
|
1863
|
+
}
|
|
1864
|
+
for (const p of [
|
|
1865
|
+
path7.join(home, ".claude", "local", "claude"),
|
|
1866
|
+
path7.join(home, ".local", "bin", "claude"),
|
|
1867
|
+
"/opt/homebrew/bin/claude",
|
|
1868
|
+
"/usr/local/bin/claude"
|
|
1869
|
+
])
|
|
1870
|
+
if (isExe(p)) return p;
|
|
1871
|
+
const app = path7.join(home, "Library", "Application Support", "Claude", "claude-code");
|
|
1872
|
+
let versions = [];
|
|
1873
|
+
try {
|
|
1874
|
+
versions = readdirSync4(app).filter((n) => /^\d+(\.\d+)*$/.test(n)).sort(cmpVersion).reverse();
|
|
1875
|
+
} catch {
|
|
1876
|
+
}
|
|
1877
|
+
for (const v of versions) {
|
|
1878
|
+
const p = path7.join(app, v, "claude.app", "Contents", "MacOS", "claude");
|
|
1879
|
+
if (isExe(p)) return p;
|
|
1880
|
+
}
|
|
1881
|
+
return null;
|
|
1882
|
+
}
|
|
1883
|
+
function optionBlock(help, flag) {
|
|
1884
|
+
const lines = help.split("\n");
|
|
1885
|
+
const re = new RegExp(`^\\s{0,4}(?:-\\w, )?${flag.replace(/-/g, "\\-")}(?![\\w-])`);
|
|
1886
|
+
const start = lines.findIndex((l) => re.test(l));
|
|
1887
|
+
if (start < 0) return null;
|
|
1888
|
+
const out = [lines[start]];
|
|
1889
|
+
for (let i = start + 1; i < lines.length && lines[i]?.trim() && !/^\s{0,4}-/.test(lines[i]); i++)
|
|
1890
|
+
out.push(lines[i]);
|
|
1891
|
+
return out.join(" ");
|
|
1892
|
+
}
|
|
1893
|
+
function parseCaps(help) {
|
|
1894
|
+
const pm = optionBlock(help, "--permission-mode");
|
|
1895
|
+
const pp = optionBlock(help, "--permission-prompts");
|
|
1896
|
+
return {
|
|
1897
|
+
hasMode: !!pm,
|
|
1898
|
+
modes: pm ? [...pm.matchAll(/"([A-Za-z]+)"/g)].map((m) => m[1]) : [],
|
|
1899
|
+
promptsNone: !!pp && /"none"/.test(pp),
|
|
1900
|
+
fork: !!optionBlock(help, "--fork-session"),
|
|
1901
|
+
stream: !!optionBlock(help, "--include-partial-messages")
|
|
1902
|
+
};
|
|
1903
|
+
}
|
|
1904
|
+
var LEVEL_MODE = {
|
|
1905
|
+
read: "plan",
|
|
1906
|
+
edits: "acceptEdits",
|
|
1907
|
+
auto: "auto"
|
|
1908
|
+
};
|
|
1909
|
+
function modeFlags(level, caps) {
|
|
1910
|
+
if (level === "inherit") return { args: [] };
|
|
1911
|
+
if (caps.unread)
|
|
1912
|
+
return { error: `this machine couldn't read its Claude Code's options (${caps.unread}); try again in a minute` };
|
|
1913
|
+
if (level === "auto" && !caps.modes.includes("auto"))
|
|
1914
|
+
return { error: "this machine is in auto mode, but its Claude Code has no auto permission mode" };
|
|
1915
|
+
if (level !== "safe") {
|
|
1916
|
+
const m = LEVEL_MODE[level];
|
|
1917
|
+
if (!caps.modes.includes(m)) return { error: `this machine's Claude Code has no ${m} permission mode` };
|
|
1918
|
+
return { args: ["--permission-mode", m] };
|
|
1919
|
+
}
|
|
1920
|
+
if (caps.modes.includes("dontAsk")) return { args: ["--permission-mode", "dontAsk"] };
|
|
1921
|
+
const args = [];
|
|
1922
|
+
if (caps.hasMode)
|
|
1923
|
+
args.push(
|
|
1924
|
+
"--permission-mode",
|
|
1925
|
+
caps.modes.includes("manual") && !caps.modes.includes("default") ? "manual" : "default"
|
|
1926
|
+
);
|
|
1927
|
+
if (caps.promptsNone) args.push("--permission-prompts", "none");
|
|
1928
|
+
return { args };
|
|
1929
|
+
}
|
|
1930
|
+
var ceilingLevel = (mode) => mode === "inherit" ? "safe" : mode;
|
|
1931
|
+
function offeredLevels(mode, caps) {
|
|
1932
|
+
if (caps.unread) return [];
|
|
1933
|
+
return START_PERMISSIONS.filter(
|
|
1934
|
+
(l) => levelRank(l) <= levelRank(ceilingLevel(mode)) && (l === "safe" || caps.modes.includes(LEVEL_MODE[l]))
|
|
1935
|
+
);
|
|
1936
|
+
}
|
|
1937
|
+
var defaultLevel = (mode) => mode;
|
|
1938
|
+
function outputArgs(caps) {
|
|
1939
|
+
return caps.stream ? ["--output-format", "stream-json", "--verbose", "--include-partial-messages"] : ["--output-format", "json"];
|
|
1940
|
+
}
|
|
1941
|
+
function claudeArgs(o) {
|
|
1942
|
+
const mf = modeFlags(o.level, o.caps);
|
|
1943
|
+
if ("error" in mf) return mf;
|
|
1944
|
+
const where = o.resume ? ["--resume", o.resume, ...o.fork ? ["--fork-session"] : []] : ["--session-id", o.session];
|
|
1945
|
+
return {
|
|
1946
|
+
args: [
|
|
1947
|
+
"-p",
|
|
1948
|
+
...outputArgs(o.caps),
|
|
1949
|
+
...mf.args,
|
|
1950
|
+
...o.model ? ["--model", o.model] : [],
|
|
1951
|
+
...o.allowedTools?.length ? [`--allowedTools=${o.allowedTools.join(",")}`] : [],
|
|
1952
|
+
...where,
|
|
1953
|
+
o.prompt.startsWith("-") ? ` ${o.prompt}` : o.prompt
|
|
1954
|
+
]
|
|
1955
|
+
};
|
|
1956
|
+
}
|
|
1957
|
+
function claudeEnv(configDir, env = process.env) {
|
|
1958
|
+
const e = withoutLineage(env);
|
|
1959
|
+
for (const k of [
|
|
1960
|
+
"CLAUDECODE",
|
|
1961
|
+
"CLAUDE_CODE_SESSION_ID",
|
|
1962
|
+
"CLAUDE_CODE_HOST_SESSION_ID",
|
|
1963
|
+
"CLAUDE_CODE_ENTRYPOINT",
|
|
1964
|
+
"CLAUDE_CODE_SESSION_ATTENDED",
|
|
1965
|
+
"CLAUDE_CODE_SSE_PORT"
|
|
1966
|
+
])
|
|
1967
|
+
delete e[k];
|
|
1968
|
+
e.SESSIONPIPE_CONTROL_JOB = "1";
|
|
1969
|
+
if (configDir !== void 0) {
|
|
1970
|
+
if (path7.resolve(configDir) === path7.resolve(os2.homedir(), ".claude")) delete e.CLAUDE_CONFIG_DIR;
|
|
1971
|
+
else e.CLAUDE_CONFIG_DIR = configDir;
|
|
1972
|
+
}
|
|
1973
|
+
return e;
|
|
1974
|
+
}
|
|
1975
|
+
function parseAuthStatus(stdout) {
|
|
1976
|
+
const text = String(stdout || "").trim();
|
|
1977
|
+
const start = text.indexOf("{");
|
|
1978
|
+
if (start < 0) return { loggedIn: null };
|
|
1979
|
+
try {
|
|
1980
|
+
const j = JSON.parse(text.slice(start));
|
|
1981
|
+
if (typeof j.loggedIn !== "boolean") return { loggedIn: null };
|
|
1982
|
+
return {
|
|
1983
|
+
loggedIn: j.loggedIn,
|
|
1984
|
+
...typeof j.authMethod === "string" ? { method: j.authMethod } : {},
|
|
1985
|
+
...typeof j.subscriptionType === "string" ? { subscription: j.subscriptionType } : {}
|
|
1986
|
+
};
|
|
1987
|
+
} catch {
|
|
1988
|
+
return { loggedIn: null };
|
|
1989
|
+
}
|
|
1990
|
+
}
|
|
1991
|
+
function claudeLogin2(bin, configDir, env = process.env) {
|
|
1992
|
+
const c = probe(bin, ["auth", "status", "--json"]);
|
|
1993
|
+
return new Promise((resolve) => {
|
|
1994
|
+
execFile(
|
|
1995
|
+
c.file,
|
|
1996
|
+
c.args,
|
|
1997
|
+
{ encoding: "utf8", timeout: 2e4, env: claudeEnv(configDir, env), maxBuffer: 256 * 1024, shell: c.shell },
|
|
1998
|
+
// `auth status` may exit non-zero when signed out; its JSON still says so.
|
|
1999
|
+
(_err, stdout) => resolve(parseAuthStatus(stdout))
|
|
2000
|
+
);
|
|
2001
|
+
});
|
|
2002
|
+
}
|
|
2003
|
+
function loginCommand(configDir) {
|
|
2004
|
+
const home = os2.homedir();
|
|
2005
|
+
const isDefault = !configDir || path7.resolve(configDir) === path7.resolve(home, ".claude");
|
|
2006
|
+
return isDefault ? "`claude auth login`" : `\`CLAUDE_CONFIG_DIR=${configDir.replace(home, "~")} claude auth login\``;
|
|
2007
|
+
}
|
|
2008
|
+
function loginHint(configDir, platform = process.platform) {
|
|
2009
|
+
const shown = configDir ? configDir.replace(os2.homedir(), "~") : "~/.claude";
|
|
2010
|
+
const login = loginCommand(configDir);
|
|
2011
|
+
if (platform === "darwin")
|
|
2012
|
+
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.`;
|
|
2013
|
+
return `Claude Code (${shown}) isn't signed in on this machine: run ${login} there.`;
|
|
2014
|
+
}
|
|
2015
|
+
function detectCaps(bin, timeoutMs = 3e4) {
|
|
2016
|
+
const c = probe(bin, ["--help"]);
|
|
2017
|
+
try {
|
|
2018
|
+
return parseCaps(
|
|
2019
|
+
execFileSync3(c.file, c.args, {
|
|
2020
|
+
encoding: "utf8",
|
|
2021
|
+
timeout: timeoutMs,
|
|
2022
|
+
env: claudeEnv(),
|
|
2023
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
2024
|
+
maxBuffer: 4 * 1024 * 1024,
|
|
2025
|
+
shell: c.shell
|
|
2026
|
+
})
|
|
2027
|
+
);
|
|
2028
|
+
} catch (e) {
|
|
2029
|
+
const err = e;
|
|
2030
|
+
const caps = parseCaps(String(err?.stdout || ""));
|
|
2031
|
+
if (caps.hasMode) return caps;
|
|
2032
|
+
const why = err?.code === "ETIMEDOUT" ? `timed out after ${timeoutMs / 1e3} s` : "failed";
|
|
2033
|
+
return { ...caps, unread: `\`${path7.basename(bin)} --help\` ${why}` };
|
|
2034
|
+
}
|
|
2035
|
+
}
|
|
2036
|
+
function transcriptIn(session, dir) {
|
|
2037
|
+
let projects = [];
|
|
2038
|
+
try {
|
|
2039
|
+
projects = readdirSync4(path7.join(dir, "projects"), { withFileTypes: true });
|
|
2040
|
+
} catch {
|
|
2041
|
+
return null;
|
|
2042
|
+
}
|
|
2043
|
+
for (const p of projects) {
|
|
2044
|
+
if (!p.isDirectory()) continue;
|
|
2045
|
+
const file = path7.join(dir, "projects", p.name, `${session}.jsonl`);
|
|
2046
|
+
try {
|
|
2047
|
+
if (statSync5(file).isFile()) return { file, configDir: dir, project: p.name };
|
|
2048
|
+
} catch {
|
|
2049
|
+
}
|
|
2050
|
+
}
|
|
2051
|
+
return null;
|
|
2052
|
+
}
|
|
2053
|
+
function findTranscript(session, dirs) {
|
|
2054
|
+
if (!UUID_RE.test(session)) return null;
|
|
2055
|
+
for (const dir of dirs) {
|
|
2056
|
+
const t = transcriptIn(session, dir);
|
|
2057
|
+
if (t) return t;
|
|
2058
|
+
}
|
|
2059
|
+
return null;
|
|
2060
|
+
}
|
|
2061
|
+
function findTranscripts(session, dirs) {
|
|
2062
|
+
if (!UUID_RE.test(session)) return [];
|
|
2063
|
+
const out = [];
|
|
2064
|
+
for (const dir of dirs) {
|
|
2065
|
+
const t = transcriptIn(session, dir);
|
|
2066
|
+
if (t) out.push(t);
|
|
2067
|
+
}
|
|
2068
|
+
return out;
|
|
2069
|
+
}
|
|
2070
|
+
var homeName = (t) => path7.basename(t.configDir);
|
|
2071
|
+
function resolveHome(session, dirs) {
|
|
2072
|
+
const all = findTranscripts(session, dirs);
|
|
2073
|
+
if (!all.length) return null;
|
|
2074
|
+
if (all.length === 1) return { transcript: all[0] };
|
|
2075
|
+
const live = all.filter((t) => liveRecords(session, t.configDir).length > 0);
|
|
2076
|
+
if (live.length === 1) return { transcript: live[0] };
|
|
2077
|
+
return {
|
|
2078
|
+
ambiguous: [...new Set(all.map(homeName))].sort(),
|
|
2079
|
+
live: [...new Set(live.map(homeName))].sort()
|
|
2080
|
+
};
|
|
2081
|
+
}
|
|
2082
|
+
var projectName = (cwd) => cwd.replace(/[^a-zA-Z0-9]/g, "-");
|
|
2083
|
+
function sessionFolder(t, cap2 = 8 * 1024 * 1024) {
|
|
2084
|
+
let first = null;
|
|
2085
|
+
let count = 0;
|
|
2086
|
+
let fd;
|
|
2087
|
+
const look = (line) => {
|
|
2088
|
+
if (line.indexOf('"cwd"') < 0) return null;
|
|
2089
|
+
let j;
|
|
2090
|
+
try {
|
|
2091
|
+
j = JSON.parse(line.toString("utf8"));
|
|
2092
|
+
} catch {
|
|
2093
|
+
return null;
|
|
2094
|
+
}
|
|
2095
|
+
if (!j || typeof j.cwd !== "string" || !path7.isAbsolute(j.cwd)) return null;
|
|
2096
|
+
if (projectName(j.cwd) === t.project) return j.cwd;
|
|
2097
|
+
first ??= j.cwd;
|
|
2098
|
+
count++;
|
|
2099
|
+
return null;
|
|
2100
|
+
};
|
|
2101
|
+
try {
|
|
2102
|
+
fd = openSync2(t.file, "r");
|
|
2103
|
+
const buf = Buffer.alloc(256 * 1024);
|
|
2104
|
+
let pos = 0;
|
|
2105
|
+
let rest = Buffer.alloc(0);
|
|
2106
|
+
let skipping = false;
|
|
2107
|
+
while (pos < cap2 && count < 50) {
|
|
2108
|
+
const n = readSync2(fd, buf, 0, buf.length, pos);
|
|
2109
|
+
if (!n) {
|
|
2110
|
+
if (!skipping && rest.length) {
|
|
2111
|
+
const hit = look(rest);
|
|
2112
|
+
if (hit) return hit;
|
|
2113
|
+
}
|
|
2114
|
+
break;
|
|
2115
|
+
}
|
|
2116
|
+
pos += n;
|
|
2117
|
+
let chunk = rest.length ? Buffer.concat([rest, buf.subarray(0, n)]) : buf.subarray(0, n);
|
|
2118
|
+
let nl = chunk.indexOf(10);
|
|
2119
|
+
while (nl >= 0) {
|
|
2120
|
+
const line = chunk.subarray(0, nl);
|
|
2121
|
+
chunk = chunk.subarray(nl + 1);
|
|
2122
|
+
if (skipping) skipping = false;
|
|
2123
|
+
else {
|
|
2124
|
+
const hit = look(line);
|
|
2125
|
+
if (hit) return hit;
|
|
2126
|
+
}
|
|
2127
|
+
nl = chunk.indexOf(10);
|
|
2128
|
+
}
|
|
2129
|
+
if (chunk.length > 2 * 1024 * 1024) {
|
|
2130
|
+
skipping = true;
|
|
2131
|
+
rest = Buffer.alloc(0);
|
|
2132
|
+
} else rest = skipping ? Buffer.alloc(0) : Buffer.from(chunk);
|
|
2133
|
+
}
|
|
2134
|
+
} catch {
|
|
2135
|
+
} finally {
|
|
2136
|
+
if (fd !== void 0)
|
|
2137
|
+
try {
|
|
2138
|
+
closeSync2(fd);
|
|
2139
|
+
} catch {
|
|
2140
|
+
}
|
|
2141
|
+
}
|
|
2142
|
+
return first;
|
|
2143
|
+
}
|
|
2144
|
+
function pidAlive(pid) {
|
|
2145
|
+
if (!Number.isSafeInteger(pid) || pid <= 1) return false;
|
|
2146
|
+
try {
|
|
2147
|
+
process.kill(pid, 0);
|
|
2148
|
+
return true;
|
|
2149
|
+
} catch (e) {
|
|
2150
|
+
return e.code === "EPERM";
|
|
2151
|
+
}
|
|
2152
|
+
}
|
|
2153
|
+
var registryRow = (file, configDir, j) => {
|
|
2154
|
+
const pid = Number(j.pid ?? path7.basename(file).slice(0, -5));
|
|
2155
|
+
const str = (v) => typeof v === "string" && v ? v : null;
|
|
2156
|
+
let keyFile = null;
|
|
2157
|
+
try {
|
|
2158
|
+
const want = new RegExp(`^${pid}\\.[0-9a-f]{64}\\.key$`);
|
|
2159
|
+
const hit = readdirSync4(path7.dirname(file)).find((n) => want.test(n));
|
|
2160
|
+
if (hit) keyFile = path7.join(path7.dirname(file), hit);
|
|
2161
|
+
} catch {
|
|
2162
|
+
}
|
|
2163
|
+
return {
|
|
2164
|
+
configDir,
|
|
2165
|
+
file,
|
|
2166
|
+
pid,
|
|
2167
|
+
socket: str(j.messagingSocketPath),
|
|
2168
|
+
peerProtocol: typeof j.peerProtocol === "number" ? j.peerProtocol : null,
|
|
2169
|
+
procStart: str(j.procStart),
|
|
2170
|
+
status: str(j.status),
|
|
2171
|
+
kind: str(j.kind),
|
|
2172
|
+
entrypoint: str(j.entrypoint),
|
|
2173
|
+
keyFile
|
|
2174
|
+
};
|
|
2175
|
+
};
|
|
2176
|
+
function liveRecords(session, dirs) {
|
|
2177
|
+
const out = [];
|
|
2178
|
+
for (const dir of typeof dirs === "string" ? [dirs] : dirs) {
|
|
2179
|
+
let names = [];
|
|
2180
|
+
try {
|
|
2181
|
+
names = readdirSync4(path7.join(dir, "sessions")).filter((n) => /^\d+\.json$/.test(n));
|
|
2182
|
+
} catch {
|
|
2183
|
+
continue;
|
|
2184
|
+
}
|
|
2185
|
+
for (const n of names) {
|
|
2186
|
+
const file = path7.join(dir, "sessions", n);
|
|
2187
|
+
let j;
|
|
2188
|
+
try {
|
|
2189
|
+
j = JSON.parse(readFileSync5(file, "utf8"));
|
|
2190
|
+
} catch {
|
|
2191
|
+
continue;
|
|
2192
|
+
}
|
|
2193
|
+
if (String(j.sessionId) !== session) continue;
|
|
2194
|
+
const row = registryRow(file, dir, j);
|
|
2195
|
+
if (pidAlive(row.pid)) out.push(row);
|
|
2196
|
+
}
|
|
2197
|
+
}
|
|
2198
|
+
return out;
|
|
2199
|
+
}
|
|
2200
|
+
function liveRecord(session, dirs) {
|
|
2201
|
+
const all = liveRecords(session, dirs);
|
|
2202
|
+
return all.length === 1 ? all[0] : null;
|
|
2203
|
+
}
|
|
2204
|
+
function liveProcess(session, configDir) {
|
|
2205
|
+
return liveRecords(session, configDir).length > 0;
|
|
2206
|
+
}
|
|
2207
|
+
var MODE_RANK = {
|
|
2208
|
+
plan: 0,
|
|
2209
|
+
default: 1,
|
|
2210
|
+
manual: 1,
|
|
2211
|
+
dontAsk: 1,
|
|
2212
|
+
acceptEdits: 2,
|
|
2213
|
+
auto: 3,
|
|
2214
|
+
bypassPermissions: 4
|
|
2215
|
+
};
|
|
2216
|
+
var CLAUDE_MODES = Object.keys(MODE_RANK);
|
|
2217
|
+
var modeRank = (m) => m && m in MODE_RANK ? MODE_RANK[m] : -1;
|
|
2218
|
+
function hookMode(payload) {
|
|
2219
|
+
if (!payload || typeof payload !== "object") return null;
|
|
2220
|
+
const p = payload;
|
|
2221
|
+
const v = typeof p.permission_mode === "string" ? p.permission_mode : p.permissionMode;
|
|
2222
|
+
return typeof v === "string" && CLAUDE_MODES.includes(v) ? v : null;
|
|
2223
|
+
}
|
|
2224
|
+
function peerClass(mode) {
|
|
2225
|
+
if (mode === "bypassPermissions") return "bypass";
|
|
2226
|
+
if (mode === "default" || mode === "manual" || mode === "dontAsk" || mode === "acceptEdits") return "prompting";
|
|
2227
|
+
return null;
|
|
2228
|
+
}
|
|
2229
|
+
var NO_HOME_SETTINGS = { defaultMode: null, disableAutoMode: false, disableBypass: false };
|
|
2230
|
+
function homeSettings(configDir, home = os2.homedir()) {
|
|
2231
|
+
const file = path7.join(configDir ?? path7.join(home, ".claude"), "settings.json");
|
|
2232
|
+
let j;
|
|
2233
|
+
try {
|
|
2234
|
+
j = JSON.parse(readFileSync5(file, "utf8"));
|
|
2235
|
+
} catch {
|
|
2236
|
+
return { ...NO_HOME_SETTINGS };
|
|
2237
|
+
}
|
|
2238
|
+
if (!j || typeof j !== "object" || Array.isArray(j)) return { ...NO_HOME_SETTINGS };
|
|
2239
|
+
const p = j.permissions && typeof j.permissions === "object" ? j.permissions : {};
|
|
2240
|
+
const off = (k) => j[k] === "disable" || p[k] === "disable";
|
|
2241
|
+
return {
|
|
2242
|
+
defaultMode: typeof p.defaultMode === "string" && CLAUDE_MODES.includes(p.defaultMode) ? p.defaultMode : null,
|
|
2243
|
+
disableAutoMode: off("disableAutoMode"),
|
|
2244
|
+
disableBypass: off("disableBypassPermissionsMode")
|
|
2245
|
+
};
|
|
2246
|
+
}
|
|
2247
|
+
function relaunchMode(recorded, home) {
|
|
2248
|
+
if (recorded === "plan") return recorded;
|
|
2249
|
+
if ((recorded === "dontAsk" || recorded === "acceptEdits") && modeRank(home.defaultMode) >= modeRank(recorded))
|
|
2250
|
+
return recorded;
|
|
2251
|
+
return null;
|
|
2252
|
+
}
|
|
2253
|
+
function recentlyWritten(file, ourLastEnd, now = Date.now()) {
|
|
2254
|
+
let m;
|
|
2255
|
+
try {
|
|
2256
|
+
m = statSync5(file).mtimeMs;
|
|
2257
|
+
} catch {
|
|
2258
|
+
return false;
|
|
2259
|
+
}
|
|
2260
|
+
if (now - m > LIVE_WRITE_MS) return false;
|
|
2261
|
+
return !(ourLastEnd && m <= ourLastEnd + 2e3);
|
|
2262
|
+
}
|
|
2263
|
+
function runClaude(bin, args, o) {
|
|
2264
|
+
return runHeadless(bin, args, { ...o, keep: o.keep ?? ((line) => line.includes('"type":"result"')) });
|
|
2265
|
+
}
|
|
2266
|
+
function parseResult(stdout) {
|
|
2267
|
+
const s = String(stdout || "").trim();
|
|
2268
|
+
if (!s) return null;
|
|
2269
|
+
const pick = (j) => {
|
|
2270
|
+
if (Array.isArray(j)) {
|
|
2271
|
+
for (let i = j.length - 1; i >= 0; i--) if (j[i]?.type === "result") return j[i];
|
|
2272
|
+
return null;
|
|
2273
|
+
}
|
|
2274
|
+
return j && typeof j === "object" ? j : null;
|
|
2275
|
+
};
|
|
2276
|
+
try {
|
|
2277
|
+
return pick(JSON.parse(s));
|
|
2278
|
+
} catch {
|
|
2279
|
+
}
|
|
2280
|
+
const lines = s.split("\n");
|
|
2281
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
2282
|
+
try {
|
|
2283
|
+
const j = pick(JSON.parse(lines[i]));
|
|
2284
|
+
if (j && (j.type === "result" || "result" in j)) return j;
|
|
2285
|
+
} catch {
|
|
2286
|
+
}
|
|
2287
|
+
}
|
|
2288
|
+
return null;
|
|
2289
|
+
}
|
|
2290
|
+
var isUuid = (s) => typeof s === "string" && UUID_RE.test(s);
|
|
2291
|
+
|
|
1589
2292
|
// ../core/src/adapters/codex.ts
|
|
1590
|
-
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync as
|
|
1591
|
-
import
|
|
2293
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync as readdirSync5, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "node:fs";
|
|
2294
|
+
import path8 from "node:path";
|
|
1592
2295
|
|
|
1593
2296
|
// ../core/src/adapters/codex-config.ts
|
|
1594
2297
|
var NOTIFY_LINE = /^[ \t]*notify[ \t]*=[ \t]*(\[.*\])[ \t]*(?:#.*)?(?=\r?$)/m;
|
|
@@ -1734,24 +2437,24 @@ var CODEX_EVENTS = [
|
|
|
1734
2437
|
];
|
|
1735
2438
|
var OPTS2 = { matcherFor: (e) => /Tool|Permission/.test(e) ? "" : void 0, timeout: 5 };
|
|
1736
2439
|
var eventsFor2 = (lean) => lean ? CODEX_EVENTS.filter((e) => e !== "PreToolUse") : [...CODEX_EVENTS];
|
|
1737
|
-
var codexHome = (env) => env.CODEX_HOME ||
|
|
1738
|
-
var hooksFile2 = (env) =>
|
|
1739
|
-
var configFile2 = (env) =>
|
|
1740
|
-
var sessionsDir = (env) =>
|
|
2440
|
+
var codexHome = (env) => env.CODEX_HOME || path8.join(homeDir(), ".codex");
|
|
2441
|
+
var hooksFile2 = (env) => path8.join(codexHome(env), "hooks.json");
|
|
2442
|
+
var configFile2 = (env) => path8.join(codexHome(env), "config.toml");
|
|
2443
|
+
var sessionsDir = (env) => path8.join(codexHome(env), "sessions");
|
|
1741
2444
|
var validTool3 = (n) => typeof n === "string" && /^[A-Za-z0-9_.:-]{1,120}$/.test(n);
|
|
1742
2445
|
var endReason2 = (r) => r === "clear" || r === "logout" || r === "exit" ? r : "other";
|
|
1743
2446
|
function installNotify(cmd, env) {
|
|
1744
2447
|
const file = configFile2(env);
|
|
1745
2448
|
let text = "";
|
|
1746
2449
|
try {
|
|
1747
|
-
text =
|
|
2450
|
+
text = readFileSync6(file, "utf8");
|
|
1748
2451
|
} catch {
|
|
1749
2452
|
}
|
|
1750
2453
|
const argv = cmd([NAME3, "notify"]).argv;
|
|
1751
2454
|
const line = `notify = ${JSON.stringify(argv)}`;
|
|
1752
2455
|
const n = readNotify(text);
|
|
1753
2456
|
const write = (next) => {
|
|
1754
|
-
mkdirSync3(
|
|
2457
|
+
mkdirSync3(path8.dirname(file), { recursive: true });
|
|
1755
2458
|
writeFileSync4(file, next);
|
|
1756
2459
|
};
|
|
1757
2460
|
if (n && !n.other) {
|
|
@@ -1790,7 +2493,7 @@ function uninstallNotify(env) {
|
|
|
1790
2493
|
const file = configFile2(env);
|
|
1791
2494
|
let text;
|
|
1792
2495
|
try {
|
|
1793
|
-
text =
|
|
2496
|
+
text = readFileSync6(file, "utf8");
|
|
1794
2497
|
} catch {
|
|
1795
2498
|
return { file, changed: false };
|
|
1796
2499
|
}
|
|
@@ -1813,12 +2516,12 @@ function findRollout(threadId, env = process.env) {
|
|
|
1813
2516
|
const walk = (dir, depth) => {
|
|
1814
2517
|
let names = [];
|
|
1815
2518
|
try {
|
|
1816
|
-
names =
|
|
2519
|
+
names = readdirSync5(dir);
|
|
1817
2520
|
} catch {
|
|
1818
2521
|
return;
|
|
1819
2522
|
}
|
|
1820
2523
|
for (const n of names.sort().reverse()) {
|
|
1821
|
-
const p =
|
|
2524
|
+
const p = path8.join(dir, n);
|
|
1822
2525
|
if (n.endsWith(`-${threadId}.jsonl`)) {
|
|
1823
2526
|
best = p;
|
|
1824
2527
|
return;
|
|
@@ -1862,7 +2565,7 @@ var codex = {
|
|
|
1862
2565
|
];
|
|
1863
2566
|
let text = "";
|
|
1864
2567
|
try {
|
|
1865
|
-
text =
|
|
2568
|
+
text = readFileSync6(configFile2(env), "utf8");
|
|
1866
2569
|
} catch {
|
|
1867
2570
|
}
|
|
1868
2571
|
const n = readNotify(text);
|
|
@@ -1989,7 +2692,7 @@ var codex = {
|
|
|
1989
2692
|
readTranscript(file, fromLine) {
|
|
1990
2693
|
let text = "";
|
|
1991
2694
|
try {
|
|
1992
|
-
text =
|
|
2695
|
+
text = readFileSync6(file, "utf8");
|
|
1993
2696
|
} catch {
|
|
1994
2697
|
return { turns: [], line: fromLine };
|
|
1995
2698
|
}
|
|
@@ -2003,7 +2706,7 @@ var codex = {
|
|
|
2003
2706
|
backfill(sinceMs, env = process.env) {
|
|
2004
2707
|
const rows2 = [];
|
|
2005
2708
|
for (const f of recentFiles(sessionsDir(env), 3, sinceMs, (n) => /^rollout-.*\.jsonl$/.test(n))) {
|
|
2006
|
-
const m = /([0-9a-f]{8}-[0-9a-f-]{27,})\.jsonl$/i.exec(
|
|
2709
|
+
const m = /([0-9a-f]{8}-[0-9a-f-]{27,})\.jsonl$/i.exec(path8.basename(f));
|
|
2007
2710
|
if (!m?.[1]) continue;
|
|
2008
2711
|
if (codexSubagent(rolloutMeta(f))) continue;
|
|
2009
2712
|
const facts = codexFacts(f);
|
|
@@ -2090,10 +2793,10 @@ function codexFacts(file) {
|
|
|
2090
2793
|
// ../core/src/adapters/devin.ts
|
|
2091
2794
|
import { createHash as createHash3 } from "node:crypto";
|
|
2092
2795
|
import { existsSync as existsSync6, readlinkSync } from "node:fs";
|
|
2093
|
-
import
|
|
2796
|
+
import path9 from "node:path";
|
|
2094
2797
|
|
|
2095
2798
|
// ../core/src/readers/sqlite.ts
|
|
2096
|
-
import { execFileSync as
|
|
2799
|
+
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
2097
2800
|
import { existsSync as existsSync5 } from "node:fs";
|
|
2098
2801
|
import { createRequire } from "node:module";
|
|
2099
2802
|
function quiet(fn) {
|
|
@@ -2173,7 +2876,7 @@ db.close();
|
|
|
2173
2876
|
process.stdout.write(JSON.stringify(out));`;
|
|
2174
2877
|
function viaChild(file, sql, params, o) {
|
|
2175
2878
|
try {
|
|
2176
|
-
const out =
|
|
2879
|
+
const out = execFileSync4(
|
|
2177
2880
|
process.execPath,
|
|
2178
2881
|
["--experimental-sqlite", "--no-warnings", "-e", CHILD, file, sql, JSON.stringify(params), String(o.timeoutMs)],
|
|
2179
2882
|
{
|
|
@@ -2217,7 +2920,7 @@ function viaCli(file, sql, params, o) {
|
|
|
2217
2920
|
const text = inlineParams(sql, params);
|
|
2218
2921
|
if (!text) return null;
|
|
2219
2922
|
try {
|
|
2220
|
-
const out =
|
|
2923
|
+
const out = execFileSync4("sqlite3", ["-readonly", "-json", file, text], {
|
|
2221
2924
|
encoding: "utf8",
|
|
2222
2925
|
timeout: o.timeoutMs + 3e3,
|
|
2223
2926
|
stdio: ["ignore", "pipe", "ignore"],
|
|
@@ -2274,11 +2977,11 @@ function foreignEvent(file) {
|
|
|
2274
2977
|
return Object.keys(hooks).find((k) => !ok.includes(k));
|
|
2275
2978
|
}
|
|
2276
2979
|
var homeOf = (env) => env.HOME || homeDir();
|
|
2277
|
-
var appData = (env) => env.APPDATA ||
|
|
2278
|
-
var devinConfigDir = (env = process.env) => process.platform === "win32" ?
|
|
2279
|
-
var configFile3 = (env) =>
|
|
2280
|
-
var devinDataDir = (env = process.env) => process.platform === "win32" ?
|
|
2281
|
-
var sessionsDb = (env) =>
|
|
2980
|
+
var appData = (env) => env.APPDATA || path9.join(homeOf(env), "AppData", "Roaming");
|
|
2981
|
+
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");
|
|
2982
|
+
var configFile3 = (env) => path9.join(devinConfigDir(env), "config.json");
|
|
2983
|
+
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");
|
|
2984
|
+
var sessionsDb = (env) => path9.join(devinDataDir(env), "sessions.db");
|
|
2282
2985
|
var SESSION_ID = /^[A-Za-z0-9._:-]{1,128}$/;
|
|
2283
2986
|
var validTool4 = (n) => typeof n === "string" && /^[A-Za-z0-9_.:-]{1,120}$/.test(n);
|
|
2284
2987
|
var endReason3 = (r) => r === "clear" || r === "logout" || r === "exit" ? r : "other";
|
|
@@ -2450,7 +3153,7 @@ var BACKFILL_SQL = `SELECT s.id, ${SESSION_FIELDS} FROM sessions s
|
|
|
2450
3153
|
function sessionFacts(row) {
|
|
2451
3154
|
const out = {};
|
|
2452
3155
|
if (typeof row.model === "string" && row.model) out.model = row.model;
|
|
2453
|
-
if (typeof row.working_directory === "string" &&
|
|
3156
|
+
if (typeof row.working_directory === "string" && path9.isAbsolute(row.working_directory))
|
|
2454
3157
|
out.cwd = row.working_directory;
|
|
2455
3158
|
const started = Number(row.created_at);
|
|
2456
3159
|
const last = Number(row.last_activity_at);
|
|
@@ -2495,8 +3198,8 @@ function readDevinChain(db, session, fromLine) {
|
|
|
2495
3198
|
}
|
|
2496
3199
|
function devinVersion(env = process.env) {
|
|
2497
3200
|
try {
|
|
2498
|
-
const target = readlinkSync(
|
|
2499
|
-
const name =
|
|
3201
|
+
const target = readlinkSync(path9.join(devinDataDir(env), "_versions", "current"));
|
|
3202
|
+
const name = path9.basename(target);
|
|
2500
3203
|
return /^[0-9][0-9A-Za-z.+-]{0,63}$/.test(name) ? name : void 0;
|
|
2501
3204
|
} catch {
|
|
2502
3205
|
return void 0;
|
|
@@ -2509,9 +3212,9 @@ function devinAccount(env = process.env) {
|
|
|
2509
3212
|
}
|
|
2510
3213
|
|
|
2511
3214
|
// ../core/src/adapters/gemini-cli.ts
|
|
2512
|
-
import { execFileSync as
|
|
2513
|
-
import { existsSync as existsSync7, readFileSync as
|
|
2514
|
-
import
|
|
3215
|
+
import { execFileSync as execFileSync5 } from "node:child_process";
|
|
3216
|
+
import { existsSync as existsSync7, readFileSync as readFileSync7 } from "node:fs";
|
|
3217
|
+
import path10 from "node:path";
|
|
2515
3218
|
var NAME5 = "gemini-cli";
|
|
2516
3219
|
var GEMINI_EVENTS = [
|
|
2517
3220
|
"SessionStart",
|
|
@@ -2524,22 +3227,22 @@ var GEMINI_EVENTS = [
|
|
|
2524
3227
|
"SessionEnd"
|
|
2525
3228
|
];
|
|
2526
3229
|
var OPTS4 = { matcherFor: (e) => /Tool/.test(e) ? ".*" : void 0, timeout: 5e3 };
|
|
2527
|
-
var geminiDir = (env) => env.GEMINI_CLI_HOME ||
|
|
2528
|
-
var settings = (env) =>
|
|
3230
|
+
var geminiDir = (env) => env.GEMINI_CLI_HOME || path10.join(homeDir(), ".gemini");
|
|
3231
|
+
var settings = (env) => path10.join(geminiDir(env), "settings.json");
|
|
2529
3232
|
var validTool5 = (n) => typeof n === "string" && /^[A-Za-z0-9_.:-]{1,120}$/.test(n);
|
|
2530
3233
|
var eventsFor4 = (lean) => lean ? GEMINI_EVENTS.filter((e) => e !== "BeforeTool") : [...GEMINI_EVENTS];
|
|
2531
3234
|
function geminiVersion(platform = process.platform, env = process.env) {
|
|
2532
3235
|
const win = platform === "win32";
|
|
2533
|
-
const bin = win ? findBinary({ names: ["gemini.exe", "gemini.cmd"], dirs: [
|
|
3236
|
+
const bin = win ? findBinary({ names: ["gemini.exe", "gemini.cmd"], dirs: [path10.dirname(process.execPath)], env }) : "gemini";
|
|
2534
3237
|
if (!bin) throw new Error("no gemini on PATH");
|
|
2535
|
-
const
|
|
3238
|
+
const c = probe(bin, ["--version"]);
|
|
3239
|
+
return execFileSync5(c.file, c.args, {
|
|
2536
3240
|
encoding: "utf8",
|
|
2537
3241
|
timeout: 4e3,
|
|
2538
3242
|
stdio: ["ignore", "pipe", "ignore"],
|
|
2539
|
-
env
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
return out.trim();
|
|
3243
|
+
env,
|
|
3244
|
+
shell: c.shell
|
|
3245
|
+
}).trim();
|
|
2543
3246
|
}
|
|
2544
3247
|
function geminiHooksActive(settingsFile) {
|
|
2545
3248
|
const tools = readJson(settingsFile).tools;
|
|
@@ -2566,7 +3269,7 @@ var geminiCli = {
|
|
|
2566
3269
|
name: NAME5,
|
|
2567
3270
|
events: GEMINI_EVENTS,
|
|
2568
3271
|
// ~/.gemini also belongs to Antigravity; Gemini CLI is here when its settings or its chat logs exist.
|
|
2569
|
-
detect: (env = process.env) => existsSync7(settings(env)) || existsSync7(
|
|
3272
|
+
detect: (env = process.env) => existsSync7(settings(env)) || existsSync7(path10.join(geminiDir(env), "tmp")),
|
|
2570
3273
|
configFiles: (env = process.env) => [settings(env)],
|
|
2571
3274
|
install: (cmd, env = process.env, opts = {}) => {
|
|
2572
3275
|
const r = installClaudeShaped(settings(env), NAME5, eventsFor4(opts.lean), cmd, OPTS4);
|
|
@@ -2664,7 +3367,7 @@ var geminiCli = {
|
|
|
2664
3367
|
readTranscript(file, fromLine) {
|
|
2665
3368
|
let text = "";
|
|
2666
3369
|
try {
|
|
2667
|
-
text =
|
|
3370
|
+
text = readFileSync7(file, "utf8");
|
|
2668
3371
|
} catch {
|
|
2669
3372
|
return { turns: [], line: fromLine };
|
|
2670
3373
|
}
|
|
@@ -2704,12 +3407,12 @@ var ADAPTERS = [claudeCode, codex, geminiCli, antigravity, devin];
|
|
|
2704
3407
|
var adapterByName = (name) => ADAPTERS.find((a) => a.name === name);
|
|
2705
3408
|
|
|
2706
3409
|
// ../core/src/config.ts
|
|
2707
|
-
import { chmodSync, existsSync as existsSync8, mkdirSync as mkdirSync4, readFileSync as
|
|
2708
|
-
import
|
|
3410
|
+
import { chmodSync, existsSync as existsSync8, mkdirSync as mkdirSync4, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "node:fs";
|
|
3411
|
+
import path11 from "node:path";
|
|
2709
3412
|
var DEFAULT_CONFIG = { sinks: [], harnesses: {}, update_check: true, keep_days: 30 };
|
|
2710
3413
|
function readConfig(file = configFile()) {
|
|
2711
3414
|
try {
|
|
2712
|
-
const j = JSON.parse(
|
|
3415
|
+
const j = JSON.parse(readFileSync8(file, "utf8"));
|
|
2713
3416
|
return {
|
|
2714
3417
|
...DEFAULT_CONFIG,
|
|
2715
3418
|
...j,
|
|
@@ -2721,7 +3424,7 @@ function readConfig(file = configFile()) {
|
|
|
2721
3424
|
}
|
|
2722
3425
|
}
|
|
2723
3426
|
function writeConfig(c, file = configFile()) {
|
|
2724
|
-
mkdirSync4(
|
|
3427
|
+
mkdirSync4(path11.dirname(file), { recursive: true, mode: 448 });
|
|
2725
3428
|
writeFileSync5(file, `${JSON.stringify(c, null, 2)}
|
|
2726
3429
|
`, { mode: 384 });
|
|
2727
3430
|
if (process.platform !== "win32" && existsSync8(file)) {
|
|
@@ -2785,38 +3488,22 @@ function makeEvent(a, ctx) {
|
|
|
2785
3488
|
};
|
|
2786
3489
|
}
|
|
2787
3490
|
|
|
2788
|
-
// ../core/src/launched-by.ts
|
|
2789
|
-
var LAUNCHED_BY_ENV = "SESSIONPIPE_LAUNCHED_BY";
|
|
2790
|
-
var LAUNCHED_BY = /^[a-z][a-z0-9-]{0,39}:[A-Za-z0-9._:-]{1,128}$/;
|
|
2791
|
-
function launchedBy(env) {
|
|
2792
|
-
const v = env[LAUNCHED_BY_ENV]?.trim();
|
|
2793
|
-
return v && LAUNCHED_BY.test(v) ? v : void 0;
|
|
2794
|
-
}
|
|
2795
|
-
function readHook(adapter, input) {
|
|
2796
|
-
const r = adapter.fromHook(input);
|
|
2797
|
-
if (r && !r.session.parent_id) {
|
|
2798
|
-
const launcher = launchedBy(input.env);
|
|
2799
|
-
if (launcher && launcher !== `${adapter.name}:${r.session.id}`) r.session.launched_by = launcher;
|
|
2800
|
-
}
|
|
2801
|
-
return r;
|
|
2802
|
-
}
|
|
2803
|
-
|
|
2804
3491
|
// ../core/src/outbox.ts
|
|
2805
3492
|
import {
|
|
2806
3493
|
appendFileSync,
|
|
2807
|
-
closeSync as
|
|
3494
|
+
closeSync as closeSync3,
|
|
2808
3495
|
existsSync as existsSync9,
|
|
2809
3496
|
mkdirSync as mkdirSync5,
|
|
2810
|
-
openSync as
|
|
2811
|
-
readdirSync as
|
|
2812
|
-
readFileSync as
|
|
2813
|
-
readSync as
|
|
3497
|
+
openSync as openSync3,
|
|
3498
|
+
readdirSync as readdirSync6,
|
|
3499
|
+
readFileSync as readFileSync9,
|
|
3500
|
+
readSync as readSync3,
|
|
2814
3501
|
renameSync,
|
|
2815
|
-
statSync as
|
|
3502
|
+
statSync as statSync6,
|
|
2816
3503
|
unlinkSync as unlinkSync3,
|
|
2817
3504
|
writeFileSync as writeFileSync6
|
|
2818
3505
|
} from "node:fs";
|
|
2819
|
-
import
|
|
3506
|
+
import path12 from "node:path";
|
|
2820
3507
|
var READ_CHUNK = 1024 * 1024;
|
|
2821
3508
|
var READ_MAX = 8 * 1024 * 1024;
|
|
2822
3509
|
var Outbox = class {
|
|
@@ -2825,13 +3512,13 @@ var Outbox = class {
|
|
|
2825
3512
|
}
|
|
2826
3513
|
root;
|
|
2827
3514
|
dir(harness) {
|
|
2828
|
-
return
|
|
3515
|
+
return path12.join(this.root, "outbox", safeId(harness));
|
|
2829
3516
|
}
|
|
2830
3517
|
file(ref) {
|
|
2831
|
-
return
|
|
3518
|
+
return path12.join(this.dir(ref.harness), `${safeId(ref.session)}.jsonl`);
|
|
2832
3519
|
}
|
|
2833
3520
|
seqFile(ref) {
|
|
2834
|
-
return
|
|
3521
|
+
return path12.join(this.dir(ref.harness), `${safeId(ref.session)}.seq`);
|
|
2835
3522
|
}
|
|
2836
3523
|
/** The next seq for a session, persisted (survives a hibernating machine and a
|
|
2837
3524
|
* restart; an in-memory counter would restart at 0 and collide). */
|
|
@@ -2839,19 +3526,19 @@ var Outbox = class {
|
|
|
2839
3526
|
const f = this.seqFile(ref);
|
|
2840
3527
|
let n = -1;
|
|
2841
3528
|
try {
|
|
2842
|
-
const t =
|
|
3529
|
+
const t = readFileSync9(f, "utf8").trim();
|
|
2843
3530
|
if (/^\d+$/.test(t)) n = Number(t);
|
|
2844
3531
|
} catch {
|
|
2845
3532
|
}
|
|
2846
3533
|
if (n < 0) n = this.lastSeqInFile(ref) + 1;
|
|
2847
|
-
mkdirSync5(
|
|
3534
|
+
mkdirSync5(path12.dirname(f), { recursive: true, mode: 448 });
|
|
2848
3535
|
writeFileSync6(f, String(n + 1), { mode: 384 });
|
|
2849
3536
|
return n;
|
|
2850
3537
|
}
|
|
2851
3538
|
lastSeqInFile(ref) {
|
|
2852
3539
|
const f = this.file(ref);
|
|
2853
3540
|
if (!existsSync9(f)) return -1;
|
|
2854
|
-
const lines =
|
|
3541
|
+
const lines = readFileSync9(f, "utf8").trim().split("\n");
|
|
2855
3542
|
for (let i = lines.length - 1; i >= 0; i--) {
|
|
2856
3543
|
try {
|
|
2857
3544
|
const j = JSON.parse(lines[i]);
|
|
@@ -2870,19 +3557,19 @@ var Outbox = class {
|
|
|
2870
3557
|
/** Every session file, newest first. */
|
|
2871
3558
|
sessions() {
|
|
2872
3559
|
const out = [];
|
|
2873
|
-
const base =
|
|
3560
|
+
const base = path12.join(this.root, "outbox");
|
|
2874
3561
|
if (!existsSync9(base)) return [];
|
|
2875
|
-
for (const h of
|
|
2876
|
-
const d =
|
|
3562
|
+
for (const h of readdirSync6(base)) {
|
|
3563
|
+
const d = path12.join(base, h);
|
|
2877
3564
|
let names = [];
|
|
2878
3565
|
try {
|
|
2879
|
-
names =
|
|
3566
|
+
names = readdirSync6(d).filter((n) => n.endsWith(".jsonl"));
|
|
2880
3567
|
} catch {
|
|
2881
3568
|
continue;
|
|
2882
3569
|
}
|
|
2883
3570
|
for (const n of names) {
|
|
2884
3571
|
try {
|
|
2885
|
-
out.push({ ref: { harness: h, session: n.slice(0, -6) }, mtime:
|
|
3572
|
+
out.push({ ref: { harness: h, session: n.slice(0, -6) }, mtime: statSync6(path12.join(d, n)).mtimeMs });
|
|
2886
3573
|
} catch {
|
|
2887
3574
|
}
|
|
2888
3575
|
}
|
|
@@ -2898,16 +3585,16 @@ var Outbox = class {
|
|
|
2898
3585
|
read(ref, from, maxEvents = 500) {
|
|
2899
3586
|
const f = this.file(ref);
|
|
2900
3587
|
if (!existsSync9(f)) return { events: [], offset: from };
|
|
2901
|
-
const size =
|
|
3588
|
+
const size = statSync6(f).size;
|
|
2902
3589
|
if (from >= size) return { events: [], offset: from };
|
|
2903
|
-
const fd =
|
|
3590
|
+
const fd = openSync3(f, "r");
|
|
2904
3591
|
try {
|
|
2905
3592
|
const events = [];
|
|
2906
3593
|
let offset = from;
|
|
2907
3594
|
let chunk = READ_CHUNK;
|
|
2908
3595
|
while (events.length < maxEvents && offset < size) {
|
|
2909
3596
|
const buf = Buffer.alloc(Math.min(size - offset, chunk));
|
|
2910
|
-
const n =
|
|
3597
|
+
const n = readSync3(fd, buf, 0, buf.length, offset);
|
|
2911
3598
|
const text = buf.subarray(0, n).toString("utf8");
|
|
2912
3599
|
let pos = 0;
|
|
2913
3600
|
let used = 0;
|
|
@@ -2930,7 +3617,7 @@ var Outbox = class {
|
|
|
2930
3617
|
}
|
|
2931
3618
|
return { events, offset };
|
|
2932
3619
|
} finally {
|
|
2933
|
-
|
|
3620
|
+
closeSync3(fd);
|
|
2934
3621
|
}
|
|
2935
3622
|
}
|
|
2936
3623
|
/** Files older than `days` are pruned; the cursor files with them. */
|
|
@@ -2940,7 +3627,7 @@ var Outbox = class {
|
|
|
2940
3627
|
for (const ref of this.sessions()) {
|
|
2941
3628
|
const f = this.file(ref);
|
|
2942
3629
|
try {
|
|
2943
|
-
if (
|
|
3630
|
+
if (statSync6(f).mtimeMs < cutoff) {
|
|
2944
3631
|
unlinkSync3(f);
|
|
2945
3632
|
try {
|
|
2946
3633
|
unlinkSync3(this.seqFile(ref));
|
|
@@ -2970,18 +3657,18 @@ var Cursors = class {
|
|
|
2970
3657
|
root;
|
|
2971
3658
|
sink;
|
|
2972
3659
|
file() {
|
|
2973
|
-
return
|
|
3660
|
+
return path12.join(this.root, "cursors", `${safeId(this.sink)}.json`);
|
|
2974
3661
|
}
|
|
2975
3662
|
read() {
|
|
2976
3663
|
try {
|
|
2977
|
-
return JSON.parse(
|
|
3664
|
+
return JSON.parse(readFileSync9(this.file(), "utf8"));
|
|
2978
3665
|
} catch {
|
|
2979
3666
|
return {};
|
|
2980
3667
|
}
|
|
2981
3668
|
}
|
|
2982
3669
|
write(c) {
|
|
2983
3670
|
const f = this.file();
|
|
2984
|
-
mkdirSync5(
|
|
3671
|
+
mkdirSync5(path12.dirname(f), { recursive: true, mode: 448 });
|
|
2985
3672
|
const tmp = `${f}.${process.pid}.tmp`;
|
|
2986
3673
|
writeFileSync6(tmp, JSON.stringify(c), { mode: 384 });
|
|
2987
3674
|
renameSync(tmp, f);
|
|
@@ -2994,11 +3681,11 @@ function takeLock(root, ref, staleMs = 5 * 6e4) {
|
|
|
2994
3681
|
return takeNamedLock(root, `${safeId(ref.harness)}-${safeId(ref.session)}`, staleMs);
|
|
2995
3682
|
}
|
|
2996
3683
|
function takeNamedLock(root, name, staleMs = 5 * 6e4) {
|
|
2997
|
-
const dir =
|
|
3684
|
+
const dir = path12.join(root, "locks");
|
|
2998
3685
|
mkdirSync5(dir, { recursive: true, mode: 448 });
|
|
2999
|
-
const f =
|
|
3686
|
+
const f = path12.join(dir, `${name}.lock`);
|
|
3000
3687
|
try {
|
|
3001
|
-
if (Date.now() -
|
|
3688
|
+
if (Date.now() - statSync6(f).mtimeMs > staleMs || !processAlive(Number(readFileSync9(f, "utf8")))) unlinkSync3(f);
|
|
3002
3689
|
else return null;
|
|
3003
3690
|
} catch {
|
|
3004
3691
|
}
|
|
@@ -3236,6 +3923,8 @@ var TYPE_TIER = {
|
|
|
3236
3923
|
"subagent.ended": 1,
|
|
3237
3924
|
"context.compacted": 1,
|
|
3238
3925
|
"limits.observed": 1,
|
|
3926
|
+
"delegation.started": 0,
|
|
3927
|
+
"delegation.ended": 0,
|
|
3239
3928
|
native: 3
|
|
3240
3929
|
};
|
|
3241
3930
|
var DATA_ALLOW = {
|
|
@@ -3246,8 +3935,8 @@ var DATA_ALLOW = {
|
|
|
3246
3935
|
"session.forgotten": { 0: [] },
|
|
3247
3936
|
"turn.started": { 0: ["turn_id", "submitted_by", "injected"], 1: ["prompt_chars"] },
|
|
3248
3937
|
"turn.ended": { 0: ["turn_id", "reason", "ms", "background"], 1: ["error"] },
|
|
3249
|
-
"turn.transcript": { 2: ["turn", "user", "assistant", "at"] },
|
|
3250
|
-
"turn.progress": { 2: ["turn", "user", "assistant", "at"] },
|
|
3938
|
+
"turn.transcript": { 2: ["turn", "user", "assistant", "at", "injected"] },
|
|
3939
|
+
"turn.progress": { 2: ["turn", "user", "assistant", "at", "injected"] },
|
|
3251
3940
|
"tool.started": { 1: ["tool", "call_id", "turn_id", "agent_id"], 3: ["input"] },
|
|
3252
3941
|
"tool.ended": { 1: ["tool", "call_id", "turn_id", "agent_id", "ms", "ok", "error"], 3: ["input", "output"] },
|
|
3253
3942
|
"files.changed": { 1: ["paths"] },
|
|
@@ -3256,7 +3945,9 @@ var DATA_ALLOW = {
|
|
|
3256
3945
|
"subagent.started": { 1: ["agent_id", "agent_type"] },
|
|
3257
3946
|
"subagent.ended": { 1: ["agent_id", "agent_type"] },
|
|
3258
3947
|
"context.compacted": { 1: ["trigger"] },
|
|
3259
|
-
"limits.observed": { 1: ["windows", "plan", "account_id"] }
|
|
3948
|
+
"limits.observed": { 1: ["windows", "plan", "account_id"] },
|
|
3949
|
+
"delegation.started": { 0: ["ask_id", "via", "harness", "to"], 1: ["text"] },
|
|
3950
|
+
"delegation.ended": { 0: ["ask_id", "to", "ok", "ms", "error", "relayed"], 2: ["reply"] }
|
|
3260
3951
|
};
|
|
3261
3952
|
var SESSION_ALLOW = [
|
|
3262
3953
|
"id",
|
|
@@ -3272,7 +3963,8 @@ var SESSION_ALLOW = [
|
|
|
3272
3963
|
"account_id",
|
|
3273
3964
|
"attended",
|
|
3274
3965
|
"parent_id",
|
|
3275
|
-
"launched_by"
|
|
3966
|
+
"launched_by",
|
|
3967
|
+
"delegated_by"
|
|
3276
3968
|
];
|
|
3277
3969
|
var MAX_SIDE = 2e4;
|
|
3278
3970
|
var MAX_IO = 64 * 1024;
|
|
@@ -3311,6 +4003,8 @@ function filterEvent(event, tier, pii, opts = {}) {
|
|
|
3311
4003
|
if (data.input !== void 0) data.input = cap(data.input, MAX_IO);
|
|
3312
4004
|
if (data.output !== void 0) data.output = cap(data.output, MAX_IO);
|
|
3313
4005
|
if (typeof data.message === "string") data.message = cap(data.message, 200);
|
|
4006
|
+
if (event.type === "delegation.started" && typeof data.text === "string") data.text = cap(data.text, 200);
|
|
4007
|
+
if (event.type === "delegation.ended" && typeof data.reply === "string") data.reply = cap(data.reply, MAX_SIDE);
|
|
3314
4008
|
const rulesets = [];
|
|
3315
4009
|
let out = { ...event, tier, session, data, privacy: { rulesets, pii } };
|
|
3316
4010
|
if (tier > 0) {
|
|
@@ -3338,7 +4032,7 @@ function shortId(harness, id) {
|
|
|
3338
4032
|
|
|
3339
4033
|
// ../core/src/sinks/file.ts
|
|
3340
4034
|
import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync6 } from "node:fs";
|
|
3341
|
-
import
|
|
4035
|
+
import path13 from "node:path";
|
|
3342
4036
|
var FileSink = class {
|
|
3343
4037
|
constructor(name, file, tier, pii) {
|
|
3344
4038
|
this.name = name;
|
|
@@ -3352,7 +4046,7 @@ var FileSink = class {
|
|
|
3352
4046
|
pii;
|
|
3353
4047
|
async send(events) {
|
|
3354
4048
|
try {
|
|
3355
|
-
mkdirSync6(
|
|
4049
|
+
mkdirSync6(path13.dirname(this.file), { recursive: true });
|
|
3356
4050
|
appendFileSync2(this.file, `${events.map((e) => JSON.stringify(e)).join("\n")}
|
|
3357
4051
|
`);
|
|
3358
4052
|
return { ok: true, acknowledged: events.length, accepted: events.length, duplicates: 0, rejected: [] };
|
|
@@ -3386,8 +4080,8 @@ var StdoutSink = class {
|
|
|
3386
4080
|
};
|
|
3387
4081
|
|
|
3388
4082
|
// src/control/aliases.ts
|
|
3389
|
-
import { chmodSync as chmodSync2, mkdirSync as mkdirSync7, readFileSync as
|
|
3390
|
-
import
|
|
4083
|
+
import { chmodSync as chmodSync2, mkdirSync as mkdirSync7, readFileSync as readFileSync10, renameSync as renameSync2, writeFileSync as writeFileSync7 } from "node:fs";
|
|
4084
|
+
import path14 from "node:path";
|
|
3391
4085
|
|
|
3392
4086
|
// src/secrets.ts
|
|
3393
4087
|
import { spawnSync } from "node:child_process";
|
|
@@ -3431,10 +4125,10 @@ function startOptionsOf(v) {
|
|
|
3431
4125
|
}
|
|
3432
4126
|
|
|
3433
4127
|
// src/control/aliases.ts
|
|
3434
|
-
var aliasFile = (controlDir) =>
|
|
4128
|
+
var aliasFile = (controlDir) => path14.join(controlDir, "aliases.json");
|
|
3435
4129
|
function readAliases(file) {
|
|
3436
4130
|
try {
|
|
3437
|
-
const j = JSON.parse(
|
|
4131
|
+
const j = JSON.parse(readFileSync10(file, "utf8"));
|
|
3438
4132
|
if (!j || typeof j !== "object" || Array.isArray(j)) return {};
|
|
3439
4133
|
const out = {};
|
|
3440
4134
|
for (const [k, v] of Object.entries(j)) {
|
|
@@ -3455,12 +4149,12 @@ function receiverIdOf(all, harness, id) {
|
|
|
3455
4149
|
|
|
3456
4150
|
// src/control/local.ts
|
|
3457
4151
|
import net from "node:net";
|
|
3458
|
-
import
|
|
3459
|
-
import
|
|
4152
|
+
import os3 from "node:os";
|
|
4153
|
+
import path15 from "node:path";
|
|
3460
4154
|
function socketPath(state, env = process.env) {
|
|
3461
4155
|
if (env.SESSIONPIPE_SOCKET) return env.SESSIONPIPE_SOCKET;
|
|
3462
|
-
if (process.platform === "win32") return `\\\\.\\pipe\\sessionpipe-control-${
|
|
3463
|
-
return
|
|
4156
|
+
if (process.platform === "win32") return `\\\\.\\pipe\\sessionpipe-control-${os3.userInfo().username}`;
|
|
4157
|
+
return path15.join(state, "control.sock");
|
|
3464
4158
|
}
|
|
3465
4159
|
function ask(sock, req, timeoutMs) {
|
|
3466
4160
|
return new Promise((resolve) => {
|
|
@@ -3496,6 +4190,11 @@ function ask(sock, req, timeoutMs) {
|
|
|
3496
4190
|
async function tellDaemon(state, job, session, cwd, transcript) {
|
|
3497
4191
|
const sock = socketPath(state);
|
|
3498
4192
|
if (process.platform !== "win32" && !existsSync10(sock)) return;
|
|
4193
|
+
let mode = null;
|
|
4194
|
+
try {
|
|
4195
|
+
mode = claude_code_control_exports.hookMode(JSON.parse(job.stdin));
|
|
4196
|
+
} catch {
|
|
4197
|
+
}
|
|
3499
4198
|
await ask(
|
|
3500
4199
|
sock,
|
|
3501
4200
|
{
|
|
@@ -3503,7 +4202,8 @@ async function tellDaemon(state, job, session, cwd, transcript) {
|
|
|
3503
4202
|
session: `${job.harness}:${session}`,
|
|
3504
4203
|
event: job.event,
|
|
3505
4204
|
...cwd ? { cwd } : {},
|
|
3506
|
-
...transcript ? { transcript } : {}
|
|
4205
|
+
...transcript ? { transcript } : {},
|
|
4206
|
+
...mode ? { mode } : {}
|
|
3507
4207
|
},
|
|
3508
4208
|
300
|
|
3509
4209
|
);
|
|
@@ -3511,7 +4211,7 @@ async function tellDaemon(state, job, session, cwd, transcript) {
|
|
|
3511
4211
|
function enqueueJob(state, job) {
|
|
3512
4212
|
const jobs = jobsDir(state);
|
|
3513
4213
|
mkdirSync8(jobs, { recursive: true, mode: 448 });
|
|
3514
|
-
const f =
|
|
4214
|
+
const f = path16.join(jobs, `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}.json`);
|
|
3515
4215
|
writeFileSync8(f, JSON.stringify(job), { mode: 384 });
|
|
3516
4216
|
return f;
|
|
3517
4217
|
}
|
|
@@ -3525,9 +4225,9 @@ function releaseJob(claimed) {
|
|
|
3525
4225
|
function log(state, line) {
|
|
3526
4226
|
try {
|
|
3527
4227
|
mkdirSync8(state, { recursive: true, mode: 448 });
|
|
3528
|
-
const f =
|
|
4228
|
+
const f = path16.join(state, "log");
|
|
3529
4229
|
try {
|
|
3530
|
-
if (
|
|
4230
|
+
if (statSync7(f).size > 1024 * 1024) renameSync3(f, `${f}.1`);
|
|
3531
4231
|
} catch {
|
|
3532
4232
|
}
|
|
3533
4233
|
appendFileSync3(f, `${(/* @__PURE__ */ new Date()).toISOString()} ${line}
|
|
@@ -3536,11 +4236,11 @@ function log(state, line) {
|
|
|
3536
4236
|
}
|
|
3537
4237
|
}
|
|
3538
4238
|
function factsState(state) {
|
|
3539
|
-
const file = (h, s) =>
|
|
4239
|
+
const file = (h, s) => path16.join(state, "facts", safeId(h), `${safeId(s)}.json`);
|
|
3540
4240
|
return {
|
|
3541
4241
|
get(h, s) {
|
|
3542
4242
|
try {
|
|
3543
|
-
return JSON.parse(
|
|
4243
|
+
return JSON.parse(readFileSync11(file(h, s), "utf8"));
|
|
3544
4244
|
} catch {
|
|
3545
4245
|
return {};
|
|
3546
4246
|
}
|
|
@@ -3549,7 +4249,7 @@ function factsState(state) {
|
|
|
3549
4249
|
const cur = this.get(h, s);
|
|
3550
4250
|
const next = { ...cur, ...patch };
|
|
3551
4251
|
if (JSON.stringify(next) === JSON.stringify(cur)) return;
|
|
3552
|
-
mkdirSync8(
|
|
4252
|
+
mkdirSync8(path16.dirname(file(h, s)), { recursive: true, mode: 448 });
|
|
3553
4253
|
writeFileSync8(file(h, s), JSON.stringify(next), { mode: 384 });
|
|
3554
4254
|
}
|
|
3555
4255
|
};
|
|
@@ -3566,7 +4266,7 @@ function buildSinks(sinks, o = {}) {
|
|
|
3566
4266
|
const tier = Math.min(s.tier, s.max_tier ?? 3);
|
|
3567
4267
|
if (s.url === "stdout") out.push(new StdoutSink(s.name, tier, !!s.pii));
|
|
3568
4268
|
else if (s.url.startsWith("file:"))
|
|
3569
|
-
out.push(new FileSink(s.name, s.url.slice(5).replace(/^~(?=\/|$)/,
|
|
4269
|
+
out.push(new FileSink(s.name, s.url.slice(5).replace(/^~(?=\/|$)/, os4.homedir()), tier, !!s.pii));
|
|
3570
4270
|
else if (/^https?:\/\//.test(s.url)) out.push(new HttpSink(s, tier));
|
|
3571
4271
|
}
|
|
3572
4272
|
return out;
|
|
@@ -3574,13 +4274,14 @@ function buildSinks(sinks, o = {}) {
|
|
|
3574
4274
|
var PROGRESS_MS = 2e4;
|
|
3575
4275
|
function openTurn(read, o) {
|
|
3576
4276
|
const last = read.turns[read.turns.length - 1];
|
|
3577
|
-
const fresh = (user, at) => ({
|
|
4277
|
+
const fresh = (user, at, injected) => ({
|
|
3578
4278
|
turn: o.base + read.turns.length,
|
|
3579
4279
|
user: user.slice(0, MAX_TURN_SIDE),
|
|
3580
4280
|
assistant: "",
|
|
3581
|
-
at
|
|
4281
|
+
at,
|
|
4282
|
+
...injected ? { injected: true } : {}
|
|
3582
4283
|
});
|
|
3583
|
-
if (read.open) return fresh(read.open.user, read.open.at);
|
|
4284
|
+
if (read.open) return fresh(read.open.user, read.open.at, read.open.injected);
|
|
3584
4285
|
const prompt = o.prompt?.trim();
|
|
3585
4286
|
if (prompt && !isInjected(prompt) && !(last && last.user.includes(prompt.slice(0, 200)))) return fresh(prompt, o.at);
|
|
3586
4287
|
if (!last) return null;
|
|
@@ -3588,6 +4289,7 @@ function openTurn(read, o) {
|
|
|
3588
4289
|
turn: o.base + read.turns.length - 1,
|
|
3589
4290
|
user: last.user.slice(0, MAX_TURN_SIDE),
|
|
3590
4291
|
assistant: capTail(last.assistant, MAX_TURN_SIDE),
|
|
4292
|
+
...last.injected ? { injected: true } : {},
|
|
3591
4293
|
at: last.at
|
|
3592
4294
|
};
|
|
3593
4295
|
}
|
|
@@ -3595,6 +4297,7 @@ var MAX_TURN_SIDE = 2e4;
|
|
|
3595
4297
|
var JOB_OWN_ENV = [
|
|
3596
4298
|
"CLAUDE_CODE_SESSION_ATTENDED",
|
|
3597
4299
|
"CLAUDE_CODE_HOST_SESSION_ID",
|
|
4300
|
+
"SESSIONPIPE_ASKED_BY",
|
|
3598
4301
|
"SESSIONPIPE_CONTROL_JOB",
|
|
3599
4302
|
"SESSIONPIPE_LAUNCHED_BY"
|
|
3600
4303
|
];
|
|
@@ -3622,7 +4325,7 @@ async function runJob(job, opts = {}) {
|
|
|
3622
4325
|
);
|
|
3623
4326
|
return { events: [], delivered: {} };
|
|
3624
4327
|
}
|
|
3625
|
-
const id = receiverIdOf(readAliases(aliasFile(
|
|
4328
|
+
const id = receiverIdOf(readAliases(aliasFile(path16.join(state, "control"))), job.harness, r.session.id) ?? r.session.id;
|
|
3626
4329
|
const claudeEnd = job.harness === "claude-code" && r.events.some((e) => e.type === "session.ended");
|
|
3627
4330
|
if (!claudeEnd) await tellDaemon(state, job, id, r.session.cwd, r.transcript);
|
|
3628
4331
|
const outbox = new Outbox(state);
|
|
@@ -3659,7 +4362,7 @@ async function runJob(job, opts = {}) {
|
|
|
3659
4362
|
learned = rest;
|
|
3660
4363
|
if (harness_version) facts.save(job.harness, id, { harness_version });
|
|
3661
4364
|
facts.save(job.harness, id, { session: { ...mem.session, ...learned } });
|
|
3662
|
-
if (CWD_FROM_HOOKS.has(job.harness) && r.session.cwd &&
|
|
4365
|
+
if (CWD_FROM_HOOKS.has(job.harness) && r.session.cwd && path16.isAbsolute(r.session.cwd))
|
|
3663
4366
|
facts.save(job.harness, id, { cwd: r.session.cwd });
|
|
3664
4367
|
}
|
|
3665
4368
|
const known = mem.session ?? {};
|
|
@@ -3672,10 +4375,11 @@ async function runJob(job, opts = {}) {
|
|
|
3672
4375
|
...learned.branch ? { branch: learned.branch } : {},
|
|
3673
4376
|
id,
|
|
3674
4377
|
seq: 0,
|
|
3675
|
-
machine: machineName(cfg,
|
|
4378
|
+
machine: machineName(cfg, os4.hostname()),
|
|
3676
4379
|
...cwdRaw ? { cwd: tilde(cwdRaw) } : {},
|
|
3677
4380
|
...r.session.model ? { model: r.session.model } : {},
|
|
3678
|
-
...r.session.launched_by ? { launched_by: r.session.launched_by } : {}
|
|
4381
|
+
...r.session.launched_by ? { launched_by: r.session.launched_by } : {},
|
|
4382
|
+
...r.session.delegated_by ? { delegated_by: r.session.delegated_by } : {}
|
|
3679
4383
|
};
|
|
3680
4384
|
const version = facts.get(job.harness, id).harness_version ?? void 0;
|
|
3681
4385
|
for (const a of r.events) {
|
|
@@ -3729,7 +4433,13 @@ async function runJob(job, opts = {}) {
|
|
|
3729
4433
|
{
|
|
3730
4434
|
type: "turn.transcript",
|
|
3731
4435
|
harnessEvent: job.event,
|
|
3732
|
-
data: {
|
|
4436
|
+
data: {
|
|
4437
|
+
turn: turnNo++,
|
|
4438
|
+
user: t.user,
|
|
4439
|
+
assistant: t.assistant,
|
|
4440
|
+
at: new Date(t.at).toISOString(),
|
|
4441
|
+
...t.injected ? { injected: true } : {}
|
|
4442
|
+
}
|
|
3733
4443
|
},
|
|
3734
4444
|
{
|
|
3735
4445
|
harness: { name: job.harness, ...version ? { version } : {} },
|
|
@@ -3775,7 +4485,7 @@ async function runJob(job, opts = {}) {
|
|
|
3775
4485
|
return { events, delivered };
|
|
3776
4486
|
}
|
|
3777
4487
|
async function drain(state, sinkConfigs, outbox, first) {
|
|
3778
|
-
const more =
|
|
4488
|
+
const more = path16.join(state, "locks", "flush.more");
|
|
3779
4489
|
let release = takeNamedLock(state, "flush");
|
|
3780
4490
|
if (!release) {
|
|
3781
4491
|
try {
|
|
@@ -3803,10 +4513,10 @@ async function drain(state, sinkConfigs, outbox, first) {
|
|
|
3803
4513
|
}
|
|
3804
4514
|
}
|
|
3805
4515
|
var BACKOFF_MS = [2e3, 1e4, 6e4, 5 * 6e4, 30 * 6e4];
|
|
3806
|
-
var backoffFile = (state, sink) =>
|
|
4516
|
+
var backoffFile = (state, sink) => path16.join(state, "backoff", `${safeId(sink)}.json`);
|
|
3807
4517
|
function readBackoff(state, sink) {
|
|
3808
4518
|
try {
|
|
3809
|
-
const b = JSON.parse(
|
|
4519
|
+
const b = JSON.parse(readFileSync11(backoffFile(state, sink), "utf8"));
|
|
3810
4520
|
return typeof b.until === "number" ? b : null;
|
|
3811
4521
|
} catch {
|
|
3812
4522
|
return null;
|
|
@@ -3819,7 +4529,7 @@ function noteFailure(state, sink, res, prev) {
|
|
|
3819
4529
|
const b = { until: Date.now() + wait, failures, why: `${res.status ?? ""} ${res.error ?? ""}`.trim() };
|
|
3820
4530
|
const f = backoffFile(state, sink);
|
|
3821
4531
|
try {
|
|
3822
|
-
mkdirSync8(
|
|
4532
|
+
mkdirSync8(path16.dirname(f), { recursive: true, mode: 448 });
|
|
3823
4533
|
writeFileSync8(f, JSON.stringify(b), { mode: 384 });
|
|
3824
4534
|
} catch {
|
|
3825
4535
|
}
|
|
@@ -3919,7 +4629,7 @@ async function flush(state, sinkConfigs, outbox, first, o = {}) {
|
|
|
3919
4629
|
const { events, offset: next } = outbox.read(ref, offset, room);
|
|
3920
4630
|
if (!events.length) break;
|
|
3921
4631
|
for (const e of events) {
|
|
3922
|
-
const f = filterEvent(e, sink.tier, sink.pii, { home:
|
|
4632
|
+
const f = filterEvent(e, sink.tier, sink.pii, { home: os4.homedir() });
|
|
3923
4633
|
if (f) batch.push(f);
|
|
3924
4634
|
}
|
|
3925
4635
|
offset = next;
|
|
@@ -3952,7 +4662,7 @@ function applySinkVerdict(name, res) {
|
|
|
3952
4662
|
} catch {
|
|
3953
4663
|
}
|
|
3954
4664
|
}
|
|
3955
|
-
var jobsDir = (state) =>
|
|
4665
|
+
var jobsDir = (state) => path16.join(state, "jobs");
|
|
3956
4666
|
export {
|
|
3957
4667
|
HttpSink,
|
|
3958
4668
|
buildSinks,
|