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/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 });
|
|
@@ -618,13 +661,7 @@ var claudeCode = {
|
|
|
618
661
|
);
|
|
619
662
|
break;
|
|
620
663
|
case "Stop":
|
|
621
|
-
events.push(
|
|
622
|
-
ev("turn.ended", {
|
|
623
|
-
turn_id,
|
|
624
|
-
reason: "stop",
|
|
625
|
-
background: Array.isArray(s.background_tasks) ? s.background_tasks.length : void 0
|
|
626
|
-
})
|
|
627
|
-
);
|
|
664
|
+
events.push(ev("turn.ended", { turn_id, reason: "stop", background: backgroundCount(s.background_tasks) }));
|
|
628
665
|
break;
|
|
629
666
|
case "StopFailure":
|
|
630
667
|
events.push(
|
|
@@ -647,7 +684,8 @@ var claudeCode = {
|
|
|
647
684
|
events.push(
|
|
648
685
|
ev("subagent.ended", {
|
|
649
686
|
agent_id: String(s.agent_id ?? ""),
|
|
650
|
-
agent_type: typeof s.agent_type === "string" ? s.agent_type : void 0
|
|
687
|
+
agent_type: typeof s.agent_type === "string" ? s.agent_type : void 0,
|
|
688
|
+
background: backgroundCount(s.background_tasks, String(s.agent_id ?? ""))
|
|
651
689
|
})
|
|
652
690
|
);
|
|
653
691
|
break;
|
|
@@ -722,6 +760,15 @@ var claudeCode = {
|
|
|
722
760
|
return rows2;
|
|
723
761
|
}
|
|
724
762
|
};
|
|
763
|
+
var WAITER_RE = /(?:^|[\s"'`/\\])sessionpipe(?:\.\w+)?["']?\s+wait(?:\s|$)/;
|
|
764
|
+
function backgroundCount(tasks, ending) {
|
|
765
|
+
if (!Array.isArray(tasks)) return void 0;
|
|
766
|
+
return tasks.filter((t) => {
|
|
767
|
+
const task = t;
|
|
768
|
+
if (ending !== void 0 && task?.id === ending) return false;
|
|
769
|
+
return !(typeof task?.command === "string" && WAITER_RE.test(task.command));
|
|
770
|
+
}).length;
|
|
771
|
+
}
|
|
725
772
|
var LINE_REFRESH_S = 60;
|
|
726
773
|
function statusLine(file, want, cmd) {
|
|
727
774
|
const f = readJsonFile(file);
|
|
@@ -1150,8 +1197,44 @@ async function claudeAiTitle(bridge, token) {
|
|
|
1150
1197
|
}
|
|
1151
1198
|
|
|
1152
1199
|
// ../core/src/adapters/headless.ts
|
|
1153
|
-
import {
|
|
1200
|
+
import { spawn } from "node:child_process";
|
|
1201
|
+
import { accessSync, constants, readFileSync as readFileSync3, statSync as statSync3 } from "node:fs";
|
|
1154
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
|
|
1155
1238
|
var JOB_TIMEOUT_MS = 30 * 6e4;
|
|
1156
1239
|
var OUT_CAP = 1024 * 1024;
|
|
1157
1240
|
function isExe(p) {
|
|
@@ -1172,13 +1255,144 @@ function findBinary(o) {
|
|
|
1172
1255
|
for (const d of o.dirs) for (const n of o.names) if (isExe(path5.join(d, n))) return path5.join(d, n);
|
|
1173
1256
|
return null;
|
|
1174
1257
|
}
|
|
1258
|
+
function shimTarget(shim) {
|
|
1259
|
+
let text = "";
|
|
1260
|
+
try {
|
|
1261
|
+
text = readFileSync3(shim, "utf8");
|
|
1262
|
+
} catch {
|
|
1263
|
+
return null;
|
|
1264
|
+
}
|
|
1265
|
+
const m = /"%(?:~dp0|dp0%)\\([^"]+)"\s+%\*/.exec(text);
|
|
1266
|
+
if (!m) return null;
|
|
1267
|
+
const target = path5.resolve(path5.dirname(shim), m[1].split("\\").join(path5.sep));
|
|
1268
|
+
try {
|
|
1269
|
+
return statSync3(target).isFile() ? target : null;
|
|
1270
|
+
} catch {
|
|
1271
|
+
return null;
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
function spawnable(bin, args) {
|
|
1275
|
+
if (!/\.(cmd|bat)$/i.test(bin)) return { file: bin, args };
|
|
1276
|
+
const target = shimTarget(bin);
|
|
1277
|
+
return target ? { file: process.execPath, args: [target, ...args] } : null;
|
|
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`;
|
|
1280
|
+
function probe(bin, args) {
|
|
1281
|
+
const s = spawnable(bin, args);
|
|
1282
|
+
return s ? { ...s, shell: false } : { file: [`"${bin}"`, ...args].join(" "), args: [], shell: true };
|
|
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
|
+
}
|
|
1175
1389
|
|
|
1176
1390
|
// ../core/src/adapters/antigravity.ts
|
|
1177
1391
|
import {
|
|
1178
1392
|
existsSync as existsSync3,
|
|
1179
1393
|
mkdirSync as mkdirSync2,
|
|
1180
1394
|
readdirSync as readdirSync3,
|
|
1181
|
-
readFileSync as
|
|
1395
|
+
readFileSync as readFileSync4,
|
|
1182
1396
|
realpathSync,
|
|
1183
1397
|
statSync as statSync4,
|
|
1184
1398
|
unlinkSync as unlinkSync2,
|
|
@@ -1307,7 +1521,7 @@ var antigravity = {
|
|
|
1307
1521
|
readTranscript(file, fromLine) {
|
|
1308
1522
|
let text = "";
|
|
1309
1523
|
try {
|
|
1310
|
-
text =
|
|
1524
|
+
text = readFileSync4(file, "utf8");
|
|
1311
1525
|
} catch {
|
|
1312
1526
|
return { turns: [], line: fromLine };
|
|
1313
1527
|
}
|
|
@@ -1386,7 +1600,7 @@ function agProjects(dir) {
|
|
|
1386
1600
|
if (!ID_RE.test(id)) continue;
|
|
1387
1601
|
let j;
|
|
1388
1602
|
try {
|
|
1389
|
-
j = JSON.parse(
|
|
1603
|
+
j = JSON.parse(readFileSync4(path6.join(dir, n), "utf8"));
|
|
1390
1604
|
} catch {
|
|
1391
1605
|
continue;
|
|
1392
1606
|
}
|
|
@@ -1423,7 +1637,7 @@ function antigravityRemoteUrl(transcript, opts) {
|
|
|
1423
1637
|
for (const d of dirs) {
|
|
1424
1638
|
try {
|
|
1425
1639
|
const m = /installation_uuid:\s*"([0-9a-f-]{36})"/i.exec(
|
|
1426
|
-
|
|
1640
|
+
readFileSync4(path6.join(d, "antigravity_state.pbtxt"), "utf8")
|
|
1427
1641
|
);
|
|
1428
1642
|
if (!m) continue;
|
|
1429
1643
|
const base = `https://antigravity.google.com/r/${m[1]}-v2`;
|
|
@@ -1461,6 +1675,50 @@ function antigravityFacts(file, opts) {
|
|
|
1461
1675
|
return out;
|
|
1462
1676
|
}
|
|
1463
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
|
+
|
|
1464
1722
|
// ../core/src/control/verify.ts
|
|
1465
1723
|
var GRANT_MAX_MS = 24 * 36e5;
|
|
1466
1724
|
var SKEW_MS = 5 * 6e4;
|
|
@@ -1471,10 +1729,10 @@ var MODEL_RE = /^[A-Za-z0-9][A-Za-z0-9._:/@[\]-]*$/;
|
|
|
1471
1729
|
var START_PERMISSIONS = ["read", "safe", "edits", "auto"];
|
|
1472
1730
|
var levelRank = (l) => START_PERMISSIONS.indexOf(l);
|
|
1473
1731
|
var FIELDS = {
|
|
1474
|
-
prompt: ["text", "files"],
|
|
1732
|
+
prompt: ["text", "files", "reply_to"],
|
|
1475
1733
|
"permission.answer": ["for", "decision"],
|
|
1476
1734
|
cancel: [],
|
|
1477
|
-
start: ["text", "cwd", "files", "permission", "model"],
|
|
1735
|
+
start: ["text", "cwd", "files", "permission", "model", "reply_to"],
|
|
1478
1736
|
"key.add": ["key", "name"],
|
|
1479
1737
|
pool: ["pool_action", "pool_account", "pool_order", "pool_code"]
|
|
1480
1738
|
};
|
|
@@ -1482,9 +1740,454 @@ var POOL_FIELDS = FIELDS.pool;
|
|
|
1482
1740
|
var enc = new TextEncoder();
|
|
1483
1741
|
var dec = new TextDecoder();
|
|
1484
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
|
+
|
|
1485
2188
|
// ../core/src/adapters/codex.ts
|
|
1486
|
-
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync as
|
|
1487
|
-
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";
|
|
1488
2191
|
|
|
1489
2192
|
// ../core/src/adapters/codex-config.ts
|
|
1490
2193
|
var NOTIFY_LINE = /^[ \t]*notify[ \t]*=[ \t]*(\[.*\])[ \t]*(?:#.*)?(?=\r?$)/m;
|
|
@@ -1630,24 +2333,24 @@ var CODEX_EVENTS = [
|
|
|
1630
2333
|
];
|
|
1631
2334
|
var OPTS2 = { matcherFor: (e) => /Tool|Permission/.test(e) ? "" : void 0, timeout: 5 };
|
|
1632
2335
|
var eventsFor2 = (lean) => lean ? CODEX_EVENTS.filter((e) => e !== "PreToolUse") : [...CODEX_EVENTS];
|
|
1633
|
-
var codexHome = (env) => env.CODEX_HOME ||
|
|
1634
|
-
var hooksFile2 = (env) =>
|
|
1635
|
-
var configFile2 = (env) =>
|
|
1636
|
-
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");
|
|
1637
2340
|
var validTool3 = (n) => typeof n === "string" && /^[A-Za-z0-9_.:-]{1,120}$/.test(n);
|
|
1638
2341
|
var endReason2 = (r) => r === "clear" || r === "logout" || r === "exit" ? r : "other";
|
|
1639
2342
|
function installNotify(cmd, env) {
|
|
1640
2343
|
const file = configFile2(env);
|
|
1641
2344
|
let text = "";
|
|
1642
2345
|
try {
|
|
1643
|
-
text =
|
|
2346
|
+
text = readFileSync6(file, "utf8");
|
|
1644
2347
|
} catch {
|
|
1645
2348
|
}
|
|
1646
2349
|
const argv = cmd([NAME3, "notify"]).argv;
|
|
1647
2350
|
const line = `notify = ${JSON.stringify(argv)}`;
|
|
1648
2351
|
const n = readNotify(text);
|
|
1649
2352
|
const write = (next) => {
|
|
1650
|
-
mkdirSync3(
|
|
2353
|
+
mkdirSync3(path8.dirname(file), { recursive: true });
|
|
1651
2354
|
writeFileSync4(file, next);
|
|
1652
2355
|
};
|
|
1653
2356
|
if (n && !n.other) {
|
|
@@ -1686,7 +2389,7 @@ function uninstallNotify(env) {
|
|
|
1686
2389
|
const file = configFile2(env);
|
|
1687
2390
|
let text;
|
|
1688
2391
|
try {
|
|
1689
|
-
text =
|
|
2392
|
+
text = readFileSync6(file, "utf8");
|
|
1690
2393
|
} catch {
|
|
1691
2394
|
return { file, changed: false };
|
|
1692
2395
|
}
|
|
@@ -1709,12 +2412,12 @@ function findRollout(threadId, env = process.env) {
|
|
|
1709
2412
|
const walk = (dir, depth) => {
|
|
1710
2413
|
let names = [];
|
|
1711
2414
|
try {
|
|
1712
|
-
names =
|
|
2415
|
+
names = readdirSync5(dir);
|
|
1713
2416
|
} catch {
|
|
1714
2417
|
return;
|
|
1715
2418
|
}
|
|
1716
2419
|
for (const n of names.sort().reverse()) {
|
|
1717
|
-
const p =
|
|
2420
|
+
const p = path8.join(dir, n);
|
|
1718
2421
|
if (n.endsWith(`-${threadId}.jsonl`)) {
|
|
1719
2422
|
best = p;
|
|
1720
2423
|
return;
|
|
@@ -1758,7 +2461,7 @@ var codex = {
|
|
|
1758
2461
|
];
|
|
1759
2462
|
let text = "";
|
|
1760
2463
|
try {
|
|
1761
|
-
text =
|
|
2464
|
+
text = readFileSync6(configFile2(env), "utf8");
|
|
1762
2465
|
} catch {
|
|
1763
2466
|
}
|
|
1764
2467
|
const n = readNotify(text);
|
|
@@ -1885,7 +2588,7 @@ var codex = {
|
|
|
1885
2588
|
readTranscript(file, fromLine) {
|
|
1886
2589
|
let text = "";
|
|
1887
2590
|
try {
|
|
1888
|
-
text =
|
|
2591
|
+
text = readFileSync6(file, "utf8");
|
|
1889
2592
|
} catch {
|
|
1890
2593
|
return { turns: [], line: fromLine };
|
|
1891
2594
|
}
|
|
@@ -1899,7 +2602,7 @@ var codex = {
|
|
|
1899
2602
|
backfill(sinceMs, env = process.env) {
|
|
1900
2603
|
const rows2 = [];
|
|
1901
2604
|
for (const f of recentFiles(sessionsDir(env), 3, sinceMs, (n) => /^rollout-.*\.jsonl$/.test(n))) {
|
|
1902
|
-
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));
|
|
1903
2606
|
if (!m?.[1]) continue;
|
|
1904
2607
|
if (codexSubagent(rolloutMeta(f))) continue;
|
|
1905
2608
|
const facts = codexFacts(f);
|
|
@@ -1986,10 +2689,10 @@ function codexFacts(file) {
|
|
|
1986
2689
|
// ../core/src/adapters/devin.ts
|
|
1987
2690
|
import { createHash as createHash2 } from "node:crypto";
|
|
1988
2691
|
import { existsSync as existsSync6, readlinkSync } from "node:fs";
|
|
1989
|
-
import
|
|
2692
|
+
import path9 from "node:path";
|
|
1990
2693
|
|
|
1991
2694
|
// ../core/src/readers/sqlite.ts
|
|
1992
|
-
import { execFileSync as
|
|
2695
|
+
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
1993
2696
|
import { existsSync as existsSync5 } from "node:fs";
|
|
1994
2697
|
import { createRequire } from "node:module";
|
|
1995
2698
|
function quiet(fn) {
|
|
@@ -2069,7 +2772,7 @@ db.close();
|
|
|
2069
2772
|
process.stdout.write(JSON.stringify(out));`;
|
|
2070
2773
|
function viaChild(file, sql, params, o) {
|
|
2071
2774
|
try {
|
|
2072
|
-
const out =
|
|
2775
|
+
const out = execFileSync4(
|
|
2073
2776
|
process.execPath,
|
|
2074
2777
|
["--experimental-sqlite", "--no-warnings", "-e", CHILD, file, sql, JSON.stringify(params), String(o.timeoutMs)],
|
|
2075
2778
|
{
|
|
@@ -2113,7 +2816,7 @@ function viaCli(file, sql, params, o) {
|
|
|
2113
2816
|
const text = inlineParams(sql, params);
|
|
2114
2817
|
if (!text) return null;
|
|
2115
2818
|
try {
|
|
2116
|
-
const out =
|
|
2819
|
+
const out = execFileSync4("sqlite3", ["-readonly", "-json", file, text], {
|
|
2117
2820
|
encoding: "utf8",
|
|
2118
2821
|
timeout: o.timeoutMs + 3e3,
|
|
2119
2822
|
stdio: ["ignore", "pipe", "ignore"],
|
|
@@ -2170,11 +2873,11 @@ function foreignEvent(file) {
|
|
|
2170
2873
|
return Object.keys(hooks).find((k) => !ok.includes(k));
|
|
2171
2874
|
}
|
|
2172
2875
|
var homeOf = (env) => env.HOME || homeDir();
|
|
2173
|
-
var appData = (env) => env.APPDATA ||
|
|
2174
|
-
var devinConfigDir = (env = process.env) => process.platform === "win32" ?
|
|
2175
|
-
var configFile3 = (env) =>
|
|
2176
|
-
var devinDataDir = (env = process.env) => process.platform === "win32" ?
|
|
2177
|
-
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");
|
|
2178
2881
|
var SESSION_ID = /^[A-Za-z0-9._:-]{1,128}$/;
|
|
2179
2882
|
var validTool4 = (n) => typeof n === "string" && /^[A-Za-z0-9_.:-]{1,120}$/.test(n);
|
|
2180
2883
|
var endReason3 = (r) => r === "clear" || r === "logout" || r === "exit" ? r : "other";
|
|
@@ -2346,7 +3049,7 @@ var BACKFILL_SQL = `SELECT s.id, ${SESSION_FIELDS} FROM sessions s
|
|
|
2346
3049
|
function sessionFacts(row) {
|
|
2347
3050
|
const out = {};
|
|
2348
3051
|
if (typeof row.model === "string" && row.model) out.model = row.model;
|
|
2349
|
-
if (typeof row.working_directory === "string" &&
|
|
3052
|
+
if (typeof row.working_directory === "string" && path9.isAbsolute(row.working_directory))
|
|
2350
3053
|
out.cwd = row.working_directory;
|
|
2351
3054
|
const started = Number(row.created_at);
|
|
2352
3055
|
const last = Number(row.last_activity_at);
|
|
@@ -2391,8 +3094,8 @@ function readDevinChain(db, session, fromLine) {
|
|
|
2391
3094
|
}
|
|
2392
3095
|
function devinVersion(env = process.env) {
|
|
2393
3096
|
try {
|
|
2394
|
-
const target = readlinkSync(
|
|
2395
|
-
const name =
|
|
3097
|
+
const target = readlinkSync(path9.join(devinDataDir(env), "_versions", "current"));
|
|
3098
|
+
const name = path9.basename(target);
|
|
2396
3099
|
return /^[0-9][0-9A-Za-z.+-]{0,63}$/.test(name) ? name : void 0;
|
|
2397
3100
|
} catch {
|
|
2398
3101
|
return void 0;
|
|
@@ -2405,9 +3108,9 @@ function devinAccount(env = process.env) {
|
|
|
2405
3108
|
}
|
|
2406
3109
|
|
|
2407
3110
|
// ../core/src/adapters/gemini-cli.ts
|
|
2408
|
-
import { execFileSync as
|
|
2409
|
-
import { existsSync as existsSync7, readFileSync as
|
|
2410
|
-
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";
|
|
2411
3114
|
var NAME5 = "gemini-cli";
|
|
2412
3115
|
var GEMINI_EVENTS = [
|
|
2413
3116
|
"SessionStart",
|
|
@@ -2420,22 +3123,22 @@ var GEMINI_EVENTS = [
|
|
|
2420
3123
|
"SessionEnd"
|
|
2421
3124
|
];
|
|
2422
3125
|
var OPTS4 = { matcherFor: (e) => /Tool/.test(e) ? ".*" : void 0, timeout: 5e3 };
|
|
2423
|
-
var geminiDir = (env) => env.GEMINI_CLI_HOME ||
|
|
2424
|
-
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");
|
|
2425
3128
|
var validTool5 = (n) => typeof n === "string" && /^[A-Za-z0-9_.:-]{1,120}$/.test(n);
|
|
2426
3129
|
var eventsFor4 = (lean) => lean ? GEMINI_EVENTS.filter((e) => e !== "BeforeTool") : [...GEMINI_EVENTS];
|
|
2427
3130
|
function geminiVersion(platform = process.platform, env = process.env) {
|
|
2428
3131
|
const win = platform === "win32";
|
|
2429
|
-
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";
|
|
2430
3133
|
if (!bin) throw new Error("no gemini on PATH");
|
|
2431
|
-
const
|
|
3134
|
+
const c = probe(bin, ["--version"]);
|
|
3135
|
+
return execFileSync5(c.file, c.args, {
|
|
2432
3136
|
encoding: "utf8",
|
|
2433
3137
|
timeout: 4e3,
|
|
2434
3138
|
stdio: ["ignore", "pipe", "ignore"],
|
|
2435
|
-
env
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
return out.trim();
|
|
3139
|
+
env,
|
|
3140
|
+
shell: c.shell
|
|
3141
|
+
}).trim();
|
|
2439
3142
|
}
|
|
2440
3143
|
function geminiHooksActive(settingsFile) {
|
|
2441
3144
|
const tools = readJson(settingsFile).tools;
|
|
@@ -2462,7 +3165,7 @@ var geminiCli = {
|
|
|
2462
3165
|
name: NAME5,
|
|
2463
3166
|
events: GEMINI_EVENTS,
|
|
2464
3167
|
// ~/.gemini also belongs to Antigravity; Gemini CLI is here when its settings or its chat logs exist.
|
|
2465
|
-
detect: (env = process.env) => existsSync7(settings(env)) || existsSync7(
|
|
3168
|
+
detect: (env = process.env) => existsSync7(settings(env)) || existsSync7(path10.join(geminiDir(env), "tmp")),
|
|
2466
3169
|
configFiles: (env = process.env) => [settings(env)],
|
|
2467
3170
|
install: (cmd, env = process.env, opts = {}) => {
|
|
2468
3171
|
const r = installClaudeShaped(settings(env), NAME5, eventsFor4(opts.lean), cmd, OPTS4);
|
|
@@ -2560,7 +3263,7 @@ var geminiCli = {
|
|
|
2560
3263
|
readTranscript(file, fromLine) {
|
|
2561
3264
|
let text = "";
|
|
2562
3265
|
try {
|
|
2563
|
-
text =
|
|
3266
|
+
text = readFileSync7(file, "utf8");
|
|
2564
3267
|
} catch {
|
|
2565
3268
|
return { turns: [], line: fromLine };
|
|
2566
3269
|
}
|
|
@@ -2600,12 +3303,12 @@ var ADAPTERS = [claudeCode, codex, geminiCli, antigravity, devin];
|
|
|
2600
3303
|
var adapterByName = (name) => ADAPTERS.find((a) => a.name === name);
|
|
2601
3304
|
|
|
2602
3305
|
// ../core/src/config.ts
|
|
2603
|
-
import { chmodSync, existsSync as existsSync8, mkdirSync as mkdirSync4, readFileSync as
|
|
2604
|
-
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";
|
|
2605
3308
|
var DEFAULT_CONFIG = { sinks: [], harnesses: {}, update_check: true, keep_days: 30 };
|
|
2606
3309
|
function readConfig(file = configFile()) {
|
|
2607
3310
|
try {
|
|
2608
|
-
const j = JSON.parse(
|
|
3311
|
+
const j = JSON.parse(readFileSync8(file, "utf8"));
|
|
2609
3312
|
return {
|
|
2610
3313
|
...DEFAULT_CONFIG,
|
|
2611
3314
|
...j,
|
|
@@ -2617,7 +3320,7 @@ function readConfig(file = configFile()) {
|
|
|
2617
3320
|
}
|
|
2618
3321
|
}
|
|
2619
3322
|
function writeConfig(c, file = configFile()) {
|
|
2620
|
-
mkdirSync4(
|
|
3323
|
+
mkdirSync4(path11.dirname(file), { recursive: true, mode: 448 });
|
|
2621
3324
|
writeFileSync5(file, `${JSON.stringify(c, null, 2)}
|
|
2622
3325
|
`, { mode: 384 });
|
|
2623
3326
|
if (process.platform !== "win32" && existsSync8(file)) {
|
|
@@ -2681,38 +3384,22 @@ function makeEvent(a, ctx) {
|
|
|
2681
3384
|
};
|
|
2682
3385
|
}
|
|
2683
3386
|
|
|
2684
|
-
// ../core/src/launched-by.ts
|
|
2685
|
-
var LAUNCHED_BY_ENV = "SESSIONPIPE_LAUNCHED_BY";
|
|
2686
|
-
var LAUNCHED_BY = /^[a-z][a-z0-9-]{0,39}:[A-Za-z0-9._:-]{1,128}$/;
|
|
2687
|
-
function launchedBy(env) {
|
|
2688
|
-
const v = env[LAUNCHED_BY_ENV]?.trim();
|
|
2689
|
-
return v && LAUNCHED_BY.test(v) ? v : void 0;
|
|
2690
|
-
}
|
|
2691
|
-
function readHook(adapter, input) {
|
|
2692
|
-
const r = adapter.fromHook(input);
|
|
2693
|
-
if (r && !r.session.parent_id) {
|
|
2694
|
-
const launcher = launchedBy(input.env);
|
|
2695
|
-
if (launcher && launcher !== `${adapter.name}:${r.session.id}`) r.session.launched_by = launcher;
|
|
2696
|
-
}
|
|
2697
|
-
return r;
|
|
2698
|
-
}
|
|
2699
|
-
|
|
2700
3387
|
// ../core/src/outbox.ts
|
|
2701
3388
|
import {
|
|
2702
3389
|
appendFileSync,
|
|
2703
|
-
closeSync as
|
|
3390
|
+
closeSync as closeSync3,
|
|
2704
3391
|
existsSync as existsSync9,
|
|
2705
3392
|
mkdirSync as mkdirSync5,
|
|
2706
|
-
openSync as
|
|
2707
|
-
readdirSync as
|
|
2708
|
-
readFileSync as
|
|
2709
|
-
readSync as
|
|
3393
|
+
openSync as openSync3,
|
|
3394
|
+
readdirSync as readdirSync6,
|
|
3395
|
+
readFileSync as readFileSync9,
|
|
3396
|
+
readSync as readSync3,
|
|
2710
3397
|
renameSync,
|
|
2711
|
-
statSync as
|
|
3398
|
+
statSync as statSync6,
|
|
2712
3399
|
unlinkSync as unlinkSync3,
|
|
2713
3400
|
writeFileSync as writeFileSync6
|
|
2714
3401
|
} from "node:fs";
|
|
2715
|
-
import
|
|
3402
|
+
import path12 from "node:path";
|
|
2716
3403
|
var READ_CHUNK = 1024 * 1024;
|
|
2717
3404
|
var READ_MAX = 8 * 1024 * 1024;
|
|
2718
3405
|
var Outbox = class {
|
|
@@ -2721,13 +3408,13 @@ var Outbox = class {
|
|
|
2721
3408
|
}
|
|
2722
3409
|
root;
|
|
2723
3410
|
dir(harness) {
|
|
2724
|
-
return
|
|
3411
|
+
return path12.join(this.root, "outbox", safeId(harness));
|
|
2725
3412
|
}
|
|
2726
3413
|
file(ref) {
|
|
2727
|
-
return
|
|
3414
|
+
return path12.join(this.dir(ref.harness), `${safeId(ref.session)}.jsonl`);
|
|
2728
3415
|
}
|
|
2729
3416
|
seqFile(ref) {
|
|
2730
|
-
return
|
|
3417
|
+
return path12.join(this.dir(ref.harness), `${safeId(ref.session)}.seq`);
|
|
2731
3418
|
}
|
|
2732
3419
|
/** The next seq for a session, persisted (survives a hibernating machine and a
|
|
2733
3420
|
* restart; an in-memory counter would restart at 0 and collide). */
|
|
@@ -2735,19 +3422,19 @@ var Outbox = class {
|
|
|
2735
3422
|
const f = this.seqFile(ref);
|
|
2736
3423
|
let n = -1;
|
|
2737
3424
|
try {
|
|
2738
|
-
const t =
|
|
3425
|
+
const t = readFileSync9(f, "utf8").trim();
|
|
2739
3426
|
if (/^\d+$/.test(t)) n = Number(t);
|
|
2740
3427
|
} catch {
|
|
2741
3428
|
}
|
|
2742
3429
|
if (n < 0) n = this.lastSeqInFile(ref) + 1;
|
|
2743
|
-
mkdirSync5(
|
|
3430
|
+
mkdirSync5(path12.dirname(f), { recursive: true, mode: 448 });
|
|
2744
3431
|
writeFileSync6(f, String(n + 1), { mode: 384 });
|
|
2745
3432
|
return n;
|
|
2746
3433
|
}
|
|
2747
3434
|
lastSeqInFile(ref) {
|
|
2748
3435
|
const f = this.file(ref);
|
|
2749
3436
|
if (!existsSync9(f)) return -1;
|
|
2750
|
-
const lines =
|
|
3437
|
+
const lines = readFileSync9(f, "utf8").trim().split("\n");
|
|
2751
3438
|
for (let i = lines.length - 1; i >= 0; i--) {
|
|
2752
3439
|
try {
|
|
2753
3440
|
const j = JSON.parse(lines[i]);
|
|
@@ -2766,19 +3453,19 @@ var Outbox = class {
|
|
|
2766
3453
|
/** Every session file, newest first. */
|
|
2767
3454
|
sessions() {
|
|
2768
3455
|
const out = [];
|
|
2769
|
-
const base =
|
|
3456
|
+
const base = path12.join(this.root, "outbox");
|
|
2770
3457
|
if (!existsSync9(base)) return [];
|
|
2771
|
-
for (const h of
|
|
2772
|
-
const d =
|
|
3458
|
+
for (const h of readdirSync6(base)) {
|
|
3459
|
+
const d = path12.join(base, h);
|
|
2773
3460
|
let names = [];
|
|
2774
3461
|
try {
|
|
2775
|
-
names =
|
|
3462
|
+
names = readdirSync6(d).filter((n) => n.endsWith(".jsonl"));
|
|
2776
3463
|
} catch {
|
|
2777
3464
|
continue;
|
|
2778
3465
|
}
|
|
2779
3466
|
for (const n of names) {
|
|
2780
3467
|
try {
|
|
2781
|
-
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 });
|
|
2782
3469
|
} catch {
|
|
2783
3470
|
}
|
|
2784
3471
|
}
|
|
@@ -2794,16 +3481,16 @@ var Outbox = class {
|
|
|
2794
3481
|
read(ref, from, maxEvents = 500) {
|
|
2795
3482
|
const f = this.file(ref);
|
|
2796
3483
|
if (!existsSync9(f)) return { events: [], offset: from };
|
|
2797
|
-
const size =
|
|
3484
|
+
const size = statSync6(f).size;
|
|
2798
3485
|
if (from >= size) return { events: [], offset: from };
|
|
2799
|
-
const fd =
|
|
3486
|
+
const fd = openSync3(f, "r");
|
|
2800
3487
|
try {
|
|
2801
3488
|
const events = [];
|
|
2802
3489
|
let offset = from;
|
|
2803
3490
|
let chunk = READ_CHUNK;
|
|
2804
3491
|
while (events.length < maxEvents && offset < size) {
|
|
2805
3492
|
const buf = Buffer.alloc(Math.min(size - offset, chunk));
|
|
2806
|
-
const n =
|
|
3493
|
+
const n = readSync3(fd, buf, 0, buf.length, offset);
|
|
2807
3494
|
const text = buf.subarray(0, n).toString("utf8");
|
|
2808
3495
|
let pos = 0;
|
|
2809
3496
|
let used = 0;
|
|
@@ -2826,7 +3513,7 @@ var Outbox = class {
|
|
|
2826
3513
|
}
|
|
2827
3514
|
return { events, offset };
|
|
2828
3515
|
} finally {
|
|
2829
|
-
|
|
3516
|
+
closeSync3(fd);
|
|
2830
3517
|
}
|
|
2831
3518
|
}
|
|
2832
3519
|
/** Files older than `days` are pruned; the cursor files with them. */
|
|
@@ -2836,7 +3523,7 @@ var Outbox = class {
|
|
|
2836
3523
|
for (const ref of this.sessions()) {
|
|
2837
3524
|
const f = this.file(ref);
|
|
2838
3525
|
try {
|
|
2839
|
-
if (
|
|
3526
|
+
if (statSync6(f).mtimeMs < cutoff) {
|
|
2840
3527
|
unlinkSync3(f);
|
|
2841
3528
|
try {
|
|
2842
3529
|
unlinkSync3(this.seqFile(ref));
|
|
@@ -2866,18 +3553,18 @@ var Cursors = class {
|
|
|
2866
3553
|
root;
|
|
2867
3554
|
sink;
|
|
2868
3555
|
file() {
|
|
2869
|
-
return
|
|
3556
|
+
return path12.join(this.root, "cursors", `${safeId(this.sink)}.json`);
|
|
2870
3557
|
}
|
|
2871
3558
|
read() {
|
|
2872
3559
|
try {
|
|
2873
|
-
return JSON.parse(
|
|
3560
|
+
return JSON.parse(readFileSync9(this.file(), "utf8"));
|
|
2874
3561
|
} catch {
|
|
2875
3562
|
return {};
|
|
2876
3563
|
}
|
|
2877
3564
|
}
|
|
2878
3565
|
write(c) {
|
|
2879
3566
|
const f = this.file();
|
|
2880
|
-
mkdirSync5(
|
|
3567
|
+
mkdirSync5(path12.dirname(f), { recursive: true, mode: 448 });
|
|
2881
3568
|
const tmp = `${f}.${process.pid}.tmp`;
|
|
2882
3569
|
writeFileSync6(tmp, JSON.stringify(c), { mode: 384 });
|
|
2883
3570
|
renameSync(tmp, f);
|
|
@@ -2890,11 +3577,11 @@ function takeLock(root, ref, staleMs = 5 * 6e4) {
|
|
|
2890
3577
|
return takeNamedLock(root, `${safeId(ref.harness)}-${safeId(ref.session)}`, staleMs);
|
|
2891
3578
|
}
|
|
2892
3579
|
function takeNamedLock(root, name, staleMs = 5 * 6e4) {
|
|
2893
|
-
const dir =
|
|
3580
|
+
const dir = path12.join(root, "locks");
|
|
2894
3581
|
mkdirSync5(dir, { recursive: true, mode: 448 });
|
|
2895
|
-
const f =
|
|
3582
|
+
const f = path12.join(dir, `${name}.lock`);
|
|
2896
3583
|
try {
|
|
2897
|
-
if (Date.now() -
|
|
3584
|
+
if (Date.now() - statSync6(f).mtimeMs > staleMs || !processAlive(Number(readFileSync9(f, "utf8")))) unlinkSync3(f);
|
|
2898
3585
|
else return null;
|
|
2899
3586
|
} catch {
|
|
2900
3587
|
}
|
|
@@ -3132,6 +3819,8 @@ var TYPE_TIER = {
|
|
|
3132
3819
|
"subagent.ended": 1,
|
|
3133
3820
|
"context.compacted": 1,
|
|
3134
3821
|
"limits.observed": 1,
|
|
3822
|
+
"delegation.started": 0,
|
|
3823
|
+
"delegation.ended": 0,
|
|
3135
3824
|
native: 3
|
|
3136
3825
|
};
|
|
3137
3826
|
var DATA_ALLOW = {
|
|
@@ -3142,8 +3831,8 @@ var DATA_ALLOW = {
|
|
|
3142
3831
|
"session.forgotten": { 0: [] },
|
|
3143
3832
|
"turn.started": { 0: ["turn_id", "submitted_by", "injected"], 1: ["prompt_chars"] },
|
|
3144
3833
|
"turn.ended": { 0: ["turn_id", "reason", "ms", "background"], 1: ["error"] },
|
|
3145
|
-
"turn.transcript": { 2: ["turn", "user", "assistant", "at"] },
|
|
3146
|
-
"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"] },
|
|
3147
3836
|
"tool.started": { 1: ["tool", "call_id", "turn_id", "agent_id"], 3: ["input"] },
|
|
3148
3837
|
"tool.ended": { 1: ["tool", "call_id", "turn_id", "agent_id", "ms", "ok", "error"], 3: ["input", "output"] },
|
|
3149
3838
|
"files.changed": { 1: ["paths"] },
|
|
@@ -3152,7 +3841,9 @@ var DATA_ALLOW = {
|
|
|
3152
3841
|
"subagent.started": { 1: ["agent_id", "agent_type"] },
|
|
3153
3842
|
"subagent.ended": { 1: ["agent_id", "agent_type"] },
|
|
3154
3843
|
"context.compacted": { 1: ["trigger"] },
|
|
3155
|
-
"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"] }
|
|
3156
3847
|
};
|
|
3157
3848
|
var SESSION_ALLOW = [
|
|
3158
3849
|
"id",
|
|
@@ -3168,7 +3859,8 @@ var SESSION_ALLOW = [
|
|
|
3168
3859
|
"account_id",
|
|
3169
3860
|
"attended",
|
|
3170
3861
|
"parent_id",
|
|
3171
|
-
"launched_by"
|
|
3862
|
+
"launched_by",
|
|
3863
|
+
"delegated_by"
|
|
3172
3864
|
];
|
|
3173
3865
|
var MAX_SIDE = 2e4;
|
|
3174
3866
|
var MAX_IO = 64 * 1024;
|
|
@@ -3207,6 +3899,8 @@ function filterEvent(event, tier, pii, opts = {}) {
|
|
|
3207
3899
|
if (data.input !== void 0) data.input = cap(data.input, MAX_IO);
|
|
3208
3900
|
if (data.output !== void 0) data.output = cap(data.output, MAX_IO);
|
|
3209
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);
|
|
3210
3904
|
const rulesets = [];
|
|
3211
3905
|
let out = { ...event, tier, session, data, privacy: { rulesets, pii } };
|
|
3212
3906
|
if (tier > 0) {
|
|
@@ -3234,7 +3928,7 @@ function shortId(harness, id) {
|
|
|
3234
3928
|
|
|
3235
3929
|
// ../core/src/sinks/file.ts
|
|
3236
3930
|
import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync6 } from "node:fs";
|
|
3237
|
-
import
|
|
3931
|
+
import path13 from "node:path";
|
|
3238
3932
|
var FileSink = class {
|
|
3239
3933
|
constructor(name, file, tier, pii) {
|
|
3240
3934
|
this.name = name;
|
|
@@ -3248,7 +3942,7 @@ var FileSink = class {
|
|
|
3248
3942
|
pii;
|
|
3249
3943
|
async send(events) {
|
|
3250
3944
|
try {
|
|
3251
|
-
mkdirSync6(
|
|
3945
|
+
mkdirSync6(path13.dirname(this.file), { recursive: true });
|
|
3252
3946
|
appendFileSync2(this.file, `${events.map((e) => JSON.stringify(e)).join("\n")}
|
|
3253
3947
|
`);
|
|
3254
3948
|
return { ok: true, acknowledged: events.length, accepted: events.length, duplicates: 0, rejected: [] };
|
|
@@ -3286,19 +3980,19 @@ import {
|
|
|
3286
3980
|
appendFileSync as appendFileSync3,
|
|
3287
3981
|
existsSync as existsSync10,
|
|
3288
3982
|
mkdirSync as mkdirSync8,
|
|
3289
|
-
readdirSync as
|
|
3290
|
-
readFileSync as
|
|
3983
|
+
readdirSync as readdirSync7,
|
|
3984
|
+
readFileSync as readFileSync11,
|
|
3291
3985
|
renameSync as renameSync3,
|
|
3292
|
-
statSync as
|
|
3986
|
+
statSync as statSync7,
|
|
3293
3987
|
unlinkSync as unlinkSync4,
|
|
3294
3988
|
writeFileSync as writeFileSync8
|
|
3295
3989
|
} from "node:fs";
|
|
3296
|
-
import
|
|
3297
|
-
import
|
|
3990
|
+
import os4 from "node:os";
|
|
3991
|
+
import path16 from "node:path";
|
|
3298
3992
|
|
|
3299
3993
|
// src/control/aliases.ts
|
|
3300
|
-
import { chmodSync as chmodSync2, mkdirSync as mkdirSync7, readFileSync as
|
|
3301
|
-
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";
|
|
3302
3996
|
|
|
3303
3997
|
// src/secrets.ts
|
|
3304
3998
|
import { spawnSync } from "node:child_process";
|
|
@@ -3342,10 +4036,10 @@ function startOptionsOf(v) {
|
|
|
3342
4036
|
}
|
|
3343
4037
|
|
|
3344
4038
|
// src/control/aliases.ts
|
|
3345
|
-
var aliasFile = (controlDir) =>
|
|
4039
|
+
var aliasFile = (controlDir) => path14.join(controlDir, "aliases.json");
|
|
3346
4040
|
function readAliases(file) {
|
|
3347
4041
|
try {
|
|
3348
|
-
const j = JSON.parse(
|
|
4042
|
+
const j = JSON.parse(readFileSync10(file, "utf8"));
|
|
3349
4043
|
if (!j || typeof j !== "object" || Array.isArray(j)) return {};
|
|
3350
4044
|
const out = {};
|
|
3351
4045
|
for (const [k, v] of Object.entries(j)) {
|
|
@@ -3366,12 +4060,12 @@ function receiverIdOf(all, harness, id) {
|
|
|
3366
4060
|
|
|
3367
4061
|
// src/control/local.ts
|
|
3368
4062
|
import net from "node:net";
|
|
3369
|
-
import
|
|
3370
|
-
import
|
|
4063
|
+
import os3 from "node:os";
|
|
4064
|
+
import path15 from "node:path";
|
|
3371
4065
|
function socketPath(state, env = process.env) {
|
|
3372
4066
|
if (env.SESSIONPIPE_SOCKET) return env.SESSIONPIPE_SOCKET;
|
|
3373
|
-
if (process.platform === "win32") return `\\\\.\\pipe\\sessionpipe-control-${
|
|
3374
|
-
return
|
|
4067
|
+
if (process.platform === "win32") return `\\\\.\\pipe\\sessionpipe-control-${os3.userInfo().username}`;
|
|
4068
|
+
return path15.join(state, "control.sock");
|
|
3375
4069
|
}
|
|
3376
4070
|
function ask(sock, req, timeoutMs) {
|
|
3377
4071
|
return new Promise((resolve) => {
|
|
@@ -3502,6 +4196,11 @@ var HttpSink = class {
|
|
|
3502
4196
|
async function tellDaemon(state, job, session, cwd, transcript) {
|
|
3503
4197
|
const sock = socketPath(state);
|
|
3504
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
|
+
}
|
|
3505
4204
|
await ask(
|
|
3506
4205
|
sock,
|
|
3507
4206
|
{
|
|
@@ -3509,7 +4208,8 @@ async function tellDaemon(state, job, session, cwd, transcript) {
|
|
|
3509
4208
|
session: `${job.harness}:${session}`,
|
|
3510
4209
|
event: job.event,
|
|
3511
4210
|
...cwd ? { cwd } : {},
|
|
3512
|
-
...transcript ? { transcript } : {}
|
|
4211
|
+
...transcript ? { transcript } : {},
|
|
4212
|
+
...mode ? { mode } : {}
|
|
3513
4213
|
},
|
|
3514
4214
|
300
|
|
3515
4215
|
);
|
|
@@ -3518,7 +4218,7 @@ var MAX_TRIES = 10;
|
|
|
3518
4218
|
function enqueueJob(state, job) {
|
|
3519
4219
|
const jobs = jobsDir(state);
|
|
3520
4220
|
mkdirSync8(jobs, { recursive: true, mode: 448 });
|
|
3521
|
-
const f =
|
|
4221
|
+
const f = path16.join(jobs, `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}.json`);
|
|
3522
4222
|
writeFileSync8(f, JSON.stringify(job), { mode: 384 });
|
|
3523
4223
|
return f;
|
|
3524
4224
|
}
|
|
@@ -3530,7 +4230,7 @@ function claimJob(file) {
|
|
|
3530
4230
|
return null;
|
|
3531
4231
|
}
|
|
3532
4232
|
try {
|
|
3533
|
-
return { job: JSON.parse(
|
|
4233
|
+
return { job: JSON.parse(readFileSync11(claimed, "utf8")), file: claimed };
|
|
3534
4234
|
} catch {
|
|
3535
4235
|
releaseJob(claimed);
|
|
3536
4236
|
return null;
|
|
@@ -3547,7 +4247,7 @@ async function sweepJobs(state, olderThanMs = 3e3, limit = 20, abandonedMs = 6e4
|
|
|
3547
4247
|
const jobs = jobsDir(state);
|
|
3548
4248
|
let names = [];
|
|
3549
4249
|
try {
|
|
3550
|
-
names =
|
|
4250
|
+
names = readdirSync7(jobs);
|
|
3551
4251
|
} catch {
|
|
3552
4252
|
return 0;
|
|
3553
4253
|
}
|
|
@@ -3555,19 +4255,19 @@ async function sweepJobs(state, olderThanMs = 3e3, limit = 20, abandonedMs = 6e4
|
|
|
3555
4255
|
for (const n of names) {
|
|
3556
4256
|
const m = /^(.*\.json)\.(\d+)\.running$/.exec(n);
|
|
3557
4257
|
if (!m) continue;
|
|
3558
|
-
const f =
|
|
4258
|
+
const f = path16.join(jobs, n);
|
|
3559
4259
|
try {
|
|
3560
|
-
if (processAlive(Number(m[2])) && Date.now() -
|
|
3561
|
-
renameSync3(f,
|
|
4260
|
+
if (processAlive(Number(m[2])) && Date.now() - statSync7(f).mtimeMs < abandonedMs) continue;
|
|
4261
|
+
renameSync3(f, path16.join(jobs, m[1]));
|
|
3562
4262
|
queued.push(m[1]);
|
|
3563
4263
|
} catch {
|
|
3564
4264
|
}
|
|
3565
4265
|
}
|
|
3566
4266
|
let ran = 0;
|
|
3567
4267
|
for (const n of queued.sort().slice(0, limit)) {
|
|
3568
|
-
const f =
|
|
4268
|
+
const f = path16.join(jobs, n);
|
|
3569
4269
|
try {
|
|
3570
|
-
if (Date.now() -
|
|
4270
|
+
if (Date.now() - statSync7(f).mtimeMs < olderThanMs) continue;
|
|
3571
4271
|
} catch {
|
|
3572
4272
|
continue;
|
|
3573
4273
|
}
|
|
@@ -3586,9 +4286,9 @@ async function sweepJobs(state, olderThanMs = 3e3, limit = 20, abandonedMs = 6e4
|
|
|
3586
4286
|
function log(state, line) {
|
|
3587
4287
|
try {
|
|
3588
4288
|
mkdirSync8(state, { recursive: true, mode: 448 });
|
|
3589
|
-
const f =
|
|
4289
|
+
const f = path16.join(state, "log");
|
|
3590
4290
|
try {
|
|
3591
|
-
if (
|
|
4291
|
+
if (statSync7(f).size > 1024 * 1024) renameSync3(f, `${f}.1`);
|
|
3592
4292
|
} catch {
|
|
3593
4293
|
}
|
|
3594
4294
|
appendFileSync3(f, `${(/* @__PURE__ */ new Date()).toISOString()} ${line}
|
|
@@ -3597,11 +4297,11 @@ function log(state, line) {
|
|
|
3597
4297
|
}
|
|
3598
4298
|
}
|
|
3599
4299
|
function factsState(state) {
|
|
3600
|
-
const file = (h, s) =>
|
|
4300
|
+
const file = (h, s) => path16.join(state, "facts", safeId(h), `${safeId(s)}.json`);
|
|
3601
4301
|
return {
|
|
3602
4302
|
get(h, s) {
|
|
3603
4303
|
try {
|
|
3604
|
-
return JSON.parse(
|
|
4304
|
+
return JSON.parse(readFileSync11(file(h, s), "utf8"));
|
|
3605
4305
|
} catch {
|
|
3606
4306
|
return {};
|
|
3607
4307
|
}
|
|
@@ -3610,7 +4310,7 @@ function factsState(state) {
|
|
|
3610
4310
|
const cur = this.get(h, s);
|
|
3611
4311
|
const next = { ...cur, ...patch };
|
|
3612
4312
|
if (JSON.stringify(next) === JSON.stringify(cur)) return;
|
|
3613
|
-
mkdirSync8(
|
|
4313
|
+
mkdirSync8(path16.dirname(file(h, s)), { recursive: true, mode: 448 });
|
|
3614
4314
|
writeFileSync8(file(h, s), JSON.stringify(next), { mode: 384 });
|
|
3615
4315
|
}
|
|
3616
4316
|
};
|
|
@@ -3627,7 +4327,7 @@ function buildSinks(sinks, o = {}) {
|
|
|
3627
4327
|
const tier = Math.min(s.tier, s.max_tier ?? 3);
|
|
3628
4328
|
if (s.url === "stdout") out.push(new StdoutSink(s.name, tier, !!s.pii));
|
|
3629
4329
|
else if (s.url.startsWith("file:"))
|
|
3630
|
-
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));
|
|
3631
4331
|
else if (/^https?:\/\//.test(s.url)) out.push(new HttpSink(s, tier));
|
|
3632
4332
|
}
|
|
3633
4333
|
return out;
|
|
@@ -3635,13 +4335,14 @@ function buildSinks(sinks, o = {}) {
|
|
|
3635
4335
|
var PROGRESS_MS = 2e4;
|
|
3636
4336
|
function openTurn(read, o) {
|
|
3637
4337
|
const last = read.turns[read.turns.length - 1];
|
|
3638
|
-
const fresh = (user, at) => ({
|
|
4338
|
+
const fresh = (user, at, injected) => ({
|
|
3639
4339
|
turn: o.base + read.turns.length,
|
|
3640
4340
|
user: user.slice(0, MAX_TURN_SIDE),
|
|
3641
4341
|
assistant: "",
|
|
3642
|
-
at
|
|
4342
|
+
at,
|
|
4343
|
+
...injected ? { injected: true } : {}
|
|
3643
4344
|
});
|
|
3644
|
-
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);
|
|
3645
4346
|
const prompt = o.prompt?.trim();
|
|
3646
4347
|
if (prompt && !isInjected(prompt) && !(last && last.user.includes(prompt.slice(0, 200)))) return fresh(prompt, o.at);
|
|
3647
4348
|
if (!last) return null;
|
|
@@ -3649,6 +4350,7 @@ function openTurn(read, o) {
|
|
|
3649
4350
|
turn: o.base + read.turns.length - 1,
|
|
3650
4351
|
user: last.user.slice(0, MAX_TURN_SIDE),
|
|
3651
4352
|
assistant: capTail(last.assistant, MAX_TURN_SIDE),
|
|
4353
|
+
...last.injected ? { injected: true } : {},
|
|
3652
4354
|
at: last.at
|
|
3653
4355
|
};
|
|
3654
4356
|
}
|
|
@@ -3656,6 +4358,7 @@ var MAX_TURN_SIDE = 2e4;
|
|
|
3656
4358
|
var JOB_OWN_ENV = [
|
|
3657
4359
|
"CLAUDE_CODE_SESSION_ATTENDED",
|
|
3658
4360
|
"CLAUDE_CODE_HOST_SESSION_ID",
|
|
4361
|
+
"SESSIONPIPE_ASKED_BY",
|
|
3659
4362
|
"SESSIONPIPE_CONTROL_JOB",
|
|
3660
4363
|
"SESSIONPIPE_LAUNCHED_BY"
|
|
3661
4364
|
];
|
|
@@ -3683,7 +4386,7 @@ async function runJob(job, opts = {}) {
|
|
|
3683
4386
|
);
|
|
3684
4387
|
return { events: [], delivered: {} };
|
|
3685
4388
|
}
|
|
3686
|
-
const id = receiverIdOf(readAliases(aliasFile(
|
|
4389
|
+
const id = receiverIdOf(readAliases(aliasFile(path16.join(state, "control"))), job.harness, r.session.id) ?? r.session.id;
|
|
3687
4390
|
const claudeEnd = job.harness === "claude-code" && r.events.some((e) => e.type === "session.ended");
|
|
3688
4391
|
if (!claudeEnd) await tellDaemon(state, job, id, r.session.cwd, r.transcript);
|
|
3689
4392
|
const outbox = new Outbox(state);
|
|
@@ -3720,7 +4423,7 @@ async function runJob(job, opts = {}) {
|
|
|
3720
4423
|
learned = rest;
|
|
3721
4424
|
if (harness_version) facts.save(job.harness, id, { harness_version });
|
|
3722
4425
|
facts.save(job.harness, id, { session: { ...mem.session, ...learned } });
|
|
3723
|
-
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))
|
|
3724
4427
|
facts.save(job.harness, id, { cwd: r.session.cwd });
|
|
3725
4428
|
}
|
|
3726
4429
|
const known = mem.session ?? {};
|
|
@@ -3733,10 +4436,11 @@ async function runJob(job, opts = {}) {
|
|
|
3733
4436
|
...learned.branch ? { branch: learned.branch } : {},
|
|
3734
4437
|
id,
|
|
3735
4438
|
seq: 0,
|
|
3736
|
-
machine: machineName(cfg,
|
|
4439
|
+
machine: machineName(cfg, os4.hostname()),
|
|
3737
4440
|
...cwdRaw ? { cwd: tilde(cwdRaw) } : {},
|
|
3738
4441
|
...r.session.model ? { model: r.session.model } : {},
|
|
3739
|
-
...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 } : {}
|
|
3740
4444
|
};
|
|
3741
4445
|
const version = facts.get(job.harness, id).harness_version ?? void 0;
|
|
3742
4446
|
for (const a of r.events) {
|
|
@@ -3790,7 +4494,13 @@ async function runJob(job, opts = {}) {
|
|
|
3790
4494
|
{
|
|
3791
4495
|
type: "turn.transcript",
|
|
3792
4496
|
harnessEvent: job.event,
|
|
3793
|
-
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
|
+
}
|
|
3794
4504
|
},
|
|
3795
4505
|
{
|
|
3796
4506
|
harness: { name: job.harness, ...version ? { version } : {} },
|
|
@@ -3836,7 +4546,7 @@ async function runJob(job, opts = {}) {
|
|
|
3836
4546
|
return { events, delivered };
|
|
3837
4547
|
}
|
|
3838
4548
|
async function drain(state, sinkConfigs, outbox, first) {
|
|
3839
|
-
const more =
|
|
4549
|
+
const more = path16.join(state, "locks", "flush.more");
|
|
3840
4550
|
let release = takeNamedLock(state, "flush");
|
|
3841
4551
|
if (!release) {
|
|
3842
4552
|
try {
|
|
@@ -3864,10 +4574,10 @@ async function drain(state, sinkConfigs, outbox, first) {
|
|
|
3864
4574
|
}
|
|
3865
4575
|
}
|
|
3866
4576
|
var BACKOFF_MS = [2e3, 1e4, 6e4, 5 * 6e4, 30 * 6e4];
|
|
3867
|
-
var backoffFile = (state, sink) =>
|
|
4577
|
+
var backoffFile = (state, sink) => path16.join(state, "backoff", `${safeId(sink)}.json`);
|
|
3868
4578
|
function readBackoff(state, sink) {
|
|
3869
4579
|
try {
|
|
3870
|
-
const b = JSON.parse(
|
|
4580
|
+
const b = JSON.parse(readFileSync11(backoffFile(state, sink), "utf8"));
|
|
3871
4581
|
return typeof b.until === "number" ? b : null;
|
|
3872
4582
|
} catch {
|
|
3873
4583
|
return null;
|
|
@@ -3880,7 +4590,7 @@ function noteFailure(state, sink, res, prev) {
|
|
|
3880
4590
|
const b = { until: Date.now() + wait, failures, why: `${res.status ?? ""} ${res.error ?? ""}`.trim() };
|
|
3881
4591
|
const f = backoffFile(state, sink);
|
|
3882
4592
|
try {
|
|
3883
|
-
mkdirSync8(
|
|
4593
|
+
mkdirSync8(path16.dirname(f), { recursive: true, mode: 448 });
|
|
3884
4594
|
writeFileSync8(f, JSON.stringify(b), { mode: 384 });
|
|
3885
4595
|
} catch {
|
|
3886
4596
|
}
|
|
@@ -3980,7 +4690,7 @@ async function flush(state, sinkConfigs, outbox, first, o = {}) {
|
|
|
3980
4690
|
const { events, offset: next } = outbox.read(ref, offset, room);
|
|
3981
4691
|
if (!events.length) break;
|
|
3982
4692
|
for (const e of events) {
|
|
3983
|
-
const f = filterEvent(e, sink.tier, sink.pii, { home:
|
|
4693
|
+
const f = filterEvent(e, sink.tier, sink.pii, { home: os4.homedir() });
|
|
3984
4694
|
if (f) batch.push(f);
|
|
3985
4695
|
}
|
|
3986
4696
|
offset = next;
|
|
@@ -4013,7 +4723,7 @@ function applySinkVerdict(name, res) {
|
|
|
4013
4723
|
} catch {
|
|
4014
4724
|
}
|
|
4015
4725
|
}
|
|
4016
|
-
var jobsDir = (state) =>
|
|
4726
|
+
var jobsDir = (state) => path16.join(state, "jobs");
|
|
4017
4727
|
|
|
4018
4728
|
// src/worker.ts
|
|
4019
4729
|
async function workerMain(argv) {
|
|
@@ -4025,7 +4735,7 @@ async function workerMain(argv) {
|
|
|
4025
4735
|
deadline.unref();
|
|
4026
4736
|
await runJob(claimed.job, { claimed: claimed.file }).then((r) => {
|
|
4027
4737
|
if (!r.requeued || (claimed.job.tries ?? 0) + 1 >= MAX_TRIES) return;
|
|
4028
|
-
const p =
|
|
4738
|
+
const p = spawn2(process.execPath, [argv[1], r.requeued], {
|
|
4029
4739
|
detached: true,
|
|
4030
4740
|
stdio: "ignore",
|
|
4031
4741
|
windowsHide: true
|