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/worker.js CHANGED
@@ -1,8 +1,13 @@
1
1
  #!/usr/bin/env node
2
- // sessionpipe 0.11.0 · Apache-2.0 · https://sessionpipe.org
2
+ // sessionpipe 0.13.0 · Apache-2.0 · https://sessionpipe.org
3
+ var __defProp = Object.defineProperty;
4
+ var __export = (target, all) => {
5
+ for (var name in all)
6
+ __defProp(target, name, { get: all[name], enumerable: true });
7
+ };
3
8
 
4
9
  // src/worker.ts
5
- import { spawn } from "node:child_process";
10
+ import { spawn as spawn2 } from "node:child_process";
6
11
  import { realpathSync as realpathSync2 } from "node:fs";
7
12
  import { pathToFileURL } from "node:url";
8
13
 
@@ -184,7 +189,14 @@ ${m.text}`;
184
189
  continue;
185
190
  }
186
191
  if (cur) turns.push(cur);
187
- cur = { user: m.text, assistant: "", at: m.at, startLine: m.line, endLine: m.line };
192
+ cur = {
193
+ user: m.text,
194
+ assistant: "",
195
+ at: m.at,
196
+ startLine: m.line,
197
+ endLine: m.line,
198
+ ...m.injected ? { injected: true } : {}
199
+ };
188
200
  } else {
189
201
  if (!cur) cur = { user: "", assistant: "", at: m.at, startLine: m.line, endLine: m.line };
190
202
  cur.assistant += (cur.assistant ? "\n\n" : "") + m.text;
@@ -194,10 +206,17 @@ ${m.text}`;
194
206
  if (cur?.assistant) turns.push(cur);
195
207
  const line = cur && !cur.assistant ? cur.startLine : endLine;
196
208
  const out = {
197
- turns: turns.map((t) => ({ user: t.user, assistant: t.assistant, at: t.at, endLine: t.endLine })),
209
+ turns: turns.map((t) => ({
210
+ user: t.user,
211
+ assistant: t.assistant,
212
+ at: t.at,
213
+ endLine: t.endLine,
214
+ ...t.injected ? { injected: true } : {}
215
+ })),
198
216
  line
199
217
  };
200
- if (cur && !cur.assistant && cur.user) out.open = { user: cur.user, at: cur.at };
218
+ if (cur && !cur.assistant && cur.user)
219
+ out.open = { user: cur.user, at: cur.at, ...cur.injected ? { injected: true } : {} };
201
220
  return out;
202
221
  }
203
222
  var isInjected = (text) => /^<[a-z_-]+(?:\s[^>]*)?>/i.test(text) && /<\/[a-z_-]+>\s*$/i.test(text) || /^<!--\s/.test(text) || /^<command-/.test(text);
@@ -220,27 +239,51 @@ var textParts = (c, kinds) => {
220
239
  (b) => b && typeof b === "object" && kinds.includes(b.type ?? "") && typeof b.text === "string"
221
240
  ).map((b) => b.text).join("\n");
222
241
  };
242
+ var SPOKEN = /* @__PURE__ */ new Set(["human", "peer"]);
243
+ var PEER_INTRO = /sent a message[^\n]*:$/;
244
+ var PEER_OUTRO = "\n\nThis came from another ";
245
+ var ENVELOPE = /^<cross-session-message\b[^>]*>\n([\s\S]*)\n<\/cross-session-message>$/;
246
+ function peerAsk(origin, text) {
247
+ const body = origin?.body;
248
+ let t = typeof body === "string" && body.trim() ? body : text;
249
+ if (t !== body) {
250
+ const nl = t.indexOf("\n");
251
+ if (nl > 0 && PEER_INTRO.test(t.slice(0, nl))) t = t.slice(nl + 1);
252
+ const outro = t.indexOf(PEER_OUTRO);
253
+ if (outro > 0) t = t.slice(0, outro);
254
+ }
255
+ return (ENVELOPE.exec(t.trim())?.[1] ?? t).trim();
256
+ }
223
257
  var queuedPrompt = (a) => {
224
258
  if (!a || typeof a !== "object") return "";
225
259
  const q = a;
226
260
  if (q.type !== "queued_command" || (q.commandMode ?? "prompt") !== "prompt") return "";
227
- if (q.origin?.kind && q.origin.kind !== "human") return "";
228
- return textParts(q.prompt, ["text"]).trim();
261
+ const kind = q.origin?.kind ?? "human";
262
+ if (!SPOKEN.has(kind)) return "";
263
+ const text = textParts(q.prompt, ["text"]).trim();
264
+ return kind === "peer" ? peerAsk(q.origin, text) : text;
229
265
  };
266
+ var channelText = (text) => /^<channel\b[^>]*>([\s\S]*)<\/channel>$/.exec(text)?.[1]?.trim() ?? "";
230
267
  function readClaude(lines, from) {
231
268
  const msgs = [];
232
269
  for (let i = from; i < lines.length; i++) {
233
270
  const j = lines[i] ? parse(lines[i]) : null;
234
271
  if (!j || j.isSidechain) continue;
235
272
  const origin = j.origin;
236
- if (j.type === "user" && (j.isMeta || j.isCompactSummary || j.repeatsCompactSummary || origin?.kind && origin.kind !== "human"))
237
- continue;
273
+ const channel = j.type === "user" && origin?.kind === "channel";
274
+ const peer = origin?.kind === "peer";
275
+ if (j.type === "user" && (j.isCompactSummary || j.repeatsCompactSummary)) continue;
276
+ if (j.type === "user" && !channel && !peer && (j.isMeta || origin?.kind && origin.kind !== "human")) continue;
238
277
  const at = Date.parse(String(j.timestamp ?? "")) || Date.now();
239
278
  const c = j.message?.content;
240
- if (j.type === "user") {
241
- const text = textParts(c, ["text"]).trim();
279
+ if (channel) {
280
+ const text = channelText(textParts(c, ["text"]).trim());
281
+ if (text) msgs.push({ role: "user", text, at, line: i, injected: true });
282
+ } else if (j.type === "user") {
283
+ const raw = textParts(c, ["text"]).trim();
284
+ const text = peer ? peerAsk(origin, raw) : raw;
242
285
  if (!text || isInjected(text)) continue;
243
- msgs.push({ role: "user", text, at, line: i });
286
+ msgs.push({ role: "user", text, at, line: i, ...peer ? { injected: true } : {} });
244
287
  } else if (j.type === "assistant" && Array.isArray(c)) {
245
288
  const text = textParts(c, ["text"]).trim();
246
289
  if (text) msgs.push({ role: "assistant", text, at, line: i });
@@ -618,13 +661,7 @@ var claudeCode = {
618
661
  );
619
662
  break;
620
663
  case "Stop":
621
- events.push(
622
- ev("turn.ended", {
623
- turn_id,
624
- reason: "stop",
625
- background: Array.isArray(s.background_tasks) ? s.background_tasks.length : void 0
626
- })
627
- );
664
+ events.push(ev("turn.ended", { turn_id, reason: "stop", background: backgroundCount(s.background_tasks) }));
628
665
  break;
629
666
  case "StopFailure":
630
667
  events.push(
@@ -647,7 +684,8 @@ var claudeCode = {
647
684
  events.push(
648
685
  ev("subagent.ended", {
649
686
  agent_id: String(s.agent_id ?? ""),
650
- agent_type: typeof s.agent_type === "string" ? s.agent_type : void 0
687
+ agent_type: typeof s.agent_type === "string" ? s.agent_type : void 0,
688
+ background: backgroundCount(s.background_tasks, String(s.agent_id ?? ""))
651
689
  })
652
690
  );
653
691
  break;
@@ -722,6 +760,15 @@ var claudeCode = {
722
760
  return rows2;
723
761
  }
724
762
  };
763
+ var WAITER_RE = /(?:^|[\s"'`/\\])sessionpipe(?:\.\w+)?["']?\s+wait(?:\s|$)/;
764
+ function backgroundCount(tasks, ending) {
765
+ if (!Array.isArray(tasks)) return void 0;
766
+ return tasks.filter((t) => {
767
+ const task = t;
768
+ if (ending !== void 0 && task?.id === ending) return false;
769
+ return !(typeof task?.command === "string" && WAITER_RE.test(task.command));
770
+ }).length;
771
+ }
725
772
  var LINE_REFRESH_S = 60;
726
773
  function statusLine(file, want, cmd) {
727
774
  const f = readJsonFile(file);
@@ -1150,8 +1197,44 @@ async function claudeAiTitle(bridge, token) {
1150
1197
  }
1151
1198
 
1152
1199
  // ../core/src/adapters/headless.ts
1153
- import { accessSync, constants, statSync as statSync3 } from "node:fs";
1200
+ import { spawn } from "node:child_process";
1201
+ import { accessSync, constants, readFileSync as readFileSync3, statSync as statSync3 } from "node:fs";
1154
1202
  import path5 from "node:path";
1203
+
1204
+ // ../core/src/launched-by.ts
1205
+ var LAUNCHED_BY_ENV = "SESSIONPIPE_LAUNCHED_BY";
1206
+ var DELEGATED_BY_ENV = "SESSIONPIPE_ASKED_BY";
1207
+ var ASK_CHAIN_ENV = "SESSIONPIPE_ASK_CHAIN";
1208
+ var LINEAGE_ENV = [LAUNCHED_BY_ENV, DELEGATED_BY_ENV, ASK_CHAIN_ENV];
1209
+ var SESSION_REF = /^[a-z][a-z0-9-]{0,39}:[A-Za-z0-9._:-]{1,128}$/;
1210
+ function withoutLineage(env) {
1211
+ const e = { ...env };
1212
+ for (const k of LINEAGE_ENV) delete e[k];
1213
+ return e;
1214
+ }
1215
+ function refIn(env, name) {
1216
+ const v = env[name]?.trim();
1217
+ return v && SESSION_REF.test(v) ? v : void 0;
1218
+ }
1219
+ function launchedBy(env) {
1220
+ return refIn(env, LAUNCHED_BY_ENV);
1221
+ }
1222
+ function delegatedBy(env) {
1223
+ return refIn(env, DELEGATED_BY_ENV);
1224
+ }
1225
+ function readHook(adapter, input) {
1226
+ const r = adapter.fromHook(input);
1227
+ if (r && !r.session.parent_id) {
1228
+ const self = `${adapter.name}:${r.session.id}`;
1229
+ const launcher = launchedBy(input.env);
1230
+ if (launcher && launcher !== self) r.session.launched_by = launcher;
1231
+ const asker = delegatedBy(input.env);
1232
+ if (asker && asker !== self) r.session.delegated_by = asker;
1233
+ }
1234
+ return r;
1235
+ }
1236
+
1237
+ // ../core/src/adapters/headless.ts
1155
1238
  var JOB_TIMEOUT_MS = 30 * 6e4;
1156
1239
  var OUT_CAP = 1024 * 1024;
1157
1240
  function isExe(p) {
@@ -1172,13 +1255,144 @@ function findBinary(o) {
1172
1255
  for (const d of o.dirs) for (const n of o.names) if (isExe(path5.join(d, n))) return path5.join(d, n);
1173
1256
  return null;
1174
1257
  }
1258
+ function shimTarget(shim) {
1259
+ let text = "";
1260
+ try {
1261
+ text = readFileSync3(shim, "utf8");
1262
+ } catch {
1263
+ return null;
1264
+ }
1265
+ const m = /"%(?:~dp0|dp0%)\\([^"]+)"\s+%\*/.exec(text);
1266
+ if (!m) return null;
1267
+ const target = path5.resolve(path5.dirname(shim), m[1].split("\\").join(path5.sep));
1268
+ try {
1269
+ return statSync3(target).isFile() ? target : null;
1270
+ } catch {
1271
+ return null;
1272
+ }
1273
+ }
1274
+ function spawnable(bin, args) {
1275
+ if (!/\.(cmd|bat)$/i.test(bin)) return { file: bin, args };
1276
+ const target = shimTarget(bin);
1277
+ return target ? { file: process.execPath, args: [target, ...args] } : null;
1278
+ }
1279
+ var shimNote = (bin) => `${path5.basename(bin)} is a launcher script that names no program to run; point the harness's override (SESSIONPIPE_CLAUDE, SESSIONPIPE_CODEX, \u2026) at the program itself`;
1280
+ function probe(bin, args) {
1281
+ const s = spawnable(bin, args);
1282
+ return s ? { ...s, shell: false } : { file: [`"${bin}"`, ...args].join(" "), args: [], shell: true };
1283
+ }
1284
+ function runHeadless(bin, args, o) {
1285
+ return new Promise((resolve) => {
1286
+ let out = [];
1287
+ let outLen = 0;
1288
+ let tooBig = false;
1289
+ let errTail = "";
1290
+ let timedOut = false;
1291
+ let settled = false;
1292
+ let child;
1293
+ const c = spawnable(bin, args);
1294
+ if (!c) {
1295
+ resolve({ error: shimNote(bin) });
1296
+ return;
1297
+ }
1298
+ try {
1299
+ child = spawn(c.file, c.args, {
1300
+ cwd: o.cwd,
1301
+ env: o.env,
1302
+ stdio: ["ignore", "pipe", "pipe"],
1303
+ detached: process.platform !== "win32",
1304
+ windowsHide: true
1305
+ });
1306
+ } catch (e) {
1307
+ resolve({ error: e.message });
1308
+ return;
1309
+ }
1310
+ const kill = (sig) => {
1311
+ try {
1312
+ if (process.platform !== "win32" && child.pid) process.kill(-child.pid, sig);
1313
+ else child.kill(sig);
1314
+ } catch {
1315
+ try {
1316
+ child.kill(sig);
1317
+ } catch {
1318
+ }
1319
+ }
1320
+ };
1321
+ const keep = (line) => {
1322
+ if (!o.keep?.(line)) return;
1323
+ out = [Buffer.from(line)];
1324
+ outLen = line.length;
1325
+ };
1326
+ let partial = "";
1327
+ child.stdout?.on("data", (b) => {
1328
+ if (o.onLine) {
1329
+ const chunk = partial + b.toString("utf8");
1330
+ const lines = chunk.split("\n");
1331
+ partial = lines.pop();
1332
+ for (const line of lines) {
1333
+ if (!line.trim()) continue;
1334
+ try {
1335
+ o.onLine(line);
1336
+ } catch {
1337
+ }
1338
+ keep(line);
1339
+ }
1340
+ if (partial.length > OUT_CAP) partial = "";
1341
+ return;
1342
+ }
1343
+ if (tooBig) return;
1344
+ if (outLen + b.length > OUT_CAP) {
1345
+ tooBig = true;
1346
+ out = [];
1347
+ return;
1348
+ }
1349
+ out.push(b);
1350
+ outLen += b.length;
1351
+ });
1352
+ child.stderr?.on("data", (b) => {
1353
+ errTail = (errTail + b.toString("utf8")).slice(-4e3);
1354
+ });
1355
+ const timer = setTimeout(() => {
1356
+ timedOut = true;
1357
+ kill("SIGTERM");
1358
+ setTimeout(() => kill("SIGKILL"), 1e4).unref();
1359
+ }, o.timeoutMs ?? JOB_TIMEOUT_MS);
1360
+ const result = (code, signal) => ({
1361
+ code,
1362
+ signal,
1363
+ stdout: tooBig ? null : Buffer.concat(out).toString("utf8"),
1364
+ tooBig,
1365
+ stderr: errTail,
1366
+ timedOut
1367
+ });
1368
+ const finish = (r) => {
1369
+ if (settled) return;
1370
+ settled = true;
1371
+ clearTimeout(timer);
1372
+ resolve(r);
1373
+ };
1374
+ child.on("error", (e) => finish({ error: e.message }));
1375
+ child.on("exit", (code, signal) => setTimeout(() => finish(result(code, signal)), 2e3).unref());
1376
+ child.on("close", (code, signal) => {
1377
+ if (o.onLine && partial.trim()) {
1378
+ try {
1379
+ o.onLine(partial);
1380
+ } catch {
1381
+ }
1382
+ keep(partial);
1383
+ partial = "";
1384
+ }
1385
+ finish(result(code, signal));
1386
+ });
1387
+ });
1388
+ }
1175
1389
 
1176
1390
  // ../core/src/adapters/antigravity.ts
1177
1391
  import {
1178
1392
  existsSync as existsSync3,
1179
1393
  mkdirSync as mkdirSync2,
1180
1394
  readdirSync as readdirSync3,
1181
- readFileSync as readFileSync3,
1395
+ readFileSync as readFileSync4,
1182
1396
  realpathSync,
1183
1397
  statSync as statSync4,
1184
1398
  unlinkSync as unlinkSync2,
@@ -1307,7 +1521,7 @@ var antigravity = {
1307
1521
  readTranscript(file, fromLine) {
1308
1522
  let text = "";
1309
1523
  try {
1310
- text = readFileSync3(file, "utf8");
1524
+ text = readFileSync4(file, "utf8");
1311
1525
  } catch {
1312
1526
  return { turns: [], line: fromLine };
1313
1527
  }
@@ -1386,7 +1600,7 @@ function agProjects(dir) {
1386
1600
  if (!ID_RE.test(id)) continue;
1387
1601
  let j;
1388
1602
  try {
1389
- j = JSON.parse(readFileSync3(path6.join(dir, n), "utf8"));
1603
+ j = JSON.parse(readFileSync4(path6.join(dir, n), "utf8"));
1390
1604
  } catch {
1391
1605
  continue;
1392
1606
  }
@@ -1423,7 +1637,7 @@ function antigravityRemoteUrl(transcript, opts) {
1423
1637
  for (const d of dirs) {
1424
1638
  try {
1425
1639
  const m = /installation_uuid:\s*"([0-9a-f-]{36})"/i.exec(
1426
- readFileSync3(path6.join(d, "antigravity_state.pbtxt"), "utf8")
1640
+ readFileSync4(path6.join(d, "antigravity_state.pbtxt"), "utf8")
1427
1641
  );
1428
1642
  if (!m) continue;
1429
1643
  const base = `https://antigravity.google.com/r/${m[1]}-v2`;
@@ -1461,6 +1675,50 @@ function antigravityFacts(file, opts) {
1461
1675
  return out;
1462
1676
  }
1463
1677
 
1678
+ // ../core/src/adapters/claude-code-control.ts
1679
+ var claude_code_control_exports = {};
1680
+ __export(claude_code_control_exports, {
1681
+ CLAUDE_MODES: () => CLAUDE_MODES,
1682
+ JOB_TIMEOUT_MS: () => JOB_TIMEOUT_MS,
1683
+ MODE_RANK: () => MODE_RANK,
1684
+ NO_HOME_SETTINGS: () => NO_HOME_SETTINGS,
1685
+ ceilingLevel: () => ceilingLevel,
1686
+ claudeArgs: () => claudeArgs,
1687
+ claudeEnv: () => claudeEnv,
1688
+ claudeLogin: () => claudeLogin2,
1689
+ cmpVersion: () => cmpVersion,
1690
+ defaultLevel: () => defaultLevel,
1691
+ detectCaps: () => detectCaps,
1692
+ findClaude: () => findClaude,
1693
+ findTranscript: () => findTranscript,
1694
+ findTranscripts: () => findTranscripts,
1695
+ homeSettings: () => homeSettings,
1696
+ hookMode: () => hookMode,
1697
+ isUuid: () => isUuid,
1698
+ liveProcess: () => liveProcess,
1699
+ liveRecord: () => liveRecord,
1700
+ liveRecords: () => liveRecords,
1701
+ loginCommand: () => loginCommand,
1702
+ loginHint: () => loginHint,
1703
+ modeFlags: () => modeFlags,
1704
+ modeRank: () => modeRank,
1705
+ offeredLevels: () => offeredLevels,
1706
+ outputArgs: () => outputArgs,
1707
+ parseAuthStatus: () => parseAuthStatus,
1708
+ parseCaps: () => parseCaps,
1709
+ parseResult: () => parseResult,
1710
+ peerClass: () => peerClass,
1711
+ recentlyWritten: () => recentlyWritten,
1712
+ relaunchMode: () => relaunchMode,
1713
+ resolveHome: () => resolveHome,
1714
+ runClaude: () => runClaude,
1715
+ sessionFolder: () => sessionFolder
1716
+ });
1717
+ import { execFile, execFileSync as execFileSync3 } from "node:child_process";
1718
+ import { closeSync as closeSync2, openSync as openSync2, readdirSync as readdirSync4, readFileSync as readFileSync5, readSync as readSync2, statSync as statSync5 } from "node:fs";
1719
+ import os2 from "node:os";
1720
+ import path7 from "node:path";
1721
+
1464
1722
  // ../core/src/control/verify.ts
1465
1723
  var GRANT_MAX_MS = 24 * 36e5;
1466
1724
  var SKEW_MS = 5 * 6e4;
@@ -1471,10 +1729,10 @@ var MODEL_RE = /^[A-Za-z0-9][A-Za-z0-9._:/@[\]-]*$/;
1471
1729
  var START_PERMISSIONS = ["read", "safe", "edits", "auto"];
1472
1730
  var levelRank = (l) => START_PERMISSIONS.indexOf(l);
1473
1731
  var FIELDS = {
1474
- prompt: ["text", "files"],
1732
+ prompt: ["text", "files", "reply_to"],
1475
1733
  "permission.answer": ["for", "decision"],
1476
1734
  cancel: [],
1477
- start: ["text", "cwd", "files", "permission", "model"],
1735
+ start: ["text", "cwd", "files", "permission", "model", "reply_to"],
1478
1736
  "key.add": ["key", "name"],
1479
1737
  pool: ["pool_action", "pool_account", "pool_order", "pool_code"]
1480
1738
  };
@@ -1482,9 +1740,454 @@ var POOL_FIELDS = FIELDS.pool;
1482
1740
  var enc = new TextEncoder();
1483
1741
  var dec = new TextDecoder();
1484
1742
 
1743
+ // ../core/src/adapters/claude-code-control.ts
1744
+ var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
1745
+ var LIVE_WRITE_MS = 9e4;
1746
+ var cmpVersion = (a, b) => {
1747
+ const pa = a.split(".").map(Number);
1748
+ const pb = b.split(".").map(Number);
1749
+ for (let i = 0; i < Math.max(pa.length, pb.length); i++)
1750
+ if ((pa[i] ?? 0) !== (pb[i] ?? 0)) return (pa[i] ?? 0) - (pb[i] ?? 0);
1751
+ return 0;
1752
+ };
1753
+ function findClaude(env = process.env, home = os2.homedir()) {
1754
+ if (env.SESSIONPIPE_CLAUDE && isExe(env.SESSIONPIPE_CLAUDE)) return env.SESSIONPIPE_CLAUDE;
1755
+ const names = process.platform === "win32" ? ["claude.exe", "claude.cmd"] : ["claude"];
1756
+ for (const d of String(env.PATH || "").split(path7.delimiter)) {
1757
+ if (!d || !path7.isAbsolute(d)) continue;
1758
+ for (const n of names) if (isExe(path7.join(d, n))) return path7.join(d, n);
1759
+ }
1760
+ for (const p of [
1761
+ path7.join(home, ".claude", "local", "claude"),
1762
+ path7.join(home, ".local", "bin", "claude"),
1763
+ "/opt/homebrew/bin/claude",
1764
+ "/usr/local/bin/claude"
1765
+ ])
1766
+ if (isExe(p)) return p;
1767
+ const app = path7.join(home, "Library", "Application Support", "Claude", "claude-code");
1768
+ let versions = [];
1769
+ try {
1770
+ versions = readdirSync4(app).filter((n) => /^\d+(\.\d+)*$/.test(n)).sort(cmpVersion).reverse();
1771
+ } catch {
1772
+ }
1773
+ for (const v of versions) {
1774
+ const p = path7.join(app, v, "claude.app", "Contents", "MacOS", "claude");
1775
+ if (isExe(p)) return p;
1776
+ }
1777
+ return null;
1778
+ }
1779
+ function optionBlock(help, flag) {
1780
+ const lines = help.split("\n");
1781
+ const re = new RegExp(`^\\s{0,4}(?:-\\w, )?${flag.replace(/-/g, "\\-")}(?![\\w-])`);
1782
+ const start = lines.findIndex((l) => re.test(l));
1783
+ if (start < 0) return null;
1784
+ const out = [lines[start]];
1785
+ for (let i = start + 1; i < lines.length && lines[i]?.trim() && !/^\s{0,4}-/.test(lines[i]); i++)
1786
+ out.push(lines[i]);
1787
+ return out.join(" ");
1788
+ }
1789
+ function parseCaps(help) {
1790
+ const pm = optionBlock(help, "--permission-mode");
1791
+ const pp = optionBlock(help, "--permission-prompts");
1792
+ return {
1793
+ hasMode: !!pm,
1794
+ modes: pm ? [...pm.matchAll(/"([A-Za-z]+)"/g)].map((m) => m[1]) : [],
1795
+ promptsNone: !!pp && /"none"/.test(pp),
1796
+ fork: !!optionBlock(help, "--fork-session"),
1797
+ stream: !!optionBlock(help, "--include-partial-messages")
1798
+ };
1799
+ }
1800
+ var LEVEL_MODE = {
1801
+ read: "plan",
1802
+ edits: "acceptEdits",
1803
+ auto: "auto"
1804
+ };
1805
+ function modeFlags(level, caps) {
1806
+ if (level === "inherit") return { args: [] };
1807
+ if (caps.unread)
1808
+ return { error: `this machine couldn't read its Claude Code's options (${caps.unread}); try again in a minute` };
1809
+ if (level === "auto" && !caps.modes.includes("auto"))
1810
+ return { error: "this machine is in auto mode, but its Claude Code has no auto permission mode" };
1811
+ if (level !== "safe") {
1812
+ const m = LEVEL_MODE[level];
1813
+ if (!caps.modes.includes(m)) return { error: `this machine's Claude Code has no ${m} permission mode` };
1814
+ return { args: ["--permission-mode", m] };
1815
+ }
1816
+ if (caps.modes.includes("dontAsk")) return { args: ["--permission-mode", "dontAsk"] };
1817
+ const args = [];
1818
+ if (caps.hasMode)
1819
+ args.push(
1820
+ "--permission-mode",
1821
+ caps.modes.includes("manual") && !caps.modes.includes("default") ? "manual" : "default"
1822
+ );
1823
+ if (caps.promptsNone) args.push("--permission-prompts", "none");
1824
+ return { args };
1825
+ }
1826
+ var ceilingLevel = (mode) => mode === "inherit" ? "safe" : mode;
1827
+ function offeredLevels(mode, caps) {
1828
+ if (caps.unread) return [];
1829
+ return START_PERMISSIONS.filter(
1830
+ (l) => levelRank(l) <= levelRank(ceilingLevel(mode)) && (l === "safe" || caps.modes.includes(LEVEL_MODE[l]))
1831
+ );
1832
+ }
1833
+ var defaultLevel = (mode) => mode;
1834
+ function outputArgs(caps) {
1835
+ return caps.stream ? ["--output-format", "stream-json", "--verbose", "--include-partial-messages"] : ["--output-format", "json"];
1836
+ }
1837
+ function claudeArgs(o) {
1838
+ const mf = modeFlags(o.level, o.caps);
1839
+ if ("error" in mf) return mf;
1840
+ const where = o.resume ? ["--resume", o.resume, ...o.fork ? ["--fork-session"] : []] : ["--session-id", o.session];
1841
+ return {
1842
+ args: [
1843
+ "-p",
1844
+ ...outputArgs(o.caps),
1845
+ ...mf.args,
1846
+ ...o.model ? ["--model", o.model] : [],
1847
+ ...o.allowedTools?.length ? [`--allowedTools=${o.allowedTools.join(",")}`] : [],
1848
+ ...where,
1849
+ o.prompt.startsWith("-") ? ` ${o.prompt}` : o.prompt
1850
+ ]
1851
+ };
1852
+ }
1853
+ function claudeEnv(configDir, env = process.env) {
1854
+ const e = withoutLineage(env);
1855
+ for (const k of [
1856
+ "CLAUDECODE",
1857
+ "CLAUDE_CODE_SESSION_ID",
1858
+ "CLAUDE_CODE_HOST_SESSION_ID",
1859
+ "CLAUDE_CODE_ENTRYPOINT",
1860
+ "CLAUDE_CODE_SESSION_ATTENDED",
1861
+ "CLAUDE_CODE_SSE_PORT"
1862
+ ])
1863
+ delete e[k];
1864
+ e.SESSIONPIPE_CONTROL_JOB = "1";
1865
+ if (configDir !== void 0) {
1866
+ if (path7.resolve(configDir) === path7.resolve(os2.homedir(), ".claude")) delete e.CLAUDE_CONFIG_DIR;
1867
+ else e.CLAUDE_CONFIG_DIR = configDir;
1868
+ }
1869
+ return e;
1870
+ }
1871
+ function parseAuthStatus(stdout) {
1872
+ const text = String(stdout || "").trim();
1873
+ const start = text.indexOf("{");
1874
+ if (start < 0) return { loggedIn: null };
1875
+ try {
1876
+ const j = JSON.parse(text.slice(start));
1877
+ if (typeof j.loggedIn !== "boolean") return { loggedIn: null };
1878
+ return {
1879
+ loggedIn: j.loggedIn,
1880
+ ...typeof j.authMethod === "string" ? { method: j.authMethod } : {},
1881
+ ...typeof j.subscriptionType === "string" ? { subscription: j.subscriptionType } : {}
1882
+ };
1883
+ } catch {
1884
+ return { loggedIn: null };
1885
+ }
1886
+ }
1887
+ function claudeLogin2(bin, configDir, env = process.env) {
1888
+ const c = probe(bin, ["auth", "status", "--json"]);
1889
+ return new Promise((resolve) => {
1890
+ execFile(
1891
+ c.file,
1892
+ c.args,
1893
+ { encoding: "utf8", timeout: 2e4, env: claudeEnv(configDir, env), maxBuffer: 256 * 1024, shell: c.shell },
1894
+ // `auth status` may exit non-zero when signed out; its JSON still says so.
1895
+ (_err, stdout) => resolve(parseAuthStatus(stdout))
1896
+ );
1897
+ });
1898
+ }
1899
+ function loginCommand(configDir) {
1900
+ const home = os2.homedir();
1901
+ const isDefault = !configDir || path7.resolve(configDir) === path7.resolve(home, ".claude");
1902
+ return isDefault ? "`claude auth login`" : `\`CLAUDE_CONFIG_DIR=${configDir.replace(home, "~")} claude auth login\``;
1903
+ }
1904
+ function loginHint(configDir, platform = process.platform) {
1905
+ const shown = configDir ? configDir.replace(os2.homedir(), "~") : "~/.claude";
1906
+ const login = loginCommand(configDir);
1907
+ if (platform === "darwin")
1908
+ return `Claude Code (${shown}) isn't signed in as this machine's background service sees it. On a Mac its login lives in the login keychain, which a background service can read only while someone is logged in at the screen: log in at the Mac, or run ${login} there.`;
1909
+ return `Claude Code (${shown}) isn't signed in on this machine: run ${login} there.`;
1910
+ }
1911
+ function detectCaps(bin, timeoutMs = 3e4) {
1912
+ const c = probe(bin, ["--help"]);
1913
+ try {
1914
+ return parseCaps(
1915
+ execFileSync3(c.file, c.args, {
1916
+ encoding: "utf8",
1917
+ timeout: timeoutMs,
1918
+ env: claudeEnv(),
1919
+ stdio: ["ignore", "pipe", "pipe"],
1920
+ maxBuffer: 4 * 1024 * 1024,
1921
+ shell: c.shell
1922
+ })
1923
+ );
1924
+ } catch (e) {
1925
+ const err = e;
1926
+ const caps = parseCaps(String(err?.stdout || ""));
1927
+ if (caps.hasMode) return caps;
1928
+ const why = err?.code === "ETIMEDOUT" ? `timed out after ${timeoutMs / 1e3} s` : "failed";
1929
+ return { ...caps, unread: `\`${path7.basename(bin)} --help\` ${why}` };
1930
+ }
1931
+ }
1932
+ function transcriptIn(session, dir) {
1933
+ let projects = [];
1934
+ try {
1935
+ projects = readdirSync4(path7.join(dir, "projects"), { withFileTypes: true });
1936
+ } catch {
1937
+ return null;
1938
+ }
1939
+ for (const p of projects) {
1940
+ if (!p.isDirectory()) continue;
1941
+ const file = path7.join(dir, "projects", p.name, `${session}.jsonl`);
1942
+ try {
1943
+ if (statSync5(file).isFile()) return { file, configDir: dir, project: p.name };
1944
+ } catch {
1945
+ }
1946
+ }
1947
+ return null;
1948
+ }
1949
+ function findTranscript(session, dirs) {
1950
+ if (!UUID_RE.test(session)) return null;
1951
+ for (const dir of dirs) {
1952
+ const t = transcriptIn(session, dir);
1953
+ if (t) return t;
1954
+ }
1955
+ return null;
1956
+ }
1957
+ function findTranscripts(session, dirs) {
1958
+ if (!UUID_RE.test(session)) return [];
1959
+ const out = [];
1960
+ for (const dir of dirs) {
1961
+ const t = transcriptIn(session, dir);
1962
+ if (t) out.push(t);
1963
+ }
1964
+ return out;
1965
+ }
1966
+ var homeName = (t) => path7.basename(t.configDir);
1967
+ function resolveHome(session, dirs) {
1968
+ const all = findTranscripts(session, dirs);
1969
+ if (!all.length) return null;
1970
+ if (all.length === 1) return { transcript: all[0] };
1971
+ const live = all.filter((t) => liveRecords(session, t.configDir).length > 0);
1972
+ if (live.length === 1) return { transcript: live[0] };
1973
+ return {
1974
+ ambiguous: [...new Set(all.map(homeName))].sort(),
1975
+ live: [...new Set(live.map(homeName))].sort()
1976
+ };
1977
+ }
1978
+ var projectName = (cwd) => cwd.replace(/[^a-zA-Z0-9]/g, "-");
1979
+ function sessionFolder(t, cap2 = 8 * 1024 * 1024) {
1980
+ let first = null;
1981
+ let count = 0;
1982
+ let fd;
1983
+ const look = (line) => {
1984
+ if (line.indexOf('"cwd"') < 0) return null;
1985
+ let j;
1986
+ try {
1987
+ j = JSON.parse(line.toString("utf8"));
1988
+ } catch {
1989
+ return null;
1990
+ }
1991
+ if (!j || typeof j.cwd !== "string" || !path7.isAbsolute(j.cwd)) return null;
1992
+ if (projectName(j.cwd) === t.project) return j.cwd;
1993
+ first ??= j.cwd;
1994
+ count++;
1995
+ return null;
1996
+ };
1997
+ try {
1998
+ fd = openSync2(t.file, "r");
1999
+ const buf = Buffer.alloc(256 * 1024);
2000
+ let pos = 0;
2001
+ let rest = Buffer.alloc(0);
2002
+ let skipping = false;
2003
+ while (pos < cap2 && count < 50) {
2004
+ const n = readSync2(fd, buf, 0, buf.length, pos);
2005
+ if (!n) {
2006
+ if (!skipping && rest.length) {
2007
+ const hit = look(rest);
2008
+ if (hit) return hit;
2009
+ }
2010
+ break;
2011
+ }
2012
+ pos += n;
2013
+ let chunk = rest.length ? Buffer.concat([rest, buf.subarray(0, n)]) : buf.subarray(0, n);
2014
+ let nl = chunk.indexOf(10);
2015
+ while (nl >= 0) {
2016
+ const line = chunk.subarray(0, nl);
2017
+ chunk = chunk.subarray(nl + 1);
2018
+ if (skipping) skipping = false;
2019
+ else {
2020
+ const hit = look(line);
2021
+ if (hit) return hit;
2022
+ }
2023
+ nl = chunk.indexOf(10);
2024
+ }
2025
+ if (chunk.length > 2 * 1024 * 1024) {
2026
+ skipping = true;
2027
+ rest = Buffer.alloc(0);
2028
+ } else rest = skipping ? Buffer.alloc(0) : Buffer.from(chunk);
2029
+ }
2030
+ } catch {
2031
+ } finally {
2032
+ if (fd !== void 0)
2033
+ try {
2034
+ closeSync2(fd);
2035
+ } catch {
2036
+ }
2037
+ }
2038
+ return first;
2039
+ }
2040
+ function pidAlive(pid) {
2041
+ if (!Number.isSafeInteger(pid) || pid <= 1) return false;
2042
+ try {
2043
+ process.kill(pid, 0);
2044
+ return true;
2045
+ } catch (e) {
2046
+ return e.code === "EPERM";
2047
+ }
2048
+ }
2049
+ var registryRow = (file, configDir, j) => {
2050
+ const pid = Number(j.pid ?? path7.basename(file).slice(0, -5));
2051
+ const str = (v) => typeof v === "string" && v ? v : null;
2052
+ let keyFile = null;
2053
+ try {
2054
+ const want = new RegExp(`^${pid}\\.[0-9a-f]{64}\\.key$`);
2055
+ const hit = readdirSync4(path7.dirname(file)).find((n) => want.test(n));
2056
+ if (hit) keyFile = path7.join(path7.dirname(file), hit);
2057
+ } catch {
2058
+ }
2059
+ return {
2060
+ configDir,
2061
+ file,
2062
+ pid,
2063
+ socket: str(j.messagingSocketPath),
2064
+ peerProtocol: typeof j.peerProtocol === "number" ? j.peerProtocol : null,
2065
+ procStart: str(j.procStart),
2066
+ status: str(j.status),
2067
+ kind: str(j.kind),
2068
+ entrypoint: str(j.entrypoint),
2069
+ keyFile
2070
+ };
2071
+ };
2072
+ function liveRecords(session, dirs) {
2073
+ const out = [];
2074
+ for (const dir of typeof dirs === "string" ? [dirs] : dirs) {
2075
+ let names = [];
2076
+ try {
2077
+ names = readdirSync4(path7.join(dir, "sessions")).filter((n) => /^\d+\.json$/.test(n));
2078
+ } catch {
2079
+ continue;
2080
+ }
2081
+ for (const n of names) {
2082
+ const file = path7.join(dir, "sessions", n);
2083
+ let j;
2084
+ try {
2085
+ j = JSON.parse(readFileSync5(file, "utf8"));
2086
+ } catch {
2087
+ continue;
2088
+ }
2089
+ if (String(j.sessionId) !== session) continue;
2090
+ const row = registryRow(file, dir, j);
2091
+ if (pidAlive(row.pid)) out.push(row);
2092
+ }
2093
+ }
2094
+ return out;
2095
+ }
2096
+ function liveRecord(session, dirs) {
2097
+ const all = liveRecords(session, dirs);
2098
+ return all.length === 1 ? all[0] : null;
2099
+ }
2100
+ function liveProcess(session, configDir) {
2101
+ return liveRecords(session, configDir).length > 0;
2102
+ }
2103
+ var MODE_RANK = {
2104
+ plan: 0,
2105
+ default: 1,
2106
+ manual: 1,
2107
+ dontAsk: 1,
2108
+ acceptEdits: 2,
2109
+ auto: 3,
2110
+ bypassPermissions: 4
2111
+ };
2112
+ var CLAUDE_MODES = Object.keys(MODE_RANK);
2113
+ var modeRank = (m) => m && m in MODE_RANK ? MODE_RANK[m] : -1;
2114
+ function hookMode(payload) {
2115
+ if (!payload || typeof payload !== "object") return null;
2116
+ const p = payload;
2117
+ const v = typeof p.permission_mode === "string" ? p.permission_mode : p.permissionMode;
2118
+ return typeof v === "string" && CLAUDE_MODES.includes(v) ? v : null;
2119
+ }
2120
+ function peerClass(mode) {
2121
+ if (mode === "bypassPermissions") return "bypass";
2122
+ if (mode === "default" || mode === "manual" || mode === "dontAsk" || mode === "acceptEdits") return "prompting";
2123
+ return null;
2124
+ }
2125
+ var NO_HOME_SETTINGS = { defaultMode: null, disableAutoMode: false, disableBypass: false };
2126
+ function homeSettings(configDir, home = os2.homedir()) {
2127
+ const file = path7.join(configDir ?? path7.join(home, ".claude"), "settings.json");
2128
+ let j;
2129
+ try {
2130
+ j = JSON.parse(readFileSync5(file, "utf8"));
2131
+ } catch {
2132
+ return { ...NO_HOME_SETTINGS };
2133
+ }
2134
+ if (!j || typeof j !== "object" || Array.isArray(j)) return { ...NO_HOME_SETTINGS };
2135
+ const p = j.permissions && typeof j.permissions === "object" ? j.permissions : {};
2136
+ const off = (k) => j[k] === "disable" || p[k] === "disable";
2137
+ return {
2138
+ defaultMode: typeof p.defaultMode === "string" && CLAUDE_MODES.includes(p.defaultMode) ? p.defaultMode : null,
2139
+ disableAutoMode: off("disableAutoMode"),
2140
+ disableBypass: off("disableBypassPermissionsMode")
2141
+ };
2142
+ }
2143
+ function relaunchMode(recorded, home) {
2144
+ if (recorded === "plan") return recorded;
2145
+ if ((recorded === "dontAsk" || recorded === "acceptEdits") && modeRank(home.defaultMode) >= modeRank(recorded))
2146
+ return recorded;
2147
+ return null;
2148
+ }
2149
+ function recentlyWritten(file, ourLastEnd, now = Date.now()) {
2150
+ let m;
2151
+ try {
2152
+ m = statSync5(file).mtimeMs;
2153
+ } catch {
2154
+ return false;
2155
+ }
2156
+ if (now - m > LIVE_WRITE_MS) return false;
2157
+ return !(ourLastEnd && m <= ourLastEnd + 2e3);
2158
+ }
2159
+ function runClaude(bin, args, o) {
2160
+ return runHeadless(bin, args, { ...o, keep: o.keep ?? ((line) => line.includes('"type":"result"')) });
2161
+ }
2162
+ function parseResult(stdout) {
2163
+ const s = String(stdout || "").trim();
2164
+ if (!s) return null;
2165
+ const pick = (j) => {
2166
+ if (Array.isArray(j)) {
2167
+ for (let i = j.length - 1; i >= 0; i--) if (j[i]?.type === "result") return j[i];
2168
+ return null;
2169
+ }
2170
+ return j && typeof j === "object" ? j : null;
2171
+ };
2172
+ try {
2173
+ return pick(JSON.parse(s));
2174
+ } catch {
2175
+ }
2176
+ const lines = s.split("\n");
2177
+ for (let i = lines.length - 1; i >= 0; i--) {
2178
+ try {
2179
+ const j = pick(JSON.parse(lines[i]));
2180
+ if (j && (j.type === "result" || "result" in j)) return j;
2181
+ } catch {
2182
+ }
2183
+ }
2184
+ return null;
2185
+ }
2186
+ var isUuid = (s) => typeof s === "string" && UUID_RE.test(s);
2187
+
1485
2188
  // ../core/src/adapters/codex.ts
1486
- import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync as readdirSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "node:fs";
1487
- import path7 from "node:path";
2189
+ import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync as readdirSync5, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "node:fs";
2190
+ import path8 from "node:path";
1488
2191
 
1489
2192
  // ../core/src/adapters/codex-config.ts
1490
2193
  var NOTIFY_LINE = /^[ \t]*notify[ \t]*=[ \t]*(\[.*\])[ \t]*(?:#.*)?(?=\r?$)/m;
@@ -1630,24 +2333,24 @@ var CODEX_EVENTS = [
1630
2333
  ];
1631
2334
  var OPTS2 = { matcherFor: (e) => /Tool|Permission/.test(e) ? "" : void 0, timeout: 5 };
1632
2335
  var eventsFor2 = (lean) => lean ? CODEX_EVENTS.filter((e) => e !== "PreToolUse") : [...CODEX_EVENTS];
1633
- var codexHome = (env) => env.CODEX_HOME || path7.join(homeDir(), ".codex");
1634
- var hooksFile2 = (env) => path7.join(codexHome(env), "hooks.json");
1635
- var configFile2 = (env) => path7.join(codexHome(env), "config.toml");
1636
- var sessionsDir = (env) => path7.join(codexHome(env), "sessions");
2336
+ var codexHome = (env) => env.CODEX_HOME || path8.join(homeDir(), ".codex");
2337
+ var hooksFile2 = (env) => path8.join(codexHome(env), "hooks.json");
2338
+ var configFile2 = (env) => path8.join(codexHome(env), "config.toml");
2339
+ var sessionsDir = (env) => path8.join(codexHome(env), "sessions");
1637
2340
  var validTool3 = (n) => typeof n === "string" && /^[A-Za-z0-9_.:-]{1,120}$/.test(n);
1638
2341
  var endReason2 = (r) => r === "clear" || r === "logout" || r === "exit" ? r : "other";
1639
2342
  function installNotify(cmd, env) {
1640
2343
  const file = configFile2(env);
1641
2344
  let text = "";
1642
2345
  try {
1643
- text = readFileSync4(file, "utf8");
2346
+ text = readFileSync6(file, "utf8");
1644
2347
  } catch {
1645
2348
  }
1646
2349
  const argv = cmd([NAME3, "notify"]).argv;
1647
2350
  const line = `notify = ${JSON.stringify(argv)}`;
1648
2351
  const n = readNotify(text);
1649
2352
  const write = (next) => {
1650
- mkdirSync3(path7.dirname(file), { recursive: true });
2353
+ mkdirSync3(path8.dirname(file), { recursive: true });
1651
2354
  writeFileSync4(file, next);
1652
2355
  };
1653
2356
  if (n && !n.other) {
@@ -1686,7 +2389,7 @@ function uninstallNotify(env) {
1686
2389
  const file = configFile2(env);
1687
2390
  let text;
1688
2391
  try {
1689
- text = readFileSync4(file, "utf8");
2392
+ text = readFileSync6(file, "utf8");
1690
2393
  } catch {
1691
2394
  return { file, changed: false };
1692
2395
  }
@@ -1709,12 +2412,12 @@ function findRollout(threadId, env = process.env) {
1709
2412
  const walk = (dir, depth) => {
1710
2413
  let names = [];
1711
2414
  try {
1712
- names = readdirSync4(dir);
2415
+ names = readdirSync5(dir);
1713
2416
  } catch {
1714
2417
  return;
1715
2418
  }
1716
2419
  for (const n of names.sort().reverse()) {
1717
- const p = path7.join(dir, n);
2420
+ const p = path8.join(dir, n);
1718
2421
  if (n.endsWith(`-${threadId}.jsonl`)) {
1719
2422
  best = p;
1720
2423
  return;
@@ -1758,7 +2461,7 @@ var codex = {
1758
2461
  ];
1759
2462
  let text = "";
1760
2463
  try {
1761
- text = readFileSync4(configFile2(env), "utf8");
2464
+ text = readFileSync6(configFile2(env), "utf8");
1762
2465
  } catch {
1763
2466
  }
1764
2467
  const n = readNotify(text);
@@ -1885,7 +2588,7 @@ var codex = {
1885
2588
  readTranscript(file, fromLine) {
1886
2589
  let text = "";
1887
2590
  try {
1888
- text = readFileSync4(file, "utf8");
2591
+ text = readFileSync6(file, "utf8");
1889
2592
  } catch {
1890
2593
  return { turns: [], line: fromLine };
1891
2594
  }
@@ -1899,7 +2602,7 @@ var codex = {
1899
2602
  backfill(sinceMs, env = process.env) {
1900
2603
  const rows2 = [];
1901
2604
  for (const f of recentFiles(sessionsDir(env), 3, sinceMs, (n) => /^rollout-.*\.jsonl$/.test(n))) {
1902
- const m = /([0-9a-f]{8}-[0-9a-f-]{27,})\.jsonl$/i.exec(path7.basename(f));
2605
+ const m = /([0-9a-f]{8}-[0-9a-f-]{27,})\.jsonl$/i.exec(path8.basename(f));
1903
2606
  if (!m?.[1]) continue;
1904
2607
  if (codexSubagent(rolloutMeta(f))) continue;
1905
2608
  const facts = codexFacts(f);
@@ -1986,10 +2689,10 @@ function codexFacts(file) {
1986
2689
  // ../core/src/adapters/devin.ts
1987
2690
  import { createHash as createHash2 } from "node:crypto";
1988
2691
  import { existsSync as existsSync6, readlinkSync } from "node:fs";
1989
- import path8 from "node:path";
2692
+ import path9 from "node:path";
1990
2693
 
1991
2694
  // ../core/src/readers/sqlite.ts
1992
- import { execFileSync as execFileSync3 } from "node:child_process";
2695
+ import { execFileSync as execFileSync4 } from "node:child_process";
1993
2696
  import { existsSync as existsSync5 } from "node:fs";
1994
2697
  import { createRequire } from "node:module";
1995
2698
  function quiet(fn) {
@@ -2069,7 +2772,7 @@ db.close();
2069
2772
  process.stdout.write(JSON.stringify(out));`;
2070
2773
  function viaChild(file, sql, params, o) {
2071
2774
  try {
2072
- const out = execFileSync3(
2775
+ const out = execFileSync4(
2073
2776
  process.execPath,
2074
2777
  ["--experimental-sqlite", "--no-warnings", "-e", CHILD, file, sql, JSON.stringify(params), String(o.timeoutMs)],
2075
2778
  {
@@ -2113,7 +2816,7 @@ function viaCli(file, sql, params, o) {
2113
2816
  const text = inlineParams(sql, params);
2114
2817
  if (!text) return null;
2115
2818
  try {
2116
- const out = execFileSync3("sqlite3", ["-readonly", "-json", file, text], {
2819
+ const out = execFileSync4("sqlite3", ["-readonly", "-json", file, text], {
2117
2820
  encoding: "utf8",
2118
2821
  timeout: o.timeoutMs + 3e3,
2119
2822
  stdio: ["ignore", "pipe", "ignore"],
@@ -2170,11 +2873,11 @@ function foreignEvent(file) {
2170
2873
  return Object.keys(hooks).find((k) => !ok.includes(k));
2171
2874
  }
2172
2875
  var homeOf = (env) => env.HOME || homeDir();
2173
- var appData = (env) => env.APPDATA || path8.join(homeOf(env), "AppData", "Roaming");
2174
- 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");
2175
- var configFile3 = (env) => path8.join(devinConfigDir(env), "config.json");
2176
- 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");
2177
- var sessionsDb = (env) => path8.join(devinDataDir(env), "sessions.db");
2876
+ var appData = (env) => env.APPDATA || path9.join(homeOf(env), "AppData", "Roaming");
2877
+ var devinConfigDir = (env = process.env) => process.platform === "win32" ? path9.join(appData(env), "devin") : path9.join(env.XDG_CONFIG_HOME || path9.join(homeOf(env), ".config"), "devin");
2878
+ var configFile3 = (env) => path9.join(devinConfigDir(env), "config.json");
2879
+ var devinDataDir = (env = process.env) => process.platform === "win32" ? path9.join(appData(env), "devin", "cli") : path9.join(env.XDG_DATA_HOME || path9.join(homeOf(env), ".local", "share"), "devin", "cli");
2880
+ var sessionsDb = (env) => path9.join(devinDataDir(env), "sessions.db");
2178
2881
  var SESSION_ID = /^[A-Za-z0-9._:-]{1,128}$/;
2179
2882
  var validTool4 = (n) => typeof n === "string" && /^[A-Za-z0-9_.:-]{1,120}$/.test(n);
2180
2883
  var endReason3 = (r) => r === "clear" || r === "logout" || r === "exit" ? r : "other";
@@ -2346,7 +3049,7 @@ var BACKFILL_SQL = `SELECT s.id, ${SESSION_FIELDS} FROM sessions s
2346
3049
  function sessionFacts(row) {
2347
3050
  const out = {};
2348
3051
  if (typeof row.model === "string" && row.model) out.model = row.model;
2349
- if (typeof row.working_directory === "string" && path8.isAbsolute(row.working_directory))
3052
+ if (typeof row.working_directory === "string" && path9.isAbsolute(row.working_directory))
2350
3053
  out.cwd = row.working_directory;
2351
3054
  const started = Number(row.created_at);
2352
3055
  const last = Number(row.last_activity_at);
@@ -2391,8 +3094,8 @@ function readDevinChain(db, session, fromLine) {
2391
3094
  }
2392
3095
  function devinVersion(env = process.env) {
2393
3096
  try {
2394
- const target = readlinkSync(path8.join(devinDataDir(env), "_versions", "current"));
2395
- const name = path8.basename(target);
3097
+ const target = readlinkSync(path9.join(devinDataDir(env), "_versions", "current"));
3098
+ const name = path9.basename(target);
2396
3099
  return /^[0-9][0-9A-Za-z.+-]{0,63}$/.test(name) ? name : void 0;
2397
3100
  } catch {
2398
3101
  return void 0;
@@ -2405,9 +3108,9 @@ function devinAccount(env = process.env) {
2405
3108
  }
2406
3109
 
2407
3110
  // ../core/src/adapters/gemini-cli.ts
2408
- import { execFileSync as execFileSync4 } from "node:child_process";
2409
- import { existsSync as existsSync7, readFileSync as readFileSync5 } from "node:fs";
2410
- import path9 from "node:path";
3111
+ import { execFileSync as execFileSync5 } from "node:child_process";
3112
+ import { existsSync as existsSync7, readFileSync as readFileSync7 } from "node:fs";
3113
+ import path10 from "node:path";
2411
3114
  var NAME5 = "gemini-cli";
2412
3115
  var GEMINI_EVENTS = [
2413
3116
  "SessionStart",
@@ -2420,22 +3123,22 @@ var GEMINI_EVENTS = [
2420
3123
  "SessionEnd"
2421
3124
  ];
2422
3125
  var OPTS4 = { matcherFor: (e) => /Tool/.test(e) ? ".*" : void 0, timeout: 5e3 };
2423
- var geminiDir = (env) => env.GEMINI_CLI_HOME || path9.join(homeDir(), ".gemini");
2424
- var settings = (env) => path9.join(geminiDir(env), "settings.json");
3126
+ var geminiDir = (env) => env.GEMINI_CLI_HOME || path10.join(homeDir(), ".gemini");
3127
+ var settings = (env) => path10.join(geminiDir(env), "settings.json");
2425
3128
  var validTool5 = (n) => typeof n === "string" && /^[A-Za-z0-9_.:-]{1,120}$/.test(n);
2426
3129
  var eventsFor4 = (lean) => lean ? GEMINI_EVENTS.filter((e) => e !== "BeforeTool") : [...GEMINI_EVENTS];
2427
3130
  function geminiVersion(platform = process.platform, env = process.env) {
2428
3131
  const win = platform === "win32";
2429
- const bin = win ? findBinary({ names: ["gemini.exe", "gemini.cmd"], dirs: [path9.dirname(process.execPath)], env }) : "gemini";
3132
+ const bin = win ? findBinary({ names: ["gemini.exe", "gemini.cmd"], dirs: [path10.dirname(process.execPath)], env }) : "gemini";
2430
3133
  if (!bin) throw new Error("no gemini on PATH");
2431
- const o = {
3134
+ const c = probe(bin, ["--version"]);
3135
+ return execFileSync5(c.file, c.args, {
2432
3136
  encoding: "utf8",
2433
3137
  timeout: 4e3,
2434
3138
  stdio: ["ignore", "pipe", "ignore"],
2435
- env
2436
- };
2437
- const out = /\.cmd$/i.test(bin) ? execFileSync4(`"${bin}" --version`, { ...o, shell: true }) : execFileSync4(bin, ["--version"], o);
2438
- return out.trim();
3139
+ env,
3140
+ shell: c.shell
3141
+ }).trim();
2439
3142
  }
2440
3143
  function geminiHooksActive(settingsFile) {
2441
3144
  const tools = readJson(settingsFile).tools;
@@ -2462,7 +3165,7 @@ var geminiCli = {
2462
3165
  name: NAME5,
2463
3166
  events: GEMINI_EVENTS,
2464
3167
  // ~/.gemini also belongs to Antigravity; Gemini CLI is here when its settings or its chat logs exist.
2465
- detect: (env = process.env) => existsSync7(settings(env)) || existsSync7(path9.join(geminiDir(env), "tmp")),
3168
+ detect: (env = process.env) => existsSync7(settings(env)) || existsSync7(path10.join(geminiDir(env), "tmp")),
2466
3169
  configFiles: (env = process.env) => [settings(env)],
2467
3170
  install: (cmd, env = process.env, opts = {}) => {
2468
3171
  const r = installClaudeShaped(settings(env), NAME5, eventsFor4(opts.lean), cmd, OPTS4);
@@ -2560,7 +3263,7 @@ var geminiCli = {
2560
3263
  readTranscript(file, fromLine) {
2561
3264
  let text = "";
2562
3265
  try {
2563
- text = readFileSync5(file, "utf8");
3266
+ text = readFileSync7(file, "utf8");
2564
3267
  } catch {
2565
3268
  return { turns: [], line: fromLine };
2566
3269
  }
@@ -2600,12 +3303,12 @@ var ADAPTERS = [claudeCode, codex, geminiCli, antigravity, devin];
2600
3303
  var adapterByName = (name) => ADAPTERS.find((a) => a.name === name);
2601
3304
 
2602
3305
  // ../core/src/config.ts
2603
- import { chmodSync, existsSync as existsSync8, mkdirSync as mkdirSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "node:fs";
2604
- import path10 from "node:path";
3306
+ import { chmodSync, existsSync as existsSync8, mkdirSync as mkdirSync4, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "node:fs";
3307
+ import path11 from "node:path";
2605
3308
  var DEFAULT_CONFIG = { sinks: [], harnesses: {}, update_check: true, keep_days: 30 };
2606
3309
  function readConfig(file = configFile()) {
2607
3310
  try {
2608
- const j = JSON.parse(readFileSync6(file, "utf8"));
3311
+ const j = JSON.parse(readFileSync8(file, "utf8"));
2609
3312
  return {
2610
3313
  ...DEFAULT_CONFIG,
2611
3314
  ...j,
@@ -2617,7 +3320,7 @@ function readConfig(file = configFile()) {
2617
3320
  }
2618
3321
  }
2619
3322
  function writeConfig(c, file = configFile()) {
2620
- mkdirSync4(path10.dirname(file), { recursive: true, mode: 448 });
3323
+ mkdirSync4(path11.dirname(file), { recursive: true, mode: 448 });
2621
3324
  writeFileSync5(file, `${JSON.stringify(c, null, 2)}
2622
3325
  `, { mode: 384 });
2623
3326
  if (process.platform !== "win32" && existsSync8(file)) {
@@ -2681,38 +3384,22 @@ function makeEvent(a, ctx) {
2681
3384
  };
2682
3385
  }
2683
3386
 
2684
- // ../core/src/launched-by.ts
2685
- var LAUNCHED_BY_ENV = "SESSIONPIPE_LAUNCHED_BY";
2686
- var LAUNCHED_BY = /^[a-z][a-z0-9-]{0,39}:[A-Za-z0-9._:-]{1,128}$/;
2687
- function launchedBy(env) {
2688
- const v = env[LAUNCHED_BY_ENV]?.trim();
2689
- return v && LAUNCHED_BY.test(v) ? v : void 0;
2690
- }
2691
- function readHook(adapter, input) {
2692
- const r = adapter.fromHook(input);
2693
- if (r && !r.session.parent_id) {
2694
- const launcher = launchedBy(input.env);
2695
- if (launcher && launcher !== `${adapter.name}:${r.session.id}`) r.session.launched_by = launcher;
2696
- }
2697
- return r;
2698
- }
2699
-
2700
3387
  // ../core/src/outbox.ts
2701
3388
  import {
2702
3389
  appendFileSync,
2703
- closeSync as closeSync2,
3390
+ closeSync as closeSync3,
2704
3391
  existsSync as existsSync9,
2705
3392
  mkdirSync as mkdirSync5,
2706
- openSync as openSync2,
2707
- readdirSync as readdirSync5,
2708
- readFileSync as readFileSync7,
2709
- readSync as readSync2,
3393
+ openSync as openSync3,
3394
+ readdirSync as readdirSync6,
3395
+ readFileSync as readFileSync9,
3396
+ readSync as readSync3,
2710
3397
  renameSync,
2711
- statSync as statSync5,
3398
+ statSync as statSync6,
2712
3399
  unlinkSync as unlinkSync3,
2713
3400
  writeFileSync as writeFileSync6
2714
3401
  } from "node:fs";
2715
- import path11 from "node:path";
3402
+ import path12 from "node:path";
2716
3403
  var READ_CHUNK = 1024 * 1024;
2717
3404
  var READ_MAX = 8 * 1024 * 1024;
2718
3405
  var Outbox = class {
@@ -2721,13 +3408,13 @@ var Outbox = class {
2721
3408
  }
2722
3409
  root;
2723
3410
  dir(harness) {
2724
- return path11.join(this.root, "outbox", safeId(harness));
3411
+ return path12.join(this.root, "outbox", safeId(harness));
2725
3412
  }
2726
3413
  file(ref) {
2727
- return path11.join(this.dir(ref.harness), `${safeId(ref.session)}.jsonl`);
3414
+ return path12.join(this.dir(ref.harness), `${safeId(ref.session)}.jsonl`);
2728
3415
  }
2729
3416
  seqFile(ref) {
2730
- return path11.join(this.dir(ref.harness), `${safeId(ref.session)}.seq`);
3417
+ return path12.join(this.dir(ref.harness), `${safeId(ref.session)}.seq`);
2731
3418
  }
2732
3419
  /** The next seq for a session, persisted (survives a hibernating machine and a
2733
3420
  * restart; an in-memory counter would restart at 0 and collide). */
@@ -2735,19 +3422,19 @@ var Outbox = class {
2735
3422
  const f = this.seqFile(ref);
2736
3423
  let n = -1;
2737
3424
  try {
2738
- const t = readFileSync7(f, "utf8").trim();
3425
+ const t = readFileSync9(f, "utf8").trim();
2739
3426
  if (/^\d+$/.test(t)) n = Number(t);
2740
3427
  } catch {
2741
3428
  }
2742
3429
  if (n < 0) n = this.lastSeqInFile(ref) + 1;
2743
- mkdirSync5(path11.dirname(f), { recursive: true, mode: 448 });
3430
+ mkdirSync5(path12.dirname(f), { recursive: true, mode: 448 });
2744
3431
  writeFileSync6(f, String(n + 1), { mode: 384 });
2745
3432
  return n;
2746
3433
  }
2747
3434
  lastSeqInFile(ref) {
2748
3435
  const f = this.file(ref);
2749
3436
  if (!existsSync9(f)) return -1;
2750
- const lines = readFileSync7(f, "utf8").trim().split("\n");
3437
+ const lines = readFileSync9(f, "utf8").trim().split("\n");
2751
3438
  for (let i = lines.length - 1; i >= 0; i--) {
2752
3439
  try {
2753
3440
  const j = JSON.parse(lines[i]);
@@ -2766,19 +3453,19 @@ var Outbox = class {
2766
3453
  /** Every session file, newest first. */
2767
3454
  sessions() {
2768
3455
  const out = [];
2769
- const base = path11.join(this.root, "outbox");
3456
+ const base = path12.join(this.root, "outbox");
2770
3457
  if (!existsSync9(base)) return [];
2771
- for (const h of readdirSync5(base)) {
2772
- const d = path11.join(base, h);
3458
+ for (const h of readdirSync6(base)) {
3459
+ const d = path12.join(base, h);
2773
3460
  let names = [];
2774
3461
  try {
2775
- names = readdirSync5(d).filter((n) => n.endsWith(".jsonl"));
3462
+ names = readdirSync6(d).filter((n) => n.endsWith(".jsonl"));
2776
3463
  } catch {
2777
3464
  continue;
2778
3465
  }
2779
3466
  for (const n of names) {
2780
3467
  try {
2781
- out.push({ ref: { harness: h, session: n.slice(0, -6) }, mtime: statSync5(path11.join(d, n)).mtimeMs });
3468
+ out.push({ ref: { harness: h, session: n.slice(0, -6) }, mtime: statSync6(path12.join(d, n)).mtimeMs });
2782
3469
  } catch {
2783
3470
  }
2784
3471
  }
@@ -2794,16 +3481,16 @@ var Outbox = class {
2794
3481
  read(ref, from, maxEvents = 500) {
2795
3482
  const f = this.file(ref);
2796
3483
  if (!existsSync9(f)) return { events: [], offset: from };
2797
- const size = statSync5(f).size;
3484
+ const size = statSync6(f).size;
2798
3485
  if (from >= size) return { events: [], offset: from };
2799
- const fd = openSync2(f, "r");
3486
+ const fd = openSync3(f, "r");
2800
3487
  try {
2801
3488
  const events = [];
2802
3489
  let offset = from;
2803
3490
  let chunk = READ_CHUNK;
2804
3491
  while (events.length < maxEvents && offset < size) {
2805
3492
  const buf = Buffer.alloc(Math.min(size - offset, chunk));
2806
- const n = readSync2(fd, buf, 0, buf.length, offset);
3493
+ const n = readSync3(fd, buf, 0, buf.length, offset);
2807
3494
  const text = buf.subarray(0, n).toString("utf8");
2808
3495
  let pos = 0;
2809
3496
  let used = 0;
@@ -2826,7 +3513,7 @@ var Outbox = class {
2826
3513
  }
2827
3514
  return { events, offset };
2828
3515
  } finally {
2829
- closeSync2(fd);
3516
+ closeSync3(fd);
2830
3517
  }
2831
3518
  }
2832
3519
  /** Files older than `days` are pruned; the cursor files with them. */
@@ -2836,7 +3523,7 @@ var Outbox = class {
2836
3523
  for (const ref of this.sessions()) {
2837
3524
  const f = this.file(ref);
2838
3525
  try {
2839
- if (statSync5(f).mtimeMs < cutoff) {
3526
+ if (statSync6(f).mtimeMs < cutoff) {
2840
3527
  unlinkSync3(f);
2841
3528
  try {
2842
3529
  unlinkSync3(this.seqFile(ref));
@@ -2866,18 +3553,18 @@ var Cursors = class {
2866
3553
  root;
2867
3554
  sink;
2868
3555
  file() {
2869
- return path11.join(this.root, "cursors", `${safeId(this.sink)}.json`);
3556
+ return path12.join(this.root, "cursors", `${safeId(this.sink)}.json`);
2870
3557
  }
2871
3558
  read() {
2872
3559
  try {
2873
- return JSON.parse(readFileSync7(this.file(), "utf8"));
3560
+ return JSON.parse(readFileSync9(this.file(), "utf8"));
2874
3561
  } catch {
2875
3562
  return {};
2876
3563
  }
2877
3564
  }
2878
3565
  write(c) {
2879
3566
  const f = this.file();
2880
- mkdirSync5(path11.dirname(f), { recursive: true, mode: 448 });
3567
+ mkdirSync5(path12.dirname(f), { recursive: true, mode: 448 });
2881
3568
  const tmp = `${f}.${process.pid}.tmp`;
2882
3569
  writeFileSync6(tmp, JSON.stringify(c), { mode: 384 });
2883
3570
  renameSync(tmp, f);
@@ -2890,11 +3577,11 @@ function takeLock(root, ref, staleMs = 5 * 6e4) {
2890
3577
  return takeNamedLock(root, `${safeId(ref.harness)}-${safeId(ref.session)}`, staleMs);
2891
3578
  }
2892
3579
  function takeNamedLock(root, name, staleMs = 5 * 6e4) {
2893
- const dir = path11.join(root, "locks");
3580
+ const dir = path12.join(root, "locks");
2894
3581
  mkdirSync5(dir, { recursive: true, mode: 448 });
2895
- const f = path11.join(dir, `${name}.lock`);
3582
+ const f = path12.join(dir, `${name}.lock`);
2896
3583
  try {
2897
- if (Date.now() - statSync5(f).mtimeMs > staleMs || !processAlive(Number(readFileSync7(f, "utf8")))) unlinkSync3(f);
3584
+ if (Date.now() - statSync6(f).mtimeMs > staleMs || !processAlive(Number(readFileSync9(f, "utf8")))) unlinkSync3(f);
2898
3585
  else return null;
2899
3586
  } catch {
2900
3587
  }
@@ -3132,6 +3819,8 @@ var TYPE_TIER = {
3132
3819
  "subagent.ended": 1,
3133
3820
  "context.compacted": 1,
3134
3821
  "limits.observed": 1,
3822
+ "delegation.started": 0,
3823
+ "delegation.ended": 0,
3135
3824
  native: 3
3136
3825
  };
3137
3826
  var DATA_ALLOW = {
@@ -3142,8 +3831,8 @@ var DATA_ALLOW = {
3142
3831
  "session.forgotten": { 0: [] },
3143
3832
  "turn.started": { 0: ["turn_id", "submitted_by", "injected"], 1: ["prompt_chars"] },
3144
3833
  "turn.ended": { 0: ["turn_id", "reason", "ms", "background"], 1: ["error"] },
3145
- "turn.transcript": { 2: ["turn", "user", "assistant", "at"] },
3146
- "turn.progress": { 2: ["turn", "user", "assistant", "at"] },
3834
+ "turn.transcript": { 2: ["turn", "user", "assistant", "at", "injected"] },
3835
+ "turn.progress": { 2: ["turn", "user", "assistant", "at", "injected"] },
3147
3836
  "tool.started": { 1: ["tool", "call_id", "turn_id", "agent_id"], 3: ["input"] },
3148
3837
  "tool.ended": { 1: ["tool", "call_id", "turn_id", "agent_id", "ms", "ok", "error"], 3: ["input", "output"] },
3149
3838
  "files.changed": { 1: ["paths"] },
@@ -3152,7 +3841,9 @@ var DATA_ALLOW = {
3152
3841
  "subagent.started": { 1: ["agent_id", "agent_type"] },
3153
3842
  "subagent.ended": { 1: ["agent_id", "agent_type"] },
3154
3843
  "context.compacted": { 1: ["trigger"] },
3155
- "limits.observed": { 1: ["windows", "plan", "account_id"] }
3844
+ "limits.observed": { 1: ["windows", "plan", "account_id"] },
3845
+ "delegation.started": { 0: ["ask_id", "via", "harness", "to"], 1: ["text"] },
3846
+ "delegation.ended": { 0: ["ask_id", "to", "ok", "ms", "error", "relayed"], 2: ["reply"] }
3156
3847
  };
3157
3848
  var SESSION_ALLOW = [
3158
3849
  "id",
@@ -3168,7 +3859,8 @@ var SESSION_ALLOW = [
3168
3859
  "account_id",
3169
3860
  "attended",
3170
3861
  "parent_id",
3171
- "launched_by"
3862
+ "launched_by",
3863
+ "delegated_by"
3172
3864
  ];
3173
3865
  var MAX_SIDE = 2e4;
3174
3866
  var MAX_IO = 64 * 1024;
@@ -3207,6 +3899,8 @@ function filterEvent(event, tier, pii, opts = {}) {
3207
3899
  if (data.input !== void 0) data.input = cap(data.input, MAX_IO);
3208
3900
  if (data.output !== void 0) data.output = cap(data.output, MAX_IO);
3209
3901
  if (typeof data.message === "string") data.message = cap(data.message, 200);
3902
+ if (event.type === "delegation.started" && typeof data.text === "string") data.text = cap(data.text, 200);
3903
+ if (event.type === "delegation.ended" && typeof data.reply === "string") data.reply = cap(data.reply, MAX_SIDE);
3210
3904
  const rulesets = [];
3211
3905
  let out = { ...event, tier, session, data, privacy: { rulesets, pii } };
3212
3906
  if (tier > 0) {
@@ -3234,7 +3928,7 @@ function shortId(harness, id) {
3234
3928
 
3235
3929
  // ../core/src/sinks/file.ts
3236
3930
  import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync6 } from "node:fs";
3237
- import path12 from "node:path";
3931
+ import path13 from "node:path";
3238
3932
  var FileSink = class {
3239
3933
  constructor(name, file, tier, pii) {
3240
3934
  this.name = name;
@@ -3248,7 +3942,7 @@ var FileSink = class {
3248
3942
  pii;
3249
3943
  async send(events) {
3250
3944
  try {
3251
- mkdirSync6(path12.dirname(this.file), { recursive: true });
3945
+ mkdirSync6(path13.dirname(this.file), { recursive: true });
3252
3946
  appendFileSync2(this.file, `${events.map((e) => JSON.stringify(e)).join("\n")}
3253
3947
  `);
3254
3948
  return { ok: true, acknowledged: events.length, accepted: events.length, duplicates: 0, rejected: [] };
@@ -3286,19 +3980,19 @@ import {
3286
3980
  appendFileSync as appendFileSync3,
3287
3981
  existsSync as existsSync10,
3288
3982
  mkdirSync as mkdirSync8,
3289
- readdirSync as readdirSync6,
3290
- readFileSync as readFileSync9,
3983
+ readdirSync as readdirSync7,
3984
+ readFileSync as readFileSync11,
3291
3985
  renameSync as renameSync3,
3292
- statSync as statSync6,
3986
+ statSync as statSync7,
3293
3987
  unlinkSync as unlinkSync4,
3294
3988
  writeFileSync as writeFileSync8
3295
3989
  } from "node:fs";
3296
- import os3 from "node:os";
3297
- import path15 from "node:path";
3990
+ import os4 from "node:os";
3991
+ import path16 from "node:path";
3298
3992
 
3299
3993
  // src/control/aliases.ts
3300
- import { chmodSync as chmodSync2, mkdirSync as mkdirSync7, readFileSync as readFileSync8, renameSync as renameSync2, writeFileSync as writeFileSync7 } from "node:fs";
3301
- import path13 from "node:path";
3994
+ import { chmodSync as chmodSync2, mkdirSync as mkdirSync7, readFileSync as readFileSync10, renameSync as renameSync2, writeFileSync as writeFileSync7 } from "node:fs";
3995
+ import path14 from "node:path";
3302
3996
 
3303
3997
  // src/secrets.ts
3304
3998
  import { spawnSync } from "node:child_process";
@@ -3342,10 +4036,10 @@ function startOptionsOf(v) {
3342
4036
  }
3343
4037
 
3344
4038
  // src/control/aliases.ts
3345
- var aliasFile = (controlDir) => path13.join(controlDir, "aliases.json");
4039
+ var aliasFile = (controlDir) => path14.join(controlDir, "aliases.json");
3346
4040
  function readAliases(file) {
3347
4041
  try {
3348
- const j = JSON.parse(readFileSync8(file, "utf8"));
4042
+ const j = JSON.parse(readFileSync10(file, "utf8"));
3349
4043
  if (!j || typeof j !== "object" || Array.isArray(j)) return {};
3350
4044
  const out = {};
3351
4045
  for (const [k, v] of Object.entries(j)) {
@@ -3366,12 +4060,12 @@ function receiverIdOf(all, harness, id) {
3366
4060
 
3367
4061
  // src/control/local.ts
3368
4062
  import net from "node:net";
3369
- import os2 from "node:os";
3370
- import path14 from "node:path";
4063
+ import os3 from "node:os";
4064
+ import path15 from "node:path";
3371
4065
  function socketPath(state, env = process.env) {
3372
4066
  if (env.SESSIONPIPE_SOCKET) return env.SESSIONPIPE_SOCKET;
3373
- if (process.platform === "win32") return `\\\\.\\pipe\\sessionpipe-control-${os2.userInfo().username}`;
3374
- return path14.join(state, "control.sock");
4067
+ if (process.platform === "win32") return `\\\\.\\pipe\\sessionpipe-control-${os3.userInfo().username}`;
4068
+ return path15.join(state, "control.sock");
3375
4069
  }
3376
4070
  function ask(sock, req, timeoutMs) {
3377
4071
  return new Promise((resolve) => {
@@ -3502,6 +4196,11 @@ var HttpSink = class {
3502
4196
  async function tellDaemon(state, job, session, cwd, transcript) {
3503
4197
  const sock = socketPath(state);
3504
4198
  if (process.platform !== "win32" && !existsSync10(sock)) return;
4199
+ let mode = null;
4200
+ try {
4201
+ mode = claude_code_control_exports.hookMode(JSON.parse(job.stdin));
4202
+ } catch {
4203
+ }
3505
4204
  await ask(
3506
4205
  sock,
3507
4206
  {
@@ -3509,7 +4208,8 @@ async function tellDaemon(state, job, session, cwd, transcript) {
3509
4208
  session: `${job.harness}:${session}`,
3510
4209
  event: job.event,
3511
4210
  ...cwd ? { cwd } : {},
3512
- ...transcript ? { transcript } : {}
4211
+ ...transcript ? { transcript } : {},
4212
+ ...mode ? { mode } : {}
3513
4213
  },
3514
4214
  300
3515
4215
  );
@@ -3518,7 +4218,7 @@ var MAX_TRIES = 10;
3518
4218
  function enqueueJob(state, job) {
3519
4219
  const jobs = jobsDir(state);
3520
4220
  mkdirSync8(jobs, { recursive: true, mode: 448 });
3521
- const f = path15.join(jobs, `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}.json`);
4221
+ const f = path16.join(jobs, `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}.json`);
3522
4222
  writeFileSync8(f, JSON.stringify(job), { mode: 384 });
3523
4223
  return f;
3524
4224
  }
@@ -3530,7 +4230,7 @@ function claimJob(file) {
3530
4230
  return null;
3531
4231
  }
3532
4232
  try {
3533
- return { job: JSON.parse(readFileSync9(claimed, "utf8")), file: claimed };
4233
+ return { job: JSON.parse(readFileSync11(claimed, "utf8")), file: claimed };
3534
4234
  } catch {
3535
4235
  releaseJob(claimed);
3536
4236
  return null;
@@ -3547,7 +4247,7 @@ async function sweepJobs(state, olderThanMs = 3e3, limit = 20, abandonedMs = 6e4
3547
4247
  const jobs = jobsDir(state);
3548
4248
  let names = [];
3549
4249
  try {
3550
- names = readdirSync6(jobs);
4250
+ names = readdirSync7(jobs);
3551
4251
  } catch {
3552
4252
  return 0;
3553
4253
  }
@@ -3555,19 +4255,19 @@ async function sweepJobs(state, olderThanMs = 3e3, limit = 20, abandonedMs = 6e4
3555
4255
  for (const n of names) {
3556
4256
  const m = /^(.*\.json)\.(\d+)\.running$/.exec(n);
3557
4257
  if (!m) continue;
3558
- const f = path15.join(jobs, n);
4258
+ const f = path16.join(jobs, n);
3559
4259
  try {
3560
- if (processAlive(Number(m[2])) && Date.now() - statSync6(f).mtimeMs < abandonedMs) continue;
3561
- renameSync3(f, path15.join(jobs, m[1]));
4260
+ if (processAlive(Number(m[2])) && Date.now() - statSync7(f).mtimeMs < abandonedMs) continue;
4261
+ renameSync3(f, path16.join(jobs, m[1]));
3562
4262
  queued.push(m[1]);
3563
4263
  } catch {
3564
4264
  }
3565
4265
  }
3566
4266
  let ran = 0;
3567
4267
  for (const n of queued.sort().slice(0, limit)) {
3568
- const f = path15.join(jobs, n);
4268
+ const f = path16.join(jobs, n);
3569
4269
  try {
3570
- if (Date.now() - statSync6(f).mtimeMs < olderThanMs) continue;
4270
+ if (Date.now() - statSync7(f).mtimeMs < olderThanMs) continue;
3571
4271
  } catch {
3572
4272
  continue;
3573
4273
  }
@@ -3586,9 +4286,9 @@ async function sweepJobs(state, olderThanMs = 3e3, limit = 20, abandonedMs = 6e4
3586
4286
  function log(state, line) {
3587
4287
  try {
3588
4288
  mkdirSync8(state, { recursive: true, mode: 448 });
3589
- const f = path15.join(state, "log");
4289
+ const f = path16.join(state, "log");
3590
4290
  try {
3591
- if (statSync6(f).size > 1024 * 1024) renameSync3(f, `${f}.1`);
4291
+ if (statSync7(f).size > 1024 * 1024) renameSync3(f, `${f}.1`);
3592
4292
  } catch {
3593
4293
  }
3594
4294
  appendFileSync3(f, `${(/* @__PURE__ */ new Date()).toISOString()} ${line}
@@ -3597,11 +4297,11 @@ function log(state, line) {
3597
4297
  }
3598
4298
  }
3599
4299
  function factsState(state) {
3600
- const file = (h, s) => path15.join(state, "facts", safeId(h), `${safeId(s)}.json`);
4300
+ const file = (h, s) => path16.join(state, "facts", safeId(h), `${safeId(s)}.json`);
3601
4301
  return {
3602
4302
  get(h, s) {
3603
4303
  try {
3604
- return JSON.parse(readFileSync9(file(h, s), "utf8"));
4304
+ return JSON.parse(readFileSync11(file(h, s), "utf8"));
3605
4305
  } catch {
3606
4306
  return {};
3607
4307
  }
@@ -3610,7 +4310,7 @@ function factsState(state) {
3610
4310
  const cur = this.get(h, s);
3611
4311
  const next = { ...cur, ...patch };
3612
4312
  if (JSON.stringify(next) === JSON.stringify(cur)) return;
3613
- mkdirSync8(path15.dirname(file(h, s)), { recursive: true, mode: 448 });
4313
+ mkdirSync8(path16.dirname(file(h, s)), { recursive: true, mode: 448 });
3614
4314
  writeFileSync8(file(h, s), JSON.stringify(next), { mode: 384 });
3615
4315
  }
3616
4316
  };
@@ -3627,7 +4327,7 @@ function buildSinks(sinks, o = {}) {
3627
4327
  const tier = Math.min(s.tier, s.max_tier ?? 3);
3628
4328
  if (s.url === "stdout") out.push(new StdoutSink(s.name, tier, !!s.pii));
3629
4329
  else if (s.url.startsWith("file:"))
3630
- out.push(new FileSink(s.name, s.url.slice(5).replace(/^~(?=\/|$)/, os3.homedir()), tier, !!s.pii));
4330
+ out.push(new FileSink(s.name, s.url.slice(5).replace(/^~(?=\/|$)/, os4.homedir()), tier, !!s.pii));
3631
4331
  else if (/^https?:\/\//.test(s.url)) out.push(new HttpSink(s, tier));
3632
4332
  }
3633
4333
  return out;
@@ -3635,13 +4335,14 @@ function buildSinks(sinks, o = {}) {
3635
4335
  var PROGRESS_MS = 2e4;
3636
4336
  function openTurn(read, o) {
3637
4337
  const last = read.turns[read.turns.length - 1];
3638
- const fresh = (user, at) => ({
4338
+ const fresh = (user, at, injected) => ({
3639
4339
  turn: o.base + read.turns.length,
3640
4340
  user: user.slice(0, MAX_TURN_SIDE),
3641
4341
  assistant: "",
3642
- at
4342
+ at,
4343
+ ...injected ? { injected: true } : {}
3643
4344
  });
3644
- if (read.open) return fresh(read.open.user, read.open.at);
4345
+ if (read.open) return fresh(read.open.user, read.open.at, read.open.injected);
3645
4346
  const prompt = o.prompt?.trim();
3646
4347
  if (prompt && !isInjected(prompt) && !(last && last.user.includes(prompt.slice(0, 200)))) return fresh(prompt, o.at);
3647
4348
  if (!last) return null;
@@ -3649,6 +4350,7 @@ function openTurn(read, o) {
3649
4350
  turn: o.base + read.turns.length - 1,
3650
4351
  user: last.user.slice(0, MAX_TURN_SIDE),
3651
4352
  assistant: capTail(last.assistant, MAX_TURN_SIDE),
4353
+ ...last.injected ? { injected: true } : {},
3652
4354
  at: last.at
3653
4355
  };
3654
4356
  }
@@ -3656,6 +4358,7 @@ var MAX_TURN_SIDE = 2e4;
3656
4358
  var JOB_OWN_ENV = [
3657
4359
  "CLAUDE_CODE_SESSION_ATTENDED",
3658
4360
  "CLAUDE_CODE_HOST_SESSION_ID",
4361
+ "SESSIONPIPE_ASKED_BY",
3659
4362
  "SESSIONPIPE_CONTROL_JOB",
3660
4363
  "SESSIONPIPE_LAUNCHED_BY"
3661
4364
  ];
@@ -3683,7 +4386,7 @@ async function runJob(job, opts = {}) {
3683
4386
  );
3684
4387
  return { events: [], delivered: {} };
3685
4388
  }
3686
- const id = receiverIdOf(readAliases(aliasFile(path15.join(state, "control"))), job.harness, r.session.id) ?? r.session.id;
4389
+ const id = receiverIdOf(readAliases(aliasFile(path16.join(state, "control"))), job.harness, r.session.id) ?? r.session.id;
3687
4390
  const claudeEnd = job.harness === "claude-code" && r.events.some((e) => e.type === "session.ended");
3688
4391
  if (!claudeEnd) await tellDaemon(state, job, id, r.session.cwd, r.transcript);
3689
4392
  const outbox = new Outbox(state);
@@ -3720,7 +4423,7 @@ async function runJob(job, opts = {}) {
3720
4423
  learned = rest;
3721
4424
  if (harness_version) facts.save(job.harness, id, { harness_version });
3722
4425
  facts.save(job.harness, id, { session: { ...mem.session, ...learned } });
3723
- if (CWD_FROM_HOOKS.has(job.harness) && r.session.cwd && path15.isAbsolute(r.session.cwd))
4426
+ if (CWD_FROM_HOOKS.has(job.harness) && r.session.cwd && path16.isAbsolute(r.session.cwd))
3724
4427
  facts.save(job.harness, id, { cwd: r.session.cwd });
3725
4428
  }
3726
4429
  const known = mem.session ?? {};
@@ -3733,10 +4436,11 @@ async function runJob(job, opts = {}) {
3733
4436
  ...learned.branch ? { branch: learned.branch } : {},
3734
4437
  id,
3735
4438
  seq: 0,
3736
- machine: machineName(cfg, os3.hostname()),
4439
+ machine: machineName(cfg, os4.hostname()),
3737
4440
  ...cwdRaw ? { cwd: tilde(cwdRaw) } : {},
3738
4441
  ...r.session.model ? { model: r.session.model } : {},
3739
- ...r.session.launched_by ? { launched_by: r.session.launched_by } : {}
4442
+ ...r.session.launched_by ? { launched_by: r.session.launched_by } : {},
4443
+ ...r.session.delegated_by ? { delegated_by: r.session.delegated_by } : {}
3740
4444
  };
3741
4445
  const version = facts.get(job.harness, id).harness_version ?? void 0;
3742
4446
  for (const a of r.events) {
@@ -3790,7 +4494,13 @@ async function runJob(job, opts = {}) {
3790
4494
  {
3791
4495
  type: "turn.transcript",
3792
4496
  harnessEvent: job.event,
3793
- data: { turn: turnNo++, user: t.user, assistant: t.assistant, at: new Date(t.at).toISOString() }
4497
+ data: {
4498
+ turn: turnNo++,
4499
+ user: t.user,
4500
+ assistant: t.assistant,
4501
+ at: new Date(t.at).toISOString(),
4502
+ ...t.injected ? { injected: true } : {}
4503
+ }
3794
4504
  },
3795
4505
  {
3796
4506
  harness: { name: job.harness, ...version ? { version } : {} },
@@ -3836,7 +4546,7 @@ async function runJob(job, opts = {}) {
3836
4546
  return { events, delivered };
3837
4547
  }
3838
4548
  async function drain(state, sinkConfigs, outbox, first) {
3839
- const more = path15.join(state, "locks", "flush.more");
4549
+ const more = path16.join(state, "locks", "flush.more");
3840
4550
  let release = takeNamedLock(state, "flush");
3841
4551
  if (!release) {
3842
4552
  try {
@@ -3864,10 +4574,10 @@ async function drain(state, sinkConfigs, outbox, first) {
3864
4574
  }
3865
4575
  }
3866
4576
  var BACKOFF_MS = [2e3, 1e4, 6e4, 5 * 6e4, 30 * 6e4];
3867
- var backoffFile = (state, sink) => path15.join(state, "backoff", `${safeId(sink)}.json`);
4577
+ var backoffFile = (state, sink) => path16.join(state, "backoff", `${safeId(sink)}.json`);
3868
4578
  function readBackoff(state, sink) {
3869
4579
  try {
3870
- const b = JSON.parse(readFileSync9(backoffFile(state, sink), "utf8"));
4580
+ const b = JSON.parse(readFileSync11(backoffFile(state, sink), "utf8"));
3871
4581
  return typeof b.until === "number" ? b : null;
3872
4582
  } catch {
3873
4583
  return null;
@@ -3880,7 +4590,7 @@ function noteFailure(state, sink, res, prev) {
3880
4590
  const b = { until: Date.now() + wait, failures, why: `${res.status ?? ""} ${res.error ?? ""}`.trim() };
3881
4591
  const f = backoffFile(state, sink);
3882
4592
  try {
3883
- mkdirSync8(path15.dirname(f), { recursive: true, mode: 448 });
4593
+ mkdirSync8(path16.dirname(f), { recursive: true, mode: 448 });
3884
4594
  writeFileSync8(f, JSON.stringify(b), { mode: 384 });
3885
4595
  } catch {
3886
4596
  }
@@ -3980,7 +4690,7 @@ async function flush(state, sinkConfigs, outbox, first, o = {}) {
3980
4690
  const { events, offset: next } = outbox.read(ref, offset, room);
3981
4691
  if (!events.length) break;
3982
4692
  for (const e of events) {
3983
- const f = filterEvent(e, sink.tier, sink.pii, { home: os3.homedir() });
4693
+ const f = filterEvent(e, sink.tier, sink.pii, { home: os4.homedir() });
3984
4694
  if (f) batch.push(f);
3985
4695
  }
3986
4696
  offset = next;
@@ -4013,7 +4723,7 @@ function applySinkVerdict(name, res) {
4013
4723
  } catch {
4014
4724
  }
4015
4725
  }
4016
- var jobsDir = (state) => path15.join(state, "jobs");
4726
+ var jobsDir = (state) => path16.join(state, "jobs");
4017
4727
 
4018
4728
  // src/worker.ts
4019
4729
  async function workerMain(argv) {
@@ -4025,7 +4735,7 @@ async function workerMain(argv) {
4025
4735
  deadline.unref();
4026
4736
  await runJob(claimed.job, { claimed: claimed.file }).then((r) => {
4027
4737
  if (!r.requeued || (claimed.job.tries ?? 0) + 1 >= MAX_TRIES) return;
4028
- const p = spawn(process.execPath, [argv[1], r.requeued], {
4738
+ const p = spawn2(process.execPath, [argv[1], r.requeued], {
4029
4739
  detached: true,
4030
4740
  stdio: "ignore",
4031
4741
  windowsHide: true