sessionpipe 0.12.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.12.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 });
@@ -1154,8 +1197,44 @@ async function claudeAiTitle(bridge, token) {
1154
1197
  }
1155
1198
 
1156
1199
  // ../core/src/adapters/headless.ts
1200
+ import { spawn } from "node:child_process";
1157
1201
  import { accessSync, constants, readFileSync as readFileSync3, statSync as statSync3 } from "node:fs";
1158
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
1159
1238
  var JOB_TIMEOUT_MS = 30 * 6e4;
1160
1239
  var OUT_CAP = 1024 * 1024;
1161
1240
  function isExe(p) {
@@ -1197,10 +1276,116 @@ function spawnable(bin, args) {
1197
1276
  const target = shimTarget(bin);
1198
1277
  return target ? { file: process.execPath, args: [target, ...args] } : null;
1199
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`;
1200
1280
  function probe(bin, args) {
1201
1281
  const s = spawnable(bin, args);
1202
1282
  return s ? { ...s, shell: false } : { file: [`"${bin}"`, ...args].join(" "), args: [], shell: true };
1203
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
+ }
1204
1389
 
1205
1390
  // ../core/src/adapters/antigravity.ts
1206
1391
  import {
@@ -1490,6 +1675,50 @@ function antigravityFacts(file, opts) {
1490
1675
  return out;
1491
1676
  }
1492
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
+
1493
1722
  // ../core/src/control/verify.ts
1494
1723
  var GRANT_MAX_MS = 24 * 36e5;
1495
1724
  var SKEW_MS = 5 * 6e4;
@@ -1500,10 +1729,10 @@ var MODEL_RE = /^[A-Za-z0-9][A-Za-z0-9._:/@[\]-]*$/;
1500
1729
  var START_PERMISSIONS = ["read", "safe", "edits", "auto"];
1501
1730
  var levelRank = (l) => START_PERMISSIONS.indexOf(l);
1502
1731
  var FIELDS = {
1503
- prompt: ["text", "files"],
1732
+ prompt: ["text", "files", "reply_to"],
1504
1733
  "permission.answer": ["for", "decision"],
1505
1734
  cancel: [],
1506
- start: ["text", "cwd", "files", "permission", "model"],
1735
+ start: ["text", "cwd", "files", "permission", "model", "reply_to"],
1507
1736
  "key.add": ["key", "name"],
1508
1737
  pool: ["pool_action", "pool_account", "pool_order", "pool_code"]
1509
1738
  };
@@ -1511,9 +1740,454 @@ var POOL_FIELDS = FIELDS.pool;
1511
1740
  var enc = new TextEncoder();
1512
1741
  var dec = new TextDecoder();
1513
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
+
1514
2188
  // ../core/src/adapters/codex.ts
1515
- import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync as readdirSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "node:fs";
1516
- 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";
1517
2191
 
1518
2192
  // ../core/src/adapters/codex-config.ts
1519
2193
  var NOTIFY_LINE = /^[ \t]*notify[ \t]*=[ \t]*(\[.*\])[ \t]*(?:#.*)?(?=\r?$)/m;
@@ -1659,24 +2333,24 @@ var CODEX_EVENTS = [
1659
2333
  ];
1660
2334
  var OPTS2 = { matcherFor: (e) => /Tool|Permission/.test(e) ? "" : void 0, timeout: 5 };
1661
2335
  var eventsFor2 = (lean) => lean ? CODEX_EVENTS.filter((e) => e !== "PreToolUse") : [...CODEX_EVENTS];
1662
- var codexHome = (env) => env.CODEX_HOME || path7.join(homeDir(), ".codex");
1663
- var hooksFile2 = (env) => path7.join(codexHome(env), "hooks.json");
1664
- var configFile2 = (env) => path7.join(codexHome(env), "config.toml");
1665
- 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");
1666
2340
  var validTool3 = (n) => typeof n === "string" && /^[A-Za-z0-9_.:-]{1,120}$/.test(n);
1667
2341
  var endReason2 = (r) => r === "clear" || r === "logout" || r === "exit" ? r : "other";
1668
2342
  function installNotify(cmd, env) {
1669
2343
  const file = configFile2(env);
1670
2344
  let text = "";
1671
2345
  try {
1672
- text = readFileSync5(file, "utf8");
2346
+ text = readFileSync6(file, "utf8");
1673
2347
  } catch {
1674
2348
  }
1675
2349
  const argv = cmd([NAME3, "notify"]).argv;
1676
2350
  const line = `notify = ${JSON.stringify(argv)}`;
1677
2351
  const n = readNotify(text);
1678
2352
  const write = (next) => {
1679
- mkdirSync3(path7.dirname(file), { recursive: true });
2353
+ mkdirSync3(path8.dirname(file), { recursive: true });
1680
2354
  writeFileSync4(file, next);
1681
2355
  };
1682
2356
  if (n && !n.other) {
@@ -1715,7 +2389,7 @@ function uninstallNotify(env) {
1715
2389
  const file = configFile2(env);
1716
2390
  let text;
1717
2391
  try {
1718
- text = readFileSync5(file, "utf8");
2392
+ text = readFileSync6(file, "utf8");
1719
2393
  } catch {
1720
2394
  return { file, changed: false };
1721
2395
  }
@@ -1738,12 +2412,12 @@ function findRollout(threadId, env = process.env) {
1738
2412
  const walk = (dir, depth) => {
1739
2413
  let names = [];
1740
2414
  try {
1741
- names = readdirSync4(dir);
2415
+ names = readdirSync5(dir);
1742
2416
  } catch {
1743
2417
  return;
1744
2418
  }
1745
2419
  for (const n of names.sort().reverse()) {
1746
- const p = path7.join(dir, n);
2420
+ const p = path8.join(dir, n);
1747
2421
  if (n.endsWith(`-${threadId}.jsonl`)) {
1748
2422
  best = p;
1749
2423
  return;
@@ -1787,7 +2461,7 @@ var codex = {
1787
2461
  ];
1788
2462
  let text = "";
1789
2463
  try {
1790
- text = readFileSync5(configFile2(env), "utf8");
2464
+ text = readFileSync6(configFile2(env), "utf8");
1791
2465
  } catch {
1792
2466
  }
1793
2467
  const n = readNotify(text);
@@ -1914,7 +2588,7 @@ var codex = {
1914
2588
  readTranscript(file, fromLine) {
1915
2589
  let text = "";
1916
2590
  try {
1917
- text = readFileSync5(file, "utf8");
2591
+ text = readFileSync6(file, "utf8");
1918
2592
  } catch {
1919
2593
  return { turns: [], line: fromLine };
1920
2594
  }
@@ -1928,7 +2602,7 @@ var codex = {
1928
2602
  backfill(sinceMs, env = process.env) {
1929
2603
  const rows2 = [];
1930
2604
  for (const f of recentFiles(sessionsDir(env), 3, sinceMs, (n) => /^rollout-.*\.jsonl$/.test(n))) {
1931
- 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));
1932
2606
  if (!m?.[1]) continue;
1933
2607
  if (codexSubagent(rolloutMeta(f))) continue;
1934
2608
  const facts = codexFacts(f);
@@ -2015,10 +2689,10 @@ function codexFacts(file) {
2015
2689
  // ../core/src/adapters/devin.ts
2016
2690
  import { createHash as createHash2 } from "node:crypto";
2017
2691
  import { existsSync as existsSync6, readlinkSync } from "node:fs";
2018
- import path8 from "node:path";
2692
+ import path9 from "node:path";
2019
2693
 
2020
2694
  // ../core/src/readers/sqlite.ts
2021
- import { execFileSync as execFileSync3 } from "node:child_process";
2695
+ import { execFileSync as execFileSync4 } from "node:child_process";
2022
2696
  import { existsSync as existsSync5 } from "node:fs";
2023
2697
  import { createRequire } from "node:module";
2024
2698
  function quiet(fn) {
@@ -2098,7 +2772,7 @@ db.close();
2098
2772
  process.stdout.write(JSON.stringify(out));`;
2099
2773
  function viaChild(file, sql, params, o) {
2100
2774
  try {
2101
- const out = execFileSync3(
2775
+ const out = execFileSync4(
2102
2776
  process.execPath,
2103
2777
  ["--experimental-sqlite", "--no-warnings", "-e", CHILD, file, sql, JSON.stringify(params), String(o.timeoutMs)],
2104
2778
  {
@@ -2142,7 +2816,7 @@ function viaCli(file, sql, params, o) {
2142
2816
  const text = inlineParams(sql, params);
2143
2817
  if (!text) return null;
2144
2818
  try {
2145
- const out = execFileSync3("sqlite3", ["-readonly", "-json", file, text], {
2819
+ const out = execFileSync4("sqlite3", ["-readonly", "-json", file, text], {
2146
2820
  encoding: "utf8",
2147
2821
  timeout: o.timeoutMs + 3e3,
2148
2822
  stdio: ["ignore", "pipe", "ignore"],
@@ -2199,11 +2873,11 @@ function foreignEvent(file) {
2199
2873
  return Object.keys(hooks).find((k) => !ok.includes(k));
2200
2874
  }
2201
2875
  var homeOf = (env) => env.HOME || homeDir();
2202
- var appData = (env) => env.APPDATA || path8.join(homeOf(env), "AppData", "Roaming");
2203
- 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");
2204
- var configFile3 = (env) => path8.join(devinConfigDir(env), "config.json");
2205
- 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");
2206
- 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");
2207
2881
  var SESSION_ID = /^[A-Za-z0-9._:-]{1,128}$/;
2208
2882
  var validTool4 = (n) => typeof n === "string" && /^[A-Za-z0-9_.:-]{1,120}$/.test(n);
2209
2883
  var endReason3 = (r) => r === "clear" || r === "logout" || r === "exit" ? r : "other";
@@ -2375,7 +3049,7 @@ var BACKFILL_SQL = `SELECT s.id, ${SESSION_FIELDS} FROM sessions s
2375
3049
  function sessionFacts(row) {
2376
3050
  const out = {};
2377
3051
  if (typeof row.model === "string" && row.model) out.model = row.model;
2378
- if (typeof row.working_directory === "string" && path8.isAbsolute(row.working_directory))
3052
+ if (typeof row.working_directory === "string" && path9.isAbsolute(row.working_directory))
2379
3053
  out.cwd = row.working_directory;
2380
3054
  const started = Number(row.created_at);
2381
3055
  const last = Number(row.last_activity_at);
@@ -2420,8 +3094,8 @@ function readDevinChain(db, session, fromLine) {
2420
3094
  }
2421
3095
  function devinVersion(env = process.env) {
2422
3096
  try {
2423
- const target = readlinkSync(path8.join(devinDataDir(env), "_versions", "current"));
2424
- const name = path8.basename(target);
3097
+ const target = readlinkSync(path9.join(devinDataDir(env), "_versions", "current"));
3098
+ const name = path9.basename(target);
2425
3099
  return /^[0-9][0-9A-Za-z.+-]{0,63}$/.test(name) ? name : void 0;
2426
3100
  } catch {
2427
3101
  return void 0;
@@ -2434,9 +3108,9 @@ function devinAccount(env = process.env) {
2434
3108
  }
2435
3109
 
2436
3110
  // ../core/src/adapters/gemini-cli.ts
2437
- import { execFileSync as execFileSync4 } from "node:child_process";
2438
- import { existsSync as existsSync7, readFileSync as readFileSync6 } from "node:fs";
2439
- 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";
2440
3114
  var NAME5 = "gemini-cli";
2441
3115
  var GEMINI_EVENTS = [
2442
3116
  "SessionStart",
@@ -2449,16 +3123,16 @@ var GEMINI_EVENTS = [
2449
3123
  "SessionEnd"
2450
3124
  ];
2451
3125
  var OPTS4 = { matcherFor: (e) => /Tool/.test(e) ? ".*" : void 0, timeout: 5e3 };
2452
- var geminiDir = (env) => env.GEMINI_CLI_HOME || path9.join(homeDir(), ".gemini");
2453
- 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");
2454
3128
  var validTool5 = (n) => typeof n === "string" && /^[A-Za-z0-9_.:-]{1,120}$/.test(n);
2455
3129
  var eventsFor4 = (lean) => lean ? GEMINI_EVENTS.filter((e) => e !== "BeforeTool") : [...GEMINI_EVENTS];
2456
3130
  function geminiVersion(platform = process.platform, env = process.env) {
2457
3131
  const win = platform === "win32";
2458
- 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";
2459
3133
  if (!bin) throw new Error("no gemini on PATH");
2460
3134
  const c = probe(bin, ["--version"]);
2461
- return execFileSync4(c.file, c.args, {
3135
+ return execFileSync5(c.file, c.args, {
2462
3136
  encoding: "utf8",
2463
3137
  timeout: 4e3,
2464
3138
  stdio: ["ignore", "pipe", "ignore"],
@@ -2491,7 +3165,7 @@ var geminiCli = {
2491
3165
  name: NAME5,
2492
3166
  events: GEMINI_EVENTS,
2493
3167
  // ~/.gemini also belongs to Antigravity; Gemini CLI is here when its settings or its chat logs exist.
2494
- 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")),
2495
3169
  configFiles: (env = process.env) => [settings(env)],
2496
3170
  install: (cmd, env = process.env, opts = {}) => {
2497
3171
  const r = installClaudeShaped(settings(env), NAME5, eventsFor4(opts.lean), cmd, OPTS4);
@@ -2589,7 +3263,7 @@ var geminiCli = {
2589
3263
  readTranscript(file, fromLine) {
2590
3264
  let text = "";
2591
3265
  try {
2592
- text = readFileSync6(file, "utf8");
3266
+ text = readFileSync7(file, "utf8");
2593
3267
  } catch {
2594
3268
  return { turns: [], line: fromLine };
2595
3269
  }
@@ -2629,12 +3303,12 @@ var ADAPTERS = [claudeCode, codex, geminiCli, antigravity, devin];
2629
3303
  var adapterByName = (name) => ADAPTERS.find((a) => a.name === name);
2630
3304
 
2631
3305
  // ../core/src/config.ts
2632
- import { chmodSync, existsSync as existsSync8, mkdirSync as mkdirSync4, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "node:fs";
2633
- 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";
2634
3308
  var DEFAULT_CONFIG = { sinks: [], harnesses: {}, update_check: true, keep_days: 30 };
2635
3309
  function readConfig(file = configFile()) {
2636
3310
  try {
2637
- const j = JSON.parse(readFileSync7(file, "utf8"));
3311
+ const j = JSON.parse(readFileSync8(file, "utf8"));
2638
3312
  return {
2639
3313
  ...DEFAULT_CONFIG,
2640
3314
  ...j,
@@ -2646,7 +3320,7 @@ function readConfig(file = configFile()) {
2646
3320
  }
2647
3321
  }
2648
3322
  function writeConfig(c, file = configFile()) {
2649
- mkdirSync4(path10.dirname(file), { recursive: true, mode: 448 });
3323
+ mkdirSync4(path11.dirname(file), { recursive: true, mode: 448 });
2650
3324
  writeFileSync5(file, `${JSON.stringify(c, null, 2)}
2651
3325
  `, { mode: 384 });
2652
3326
  if (process.platform !== "win32" && existsSync8(file)) {
@@ -2710,38 +3384,22 @@ function makeEvent(a, ctx) {
2710
3384
  };
2711
3385
  }
2712
3386
 
2713
- // ../core/src/launched-by.ts
2714
- var LAUNCHED_BY_ENV = "SESSIONPIPE_LAUNCHED_BY";
2715
- var LAUNCHED_BY = /^[a-z][a-z0-9-]{0,39}:[A-Za-z0-9._:-]{1,128}$/;
2716
- function launchedBy(env) {
2717
- const v = env[LAUNCHED_BY_ENV]?.trim();
2718
- return v && LAUNCHED_BY.test(v) ? v : void 0;
2719
- }
2720
- function readHook(adapter, input) {
2721
- const r = adapter.fromHook(input);
2722
- if (r && !r.session.parent_id) {
2723
- const launcher = launchedBy(input.env);
2724
- if (launcher && launcher !== `${adapter.name}:${r.session.id}`) r.session.launched_by = launcher;
2725
- }
2726
- return r;
2727
- }
2728
-
2729
3387
  // ../core/src/outbox.ts
2730
3388
  import {
2731
3389
  appendFileSync,
2732
- closeSync as closeSync2,
3390
+ closeSync as closeSync3,
2733
3391
  existsSync as existsSync9,
2734
3392
  mkdirSync as mkdirSync5,
2735
- openSync as openSync2,
2736
- readdirSync as readdirSync5,
2737
- readFileSync as readFileSync8,
2738
- readSync as readSync2,
3393
+ openSync as openSync3,
3394
+ readdirSync as readdirSync6,
3395
+ readFileSync as readFileSync9,
3396
+ readSync as readSync3,
2739
3397
  renameSync,
2740
- statSync as statSync5,
3398
+ statSync as statSync6,
2741
3399
  unlinkSync as unlinkSync3,
2742
3400
  writeFileSync as writeFileSync6
2743
3401
  } from "node:fs";
2744
- import path11 from "node:path";
3402
+ import path12 from "node:path";
2745
3403
  var READ_CHUNK = 1024 * 1024;
2746
3404
  var READ_MAX = 8 * 1024 * 1024;
2747
3405
  var Outbox = class {
@@ -2750,13 +3408,13 @@ var Outbox = class {
2750
3408
  }
2751
3409
  root;
2752
3410
  dir(harness) {
2753
- return path11.join(this.root, "outbox", safeId(harness));
3411
+ return path12.join(this.root, "outbox", safeId(harness));
2754
3412
  }
2755
3413
  file(ref) {
2756
- return path11.join(this.dir(ref.harness), `${safeId(ref.session)}.jsonl`);
3414
+ return path12.join(this.dir(ref.harness), `${safeId(ref.session)}.jsonl`);
2757
3415
  }
2758
3416
  seqFile(ref) {
2759
- return path11.join(this.dir(ref.harness), `${safeId(ref.session)}.seq`);
3417
+ return path12.join(this.dir(ref.harness), `${safeId(ref.session)}.seq`);
2760
3418
  }
2761
3419
  /** The next seq for a session, persisted (survives a hibernating machine and a
2762
3420
  * restart; an in-memory counter would restart at 0 and collide). */
@@ -2764,19 +3422,19 @@ var Outbox = class {
2764
3422
  const f = this.seqFile(ref);
2765
3423
  let n = -1;
2766
3424
  try {
2767
- const t = readFileSync8(f, "utf8").trim();
3425
+ const t = readFileSync9(f, "utf8").trim();
2768
3426
  if (/^\d+$/.test(t)) n = Number(t);
2769
3427
  } catch {
2770
3428
  }
2771
3429
  if (n < 0) n = this.lastSeqInFile(ref) + 1;
2772
- mkdirSync5(path11.dirname(f), { recursive: true, mode: 448 });
3430
+ mkdirSync5(path12.dirname(f), { recursive: true, mode: 448 });
2773
3431
  writeFileSync6(f, String(n + 1), { mode: 384 });
2774
3432
  return n;
2775
3433
  }
2776
3434
  lastSeqInFile(ref) {
2777
3435
  const f = this.file(ref);
2778
3436
  if (!existsSync9(f)) return -1;
2779
- const lines = readFileSync8(f, "utf8").trim().split("\n");
3437
+ const lines = readFileSync9(f, "utf8").trim().split("\n");
2780
3438
  for (let i = lines.length - 1; i >= 0; i--) {
2781
3439
  try {
2782
3440
  const j = JSON.parse(lines[i]);
@@ -2795,19 +3453,19 @@ var Outbox = class {
2795
3453
  /** Every session file, newest first. */
2796
3454
  sessions() {
2797
3455
  const out = [];
2798
- const base = path11.join(this.root, "outbox");
3456
+ const base = path12.join(this.root, "outbox");
2799
3457
  if (!existsSync9(base)) return [];
2800
- for (const h of readdirSync5(base)) {
2801
- const d = path11.join(base, h);
3458
+ for (const h of readdirSync6(base)) {
3459
+ const d = path12.join(base, h);
2802
3460
  let names = [];
2803
3461
  try {
2804
- names = readdirSync5(d).filter((n) => n.endsWith(".jsonl"));
3462
+ names = readdirSync6(d).filter((n) => n.endsWith(".jsonl"));
2805
3463
  } catch {
2806
3464
  continue;
2807
3465
  }
2808
3466
  for (const n of names) {
2809
3467
  try {
2810
- 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 });
2811
3469
  } catch {
2812
3470
  }
2813
3471
  }
@@ -2823,16 +3481,16 @@ var Outbox = class {
2823
3481
  read(ref, from, maxEvents = 500) {
2824
3482
  const f = this.file(ref);
2825
3483
  if (!existsSync9(f)) return { events: [], offset: from };
2826
- const size = statSync5(f).size;
3484
+ const size = statSync6(f).size;
2827
3485
  if (from >= size) return { events: [], offset: from };
2828
- const fd = openSync2(f, "r");
3486
+ const fd = openSync3(f, "r");
2829
3487
  try {
2830
3488
  const events = [];
2831
3489
  let offset = from;
2832
3490
  let chunk = READ_CHUNK;
2833
3491
  while (events.length < maxEvents && offset < size) {
2834
3492
  const buf = Buffer.alloc(Math.min(size - offset, chunk));
2835
- const n = readSync2(fd, buf, 0, buf.length, offset);
3493
+ const n = readSync3(fd, buf, 0, buf.length, offset);
2836
3494
  const text = buf.subarray(0, n).toString("utf8");
2837
3495
  let pos = 0;
2838
3496
  let used = 0;
@@ -2855,7 +3513,7 @@ var Outbox = class {
2855
3513
  }
2856
3514
  return { events, offset };
2857
3515
  } finally {
2858
- closeSync2(fd);
3516
+ closeSync3(fd);
2859
3517
  }
2860
3518
  }
2861
3519
  /** Files older than `days` are pruned; the cursor files with them. */
@@ -2865,7 +3523,7 @@ var Outbox = class {
2865
3523
  for (const ref of this.sessions()) {
2866
3524
  const f = this.file(ref);
2867
3525
  try {
2868
- if (statSync5(f).mtimeMs < cutoff) {
3526
+ if (statSync6(f).mtimeMs < cutoff) {
2869
3527
  unlinkSync3(f);
2870
3528
  try {
2871
3529
  unlinkSync3(this.seqFile(ref));
@@ -2895,18 +3553,18 @@ var Cursors = class {
2895
3553
  root;
2896
3554
  sink;
2897
3555
  file() {
2898
- return path11.join(this.root, "cursors", `${safeId(this.sink)}.json`);
3556
+ return path12.join(this.root, "cursors", `${safeId(this.sink)}.json`);
2899
3557
  }
2900
3558
  read() {
2901
3559
  try {
2902
- return JSON.parse(readFileSync8(this.file(), "utf8"));
3560
+ return JSON.parse(readFileSync9(this.file(), "utf8"));
2903
3561
  } catch {
2904
3562
  return {};
2905
3563
  }
2906
3564
  }
2907
3565
  write(c) {
2908
3566
  const f = this.file();
2909
- mkdirSync5(path11.dirname(f), { recursive: true, mode: 448 });
3567
+ mkdirSync5(path12.dirname(f), { recursive: true, mode: 448 });
2910
3568
  const tmp = `${f}.${process.pid}.tmp`;
2911
3569
  writeFileSync6(tmp, JSON.stringify(c), { mode: 384 });
2912
3570
  renameSync(tmp, f);
@@ -2919,11 +3577,11 @@ function takeLock(root, ref, staleMs = 5 * 6e4) {
2919
3577
  return takeNamedLock(root, `${safeId(ref.harness)}-${safeId(ref.session)}`, staleMs);
2920
3578
  }
2921
3579
  function takeNamedLock(root, name, staleMs = 5 * 6e4) {
2922
- const dir = path11.join(root, "locks");
3580
+ const dir = path12.join(root, "locks");
2923
3581
  mkdirSync5(dir, { recursive: true, mode: 448 });
2924
- const f = path11.join(dir, `${name}.lock`);
3582
+ const f = path12.join(dir, `${name}.lock`);
2925
3583
  try {
2926
- if (Date.now() - statSync5(f).mtimeMs > staleMs || !processAlive(Number(readFileSync8(f, "utf8")))) unlinkSync3(f);
3584
+ if (Date.now() - statSync6(f).mtimeMs > staleMs || !processAlive(Number(readFileSync9(f, "utf8")))) unlinkSync3(f);
2927
3585
  else return null;
2928
3586
  } catch {
2929
3587
  }
@@ -3161,6 +3819,8 @@ var TYPE_TIER = {
3161
3819
  "subagent.ended": 1,
3162
3820
  "context.compacted": 1,
3163
3821
  "limits.observed": 1,
3822
+ "delegation.started": 0,
3823
+ "delegation.ended": 0,
3164
3824
  native: 3
3165
3825
  };
3166
3826
  var DATA_ALLOW = {
@@ -3171,8 +3831,8 @@ var DATA_ALLOW = {
3171
3831
  "session.forgotten": { 0: [] },
3172
3832
  "turn.started": { 0: ["turn_id", "submitted_by", "injected"], 1: ["prompt_chars"] },
3173
3833
  "turn.ended": { 0: ["turn_id", "reason", "ms", "background"], 1: ["error"] },
3174
- "turn.transcript": { 2: ["turn", "user", "assistant", "at"] },
3175
- "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"] },
3176
3836
  "tool.started": { 1: ["tool", "call_id", "turn_id", "agent_id"], 3: ["input"] },
3177
3837
  "tool.ended": { 1: ["tool", "call_id", "turn_id", "agent_id", "ms", "ok", "error"], 3: ["input", "output"] },
3178
3838
  "files.changed": { 1: ["paths"] },
@@ -3181,7 +3841,9 @@ var DATA_ALLOW = {
3181
3841
  "subagent.started": { 1: ["agent_id", "agent_type"] },
3182
3842
  "subagent.ended": { 1: ["agent_id", "agent_type"] },
3183
3843
  "context.compacted": { 1: ["trigger"] },
3184
- "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"] }
3185
3847
  };
3186
3848
  var SESSION_ALLOW = [
3187
3849
  "id",
@@ -3197,7 +3859,8 @@ var SESSION_ALLOW = [
3197
3859
  "account_id",
3198
3860
  "attended",
3199
3861
  "parent_id",
3200
- "launched_by"
3862
+ "launched_by",
3863
+ "delegated_by"
3201
3864
  ];
3202
3865
  var MAX_SIDE = 2e4;
3203
3866
  var MAX_IO = 64 * 1024;
@@ -3236,6 +3899,8 @@ function filterEvent(event, tier, pii, opts = {}) {
3236
3899
  if (data.input !== void 0) data.input = cap(data.input, MAX_IO);
3237
3900
  if (data.output !== void 0) data.output = cap(data.output, MAX_IO);
3238
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);
3239
3904
  const rulesets = [];
3240
3905
  let out = { ...event, tier, session, data, privacy: { rulesets, pii } };
3241
3906
  if (tier > 0) {
@@ -3263,7 +3928,7 @@ function shortId(harness, id) {
3263
3928
 
3264
3929
  // ../core/src/sinks/file.ts
3265
3930
  import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync6 } from "node:fs";
3266
- import path12 from "node:path";
3931
+ import path13 from "node:path";
3267
3932
  var FileSink = class {
3268
3933
  constructor(name, file, tier, pii) {
3269
3934
  this.name = name;
@@ -3277,7 +3942,7 @@ var FileSink = class {
3277
3942
  pii;
3278
3943
  async send(events) {
3279
3944
  try {
3280
- mkdirSync6(path12.dirname(this.file), { recursive: true });
3945
+ mkdirSync6(path13.dirname(this.file), { recursive: true });
3281
3946
  appendFileSync2(this.file, `${events.map((e) => JSON.stringify(e)).join("\n")}
3282
3947
  `);
3283
3948
  return { ok: true, acknowledged: events.length, accepted: events.length, duplicates: 0, rejected: [] };
@@ -3315,19 +3980,19 @@ import {
3315
3980
  appendFileSync as appendFileSync3,
3316
3981
  existsSync as existsSync10,
3317
3982
  mkdirSync as mkdirSync8,
3318
- readdirSync as readdirSync6,
3319
- readFileSync as readFileSync10,
3983
+ readdirSync as readdirSync7,
3984
+ readFileSync as readFileSync11,
3320
3985
  renameSync as renameSync3,
3321
- statSync as statSync6,
3986
+ statSync as statSync7,
3322
3987
  unlinkSync as unlinkSync4,
3323
3988
  writeFileSync as writeFileSync8
3324
3989
  } from "node:fs";
3325
- import os3 from "node:os";
3326
- import path15 from "node:path";
3990
+ import os4 from "node:os";
3991
+ import path16 from "node:path";
3327
3992
 
3328
3993
  // src/control/aliases.ts
3329
- import { chmodSync as chmodSync2, mkdirSync as mkdirSync7, readFileSync as readFileSync9, renameSync as renameSync2, writeFileSync as writeFileSync7 } from "node:fs";
3330
- 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";
3331
3996
 
3332
3997
  // src/secrets.ts
3333
3998
  import { spawnSync } from "node:child_process";
@@ -3371,10 +4036,10 @@ function startOptionsOf(v) {
3371
4036
  }
3372
4037
 
3373
4038
  // src/control/aliases.ts
3374
- var aliasFile = (controlDir) => path13.join(controlDir, "aliases.json");
4039
+ var aliasFile = (controlDir) => path14.join(controlDir, "aliases.json");
3375
4040
  function readAliases(file) {
3376
4041
  try {
3377
- const j = JSON.parse(readFileSync9(file, "utf8"));
4042
+ const j = JSON.parse(readFileSync10(file, "utf8"));
3378
4043
  if (!j || typeof j !== "object" || Array.isArray(j)) return {};
3379
4044
  const out = {};
3380
4045
  for (const [k, v] of Object.entries(j)) {
@@ -3395,12 +4060,12 @@ function receiverIdOf(all, harness, id) {
3395
4060
 
3396
4061
  // src/control/local.ts
3397
4062
  import net from "node:net";
3398
- import os2 from "node:os";
3399
- import path14 from "node:path";
4063
+ import os3 from "node:os";
4064
+ import path15 from "node:path";
3400
4065
  function socketPath(state, env = process.env) {
3401
4066
  if (env.SESSIONPIPE_SOCKET) return env.SESSIONPIPE_SOCKET;
3402
- if (process.platform === "win32") return `\\\\.\\pipe\\sessionpipe-control-${os2.userInfo().username}`;
3403
- 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");
3404
4069
  }
3405
4070
  function ask(sock, req, timeoutMs) {
3406
4071
  return new Promise((resolve) => {
@@ -3531,6 +4196,11 @@ var HttpSink = class {
3531
4196
  async function tellDaemon(state, job, session, cwd, transcript) {
3532
4197
  const sock = socketPath(state);
3533
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
+ }
3534
4204
  await ask(
3535
4205
  sock,
3536
4206
  {
@@ -3538,7 +4208,8 @@ async function tellDaemon(state, job, session, cwd, transcript) {
3538
4208
  session: `${job.harness}:${session}`,
3539
4209
  event: job.event,
3540
4210
  ...cwd ? { cwd } : {},
3541
- ...transcript ? { transcript } : {}
4211
+ ...transcript ? { transcript } : {},
4212
+ ...mode ? { mode } : {}
3542
4213
  },
3543
4214
  300
3544
4215
  );
@@ -3547,7 +4218,7 @@ var MAX_TRIES = 10;
3547
4218
  function enqueueJob(state, job) {
3548
4219
  const jobs = jobsDir(state);
3549
4220
  mkdirSync8(jobs, { recursive: true, mode: 448 });
3550
- 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`);
3551
4222
  writeFileSync8(f, JSON.stringify(job), { mode: 384 });
3552
4223
  return f;
3553
4224
  }
@@ -3559,7 +4230,7 @@ function claimJob(file) {
3559
4230
  return null;
3560
4231
  }
3561
4232
  try {
3562
- return { job: JSON.parse(readFileSync10(claimed, "utf8")), file: claimed };
4233
+ return { job: JSON.parse(readFileSync11(claimed, "utf8")), file: claimed };
3563
4234
  } catch {
3564
4235
  releaseJob(claimed);
3565
4236
  return null;
@@ -3576,7 +4247,7 @@ async function sweepJobs(state, olderThanMs = 3e3, limit = 20, abandonedMs = 6e4
3576
4247
  const jobs = jobsDir(state);
3577
4248
  let names = [];
3578
4249
  try {
3579
- names = readdirSync6(jobs);
4250
+ names = readdirSync7(jobs);
3580
4251
  } catch {
3581
4252
  return 0;
3582
4253
  }
@@ -3584,19 +4255,19 @@ async function sweepJobs(state, olderThanMs = 3e3, limit = 20, abandonedMs = 6e4
3584
4255
  for (const n of names) {
3585
4256
  const m = /^(.*\.json)\.(\d+)\.running$/.exec(n);
3586
4257
  if (!m) continue;
3587
- const f = path15.join(jobs, n);
4258
+ const f = path16.join(jobs, n);
3588
4259
  try {
3589
- if (processAlive(Number(m[2])) && Date.now() - statSync6(f).mtimeMs < abandonedMs) continue;
3590
- 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]));
3591
4262
  queued.push(m[1]);
3592
4263
  } catch {
3593
4264
  }
3594
4265
  }
3595
4266
  let ran = 0;
3596
4267
  for (const n of queued.sort().slice(0, limit)) {
3597
- const f = path15.join(jobs, n);
4268
+ const f = path16.join(jobs, n);
3598
4269
  try {
3599
- if (Date.now() - statSync6(f).mtimeMs < olderThanMs) continue;
4270
+ if (Date.now() - statSync7(f).mtimeMs < olderThanMs) continue;
3600
4271
  } catch {
3601
4272
  continue;
3602
4273
  }
@@ -3615,9 +4286,9 @@ async function sweepJobs(state, olderThanMs = 3e3, limit = 20, abandonedMs = 6e4
3615
4286
  function log(state, line) {
3616
4287
  try {
3617
4288
  mkdirSync8(state, { recursive: true, mode: 448 });
3618
- const f = path15.join(state, "log");
4289
+ const f = path16.join(state, "log");
3619
4290
  try {
3620
- if (statSync6(f).size > 1024 * 1024) renameSync3(f, `${f}.1`);
4291
+ if (statSync7(f).size > 1024 * 1024) renameSync3(f, `${f}.1`);
3621
4292
  } catch {
3622
4293
  }
3623
4294
  appendFileSync3(f, `${(/* @__PURE__ */ new Date()).toISOString()} ${line}
@@ -3626,11 +4297,11 @@ function log(state, line) {
3626
4297
  }
3627
4298
  }
3628
4299
  function factsState(state) {
3629
- 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`);
3630
4301
  return {
3631
4302
  get(h, s) {
3632
4303
  try {
3633
- return JSON.parse(readFileSync10(file(h, s), "utf8"));
4304
+ return JSON.parse(readFileSync11(file(h, s), "utf8"));
3634
4305
  } catch {
3635
4306
  return {};
3636
4307
  }
@@ -3639,7 +4310,7 @@ function factsState(state) {
3639
4310
  const cur = this.get(h, s);
3640
4311
  const next = { ...cur, ...patch };
3641
4312
  if (JSON.stringify(next) === JSON.stringify(cur)) return;
3642
- mkdirSync8(path15.dirname(file(h, s)), { recursive: true, mode: 448 });
4313
+ mkdirSync8(path16.dirname(file(h, s)), { recursive: true, mode: 448 });
3643
4314
  writeFileSync8(file(h, s), JSON.stringify(next), { mode: 384 });
3644
4315
  }
3645
4316
  };
@@ -3656,7 +4327,7 @@ function buildSinks(sinks, o = {}) {
3656
4327
  const tier = Math.min(s.tier, s.max_tier ?? 3);
3657
4328
  if (s.url === "stdout") out.push(new StdoutSink(s.name, tier, !!s.pii));
3658
4329
  else if (s.url.startsWith("file:"))
3659
- 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));
3660
4331
  else if (/^https?:\/\//.test(s.url)) out.push(new HttpSink(s, tier));
3661
4332
  }
3662
4333
  return out;
@@ -3664,13 +4335,14 @@ function buildSinks(sinks, o = {}) {
3664
4335
  var PROGRESS_MS = 2e4;
3665
4336
  function openTurn(read, o) {
3666
4337
  const last = read.turns[read.turns.length - 1];
3667
- const fresh = (user, at) => ({
4338
+ const fresh = (user, at, injected) => ({
3668
4339
  turn: o.base + read.turns.length,
3669
4340
  user: user.slice(0, MAX_TURN_SIDE),
3670
4341
  assistant: "",
3671
- at
4342
+ at,
4343
+ ...injected ? { injected: true } : {}
3672
4344
  });
3673
- 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);
3674
4346
  const prompt = o.prompt?.trim();
3675
4347
  if (prompt && !isInjected(prompt) && !(last && last.user.includes(prompt.slice(0, 200)))) return fresh(prompt, o.at);
3676
4348
  if (!last) return null;
@@ -3678,6 +4350,7 @@ function openTurn(read, o) {
3678
4350
  turn: o.base + read.turns.length - 1,
3679
4351
  user: last.user.slice(0, MAX_TURN_SIDE),
3680
4352
  assistant: capTail(last.assistant, MAX_TURN_SIDE),
4353
+ ...last.injected ? { injected: true } : {},
3681
4354
  at: last.at
3682
4355
  };
3683
4356
  }
@@ -3685,6 +4358,7 @@ var MAX_TURN_SIDE = 2e4;
3685
4358
  var JOB_OWN_ENV = [
3686
4359
  "CLAUDE_CODE_SESSION_ATTENDED",
3687
4360
  "CLAUDE_CODE_HOST_SESSION_ID",
4361
+ "SESSIONPIPE_ASKED_BY",
3688
4362
  "SESSIONPIPE_CONTROL_JOB",
3689
4363
  "SESSIONPIPE_LAUNCHED_BY"
3690
4364
  ];
@@ -3712,7 +4386,7 @@ async function runJob(job, opts = {}) {
3712
4386
  );
3713
4387
  return { events: [], delivered: {} };
3714
4388
  }
3715
- 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;
3716
4390
  const claudeEnd = job.harness === "claude-code" && r.events.some((e) => e.type === "session.ended");
3717
4391
  if (!claudeEnd) await tellDaemon(state, job, id, r.session.cwd, r.transcript);
3718
4392
  const outbox = new Outbox(state);
@@ -3749,7 +4423,7 @@ async function runJob(job, opts = {}) {
3749
4423
  learned = rest;
3750
4424
  if (harness_version) facts.save(job.harness, id, { harness_version });
3751
4425
  facts.save(job.harness, id, { session: { ...mem.session, ...learned } });
3752
- 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))
3753
4427
  facts.save(job.harness, id, { cwd: r.session.cwd });
3754
4428
  }
3755
4429
  const known = mem.session ?? {};
@@ -3762,10 +4436,11 @@ async function runJob(job, opts = {}) {
3762
4436
  ...learned.branch ? { branch: learned.branch } : {},
3763
4437
  id,
3764
4438
  seq: 0,
3765
- machine: machineName(cfg, os3.hostname()),
4439
+ machine: machineName(cfg, os4.hostname()),
3766
4440
  ...cwdRaw ? { cwd: tilde(cwdRaw) } : {},
3767
4441
  ...r.session.model ? { model: r.session.model } : {},
3768
- ...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 } : {}
3769
4444
  };
3770
4445
  const version = facts.get(job.harness, id).harness_version ?? void 0;
3771
4446
  for (const a of r.events) {
@@ -3819,7 +4494,13 @@ async function runJob(job, opts = {}) {
3819
4494
  {
3820
4495
  type: "turn.transcript",
3821
4496
  harnessEvent: job.event,
3822
- 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
+ }
3823
4504
  },
3824
4505
  {
3825
4506
  harness: { name: job.harness, ...version ? { version } : {} },
@@ -3865,7 +4546,7 @@ async function runJob(job, opts = {}) {
3865
4546
  return { events, delivered };
3866
4547
  }
3867
4548
  async function drain(state, sinkConfigs, outbox, first) {
3868
- const more = path15.join(state, "locks", "flush.more");
4549
+ const more = path16.join(state, "locks", "flush.more");
3869
4550
  let release = takeNamedLock(state, "flush");
3870
4551
  if (!release) {
3871
4552
  try {
@@ -3893,10 +4574,10 @@ async function drain(state, sinkConfigs, outbox, first) {
3893
4574
  }
3894
4575
  }
3895
4576
  var BACKOFF_MS = [2e3, 1e4, 6e4, 5 * 6e4, 30 * 6e4];
3896
- var backoffFile = (state, sink) => path15.join(state, "backoff", `${safeId(sink)}.json`);
4577
+ var backoffFile = (state, sink) => path16.join(state, "backoff", `${safeId(sink)}.json`);
3897
4578
  function readBackoff(state, sink) {
3898
4579
  try {
3899
- const b = JSON.parse(readFileSync10(backoffFile(state, sink), "utf8"));
4580
+ const b = JSON.parse(readFileSync11(backoffFile(state, sink), "utf8"));
3900
4581
  return typeof b.until === "number" ? b : null;
3901
4582
  } catch {
3902
4583
  return null;
@@ -3909,7 +4590,7 @@ function noteFailure(state, sink, res, prev) {
3909
4590
  const b = { until: Date.now() + wait, failures, why: `${res.status ?? ""} ${res.error ?? ""}`.trim() };
3910
4591
  const f = backoffFile(state, sink);
3911
4592
  try {
3912
- mkdirSync8(path15.dirname(f), { recursive: true, mode: 448 });
4593
+ mkdirSync8(path16.dirname(f), { recursive: true, mode: 448 });
3913
4594
  writeFileSync8(f, JSON.stringify(b), { mode: 384 });
3914
4595
  } catch {
3915
4596
  }
@@ -4009,7 +4690,7 @@ async function flush(state, sinkConfigs, outbox, first, o = {}) {
4009
4690
  const { events, offset: next } = outbox.read(ref, offset, room);
4010
4691
  if (!events.length) break;
4011
4692
  for (const e of events) {
4012
- const f = filterEvent(e, sink.tier, sink.pii, { home: os3.homedir() });
4693
+ const f = filterEvent(e, sink.tier, sink.pii, { home: os4.homedir() });
4013
4694
  if (f) batch.push(f);
4014
4695
  }
4015
4696
  offset = next;
@@ -4042,7 +4723,7 @@ function applySinkVerdict(name, res) {
4042
4723
  } catch {
4043
4724
  }
4044
4725
  }
4045
- var jobsDir = (state) => path15.join(state, "jobs");
4726
+ var jobsDir = (state) => path16.join(state, "jobs");
4046
4727
 
4047
4728
  // src/worker.ts
4048
4729
  async function workerMain(argv) {
@@ -4054,7 +4735,7 @@ async function workerMain(argv) {
4054
4735
  deadline.unref();
4055
4736
  await runJob(claimed.job, { claimed: claimed.file }).then((r) => {
4056
4737
  if (!r.requeued || (claimed.job.tries ?? 0) + 1 >= MAX_TRIES) return;
4057
- const p = spawn(process.execPath, [argv[1], r.requeued], {
4738
+ const p = spawn2(process.execPath, [argv[1], r.requeued], {
4058
4739
  detached: true,
4059
4740
  stdio: "ignore",
4060
4741
  windowsHide: true