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