sessionpipe-core 0.12.0 → 0.13.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/control.js +26 -11
- package/dist/control.js.map +3 -3
- package/dist/index.js +550 -175
- package/dist/index.js.map +4 -4
- package/dist/schema/v1.d.ts +87 -1
- package/dist/schema/v1.d.ts.map +1 -1
- package/dist/schema.js +86 -11
- package/dist/schema.js.map +2 -2
- package/dist/service.js +1 -1
- package/dist/src/adapters/acp-control.d.ts.map +1 -1
- package/dist/src/adapters/antigravity.d.ts.map +1 -1
- package/dist/src/adapters/claude-code-control.d.ts +136 -6
- package/dist/src/adapters/claude-code-control.d.ts.map +1 -1
- package/dist/src/adapters/claude-code.d.ts.map +1 -1
- package/dist/src/adapters/claude-shaped.d.ts +10 -0
- package/dist/src/adapters/claude-shaped.d.ts.map +1 -1
- package/dist/src/adapters/codex.d.ts.map +1 -1
- package/dist/src/adapters/devin.d.ts +1 -1
- package/dist/src/adapters/devin.d.ts.map +1 -1
- package/dist/src/adapters/headless.d.ts +3 -1
- package/dist/src/adapters/headless.d.ts.map +1 -1
- package/dist/src/adapters/types.d.ts +8 -1
- package/dist/src/adapters/types.d.ts.map +1 -1
- package/dist/src/config.d.ts +15 -1
- package/dist/src/config.d.ts.map +1 -1
- package/dist/src/control/verify.d.ts +3 -0
- package/dist/src/control/verify.d.ts.map +1 -1
- package/dist/src/index.d.ts +4 -4
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/launched-by.d.ts +20 -3
- package/dist/src/launched-by.d.ts.map +1 -1
- package/dist/src/outbox.d.ts +35 -2
- package/dist/src/outbox.d.ts.map +1 -1
- package/dist/src/privacy/filter.d.ts.map +1 -1
- package/dist/src/privacy/pii.d.ts.map +1 -1
- package/dist/src/readers/transcripts.d.ts +11 -1
- package/dist/src/readers/transcripts.d.ts.map +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// sessionpipe-core 0.
|
|
1
|
+
// sessionpipe-core 0.13.1 · Apache-2.0 · https://sessionpipe.org
|
|
2
2
|
var __defProp = Object.defineProperty;
|
|
3
3
|
var __export = (target, all) => {
|
|
4
4
|
for (var name in all)
|
|
@@ -59,7 +59,7 @@ var safeId = (s) => String(s).replace(/[^A-Za-z0-9_.-]/g, "_").slice(0, 160);
|
|
|
59
59
|
// src/adapters/claude-code.ts
|
|
60
60
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
61
61
|
import { createHash } from "node:crypto";
|
|
62
|
-
import { existsSync as existsSync2, readdirSync as readdirSync2, readFileSync as readFileSync2, statSync as statSync2
|
|
62
|
+
import { existsSync as existsSync2, readdirSync as readdirSync2, readFileSync as readFileSync2, statSync as statSync2 } from "node:fs";
|
|
63
63
|
import { userInfo } from "node:os";
|
|
64
64
|
import path4 from "node:path";
|
|
65
65
|
|
|
@@ -211,7 +211,14 @@ ${m.text}`;
|
|
|
211
211
|
continue;
|
|
212
212
|
}
|
|
213
213
|
if (cur) turns.push(cur);
|
|
214
|
-
cur = {
|
|
214
|
+
cur = {
|
|
215
|
+
user: m.text,
|
|
216
|
+
assistant: "",
|
|
217
|
+
at: m.at,
|
|
218
|
+
startLine: m.line,
|
|
219
|
+
endLine: m.line,
|
|
220
|
+
...m.injected ? { injected: true } : {}
|
|
221
|
+
};
|
|
215
222
|
} else {
|
|
216
223
|
if (!cur) cur = { user: "", assistant: "", at: m.at, startLine: m.line, endLine: m.line };
|
|
217
224
|
cur.assistant += (cur.assistant ? "\n\n" : "") + m.text;
|
|
@@ -221,10 +228,17 @@ ${m.text}`;
|
|
|
221
228
|
if (cur?.assistant) turns.push(cur);
|
|
222
229
|
const line = cur && !cur.assistant ? cur.startLine : endLine;
|
|
223
230
|
const out = {
|
|
224
|
-
turns: turns.map((t) => ({
|
|
231
|
+
turns: turns.map((t) => ({
|
|
232
|
+
user: t.user,
|
|
233
|
+
assistant: t.assistant,
|
|
234
|
+
at: t.at,
|
|
235
|
+
endLine: t.endLine,
|
|
236
|
+
...t.injected ? { injected: true } : {}
|
|
237
|
+
})),
|
|
225
238
|
line
|
|
226
239
|
};
|
|
227
|
-
if (cur && !cur.assistant && cur.user)
|
|
240
|
+
if (cur && !cur.assistant && cur.user)
|
|
241
|
+
out.open = { user: cur.user, at: cur.at, ...cur.injected ? { injected: true } : {} };
|
|
228
242
|
return out;
|
|
229
243
|
}
|
|
230
244
|
var isInjected = (text) => /^<[a-z_-]+(?:\s[^>]*)?>/i.test(text) && /<\/[a-z_-]+>\s*$/i.test(text) || /^<!--\s/.test(text) || /^<command-/.test(text);
|
|
@@ -247,27 +261,51 @@ var textParts = (c, kinds) => {
|
|
|
247
261
|
(b) => b && typeof b === "object" && kinds.includes(b.type ?? "") && typeof b.text === "string"
|
|
248
262
|
).map((b) => b.text).join("\n");
|
|
249
263
|
};
|
|
264
|
+
var SPOKEN = /* @__PURE__ */ new Set(["human", "peer"]);
|
|
265
|
+
var PEER_INTRO = /sent a message[^\n]*:$/;
|
|
266
|
+
var PEER_OUTRO = "\n\nThis came from another ";
|
|
267
|
+
var ENVELOPE = /^<cross-session-message\b[^>]*>\n([\s\S]*)\n<\/cross-session-message>$/;
|
|
268
|
+
function peerAsk(origin, text) {
|
|
269
|
+
const body = origin?.body;
|
|
270
|
+
let t = typeof body === "string" && body.trim() ? body : text;
|
|
271
|
+
if (t !== body) {
|
|
272
|
+
const nl = t.indexOf("\n");
|
|
273
|
+
if (nl > 0 && PEER_INTRO.test(t.slice(0, nl))) t = t.slice(nl + 1);
|
|
274
|
+
const outro = t.indexOf(PEER_OUTRO);
|
|
275
|
+
if (outro > 0) t = t.slice(0, outro);
|
|
276
|
+
}
|
|
277
|
+
return (ENVELOPE.exec(t.trim())?.[1] ?? t).trim();
|
|
278
|
+
}
|
|
250
279
|
var queuedPrompt = (a) => {
|
|
251
280
|
if (!a || typeof a !== "object") return "";
|
|
252
281
|
const q = a;
|
|
253
282
|
if (q.type !== "queued_command" || (q.commandMode ?? "prompt") !== "prompt") return "";
|
|
254
|
-
|
|
255
|
-
|
|
283
|
+
const kind = q.origin?.kind ?? "human";
|
|
284
|
+
if (!SPOKEN.has(kind)) return "";
|
|
285
|
+
const text = textParts(q.prompt, ["text"]).trim();
|
|
286
|
+
return kind === "peer" ? peerAsk(q.origin, text) : text;
|
|
256
287
|
};
|
|
288
|
+
var channelText = (text) => /^<channel\b[^>]*>([\s\S]*)<\/channel>$/.exec(text)?.[1]?.trim() ?? "";
|
|
257
289
|
function readClaude(lines, from) {
|
|
258
290
|
const msgs = [];
|
|
259
291
|
for (let i = from; i < lines.length; i++) {
|
|
260
292
|
const j = lines[i] ? parse(lines[i]) : null;
|
|
261
293
|
if (!j || j.isSidechain) continue;
|
|
262
294
|
const origin = j.origin;
|
|
263
|
-
|
|
264
|
-
|
|
295
|
+
const channel = j.type === "user" && origin?.kind === "channel";
|
|
296
|
+
const peer = origin?.kind === "peer";
|
|
297
|
+
if (j.type === "user" && (j.isCompactSummary || j.repeatsCompactSummary)) continue;
|
|
298
|
+
if (j.type === "user" && !channel && !peer && (j.isMeta || origin?.kind && origin.kind !== "human")) continue;
|
|
265
299
|
const at = Date.parse(String(j.timestamp ?? "")) || Date.now();
|
|
266
300
|
const c = j.message?.content;
|
|
267
|
-
if (
|
|
268
|
-
const text = textParts(c, ["text"]).trim();
|
|
301
|
+
if (channel) {
|
|
302
|
+
const text = channelText(textParts(c, ["text"]).trim());
|
|
303
|
+
if (text) msgs.push({ role: "user", text, at, line: i, injected: true });
|
|
304
|
+
} else if (j.type === "user") {
|
|
305
|
+
const raw = textParts(c, ["text"]).trim();
|
|
306
|
+
const text = peer ? peerAsk(origin, raw) : raw;
|
|
269
307
|
if (!text || isInjected(text)) continue;
|
|
270
|
-
msgs.push({ role: "user", text, at, line: i });
|
|
308
|
+
msgs.push({ role: "user", text, at, line: i, ...peer ? { injected: true } : {} });
|
|
271
309
|
} else if (j.type === "assistant" && Array.isArray(c)) {
|
|
272
310
|
const text = textParts(c, ["text"]).trim();
|
|
273
311
|
if (text) msgs.push({ role: "assistant", text, at, line: i });
|
|
@@ -379,6 +417,41 @@ function readJson(file) {
|
|
|
379
417
|
const f = readJsonFile(file);
|
|
380
418
|
return f.ok ? f.value : {};
|
|
381
419
|
}
|
|
420
|
+
function refused(file, e, what, existed) {
|
|
421
|
+
const { code, syscall } = e;
|
|
422
|
+
const why = `can't ${what} it (${code ?? String(e)})`;
|
|
423
|
+
if (what === "remove" || syscall === "open" || syscall === "mkdir")
|
|
424
|
+
return { file, changed: false, skipped: true, note: `${why}; not touched` };
|
|
425
|
+
return {
|
|
426
|
+
file,
|
|
427
|
+
changed: true,
|
|
428
|
+
skipped: true,
|
|
429
|
+
...existed ? {} : { created: true },
|
|
430
|
+
note: `${why} part way; check the file before the harness reads it`
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
function writeBack(file, text, existed = true) {
|
|
434
|
+
try {
|
|
435
|
+
mkdirSync(path3.dirname(file), { recursive: true });
|
|
436
|
+
writeFileSync(file, text);
|
|
437
|
+
return null;
|
|
438
|
+
} catch (e) {
|
|
439
|
+
return refused(file, e, "write", existed);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
function removeEmptied(file, empty) {
|
|
443
|
+
try {
|
|
444
|
+
unlinkSync(file);
|
|
445
|
+
return { file, changed: true, note: "removed (sessionpipe created it)" };
|
|
446
|
+
} catch (e) {
|
|
447
|
+
const bad = refused(file, e, "remove", true);
|
|
448
|
+
return writeBack(file, empty) ?? {
|
|
449
|
+
file,
|
|
450
|
+
changed: true,
|
|
451
|
+
note: `emptied; ${bad.note.replace(/; not touched$/, "")}, so the empty file stays`
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
}
|
|
382
455
|
function entryFor(event, harness, cmd, o) {
|
|
383
456
|
const matcher = o.matcherFor(event);
|
|
384
457
|
return {
|
|
@@ -420,8 +493,8 @@ function installClaudeShaped(file, harness, events, cmd, o) {
|
|
|
420
493
|
}
|
|
421
494
|
if (changed) {
|
|
422
495
|
settings2.hooks = hooks;
|
|
423
|
-
|
|
424
|
-
|
|
496
|
+
const bad = writeBack(file, formatJson(settings2, f), f.existed);
|
|
497
|
+
if (bad) return bad;
|
|
425
498
|
}
|
|
426
499
|
return { file, changed, created: !f.existed && changed };
|
|
427
500
|
}
|
|
@@ -444,12 +517,8 @@ function uninstallClaudeShaped(file, events, opts = {}) {
|
|
|
444
517
|
}
|
|
445
518
|
if (!changed) return { file, changed: false };
|
|
446
519
|
if (!Object.keys(hooks).length) delete settings2.hooks;
|
|
447
|
-
if (opts.created && !Object.keys(settings2).length)
|
|
448
|
-
|
|
449
|
-
return { file, changed: true, note: "removed (sessionpipe created it)" };
|
|
450
|
-
}
|
|
451
|
-
writeFileSync(file, formatJson(settings2, f));
|
|
452
|
-
return { file, changed: true };
|
|
520
|
+
if (opts.created && !Object.keys(settings2).length) return removeEmptied(file, formatJson(settings2, f));
|
|
521
|
+
return writeBack(file, formatJson(settings2, f)) ?? { file, changed: true };
|
|
453
522
|
}
|
|
454
523
|
function installedClaudeShaped(file, harness, events, cmd, o) {
|
|
455
524
|
const f = readJsonFile(file);
|
|
@@ -528,7 +597,7 @@ var claudeCode = {
|
|
|
528
597
|
configFiles: (env = process.env) => claudeDirs(env).map(settingsIn),
|
|
529
598
|
install: (cmd, env = process.env, opts = {}) => claudeDirs(env).flatMap((d) => {
|
|
530
599
|
const hooks = installClaudeShaped(settingsIn(d), NAME, eventsFor(opts.lean), cmd, OPTS);
|
|
531
|
-
const line = opts.statusLine === void 0 ? null : statusLine(settingsIn(d), opts.statusLine, cmd);
|
|
600
|
+
const line = opts.statusLine === void 0 || hooks.skipped ? null : statusLine(settingsIn(d), opts.statusLine, cmd);
|
|
532
601
|
return line ? [hooks, line] : [hooks];
|
|
533
602
|
}),
|
|
534
603
|
uninstall: (env = process.env, opts = {}) => claudeDirs(env).map((d) => {
|
|
@@ -536,7 +605,9 @@ var claudeCode = {
|
|
|
536
605
|
const hooks = uninstallClaudeShaped(settingsIn(d), CLAUDE_EVENTS, {
|
|
537
606
|
created: opts.created?.includes(settingsIn(d))
|
|
538
607
|
});
|
|
539
|
-
|
|
608
|
+
if (line?.changed && !line.skipped && hooks.skipped)
|
|
609
|
+
return { ...hooks, note: `status line removed, hooks not: ${hooks.note?.replace(/; not touched$/, "")}` };
|
|
610
|
+
return line && !hooks.changed && !hooks.skipped ? line : hooks;
|
|
540
611
|
}),
|
|
541
612
|
installed: (cmd, env = process.env, opts = {}) => claudeDirs(env).map((d) => ({
|
|
542
613
|
file: settingsIn(d),
|
|
@@ -631,7 +702,8 @@ var claudeCode = {
|
|
|
631
702
|
attention_id: call_id ?? permissionId(),
|
|
632
703
|
kind: "permission",
|
|
633
704
|
tool,
|
|
634
|
-
message: tool ? `${tool}${describe(s.tool_input)}` : void 0
|
|
705
|
+
message: tool ? `${tool}${describe(s.tool_input)}` : void 0,
|
|
706
|
+
agent_id
|
|
635
707
|
})
|
|
636
708
|
);
|
|
637
709
|
break;
|
|
@@ -640,7 +712,8 @@ var claudeCode = {
|
|
|
640
712
|
ev("attention.needed", {
|
|
641
713
|
attention_id: `notif-${Date.now()}`,
|
|
642
714
|
kind: notifKind(s.notification_type),
|
|
643
|
-
message: typeof s.message === "string" ? s.message.slice(0, 200) : void 0
|
|
715
|
+
message: typeof s.message === "string" ? s.message.slice(0, 200) : void 0,
|
|
716
|
+
agent_id
|
|
644
717
|
})
|
|
645
718
|
);
|
|
646
719
|
break;
|
|
@@ -765,8 +838,11 @@ function statusLine(file, want, cmd) {
|
|
|
765
838
|
if (want ? ours && cur?.command === command && cur?.refreshInterval === LINE_REFRESH_S : !ours) return null;
|
|
766
839
|
if (want) f.value.statusLine = { type: "command", command, refreshInterval: LINE_REFRESH_S };
|
|
767
840
|
else delete f.value.statusLine;
|
|
768
|
-
|
|
769
|
-
|
|
841
|
+
return writeBack(file, formatJson(f.value, f), f.existed) ?? {
|
|
842
|
+
file,
|
|
843
|
+
changed: true,
|
|
844
|
+
note: want ? "status line written (account limits)" : "status line removed"
|
|
845
|
+
};
|
|
770
846
|
}
|
|
771
847
|
var epochIso = (t) => typeof t === "number" && t > 0 && t < 1e11 ? new Date(t * 1e3).toISOString() : void 0;
|
|
772
848
|
var WINDOW_MINUTES = { five_hour: 300, seven_day: 10080 };
|
|
@@ -1635,6 +1711,41 @@ import path7 from "node:path";
|
|
|
1635
1711
|
import { spawn } from "node:child_process";
|
|
1636
1712
|
import { accessSync, constants, readFileSync as readFileSync4, statSync as statSync3 } from "node:fs";
|
|
1637
1713
|
import path6 from "node:path";
|
|
1714
|
+
|
|
1715
|
+
// src/launched-by.ts
|
|
1716
|
+
var LAUNCHED_BY_ENV = "SESSIONPIPE_LAUNCHED_BY";
|
|
1717
|
+
var DELEGATED_BY_ENV = "SESSIONPIPE_ASKED_BY";
|
|
1718
|
+
var ASK_CHAIN_ENV = "SESSIONPIPE_ASK_CHAIN";
|
|
1719
|
+
var LINEAGE_ENV = [LAUNCHED_BY_ENV, DELEGATED_BY_ENV, ASK_CHAIN_ENV];
|
|
1720
|
+
var SESSION_REF = /^[a-z][a-z0-9-]{0,39}:[A-Za-z0-9._:-]{1,128}$/;
|
|
1721
|
+
function withoutLineage(env) {
|
|
1722
|
+
const e = { ...env };
|
|
1723
|
+
for (const k of LINEAGE_ENV) delete e[k];
|
|
1724
|
+
return e;
|
|
1725
|
+
}
|
|
1726
|
+
function refIn(env, name) {
|
|
1727
|
+
const v = env[name]?.trim();
|
|
1728
|
+
return v && SESSION_REF.test(v) ? v : void 0;
|
|
1729
|
+
}
|
|
1730
|
+
function launchedBy(env) {
|
|
1731
|
+
return refIn(env, LAUNCHED_BY_ENV);
|
|
1732
|
+
}
|
|
1733
|
+
function delegatedBy(env) {
|
|
1734
|
+
return refIn(env, DELEGATED_BY_ENV);
|
|
1735
|
+
}
|
|
1736
|
+
function readHook(adapter, input) {
|
|
1737
|
+
const r = adapter.fromHook(input);
|
|
1738
|
+
if (r && !r.session.parent_id) {
|
|
1739
|
+
const self = `${adapter.name}:${r.session.id}`;
|
|
1740
|
+
const launcher = launchedBy(input.env);
|
|
1741
|
+
if (launcher && launcher !== self) r.session.launched_by = launcher;
|
|
1742
|
+
const asker = delegatedBy(input.env);
|
|
1743
|
+
if (asker && asker !== self) r.session.delegated_by = asker;
|
|
1744
|
+
}
|
|
1745
|
+
return r;
|
|
1746
|
+
}
|
|
1747
|
+
|
|
1748
|
+
// src/adapters/headless.ts
|
|
1638
1749
|
var JOB_TIMEOUT_MS = 30 * 6e4;
|
|
1639
1750
|
var OUT_CAP = 1024 * 1024;
|
|
1640
1751
|
function isExe(p) {
|
|
@@ -1682,7 +1793,7 @@ function probe(bin, args) {
|
|
|
1682
1793
|
return s ? { ...s, shell: false } : { file: [`"${bin}"`, ...args].join(" "), args: [], shell: true };
|
|
1683
1794
|
}
|
|
1684
1795
|
function jobEnv(env = process.env) {
|
|
1685
|
-
return { ...env, SESSIONPIPE_CONTROL_JOB: "1" };
|
|
1796
|
+
return { ...withoutLineage(env), SESSIONPIPE_CONTROL_JOB: "1" };
|
|
1686
1797
|
}
|
|
1687
1798
|
function runHeadless(bin, args, o) {
|
|
1688
1799
|
return new Promise((resolve) => {
|
|
@@ -1904,7 +2015,9 @@ function startAcp(o) {
|
|
|
1904
2015
|
try {
|
|
1905
2016
|
child = spawn2(c.file, c.args, {
|
|
1906
2017
|
cwd: o.cwd,
|
|
1907
|
-
env
|
|
2018
|
+
// The caller's env as given (the daemon's is jobEnv's already, with the ask's
|
|
2019
|
+
// lineage on top), marked as a control job.
|
|
2020
|
+
env: { ...o.env, SESSIONPIPE_CONTROL_JOB: "1" },
|
|
1908
2021
|
stdio: ["pipe", "pipe", "pipe"],
|
|
1909
2022
|
detached: process.platform !== "win32",
|
|
1910
2023
|
windowsHide: true
|
|
@@ -2091,18 +2204,18 @@ function startAcp(o) {
|
|
|
2091
2204
|
);
|
|
2092
2205
|
const stop = str(res?.stopReason);
|
|
2093
2206
|
const words = t.text.trim() || `(${label} finished without a written reply.)`;
|
|
2094
|
-
const
|
|
2095
|
-
if (stop === "end_turn" || stop === "max_tokens") return { reply: words +
|
|
2207
|
+
const refused2 = deniedNote(t.denied, t.count);
|
|
2208
|
+
if (stop === "end_turn" || stop === "max_tokens") return { reply: words + refused2 };
|
|
2096
2209
|
if (stop === "refusal") return { reply: `${words}
|
|
2097
2210
|
|
|
2098
|
-
(${label} refused the request.)${
|
|
2211
|
+
(${label} refused the request.)${refused2}` };
|
|
2099
2212
|
if (stop === "cancelled")
|
|
2100
2213
|
return timedOut ? {
|
|
2101
2214
|
reply: "",
|
|
2102
2215
|
error: `${label} was still working after ${Math.round(limit / 6e4)} minutes, so it was stopped`
|
|
2103
2216
|
} : { reply: `${words}
|
|
2104
2217
|
|
|
2105
|
-
(The turn was cancelled.)${
|
|
2218
|
+
(The turn was cancelled.)${refused2}` };
|
|
2106
2219
|
return { reply: "", error: `${label} ended the turn with ${stop || "no stop reason"}` };
|
|
2107
2220
|
} catch (e) {
|
|
2108
2221
|
return { reply: "", error: e.message };
|
|
@@ -2163,16 +2276,7 @@ function startAcp(o) {
|
|
|
2163
2276
|
}
|
|
2164
2277
|
|
|
2165
2278
|
// src/adapters/antigravity.ts
|
|
2166
|
-
import {
|
|
2167
|
-
existsSync as existsSync3,
|
|
2168
|
-
mkdirSync as mkdirSync2,
|
|
2169
|
-
readdirSync as readdirSync3,
|
|
2170
|
-
readFileSync as readFileSync5,
|
|
2171
|
-
realpathSync,
|
|
2172
|
-
statSync as statSync4,
|
|
2173
|
-
unlinkSync as unlinkSync2,
|
|
2174
|
-
writeFileSync as writeFileSync3
|
|
2175
|
-
} from "node:fs";
|
|
2279
|
+
import { existsSync as existsSync3, readdirSync as readdirSync3, readFileSync as readFileSync5, realpathSync, statSync as statSync4 } from "node:fs";
|
|
2176
2280
|
import path8 from "node:path";
|
|
2177
2281
|
var NAME2 = "antigravity";
|
|
2178
2282
|
var HOOK_NAME = "sessionpipe";
|
|
@@ -2204,10 +2308,8 @@ var antigravity = {
|
|
|
2204
2308
|
const e = entry(cmd);
|
|
2205
2309
|
if (JSON.stringify(hooks[HOOK_NAME]) === JSON.stringify(e)) return [{ file, changed: false }];
|
|
2206
2310
|
hooks[HOOK_NAME] = e;
|
|
2207
|
-
mkdirSync2(path8.dirname(file), { recursive: true });
|
|
2208
|
-
writeFileSync3(file, formatJson(hooks, f));
|
|
2209
2311
|
return [
|
|
2210
|
-
{
|
|
2312
|
+
writeBack(file, formatJson(hooks, f), f.existed) ?? {
|
|
2211
2313
|
file,
|
|
2212
2314
|
changed: true,
|
|
2213
2315
|
created: !f.existed,
|
|
@@ -2223,12 +2325,8 @@ var antigravity = {
|
|
|
2223
2325
|
const hooks = f.value;
|
|
2224
2326
|
if (!(HOOK_NAME in hooks)) return [{ file, changed: false }];
|
|
2225
2327
|
delete hooks[HOOK_NAME];
|
|
2226
|
-
if (opts.created?.includes(file) && !Object.keys(hooks).length)
|
|
2227
|
-
|
|
2228
|
-
return [{ file, changed: true, note: "removed (sessionpipe created it)" }];
|
|
2229
|
-
}
|
|
2230
|
-
writeFileSync3(file, formatJson(hooks, f));
|
|
2231
|
-
return [{ file, changed: true }];
|
|
2328
|
+
if (opts.created?.includes(file) && !Object.keys(hooks).length) return [removeEmptied(file, formatJson(hooks, f))];
|
|
2329
|
+
return [writeBack(file, formatJson(hooks, f)) ?? { file, changed: true }];
|
|
2232
2330
|
},
|
|
2233
2331
|
installed(cmd, env = process.env) {
|
|
2234
2332
|
const file = hooksFile(env);
|
|
@@ -2591,7 +2689,10 @@ var reading = () => new AgyReading();
|
|
|
2591
2689
|
// src/adapters/claude-code-control.ts
|
|
2592
2690
|
var claude_code_control_exports = {};
|
|
2593
2691
|
__export(claude_code_control_exports, {
|
|
2692
|
+
CLAUDE_MODES: () => CLAUDE_MODES,
|
|
2594
2693
|
JOB_TIMEOUT_MS: () => JOB_TIMEOUT_MS,
|
|
2694
|
+
MODE_RANK: () => MODE_RANK,
|
|
2695
|
+
NO_HOME_SETTINGS: () => NO_HOME_SETTINGS,
|
|
2595
2696
|
ceilingLevel: () => ceilingLevel,
|
|
2596
2697
|
claudeArgs: () => claudeArgs,
|
|
2597
2698
|
claudeEnv: () => claudeEnv,
|
|
@@ -2601,17 +2702,26 @@ __export(claude_code_control_exports, {
|
|
|
2601
2702
|
detectCaps: () => detectCaps,
|
|
2602
2703
|
findClaude: () => findClaude,
|
|
2603
2704
|
findTranscript: () => findTranscript,
|
|
2705
|
+
findTranscripts: () => findTranscripts,
|
|
2706
|
+
homeSettings: () => homeSettings,
|
|
2707
|
+
hookMode: () => hookMode,
|
|
2604
2708
|
isUuid: () => isUuid,
|
|
2605
2709
|
liveProcess: () => liveProcess,
|
|
2710
|
+
liveRecord: () => liveRecord,
|
|
2711
|
+
liveRecords: () => liveRecords,
|
|
2606
2712
|
loginCommand: () => loginCommand,
|
|
2607
2713
|
loginHint: () => loginHint,
|
|
2608
2714
|
modeFlags: () => modeFlags,
|
|
2715
|
+
modeRank: () => modeRank,
|
|
2609
2716
|
offeredLevels: () => offeredLevels2,
|
|
2610
2717
|
outputArgs: () => outputArgs,
|
|
2611
2718
|
parseAuthStatus: () => parseAuthStatus,
|
|
2612
2719
|
parseCaps: () => parseCaps,
|
|
2613
2720
|
parseResult: () => parseResult,
|
|
2721
|
+
peerClass: () => peerClass,
|
|
2614
2722
|
recentlyWritten: () => recentlyWritten,
|
|
2723
|
+
relaunchMode: () => relaunchMode,
|
|
2724
|
+
resolveHome: () => resolveHome,
|
|
2615
2725
|
runClaude: () => runClaude,
|
|
2616
2726
|
sessionFolder: () => sessionFolder
|
|
2617
2727
|
});
|
|
@@ -2630,10 +2740,10 @@ var MODEL_RE = /^[A-Za-z0-9][A-Za-z0-9._:/@[\]-]*$/;
|
|
|
2630
2740
|
var START_PERMISSIONS = ["read", "safe", "edits", "auto"];
|
|
2631
2741
|
var levelRank = (l) => START_PERMISSIONS.indexOf(l);
|
|
2632
2742
|
var FIELDS = {
|
|
2633
|
-
prompt: ["text", "files"],
|
|
2743
|
+
prompt: ["text", "files", "reply_to"],
|
|
2634
2744
|
"permission.answer": ["for", "decision"],
|
|
2635
2745
|
cancel: [],
|
|
2636
|
-
start: ["text", "cwd", "files", "permission", "model"],
|
|
2746
|
+
start: ["text", "cwd", "files", "permission", "model", "reply_to"],
|
|
2637
2747
|
"key.add": ["key", "name"],
|
|
2638
2748
|
pool: ["pool_action", "pool_account", "pool_order", "pool_code"]
|
|
2639
2749
|
};
|
|
@@ -2705,6 +2815,8 @@ var LEVEL_MODE = {
|
|
|
2705
2815
|
};
|
|
2706
2816
|
function modeFlags(level, caps) {
|
|
2707
2817
|
if (level === "inherit") return { args: [] };
|
|
2818
|
+
if (caps.unread)
|
|
2819
|
+
return { error: `this machine couldn't read its Claude Code's options (${caps.unread}); try again in a minute` };
|
|
2708
2820
|
if (level === "auto" && !caps.modes.includes("auto"))
|
|
2709
2821
|
return { error: "this machine is in auto mode, but its Claude Code has no auto permission mode" };
|
|
2710
2822
|
if (level !== "safe") {
|
|
@@ -2724,6 +2836,7 @@ function modeFlags(level, caps) {
|
|
|
2724
2836
|
}
|
|
2725
2837
|
var ceilingLevel = (mode) => mode === "inherit" ? "safe" : mode;
|
|
2726
2838
|
function offeredLevels2(mode, caps) {
|
|
2839
|
+
if (caps.unread) return [];
|
|
2727
2840
|
return START_PERMISSIONS.filter(
|
|
2728
2841
|
(l) => levelRank(l) <= levelRank(ceilingLevel(mode)) && (l === "safe" || caps.modes.includes(LEVEL_MODE[l]))
|
|
2729
2842
|
);
|
|
@@ -2749,7 +2862,7 @@ function claudeArgs(o) {
|
|
|
2749
2862
|
};
|
|
2750
2863
|
}
|
|
2751
2864
|
function claudeEnv(configDir, env = process.env) {
|
|
2752
|
-
const e =
|
|
2865
|
+
const e = withoutLineage(env);
|
|
2753
2866
|
for (const k of [
|
|
2754
2867
|
"CLAUDECODE",
|
|
2755
2868
|
"CLAUDE_CODE_SESSION_ID",
|
|
@@ -2806,43 +2919,73 @@ function loginHint(configDir, platform = process.platform) {
|
|
|
2806
2919
|
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.`;
|
|
2807
2920
|
return `Claude Code (${shown}) isn't signed in on this machine: run ${login} there.`;
|
|
2808
2921
|
}
|
|
2809
|
-
function detectCaps(bin) {
|
|
2810
|
-
let help = "";
|
|
2922
|
+
function detectCaps(bin, timeoutMs = 3e4) {
|
|
2811
2923
|
const c = probe(bin, ["--help"]);
|
|
2812
2924
|
try {
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2925
|
+
return parseCaps(
|
|
2926
|
+
execFileSync3(c.file, c.args, {
|
|
2927
|
+
encoding: "utf8",
|
|
2928
|
+
timeout: timeoutMs,
|
|
2929
|
+
env: claudeEnv(),
|
|
2930
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
2931
|
+
maxBuffer: 4 * 1024 * 1024,
|
|
2932
|
+
shell: c.shell
|
|
2933
|
+
})
|
|
2934
|
+
);
|
|
2821
2935
|
} catch (e) {
|
|
2822
|
-
|
|
2936
|
+
const err = e;
|
|
2937
|
+
const caps = parseCaps(String(err?.stdout || ""));
|
|
2938
|
+
if (caps.hasMode) return caps;
|
|
2939
|
+
const why = err?.code === "ETIMEDOUT" ? `timed out after ${timeoutMs / 1e3} s` : "failed";
|
|
2940
|
+
return { ...caps, unread: `\`${path10.basename(bin)} --help\` ${why}` };
|
|
2823
2941
|
}
|
|
2824
|
-
return parseCaps(help);
|
|
2825
2942
|
}
|
|
2826
|
-
function
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2943
|
+
function transcriptIn(session, dir) {
|
|
2944
|
+
let projects = [];
|
|
2945
|
+
try {
|
|
2946
|
+
projects = readdirSync4(path10.join(dir, "projects"), { withFileTypes: true });
|
|
2947
|
+
} catch {
|
|
2948
|
+
return null;
|
|
2949
|
+
}
|
|
2950
|
+
for (const p of projects) {
|
|
2951
|
+
if (!p.isDirectory()) continue;
|
|
2952
|
+
const file = path10.join(dir, "projects", p.name, `${session}.jsonl`);
|
|
2830
2953
|
try {
|
|
2831
|
-
|
|
2954
|
+
if (statSync5(file).isFile()) return { file, configDir: dir, project: p.name };
|
|
2832
2955
|
} catch {
|
|
2833
|
-
continue;
|
|
2834
|
-
}
|
|
2835
|
-
for (const p of projects) {
|
|
2836
|
-
if (!p.isDirectory()) continue;
|
|
2837
|
-
const file = path10.join(dir, "projects", p.name, `${session}.jsonl`);
|
|
2838
|
-
try {
|
|
2839
|
-
if (statSync5(file).isFile()) return { file, configDir: dir, project: p.name };
|
|
2840
|
-
} catch {
|
|
2841
|
-
}
|
|
2842
2956
|
}
|
|
2843
2957
|
}
|
|
2844
2958
|
return null;
|
|
2845
2959
|
}
|
|
2960
|
+
function findTranscript(session, dirs) {
|
|
2961
|
+
if (!UUID_RE2.test(session)) return null;
|
|
2962
|
+
for (const dir of dirs) {
|
|
2963
|
+
const t = transcriptIn(session, dir);
|
|
2964
|
+
if (t) return t;
|
|
2965
|
+
}
|
|
2966
|
+
return null;
|
|
2967
|
+
}
|
|
2968
|
+
function findTranscripts(session, dirs) {
|
|
2969
|
+
if (!UUID_RE2.test(session)) return [];
|
|
2970
|
+
const out = [];
|
|
2971
|
+
for (const dir of dirs) {
|
|
2972
|
+
const t = transcriptIn(session, dir);
|
|
2973
|
+
if (t) out.push(t);
|
|
2974
|
+
}
|
|
2975
|
+
return out;
|
|
2976
|
+
}
|
|
2977
|
+
var homeName = (t) => path10.basename(t.configDir);
|
|
2978
|
+
function resolveHome(session, dirs) {
|
|
2979
|
+
const all = findTranscripts(session, dirs);
|
|
2980
|
+
if (!all.length) return null;
|
|
2981
|
+
if (all.length === 1) return { transcript: all[0] };
|
|
2982
|
+
const live = all.filter((t) => liveRecords(session, t.configDir).length > 0);
|
|
2983
|
+
if (live.length === 1) return { transcript: live[0] };
|
|
2984
|
+
return {
|
|
2985
|
+
ambiguous: [...new Set(all.map(homeName))].sort(),
|
|
2986
|
+
live: [...new Set(live.map(homeName))].sort()
|
|
2987
|
+
};
|
|
2988
|
+
}
|
|
2846
2989
|
var projectName = (cwd) => cwd.replace(/[^a-zA-Z0-9]/g, "-");
|
|
2847
2990
|
function sessionFolder(t, cap2 = 8 * 1024 * 1024) {
|
|
2848
2991
|
let first = null;
|
|
@@ -2914,22 +3057,105 @@ function pidAlive(pid) {
|
|
|
2914
3057
|
return e.code === "EPERM";
|
|
2915
3058
|
}
|
|
2916
3059
|
}
|
|
2917
|
-
|
|
2918
|
-
|
|
3060
|
+
var registryRow = (file, configDir, j) => {
|
|
3061
|
+
const pid = Number(j.pid ?? path10.basename(file).slice(0, -5));
|
|
3062
|
+
const str2 = (v) => typeof v === "string" && v ? v : null;
|
|
3063
|
+
let keyFile = null;
|
|
2919
3064
|
try {
|
|
2920
|
-
|
|
3065
|
+
const want = new RegExp(`^${pid}\\.[0-9a-f]{64}\\.key$`);
|
|
3066
|
+
const hit = readdirSync4(path10.dirname(file)).find((n) => want.test(n));
|
|
3067
|
+
if (hit) keyFile = path10.join(path10.dirname(file), hit);
|
|
2921
3068
|
} catch {
|
|
2922
|
-
return false;
|
|
2923
3069
|
}
|
|
2924
|
-
|
|
3070
|
+
return {
|
|
3071
|
+
configDir,
|
|
3072
|
+
file,
|
|
3073
|
+
pid,
|
|
3074
|
+
socket: str2(j.messagingSocketPath),
|
|
3075
|
+
peerProtocol: typeof j.peerProtocol === "number" ? j.peerProtocol : null,
|
|
3076
|
+
procStart: str2(j.procStart),
|
|
3077
|
+
status: str2(j.status),
|
|
3078
|
+
kind: str2(j.kind),
|
|
3079
|
+
entrypoint: str2(j.entrypoint),
|
|
3080
|
+
keyFile
|
|
3081
|
+
};
|
|
3082
|
+
};
|
|
3083
|
+
function liveRecords(session, dirs) {
|
|
3084
|
+
const out = [];
|
|
3085
|
+
for (const dir of typeof dirs === "string" ? [dirs] : dirs) {
|
|
3086
|
+
let names = [];
|
|
2925
3087
|
try {
|
|
2926
|
-
|
|
2927
|
-
if (String(j.sessionId) !== session) continue;
|
|
2928
|
-
if (pidAlive(Number(j.pid ?? n.slice(0, -5)))) return true;
|
|
3088
|
+
names = readdirSync4(path10.join(dir, "sessions")).filter((n) => /^\d+\.json$/.test(n));
|
|
2929
3089
|
} catch {
|
|
3090
|
+
continue;
|
|
3091
|
+
}
|
|
3092
|
+
for (const n of names) {
|
|
3093
|
+
const file = path10.join(dir, "sessions", n);
|
|
3094
|
+
let j;
|
|
3095
|
+
try {
|
|
3096
|
+
j = JSON.parse(readFileSync6(file, "utf8"));
|
|
3097
|
+
} catch {
|
|
3098
|
+
continue;
|
|
3099
|
+
}
|
|
3100
|
+
if (String(j.sessionId) !== session) continue;
|
|
3101
|
+
const row = registryRow(file, dir, j);
|
|
3102
|
+
if (pidAlive(row.pid)) out.push(row);
|
|
2930
3103
|
}
|
|
2931
3104
|
}
|
|
2932
|
-
return
|
|
3105
|
+
return out;
|
|
3106
|
+
}
|
|
3107
|
+
function liveRecord(session, dirs) {
|
|
3108
|
+
const all = liveRecords(session, dirs);
|
|
3109
|
+
return all.length === 1 ? all[0] : null;
|
|
3110
|
+
}
|
|
3111
|
+
function liveProcess(session, configDir) {
|
|
3112
|
+
return liveRecords(session, configDir).length > 0;
|
|
3113
|
+
}
|
|
3114
|
+
var MODE_RANK = {
|
|
3115
|
+
plan: 0,
|
|
3116
|
+
default: 1,
|
|
3117
|
+
manual: 1,
|
|
3118
|
+
dontAsk: 1,
|
|
3119
|
+
acceptEdits: 2,
|
|
3120
|
+
auto: 3,
|
|
3121
|
+
bypassPermissions: 4
|
|
3122
|
+
};
|
|
3123
|
+
var CLAUDE_MODES = Object.keys(MODE_RANK);
|
|
3124
|
+
var modeRank = (m) => m && m in MODE_RANK ? MODE_RANK[m] : -1;
|
|
3125
|
+
function hookMode(payload) {
|
|
3126
|
+
if (!payload || typeof payload !== "object") return null;
|
|
3127
|
+
const p = payload;
|
|
3128
|
+
const v = typeof p.permission_mode === "string" ? p.permission_mode : p.permissionMode;
|
|
3129
|
+
return typeof v === "string" && CLAUDE_MODES.includes(v) ? v : null;
|
|
3130
|
+
}
|
|
3131
|
+
function peerClass(mode) {
|
|
3132
|
+
if (mode === "bypassPermissions") return "bypass";
|
|
3133
|
+
if (mode === "default" || mode === "manual" || mode === "dontAsk" || mode === "acceptEdits") return "prompting";
|
|
3134
|
+
return null;
|
|
3135
|
+
}
|
|
3136
|
+
var NO_HOME_SETTINGS = { defaultMode: null, disableAutoMode: false, disableBypass: false };
|
|
3137
|
+
function homeSettings(configDir, home = os5.homedir()) {
|
|
3138
|
+
const file = path10.join(configDir ?? path10.join(home, ".claude"), "settings.json");
|
|
3139
|
+
let j;
|
|
3140
|
+
try {
|
|
3141
|
+
j = JSON.parse(readFileSync6(file, "utf8"));
|
|
3142
|
+
} catch {
|
|
3143
|
+
return { ...NO_HOME_SETTINGS };
|
|
3144
|
+
}
|
|
3145
|
+
if (!j || typeof j !== "object" || Array.isArray(j)) return { ...NO_HOME_SETTINGS };
|
|
3146
|
+
const p = j.permissions && typeof j.permissions === "object" ? j.permissions : {};
|
|
3147
|
+
const off = (k) => j[k] === "disable" || p[k] === "disable";
|
|
3148
|
+
return {
|
|
3149
|
+
defaultMode: typeof p.defaultMode === "string" && CLAUDE_MODES.includes(p.defaultMode) ? p.defaultMode : null,
|
|
3150
|
+
disableAutoMode: off("disableAutoMode"),
|
|
3151
|
+
disableBypass: off("disableBypassPermissionsMode")
|
|
3152
|
+
};
|
|
3153
|
+
}
|
|
3154
|
+
function relaunchMode(recorded, home) {
|
|
3155
|
+
if (recorded === "plan") return recorded;
|
|
3156
|
+
if ((recorded === "dontAsk" || recorded === "acceptEdits") && modeRank(home.defaultMode) >= modeRank(recorded))
|
|
3157
|
+
return recorded;
|
|
3158
|
+
return null;
|
|
2933
3159
|
}
|
|
2934
3160
|
function recentlyWritten(file, ourLastEnd, now = Date.now()) {
|
|
2935
3161
|
let m;
|
|
@@ -2971,7 +3197,7 @@ function parseResult(stdout) {
|
|
|
2971
3197
|
var isUuid = (s) => typeof s === "string" && UUID_RE2.test(s);
|
|
2972
3198
|
|
|
2973
3199
|
// src/adapters/codex.ts
|
|
2974
|
-
import { existsSync as existsSync4,
|
|
3200
|
+
import { existsSync as existsSync4, readdirSync as readdirSync5, readFileSync as readFileSync7 } from "node:fs";
|
|
2975
3201
|
import path11 from "node:path";
|
|
2976
3202
|
|
|
2977
3203
|
// src/adapters/codex-config.ts
|
|
@@ -3124,29 +3350,30 @@ var configFile2 = (env) => path11.join(codexHome(env), "config.toml");
|
|
|
3124
3350
|
var sessionsDir = (env) => path11.join(codexHome(env), "sessions");
|
|
3125
3351
|
var validTool3 = (n) => typeof n === "string" && /^[A-Za-z0-9_.:-]{1,120}$/.test(n);
|
|
3126
3352
|
var endReason2 = (r) => r === "clear" || r === "logout" || r === "exit" ? r : "other";
|
|
3353
|
+
var UNTIL_APPROVED = "Codex will report nothing until you approve the hooks: start `codex` and accept the hook review";
|
|
3127
3354
|
function installNotify(cmd, env) {
|
|
3128
3355
|
const file = configFile2(env);
|
|
3129
3356
|
let text = "";
|
|
3357
|
+
let existed = true;
|
|
3130
3358
|
try {
|
|
3131
3359
|
text = readFileSync7(file, "utf8");
|
|
3132
|
-
} catch {
|
|
3360
|
+
} catch (e) {
|
|
3361
|
+
existed = e.code !== "ENOENT";
|
|
3133
3362
|
}
|
|
3134
3363
|
const argv = cmd([NAME3, "notify"]).argv;
|
|
3135
3364
|
const line = `notify = ${JSON.stringify(argv)}`;
|
|
3136
3365
|
const n = readNotify(text);
|
|
3137
|
-
const write = (next) => {
|
|
3138
|
-
|
|
3139
|
-
|
|
3366
|
+
const write = (next, note) => {
|
|
3367
|
+
const bad = writeBack(file, next, existed);
|
|
3368
|
+
return bad ? { ...bad, note: `${bad.note}. ${UNTIL_APPROVED}` } : { file, changed: true, note };
|
|
3140
3369
|
};
|
|
3141
3370
|
if (n && !n.other) {
|
|
3142
3371
|
if (!n.misplaced && JSON.stringify(n.argv) === JSON.stringify(argv))
|
|
3143
3372
|
return { file, changed: false, note: "notify current" };
|
|
3144
|
-
write(
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
note: n.misplaced ? "notify moved to the top level (it sat inside a [table], where Codex never reads it)" : "notify updated"
|
|
3149
|
-
};
|
|
3373
|
+
return write(
|
|
3374
|
+
withNotify(text, line),
|
|
3375
|
+
n.misplaced ? "notify moved to the top level (it sat inside a [table], where Codex never reads it)" : "notify updated"
|
|
3376
|
+
);
|
|
3150
3377
|
}
|
|
3151
3378
|
if (n?.other) {
|
|
3152
3379
|
const prev = previousNotify(n.argv);
|
|
@@ -3155,20 +3382,20 @@ function installNotify(cmd, env) {
|
|
|
3155
3382
|
if (JSON.stringify(prev) === JSON.stringify(argv))
|
|
3156
3383
|
return { file, changed: false, note: "notify current (after Codex Computer Use)" };
|
|
3157
3384
|
}
|
|
3158
|
-
if (!prev || OURS2.test(JSON.stringify(prev)))
|
|
3159
|
-
write(
|
|
3160
|
-
|
|
3161
|
-
|
|
3385
|
+
if (!prev || OURS2.test(JSON.stringify(prev)))
|
|
3386
|
+
return write(
|
|
3387
|
+
replaceNotify(text, n, `notify = ${withPreviousRaw(n.raw, argv)}`),
|
|
3388
|
+
"chained after Codex Computer Use (--previous-notify)"
|
|
3389
|
+
);
|
|
3162
3390
|
}
|
|
3163
3391
|
return {
|
|
3164
3392
|
file,
|
|
3165
3393
|
changed: false,
|
|
3166
3394
|
skipped: true,
|
|
3167
|
-
note: `notify belongs to ${(n.argv?.[0] ?? n.raw).replace(/^.*\//, "").slice(0, 40)}; left alone.
|
|
3395
|
+
note: `notify belongs to ${(n.argv?.[0] ?? n.raw).replace(/^.*\//, "").slice(0, 40)}; left alone. ${UNTIL_APPROVED}`
|
|
3168
3396
|
};
|
|
3169
3397
|
}
|
|
3170
|
-
write(withNotify(text, line));
|
|
3171
|
-
return { file, changed: true, note: "notify set at the top level" };
|
|
3398
|
+
return write(withNotify(text, line), "notify set at the top level");
|
|
3172
3399
|
}
|
|
3173
3400
|
function uninstallNotify(env) {
|
|
3174
3401
|
const file = configFile2(env);
|
|
@@ -3180,15 +3407,13 @@ function uninstallNotify(env) {
|
|
|
3180
3407
|
}
|
|
3181
3408
|
const n = readNotify(text);
|
|
3182
3409
|
if (!n) return { file, changed: false };
|
|
3183
|
-
if (!n.other) {
|
|
3184
|
-
writeFileSync4(file, withoutNotify(text));
|
|
3185
|
-
return { file, changed: true };
|
|
3186
|
-
}
|
|
3410
|
+
if (!n.other) return writeBack(file, withoutNotify(text)) ?? { file, changed: true };
|
|
3187
3411
|
const prev = previousNotify(n.argv);
|
|
3188
|
-
if (isComputerUse(n.argv) && prev && OURS2.test(JSON.stringify(prev)))
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3412
|
+
if (isComputerUse(n.argv) && prev && OURS2.test(JSON.stringify(prev)))
|
|
3413
|
+
return writeBack(file, replaceNotify(text, n, `notify = ${stripPreviousRaw(n.raw)}`)) ?? {
|
|
3414
|
+
file,
|
|
3415
|
+
changed: true
|
|
3416
|
+
};
|
|
3192
3417
|
return { file, changed: false };
|
|
3193
3418
|
}
|
|
3194
3419
|
function findRollout(threadId, env = process.env) {
|
|
@@ -3220,8 +3445,12 @@ function rolloutMeta(file) {
|
|
|
3220
3445
|
return scanLines(file, (line) => parseLines([line])[0] ?? {});
|
|
3221
3446
|
}
|
|
3222
3447
|
function ofAnotherThread(file, id) {
|
|
3448
|
+
const other = threadOf(file);
|
|
3449
|
+
return !!other && other !== id.toLowerCase();
|
|
3450
|
+
}
|
|
3451
|
+
function threadOf(file) {
|
|
3223
3452
|
const m = /-([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\.jsonl(?:\.zst)?$/i.exec(file);
|
|
3224
|
-
return
|
|
3453
|
+
return m?.[1] ? m[1].toLowerCase() : null;
|
|
3225
3454
|
}
|
|
3226
3455
|
var codex = {
|
|
3227
3456
|
name: NAME3,
|
|
@@ -3300,6 +3529,8 @@ var codex = {
|
|
|
3300
3529
|
const turn_id = typeof s.turn_id === "string" ? s.turn_id : void 0;
|
|
3301
3530
|
const tool = validTool3(s.tool_name) ? s.tool_name : void 0;
|
|
3302
3531
|
const call_id = typeof s.tool_use_id === "string" ? s.tool_use_id : void 0;
|
|
3532
|
+
const thread = typeof s.transcript_path === "string" ? threadOf(s.transcript_path) : null;
|
|
3533
|
+
const agent_id = typeof s.agent_id === "string" && s.agent_id ? s.agent_id : typeof s.subagent_id === "string" && s.subagent_id ? s.subagent_id : thread && thread !== String(id).toLowerCase() ? thread : void 0;
|
|
3303
3534
|
const ev = (type, data) => ({ type, data: prune3(data), harnessEvent: event });
|
|
3304
3535
|
const events = [];
|
|
3305
3536
|
switch (event) {
|
|
@@ -3319,17 +3550,30 @@ var codex = {
|
|
|
3319
3550
|
);
|
|
3320
3551
|
break;
|
|
3321
3552
|
case "PreToolUse":
|
|
3322
|
-
if (tool) events.push(ev("tool.started", { tool, call_id, turn_id, input: s.tool_input }));
|
|
3553
|
+
if (tool) events.push(ev("tool.started", { tool, call_id, turn_id, agent_id, input: s.tool_input }));
|
|
3323
3554
|
break;
|
|
3324
3555
|
case "PostToolUse":
|
|
3325
3556
|
if (tool)
|
|
3326
3557
|
events.push(
|
|
3327
|
-
ev("tool.ended", {
|
|
3558
|
+
ev("tool.ended", {
|
|
3559
|
+
tool,
|
|
3560
|
+
call_id,
|
|
3561
|
+
turn_id,
|
|
3562
|
+
agent_id,
|
|
3563
|
+
ok: true,
|
|
3564
|
+
input: s.tool_input,
|
|
3565
|
+
output: s.tool_response
|
|
3566
|
+
})
|
|
3328
3567
|
);
|
|
3329
3568
|
break;
|
|
3330
3569
|
case "PermissionRequest":
|
|
3331
3570
|
events.push(
|
|
3332
|
-
ev("attention.needed", {
|
|
3571
|
+
ev("attention.needed", {
|
|
3572
|
+
attention_id: call_id ?? `perm-${Date.now()}`,
|
|
3573
|
+
kind: "permission",
|
|
3574
|
+
tool,
|
|
3575
|
+
agent_id
|
|
3576
|
+
})
|
|
3333
3577
|
);
|
|
3334
3578
|
break;
|
|
3335
3579
|
case "Stop":
|
|
@@ -3987,7 +4231,7 @@ var CHAIN_LIMIT = 4e3;
|
|
|
3987
4231
|
var CHAIN_SQL = `WITH RECURSIVE chain(node_id, parent_node_id, chat_message, created_at) AS (
|
|
3988
4232
|
SELECT node_id, parent_node_id, chat_message, created_at FROM message_nodes
|
|
3989
4233
|
WHERE session_id = ? AND node_id = (SELECT main_chain_id FROM sessions WHERE id = ?)
|
|
3990
|
-
UNION
|
|
4234
|
+
UNION
|
|
3991
4235
|
SELECT m.node_id, m.parent_node_id, m.chat_message, m.created_at FROM message_nodes m
|
|
3992
4236
|
JOIN chain c ON m.node_id = c.parent_node_id AND m.session_id = ? AND m.node_id >= ?
|
|
3993
4237
|
)
|
|
@@ -4265,7 +4509,7 @@ function drivableHarnesses(env = process.env) {
|
|
|
4265
4509
|
}
|
|
4266
4510
|
|
|
4267
4511
|
// src/config.ts
|
|
4268
|
-
import { chmodSync, existsSync as existsSync8, mkdirSync as
|
|
4512
|
+
import { chmodSync, existsSync as existsSync8, mkdirSync as mkdirSync2, readFileSync as readFileSync10, writeFileSync as writeFileSync2 } from "node:fs";
|
|
4269
4513
|
import path15 from "node:path";
|
|
4270
4514
|
var DEFAULT_CONFIG = { sinks: [], harnesses: {}, update_check: true, keep_days: 30 };
|
|
4271
4515
|
function readConfig(file = configFile()) {
|
|
@@ -4281,9 +4525,36 @@ function readConfig(file = configFile()) {
|
|
|
4281
4525
|
return { ...DEFAULT_CONFIG, sinks: [], harnesses: {} };
|
|
4282
4526
|
}
|
|
4283
4527
|
}
|
|
4528
|
+
function configProblem(file) {
|
|
4529
|
+
let text;
|
|
4530
|
+
try {
|
|
4531
|
+
text = readFileSync10(file, "utf8");
|
|
4532
|
+
} catch (e) {
|
|
4533
|
+
const code = e.code;
|
|
4534
|
+
return code === "ENOENT" ? void 0 : `could not be read (${code})`;
|
|
4535
|
+
}
|
|
4536
|
+
if (!text.trim()) return void 0;
|
|
4537
|
+
if (text.charCodeAt(0) === 65279) return "starts with a byte-order mark (save it as UTF-8 without one)";
|
|
4538
|
+
try {
|
|
4539
|
+
const j = JSON.parse(text);
|
|
4540
|
+
return j && typeof j === "object" && !Array.isArray(j) ? void 0 : "is not a JSON object";
|
|
4541
|
+
} catch (e) {
|
|
4542
|
+
const m = e.message;
|
|
4543
|
+
const at = (/line \d+ column \d+/.exec(m) ?? /position \d+/.exec(m))?.[0];
|
|
4544
|
+
return `is not valid JSON${at ? ` at ${at}` : ""}`;
|
|
4545
|
+
}
|
|
4546
|
+
}
|
|
4547
|
+
function assertConfigWritable(file = configFile()) {
|
|
4548
|
+
const problem = configProblem(file);
|
|
4549
|
+
if (problem)
|
|
4550
|
+
throw new Error(
|
|
4551
|
+
`${tilde(file)} ${problem}, so it was not written over: that would lose its machine name and sinks. Fix it, or move it aside to start again, then run this again.`
|
|
4552
|
+
);
|
|
4553
|
+
}
|
|
4284
4554
|
function writeConfig(c, file = configFile()) {
|
|
4285
|
-
|
|
4286
|
-
|
|
4555
|
+
assertConfigWritable(file);
|
|
4556
|
+
mkdirSync2(path15.dirname(file), { recursive: true, mode: 448 });
|
|
4557
|
+
writeFileSync2(file, `${JSON.stringify(c, null, 2)}
|
|
4287
4558
|
`, { mode: 384 });
|
|
4288
4559
|
if (process.platform !== "win32" && existsSync8(file)) {
|
|
4289
4560
|
try {
|
|
@@ -4347,36 +4618,21 @@ function makeEvent(a, ctx) {
|
|
|
4347
4618
|
};
|
|
4348
4619
|
}
|
|
4349
4620
|
|
|
4350
|
-
// src/launched-by.ts
|
|
4351
|
-
var LAUNCHED_BY_ENV = "SESSIONPIPE_LAUNCHED_BY";
|
|
4352
|
-
var LAUNCHED_BY = /^[a-z][a-z0-9-]{0,39}:[A-Za-z0-9._:-]{1,128}$/;
|
|
4353
|
-
function launchedBy(env) {
|
|
4354
|
-
const v = env[LAUNCHED_BY_ENV]?.trim();
|
|
4355
|
-
return v && LAUNCHED_BY.test(v) ? v : void 0;
|
|
4356
|
-
}
|
|
4357
|
-
function readHook(adapter, input) {
|
|
4358
|
-
const r = adapter.fromHook(input);
|
|
4359
|
-
if (r && !r.session.parent_id) {
|
|
4360
|
-
const launcher = launchedBy(input.env);
|
|
4361
|
-
if (launcher && launcher !== `${adapter.name}:${r.session.id}`) r.session.launched_by = launcher;
|
|
4362
|
-
}
|
|
4363
|
-
return r;
|
|
4364
|
-
}
|
|
4365
|
-
|
|
4366
4621
|
// src/outbox.ts
|
|
4622
|
+
import { randomBytes as randomBytes3 } from "node:crypto";
|
|
4367
4623
|
import {
|
|
4368
4624
|
appendFileSync,
|
|
4369
4625
|
closeSync as closeSync3,
|
|
4370
4626
|
existsSync as existsSync9,
|
|
4371
|
-
mkdirSync as
|
|
4627
|
+
mkdirSync as mkdirSync3,
|
|
4372
4628
|
openSync as openSync3,
|
|
4373
4629
|
readdirSync as readdirSync6,
|
|
4374
4630
|
readFileSync as readFileSync11,
|
|
4375
4631
|
readSync as readSync3,
|
|
4376
4632
|
renameSync,
|
|
4377
4633
|
statSync as statSync6,
|
|
4378
|
-
unlinkSync as
|
|
4379
|
-
writeFileSync as
|
|
4634
|
+
unlinkSync as unlinkSync2,
|
|
4635
|
+
writeFileSync as writeFileSync3
|
|
4380
4636
|
} from "node:fs";
|
|
4381
4637
|
import path16 from "node:path";
|
|
4382
4638
|
var READ_CHUNK = 1024 * 1024;
|
|
@@ -4406,8 +4662,8 @@ var Outbox = class {
|
|
|
4406
4662
|
} catch {
|
|
4407
4663
|
}
|
|
4408
4664
|
if (n < 0) n = this.lastSeqInFile(ref) + 1;
|
|
4409
|
-
|
|
4410
|
-
|
|
4665
|
+
mkdirSync3(path16.dirname(f), { recursive: true, mode: 448 });
|
|
4666
|
+
writeFileSync3(f, String(n + 1), { mode: 384 });
|
|
4411
4667
|
return n;
|
|
4412
4668
|
}
|
|
4413
4669
|
lastSeqInFile(ref) {
|
|
@@ -4425,7 +4681,7 @@ var Outbox = class {
|
|
|
4425
4681
|
}
|
|
4426
4682
|
append(ref, events) {
|
|
4427
4683
|
if (!events.length) return;
|
|
4428
|
-
|
|
4684
|
+
mkdirSync3(this.dir(ref.harness), { recursive: true, mode: 448 });
|
|
4429
4685
|
appendFileSync(this.file(ref), `${events.map((e) => JSON.stringify(e)).join("\n")}
|
|
4430
4686
|
`, { mode: 384 });
|
|
4431
4687
|
}
|
|
@@ -4495,34 +4751,106 @@ var Outbox = class {
|
|
|
4495
4751
|
closeSync3(fd);
|
|
4496
4752
|
}
|
|
4497
4753
|
}
|
|
4498
|
-
/** Files older than `days` are pruned
|
|
4754
|
+
/** Files older than `days` are pruned, and each sink's cursor into them. The seq
|
|
4755
|
+
* counter stays: a session resumed after the window counts on from where it was,
|
|
4756
|
+
* so a receiver's (harness, session, seq) dedup doesn't take its new events for
|
|
4757
|
+
* ones it already has, and the sinks read the new file from its start.
|
|
4758
|
+
* It all happens under the drain's `flush` lock, since a drain holds the whole
|
|
4759
|
+
* cursor map and writes it back: a file and its cursor keys go together, so a
|
|
4760
|
+
* session resumed in between can't keep a key past its new file's end. While a
|
|
4761
|
+
* drain runs, nothing is pruned (0); the next prune does it. It never throws: it
|
|
4762
|
+
* is a cleanup, and a hook's drain comes after it. */
|
|
4499
4763
|
prune(days, now = Date.now()) {
|
|
4764
|
+
let release = null;
|
|
4765
|
+
try {
|
|
4766
|
+
release = takeNamedLock(this.root, "flush");
|
|
4767
|
+
} catch {
|
|
4768
|
+
}
|
|
4769
|
+
if (!release) return 0;
|
|
4500
4770
|
let n = 0;
|
|
4501
|
-
|
|
4502
|
-
|
|
4503
|
-
const
|
|
4504
|
-
|
|
4505
|
-
|
|
4506
|
-
|
|
4507
|
-
|
|
4508
|
-
|
|
4509
|
-
|
|
4771
|
+
try {
|
|
4772
|
+
const cutoff = now - days * 864e5;
|
|
4773
|
+
const aged = this.sessions().filter((ref) => {
|
|
4774
|
+
try {
|
|
4775
|
+
return statSync6(this.file(ref)).mtimeMs < cutoff;
|
|
4776
|
+
} catch {
|
|
4777
|
+
return false;
|
|
4778
|
+
}
|
|
4779
|
+
});
|
|
4780
|
+
this.dropStaleCursors(new Set(aged.map((ref) => `${ref.harness}/${ref.session}`)));
|
|
4781
|
+
for (const ref of aged) {
|
|
4782
|
+
const f = this.file(ref);
|
|
4783
|
+
try {
|
|
4784
|
+
if (statSync6(f).mtimeMs < cutoff) {
|
|
4785
|
+
unlinkSync2(f);
|
|
4786
|
+
n++;
|
|
4510
4787
|
}
|
|
4511
|
-
|
|
4788
|
+
} catch {
|
|
4512
4789
|
}
|
|
4513
|
-
} catch {
|
|
4514
4790
|
}
|
|
4791
|
+
} catch {
|
|
4792
|
+
} finally {
|
|
4793
|
+
release();
|
|
4515
4794
|
}
|
|
4516
4795
|
return n;
|
|
4517
4796
|
}
|
|
4797
|
+
/** A sink's cursor into a session file about to be pruned (`gone`), or one already
|
|
4798
|
+
* missing (an earlier prune, an older release, by hand) is removed. The caller
|
|
4799
|
+
* holds the `flush` lock. */
|
|
4800
|
+
dropStaleCursors(gone) {
|
|
4801
|
+
let names = [];
|
|
4802
|
+
try {
|
|
4803
|
+
names = readdirSync6(path16.join(this.root, "cursors")).filter((n) => n.endsWith(".json"));
|
|
4804
|
+
} catch {
|
|
4805
|
+
}
|
|
4806
|
+
const stale = (c) => Object.keys(c).filter((k) => {
|
|
4807
|
+
if (gone.has(k)) return true;
|
|
4808
|
+
const i = k.indexOf("/");
|
|
4809
|
+
return !existsSync9(this.file({ harness: k.slice(0, i), session: k.slice(i + 1) }));
|
|
4810
|
+
});
|
|
4811
|
+
for (const n of names) {
|
|
4812
|
+
const s = new Cursors(this.root, n.slice(0, -5));
|
|
4813
|
+
const c = s.read();
|
|
4814
|
+
const keys = stale(c);
|
|
4815
|
+
if (!keys.length) continue;
|
|
4816
|
+
for (const k of keys) delete c[k];
|
|
4817
|
+
s.write(c);
|
|
4818
|
+
}
|
|
4819
|
+
}
|
|
4518
4820
|
delete(ref) {
|
|
4821
|
+
this.dropCursors(ref);
|
|
4519
4822
|
for (const f of [this.file(ref), this.seqFile(ref)]) {
|
|
4520
4823
|
try {
|
|
4521
|
-
|
|
4824
|
+
unlinkSync2(f);
|
|
4522
4825
|
} catch {
|
|
4523
4826
|
}
|
|
4524
4827
|
}
|
|
4525
4828
|
}
|
|
4829
|
+
/** The key every sink's cursor holds for a session is removed with the session's
|
|
4830
|
+
* file: the key alone pointed into the old file, and a session that went on (still
|
|
4831
|
+
* running, or resumed later) had its new file's first events unsent until it grew
|
|
4832
|
+
* past that offset. Every file under <state>/cursors is taken (a sink since taken
|
|
4833
|
+
* out of the config still has one; one that doesn't parse reads as empty and is
|
|
4834
|
+
* left), by the key a drain writes (the file's own name, `safeId`) and by the ref
|
|
4835
|
+
* as given. The caller holds the drain's `flush` lock: a drain holds the whole
|
|
4836
|
+
* cursor map and writes it back, so a key dropped under it would come back. */
|
|
4837
|
+
dropCursors(ref) {
|
|
4838
|
+
const keys = /* @__PURE__ */ new Set([`${ref.harness}/${ref.session}`, `${safeId(ref.harness)}/${safeId(ref.session)}`]);
|
|
4839
|
+
let names = [];
|
|
4840
|
+
try {
|
|
4841
|
+
names = readdirSync6(path16.join(this.root, "cursors")).filter((n) => n.endsWith(".json"));
|
|
4842
|
+
} catch {
|
|
4843
|
+
return;
|
|
4844
|
+
}
|
|
4845
|
+
for (const n of names) {
|
|
4846
|
+
const s = new Cursors(this.root, n.slice(0, -5));
|
|
4847
|
+
const c = s.read();
|
|
4848
|
+
const had = Object.keys(c).filter((k) => keys.has(k));
|
|
4849
|
+
if (!had.length) continue;
|
|
4850
|
+
for (const k of had) delete c[k];
|
|
4851
|
+
s.write(c);
|
|
4852
|
+
}
|
|
4853
|
+
}
|
|
4526
4854
|
};
|
|
4527
4855
|
var Cursors = class {
|
|
4528
4856
|
constructor(root, sink) {
|
|
@@ -4534,18 +4862,21 @@ var Cursors = class {
|
|
|
4534
4862
|
file() {
|
|
4535
4863
|
return path16.join(this.root, "cursors", `${safeId(this.sink)}.json`);
|
|
4536
4864
|
}
|
|
4865
|
+
/** The map; empty when the file is missing, cut off mid-write, or holds anything
|
|
4866
|
+
* but an object (`null`, a list), so a drain never indexes into those. */
|
|
4537
4867
|
read() {
|
|
4538
4868
|
try {
|
|
4539
|
-
|
|
4869
|
+
const c = JSON.parse(readFileSync11(this.file(), "utf8"));
|
|
4870
|
+
return c && typeof c === "object" && !Array.isArray(c) ? c : {};
|
|
4540
4871
|
} catch {
|
|
4541
4872
|
return {};
|
|
4542
4873
|
}
|
|
4543
4874
|
}
|
|
4544
4875
|
write(c) {
|
|
4545
4876
|
const f = this.file();
|
|
4546
|
-
|
|
4877
|
+
mkdirSync3(path16.dirname(f), { recursive: true, mode: 448 });
|
|
4547
4878
|
const tmp = `${f}.${process.pid}.tmp`;
|
|
4548
|
-
|
|
4879
|
+
writeFileSync3(tmp, JSON.stringify(c), { mode: 384 });
|
|
4549
4880
|
renameSync(tmp, f);
|
|
4550
4881
|
}
|
|
4551
4882
|
key(ref) {
|
|
@@ -4557,25 +4888,54 @@ function takeLock(root, ref, staleMs = 5 * 6e4) {
|
|
|
4557
4888
|
}
|
|
4558
4889
|
function takeNamedLock(root, name, staleMs = 5 * 6e4) {
|
|
4559
4890
|
const dir = path16.join(root, "locks");
|
|
4560
|
-
|
|
4891
|
+
mkdirSync3(dir, { recursive: true, mode: 448 });
|
|
4561
4892
|
const f = path16.join(dir, `${name}.lock`);
|
|
4893
|
+
const nonce = randomBytes3(8).toString("hex");
|
|
4894
|
+
const mine = `${process.pid} ${nonce}`;
|
|
4895
|
+
let body = null;
|
|
4562
4896
|
try {
|
|
4563
|
-
|
|
4564
|
-
|
|
4897
|
+
body = readFileSync11(f, "utf8");
|
|
4898
|
+
const stale = Date.now() - statSync6(f).mtimeMs > staleMs || !processAlive(Number(body.split(" ")[0]));
|
|
4899
|
+
if (!stale) return null;
|
|
4565
4900
|
} catch {
|
|
4901
|
+
body = null;
|
|
4902
|
+
}
|
|
4903
|
+
if (body !== null) {
|
|
4904
|
+
const aside = `${f}.${process.pid}.${nonce}.stale`;
|
|
4905
|
+
try {
|
|
4906
|
+
renameSync(f, aside);
|
|
4907
|
+
} catch {
|
|
4908
|
+
return null;
|
|
4909
|
+
}
|
|
4910
|
+
const moved = readFileSync11(aside, "utf8");
|
|
4911
|
+
unlinkSync2(aside);
|
|
4912
|
+
if (moved !== body) {
|
|
4913
|
+
try {
|
|
4914
|
+
writeFileSync3(f, moved, { flag: "wx" });
|
|
4915
|
+
} catch {
|
|
4916
|
+
}
|
|
4917
|
+
return null;
|
|
4918
|
+
}
|
|
4566
4919
|
}
|
|
4567
4920
|
try {
|
|
4568
|
-
|
|
4921
|
+
writeFileSync3(f, mine, { flag: "wx" });
|
|
4569
4922
|
} catch {
|
|
4570
4923
|
return null;
|
|
4571
4924
|
}
|
|
4572
4925
|
return () => {
|
|
4573
4926
|
try {
|
|
4574
|
-
|
|
4927
|
+
if (readFileSync11(f, "utf8") === mine) unlinkSync2(f);
|
|
4575
4928
|
} catch {
|
|
4576
4929
|
}
|
|
4577
4930
|
};
|
|
4578
4931
|
}
|
|
4932
|
+
async function waitNamedLock(root, name, forMs, everyMs = 100) {
|
|
4933
|
+
for (let waited = 0; ; waited += everyMs) {
|
|
4934
|
+
const release = takeNamedLock(root, name);
|
|
4935
|
+
if (release || waited >= forMs) return release;
|
|
4936
|
+
await new Promise((r) => setTimeout(r, everyMs));
|
|
4937
|
+
}
|
|
4938
|
+
}
|
|
4579
4939
|
function processAlive(pid) {
|
|
4580
4940
|
if (!Number.isInteger(pid) || pid <= 0) return true;
|
|
4581
4941
|
try {
|
|
@@ -4590,7 +4950,7 @@ function processAlive(pid) {
|
|
|
4590
4950
|
import { createHash as createHash4 } from "node:crypto";
|
|
4591
4951
|
var RULESET_PII = "pii@1";
|
|
4592
4952
|
var EMAIL = /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b/g;
|
|
4593
|
-
var PHONE = /(?<![\w.])(?:\+\d{1,3}[ .-]?)?(?:\(\d{2,4}\)[ .-]?)?\d{2,4}(?:[ .-]?\d{2,4}){1,3}(?!\w)/g;
|
|
4953
|
+
var PHONE = /(?<![\w.])(?:(?<!\)[ .-]?|\+\d{1,3}[ .-]?)(\+?(?:19|20)\d{2}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01]))(?:(?= (?:[01]?\d|2[0-3]):)|(?!\w|[ .-]\d\d))|(?:\+\d{1,3}[ .-]?)?(?:\(\d{2,4}\)[ .-]?)?\d{2,4}(?:[ .-]?\d{2,4}){1,3}(?!\w))/g;
|
|
4594
4954
|
function looksPhone(s) {
|
|
4595
4955
|
const digits = s.replace(/\D/g, "");
|
|
4596
4956
|
if (digits.length < 7 || digits.length > 15) return false;
|
|
@@ -4601,7 +4961,7 @@ function looksPhone(s) {
|
|
|
4601
4961
|
function reducePii(text, home) {
|
|
4602
4962
|
if (!text) return text;
|
|
4603
4963
|
let out = text.replace(EMAIL, "[email]");
|
|
4604
|
-
out = out.replace(PHONE, (m) => looksPhone(m) ? "[phone]" : m);
|
|
4964
|
+
out = out.replace(PHONE, (m, date) => !date && looksPhone(m) ? "[phone]" : m);
|
|
4605
4965
|
if (home) {
|
|
4606
4966
|
const h = home.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
4607
4967
|
out = out.replace(new RegExp(`(?<![A-Za-z0-9_])${h}(?=/|$|\\s|["'\`])`, "g"), "~");
|
|
@@ -4801,6 +5161,8 @@ var TYPE_TIER = {
|
|
|
4801
5161
|
"subagent.ended": 1,
|
|
4802
5162
|
"context.compacted": 1,
|
|
4803
5163
|
"limits.observed": 1,
|
|
5164
|
+
"delegation.started": 0,
|
|
5165
|
+
"delegation.ended": 0,
|
|
4804
5166
|
native: 3
|
|
4805
5167
|
};
|
|
4806
5168
|
var DATA_ALLOW = {
|
|
@@ -4811,17 +5173,19 @@ var DATA_ALLOW = {
|
|
|
4811
5173
|
"session.forgotten": { 0: [] },
|
|
4812
5174
|
"turn.started": { 0: ["turn_id", "submitted_by", "injected"], 1: ["prompt_chars"] },
|
|
4813
5175
|
"turn.ended": { 0: ["turn_id", "reason", "ms", "background"], 1: ["error"] },
|
|
4814
|
-
"turn.transcript": { 2: ["turn", "user", "assistant", "at"] },
|
|
4815
|
-
"turn.progress": { 2: ["turn", "user", "assistant", "at"] },
|
|
5176
|
+
"turn.transcript": { 2: ["turn", "user", "assistant", "at", "injected"] },
|
|
5177
|
+
"turn.progress": { 2: ["turn", "user", "assistant", "at", "injected"] },
|
|
4816
5178
|
"tool.started": { 1: ["tool", "call_id", "turn_id", "agent_id"], 3: ["input"] },
|
|
4817
5179
|
"tool.ended": { 1: ["tool", "call_id", "turn_id", "agent_id", "ms", "ok", "error"], 3: ["input", "output"] },
|
|
4818
5180
|
"files.changed": { 1: ["paths"] },
|
|
4819
|
-
"attention.needed": { 0: ["attention_id", "kind"], 1: ["tool", "message"] },
|
|
5181
|
+
"attention.needed": { 0: ["attention_id", "kind"], 1: ["tool", "message", "agent_id"] },
|
|
4820
5182
|
"attention.cleared": { 0: ["attention_id", "how"] },
|
|
4821
5183
|
"subagent.started": { 1: ["agent_id", "agent_type"] },
|
|
4822
5184
|
"subagent.ended": { 1: ["agent_id", "agent_type"] },
|
|
4823
5185
|
"context.compacted": { 1: ["trigger"] },
|
|
4824
|
-
"limits.observed": { 1: ["windows", "plan", "account_id"] }
|
|
5186
|
+
"limits.observed": { 1: ["windows", "plan", "account_id"] },
|
|
5187
|
+
"delegation.started": { 0: ["ask_id", "via", "harness", "to"], 1: ["text"] },
|
|
5188
|
+
"delegation.ended": { 0: ["ask_id", "to", "ok", "ms", "error", "relayed"], 2: ["reply"] }
|
|
4825
5189
|
};
|
|
4826
5190
|
var SESSION_ALLOW = [
|
|
4827
5191
|
"id",
|
|
@@ -4837,7 +5201,8 @@ var SESSION_ALLOW = [
|
|
|
4837
5201
|
"account_id",
|
|
4838
5202
|
"attended",
|
|
4839
5203
|
"parent_id",
|
|
4840
|
-
"launched_by"
|
|
5204
|
+
"launched_by",
|
|
5205
|
+
"delegated_by"
|
|
4841
5206
|
];
|
|
4842
5207
|
var MAX_SIDE = 2e4;
|
|
4843
5208
|
var MAX_IO = 64 * 1024;
|
|
@@ -4876,6 +5241,8 @@ function filterEvent(event, tier, pii, opts = {}) {
|
|
|
4876
5241
|
if (data.input !== void 0) data.input = cap(data.input, MAX_IO);
|
|
4877
5242
|
if (data.output !== void 0) data.output = cap(data.output, MAX_IO);
|
|
4878
5243
|
if (typeof data.message === "string") data.message = cap(data.message, 200);
|
|
5244
|
+
if (event.type === "delegation.started" && typeof data.text === "string") data.text = cap(data.text, 200);
|
|
5245
|
+
if (event.type === "delegation.ended" && typeof data.reply === "string") data.reply = cap(data.reply, MAX_SIDE);
|
|
4879
5246
|
const rulesets = [];
|
|
4880
5247
|
let out = { ...event, tier, session, data, privacy: { rulesets, pii } };
|
|
4881
5248
|
if (tier > 0) {
|
|
@@ -4902,7 +5269,7 @@ function shortId(harness, id) {
|
|
|
4902
5269
|
}
|
|
4903
5270
|
|
|
4904
5271
|
// src/sinks/file.ts
|
|
4905
|
-
import { appendFileSync as appendFileSync2, mkdirSync as
|
|
5272
|
+
import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync4 } from "node:fs";
|
|
4906
5273
|
import path17 from "node:path";
|
|
4907
5274
|
var FileSink = class {
|
|
4908
5275
|
constructor(name, file, tier, pii) {
|
|
@@ -4917,7 +5284,7 @@ var FileSink = class {
|
|
|
4917
5284
|
pii;
|
|
4918
5285
|
async send(events) {
|
|
4919
5286
|
try {
|
|
4920
|
-
|
|
5287
|
+
mkdirSync4(path17.dirname(this.file), { recursive: true });
|
|
4921
5288
|
appendFileSync2(this.file, `${events.map((e) => JSON.stringify(e)).join("\n")}
|
|
4922
5289
|
`);
|
|
4923
5290
|
return { ok: true, acknowledged: events.length, accepted: events.length, duplicates: 0, rejected: [] };
|
|
@@ -4955,13 +5322,16 @@ var PROTOCOL_VERSION = 1;
|
|
|
4955
5322
|
export {
|
|
4956
5323
|
ADAPTERS,
|
|
4957
5324
|
AG_STALE_PRETOOL,
|
|
5325
|
+
ASK_CHAIN_ENV,
|
|
4958
5326
|
Cursors,
|
|
4959
5327
|
DEFAULT_CONFIG,
|
|
5328
|
+
DELEGATED_BY_ENV,
|
|
4960
5329
|
DEVIN_EVENTS,
|
|
4961
5330
|
FileSink,
|
|
4962
5331
|
HELLO_HARNESSES_MAX,
|
|
4963
5332
|
JOB_TIMEOUT_MS,
|
|
4964
5333
|
LAUNCHED_BY_ENV,
|
|
5334
|
+
LINEAGE_ENV,
|
|
4965
5335
|
Outbox,
|
|
4966
5336
|
PROTOCOL_VERSION,
|
|
4967
5337
|
REDACTED,
|
|
@@ -4974,6 +5344,7 @@ export {
|
|
|
4974
5344
|
antigravity,
|
|
4975
5345
|
antigravityAnswer,
|
|
4976
5346
|
antigravity_control_exports as antigravityControl,
|
|
5347
|
+
assertConfigWritable,
|
|
4977
5348
|
capTail,
|
|
4978
5349
|
claudeAccounts,
|
|
4979
5350
|
claudeCode,
|
|
@@ -4983,6 +5354,7 @@ export {
|
|
|
4983
5354
|
codex,
|
|
4984
5355
|
codex_control_exports as codexControl,
|
|
4985
5356
|
configFile,
|
|
5357
|
+
delegatedBy,
|
|
4986
5358
|
deniedNote,
|
|
4987
5359
|
devin,
|
|
4988
5360
|
devinConfigDir,
|
|
@@ -5003,6 +5375,7 @@ export {
|
|
|
5003
5375
|
makeEvent,
|
|
5004
5376
|
notClaudeCode,
|
|
5005
5377
|
pairTurns,
|
|
5378
|
+
peerAsk,
|
|
5006
5379
|
processAlive,
|
|
5007
5380
|
readAntigravity,
|
|
5008
5381
|
readClaude,
|
|
@@ -5025,6 +5398,8 @@ export {
|
|
|
5025
5398
|
tilde,
|
|
5026
5399
|
ulid,
|
|
5027
5400
|
untilde,
|
|
5401
|
+
waitNamedLock,
|
|
5402
|
+
withoutLineage,
|
|
5028
5403
|
writeConfig
|
|
5029
5404
|
};
|
|
5030
5405
|
//# sourceMappingURL=index.js.map
|