sessionpipe-core 0.11.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- // sessionpipe-core 0.11.0 · Apache-2.0 · https://sessionpipe.org
1
+ // sessionpipe-core 0.13.0 · Apache-2.0 · https://sessionpipe.org
2
2
  var __defProp = Object.defineProperty;
3
3
  var __export = (target, all) => {
4
4
  for (var name in all)
@@ -211,7 +211,14 @@ ${m.text}`;
211
211
  continue;
212
212
  }
213
213
  if (cur) turns.push(cur);
214
- cur = { user: m.text, assistant: "", at: m.at, startLine: m.line, endLine: m.line };
214
+ cur = {
215
+ user: m.text,
216
+ assistant: "",
217
+ at: m.at,
218
+ startLine: m.line,
219
+ endLine: m.line,
220
+ ...m.injected ? { injected: true } : {}
221
+ };
215
222
  } else {
216
223
  if (!cur) cur = { user: "", assistant: "", at: m.at, startLine: m.line, endLine: m.line };
217
224
  cur.assistant += (cur.assistant ? "\n\n" : "") + m.text;
@@ -221,10 +228,17 @@ ${m.text}`;
221
228
  if (cur?.assistant) turns.push(cur);
222
229
  const line = cur && !cur.assistant ? cur.startLine : endLine;
223
230
  const out = {
224
- turns: turns.map((t) => ({ user: t.user, assistant: t.assistant, at: t.at, endLine: t.endLine })),
231
+ turns: turns.map((t) => ({
232
+ user: t.user,
233
+ assistant: t.assistant,
234
+ at: t.at,
235
+ endLine: t.endLine,
236
+ ...t.injected ? { injected: true } : {}
237
+ })),
225
238
  line
226
239
  };
227
- if (cur && !cur.assistant && cur.user) out.open = { user: cur.user, at: cur.at };
240
+ if (cur && !cur.assistant && cur.user)
241
+ out.open = { user: cur.user, at: cur.at, ...cur.injected ? { injected: true } : {} };
228
242
  return out;
229
243
  }
230
244
  var isInjected = (text) => /^<[a-z_-]+(?:\s[^>]*)?>/i.test(text) && /<\/[a-z_-]+>\s*$/i.test(text) || /^<!--\s/.test(text) || /^<command-/.test(text);
@@ -247,27 +261,51 @@ var textParts = (c, kinds) => {
247
261
  (b) => b && typeof b === "object" && kinds.includes(b.type ?? "") && typeof b.text === "string"
248
262
  ).map((b) => b.text).join("\n");
249
263
  };
264
+ var SPOKEN = /* @__PURE__ */ new Set(["human", "peer"]);
265
+ var PEER_INTRO = /sent a message[^\n]*:$/;
266
+ var PEER_OUTRO = "\n\nThis came from another ";
267
+ var ENVELOPE = /^<cross-session-message\b[^>]*>\n([\s\S]*)\n<\/cross-session-message>$/;
268
+ function peerAsk(origin, text) {
269
+ const body = origin?.body;
270
+ let t = typeof body === "string" && body.trim() ? body : text;
271
+ if (t !== body) {
272
+ const nl = t.indexOf("\n");
273
+ if (nl > 0 && PEER_INTRO.test(t.slice(0, nl))) t = t.slice(nl + 1);
274
+ const outro = t.indexOf(PEER_OUTRO);
275
+ if (outro > 0) t = t.slice(0, outro);
276
+ }
277
+ return (ENVELOPE.exec(t.trim())?.[1] ?? t).trim();
278
+ }
250
279
  var queuedPrompt = (a) => {
251
280
  if (!a || typeof a !== "object") return "";
252
281
  const q = a;
253
282
  if (q.type !== "queued_command" || (q.commandMode ?? "prompt") !== "prompt") return "";
254
- if (q.origin?.kind && q.origin.kind !== "human") return "";
255
- return textParts(q.prompt, ["text"]).trim();
283
+ const kind = q.origin?.kind ?? "human";
284
+ if (!SPOKEN.has(kind)) return "";
285
+ const text = textParts(q.prompt, ["text"]).trim();
286
+ return kind === "peer" ? peerAsk(q.origin, text) : text;
256
287
  };
288
+ var channelText = (text) => /^<channel\b[^>]*>([\s\S]*)<\/channel>$/.exec(text)?.[1]?.trim() ?? "";
257
289
  function readClaude(lines, from) {
258
290
  const msgs = [];
259
291
  for (let i = from; i < lines.length; i++) {
260
292
  const j = lines[i] ? parse(lines[i]) : null;
261
293
  if (!j || j.isSidechain) continue;
262
294
  const origin = j.origin;
263
- if (j.type === "user" && (j.isMeta || j.isCompactSummary || j.repeatsCompactSummary || origin?.kind && origin.kind !== "human"))
264
- continue;
295
+ const channel = j.type === "user" && origin?.kind === "channel";
296
+ const peer = origin?.kind === "peer";
297
+ if (j.type === "user" && (j.isCompactSummary || j.repeatsCompactSummary)) continue;
298
+ if (j.type === "user" && !channel && !peer && (j.isMeta || origin?.kind && origin.kind !== "human")) continue;
265
299
  const at = Date.parse(String(j.timestamp ?? "")) || Date.now();
266
300
  const c = j.message?.content;
267
- if (j.type === "user") {
268
- const text = textParts(c, ["text"]).trim();
301
+ if (channel) {
302
+ const text = channelText(textParts(c, ["text"]).trim());
303
+ if (text) msgs.push({ role: "user", text, at, line: i, injected: true });
304
+ } else if (j.type === "user") {
305
+ const raw = textParts(c, ["text"]).trim();
306
+ const text = peer ? peerAsk(origin, raw) : raw;
269
307
  if (!text || isInjected(text)) continue;
270
- msgs.push({ role: "user", text, at, line: i });
308
+ msgs.push({ role: "user", text, at, line: i, ...peer ? { injected: true } : {} });
271
309
  } else if (j.type === "assistant" && Array.isArray(c)) {
272
310
  const text = textParts(c, ["text"]).trim();
273
311
  if (text) msgs.push({ role: "assistant", text, at, line: i });
@@ -645,13 +683,7 @@ var claudeCode = {
645
683
  );
646
684
  break;
647
685
  case "Stop":
648
- events.push(
649
- ev("turn.ended", {
650
- turn_id,
651
- reason: "stop",
652
- background: Array.isArray(s.background_tasks) ? s.background_tasks.length : void 0
653
- })
654
- );
686
+ events.push(ev("turn.ended", { turn_id, reason: "stop", background: backgroundCount(s.background_tasks) }));
655
687
  break;
656
688
  case "StopFailure":
657
689
  events.push(
@@ -674,7 +706,8 @@ var claudeCode = {
674
706
  events.push(
675
707
  ev("subagent.ended", {
676
708
  agent_id: String(s.agent_id ?? ""),
677
- agent_type: typeof s.agent_type === "string" ? s.agent_type : void 0
709
+ agent_type: typeof s.agent_type === "string" ? s.agent_type : void 0,
710
+ background: backgroundCount(s.background_tasks, String(s.agent_id ?? ""))
678
711
  })
679
712
  );
680
713
  break;
@@ -749,6 +782,15 @@ var claudeCode = {
749
782
  return rows2;
750
783
  }
751
784
  };
785
+ var WAITER_RE = /(?:^|[\s"'`/\\])sessionpipe(?:\.\w+)?["']?\s+wait(?:\s|$)/;
786
+ function backgroundCount(tasks, ending) {
787
+ if (!Array.isArray(tasks)) return void 0;
788
+ return tasks.filter((t) => {
789
+ const task = t;
790
+ if (ending !== void 0 && task?.id === ending) return false;
791
+ return !(typeof task?.command === "string" && WAITER_RE.test(task.command));
792
+ }).length;
793
+ }
752
794
  var LINE_REFRESH_S = 60;
753
795
  function statusLine(file, want, cmd) {
754
796
  const f = readJsonFile(file);
@@ -1629,8 +1671,43 @@ import path7 from "node:path";
1629
1671
 
1630
1672
  // src/adapters/headless.ts
1631
1673
  import { spawn } from "node:child_process";
1632
- import { accessSync, constants, statSync as statSync3 } from "node:fs";
1674
+ import { accessSync, constants, readFileSync as readFileSync4, statSync as statSync3 } from "node:fs";
1633
1675
  import path6 from "node:path";
1676
+
1677
+ // src/launched-by.ts
1678
+ var LAUNCHED_BY_ENV = "SESSIONPIPE_LAUNCHED_BY";
1679
+ var DELEGATED_BY_ENV = "SESSIONPIPE_ASKED_BY";
1680
+ var ASK_CHAIN_ENV = "SESSIONPIPE_ASK_CHAIN";
1681
+ var LINEAGE_ENV = [LAUNCHED_BY_ENV, DELEGATED_BY_ENV, ASK_CHAIN_ENV];
1682
+ var SESSION_REF = /^[a-z][a-z0-9-]{0,39}:[A-Za-z0-9._:-]{1,128}$/;
1683
+ function withoutLineage(env) {
1684
+ const e = { ...env };
1685
+ for (const k of LINEAGE_ENV) delete e[k];
1686
+ return e;
1687
+ }
1688
+ function refIn(env, name) {
1689
+ const v = env[name]?.trim();
1690
+ return v && SESSION_REF.test(v) ? v : void 0;
1691
+ }
1692
+ function launchedBy(env) {
1693
+ return refIn(env, LAUNCHED_BY_ENV);
1694
+ }
1695
+ function delegatedBy(env) {
1696
+ return refIn(env, DELEGATED_BY_ENV);
1697
+ }
1698
+ function readHook(adapter, input) {
1699
+ const r = adapter.fromHook(input);
1700
+ if (r && !r.session.parent_id) {
1701
+ const self = `${adapter.name}:${r.session.id}`;
1702
+ const launcher = launchedBy(input.env);
1703
+ if (launcher && launcher !== self) r.session.launched_by = launcher;
1704
+ const asker = delegatedBy(input.env);
1705
+ if (asker && asker !== self) r.session.delegated_by = asker;
1706
+ }
1707
+ return r;
1708
+ }
1709
+
1710
+ // src/adapters/headless.ts
1634
1711
  var JOB_TIMEOUT_MS = 30 * 6e4;
1635
1712
  var OUT_CAP = 1024 * 1024;
1636
1713
  function isExe(p) {
@@ -1651,8 +1728,34 @@ function findBinary(o) {
1651
1728
  for (const d of o.dirs) for (const n of o.names) if (isExe(path6.join(d, n))) return path6.join(d, n);
1652
1729
  return null;
1653
1730
  }
1731
+ function shimTarget(shim) {
1732
+ let text = "";
1733
+ try {
1734
+ text = readFileSync4(shim, "utf8");
1735
+ } catch {
1736
+ return null;
1737
+ }
1738
+ const m = /"%(?:~dp0|dp0%)\\([^"]+)"\s+%\*/.exec(text);
1739
+ if (!m) return null;
1740
+ const target = path6.resolve(path6.dirname(shim), m[1].split("\\").join(path6.sep));
1741
+ try {
1742
+ return statSync3(target).isFile() ? target : null;
1743
+ } catch {
1744
+ return null;
1745
+ }
1746
+ }
1747
+ function spawnable(bin, args) {
1748
+ if (!/\.(cmd|bat)$/i.test(bin)) return { file: bin, args };
1749
+ const target = shimTarget(bin);
1750
+ return target ? { file: process.execPath, args: [target, ...args] } : null;
1751
+ }
1752
+ var shimNote = (bin) => `${path6.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`;
1753
+ function probe(bin, args) {
1754
+ const s = spawnable(bin, args);
1755
+ return s ? { ...s, shell: false } : { file: [`"${bin}"`, ...args].join(" "), args: [], shell: true };
1756
+ }
1654
1757
  function jobEnv(env = process.env) {
1655
- return { ...env, SESSIONPIPE_CONTROL_JOB: "1" };
1758
+ return { ...withoutLineage(env), SESSIONPIPE_CONTROL_JOB: "1" };
1656
1759
  }
1657
1760
  function runHeadless(bin, args, o) {
1658
1761
  return new Promise((resolve) => {
@@ -1663,8 +1766,13 @@ function runHeadless(bin, args, o) {
1663
1766
  let timedOut = false;
1664
1767
  let settled = false;
1665
1768
  let child;
1769
+ const c = spawnable(bin, args);
1770
+ if (!c) {
1771
+ resolve({ error: shimNote(bin) });
1772
+ return;
1773
+ }
1666
1774
  try {
1667
- child = spawn(bin, args, {
1775
+ child = spawn(c.file, c.args, {
1668
1776
  cwd: o.cwd,
1669
1777
  env: o.env,
1670
1778
  stdio: ["ignore", "pipe", "pipe"],
@@ -1864,10 +1972,14 @@ function startAcp(o) {
1864
1972
  });
1865
1973
  const label = o.label;
1866
1974
  let child;
1975
+ const c = spawnable(o.bin, o.args);
1976
+ if (!c) return Promise.resolve({ failed: `couldn't start ${label}: ${shimNote(o.bin)}` });
1867
1977
  try {
1868
- child = spawn2(o.bin, o.args, {
1978
+ child = spawn2(c.file, c.args, {
1869
1979
  cwd: o.cwd,
1870
- env: jobEnv(o.env),
1980
+ // The caller's env as given (the daemon's is jobEnv's already, with the ask's
1981
+ // lineage on top), marked as a control job.
1982
+ env: { ...o.env, SESSIONPIPE_CONTROL_JOB: "1" },
1871
1983
  stdio: ["pipe", "pipe", "pipe"],
1872
1984
  detached: process.platform !== "win32",
1873
1985
  windowsHide: true
@@ -1926,15 +2038,15 @@ function startAcp(o) {
1926
2038
  return;
1927
2039
  }
1928
2040
  if (kind !== "agent_message_chunk" || !turn) return;
1929
- const c = obj(u.content);
1930
- if (c?.type !== "text" || !str(c.text)) return;
2041
+ const c2 = obj(u.content);
2042
+ if (c2?.type !== "text" || !str(c2.text)) return;
1931
2043
  const mid = str(u.messageId) || null;
1932
2044
  if (turn.message !== void 0 && mid !== turn.message && turn.text.trim())
1933
2045
  turn.text = `${turn.text.trimEnd()}
1934
2046
 
1935
2047
  `;
1936
2048
  turn.message = mid;
1937
- turn.text += str(c.text);
2049
+ turn.text += str(c2.text);
1938
2050
  o.onText?.(turn.text);
1939
2051
  };
1940
2052
  const onRequest = (id, method, params) => {
@@ -2130,7 +2242,7 @@ import {
2130
2242
  existsSync as existsSync3,
2131
2243
  mkdirSync as mkdirSync2,
2132
2244
  readdirSync as readdirSync3,
2133
- readFileSync as readFileSync4,
2245
+ readFileSync as readFileSync5,
2134
2246
  realpathSync,
2135
2247
  statSync as statSync4,
2136
2248
  unlinkSync as unlinkSync2,
@@ -2259,7 +2371,7 @@ var antigravity = {
2259
2371
  readTranscript(file, fromLine) {
2260
2372
  let text = "";
2261
2373
  try {
2262
- text = readFileSync4(file, "utf8");
2374
+ text = readFileSync5(file, "utf8");
2263
2375
  } catch {
2264
2376
  return { turns: [], line: fromLine };
2265
2377
  }
@@ -2338,7 +2450,7 @@ function agProjects(dir) {
2338
2450
  if (!ID_RE2.test(id)) continue;
2339
2451
  let j;
2340
2452
  try {
2341
- j = JSON.parse(readFileSync4(path8.join(dir, n), "utf8"));
2453
+ j = JSON.parse(readFileSync5(path8.join(dir, n), "utf8"));
2342
2454
  } catch {
2343
2455
  continue;
2344
2456
  }
@@ -2375,7 +2487,7 @@ function antigravityRemoteUrl(transcript, opts) {
2375
2487
  for (const d of dirs) {
2376
2488
  try {
2377
2489
  const m = /installation_uuid:\s*"([0-9a-f-]{36})"/i.exec(
2378
- readFileSync4(path8.join(d, "antigravity_state.pbtxt"), "utf8")
2490
+ readFileSync5(path8.join(d, "antigravity_state.pbtxt"), "utf8")
2379
2491
  );
2380
2492
  if (!m) continue;
2381
2493
  const base = `https://antigravity.google.com/r/${m[1]}-v2`;
@@ -2554,7 +2666,10 @@ var reading = () => new AgyReading();
2554
2666
  // src/adapters/claude-code-control.ts
2555
2667
  var claude_code_control_exports = {};
2556
2668
  __export(claude_code_control_exports, {
2669
+ CLAUDE_MODES: () => CLAUDE_MODES,
2557
2670
  JOB_TIMEOUT_MS: () => JOB_TIMEOUT_MS,
2671
+ MODE_RANK: () => MODE_RANK,
2672
+ NO_HOME_SETTINGS: () => NO_HOME_SETTINGS,
2558
2673
  ceilingLevel: () => ceilingLevel,
2559
2674
  claudeArgs: () => claudeArgs,
2560
2675
  claudeEnv: () => claudeEnv,
@@ -2564,22 +2679,31 @@ __export(claude_code_control_exports, {
2564
2679
  detectCaps: () => detectCaps,
2565
2680
  findClaude: () => findClaude,
2566
2681
  findTranscript: () => findTranscript,
2682
+ findTranscripts: () => findTranscripts,
2683
+ homeSettings: () => homeSettings,
2684
+ hookMode: () => hookMode,
2567
2685
  isUuid: () => isUuid,
2568
2686
  liveProcess: () => liveProcess,
2687
+ liveRecord: () => liveRecord,
2688
+ liveRecords: () => liveRecords,
2569
2689
  loginCommand: () => loginCommand,
2570
2690
  loginHint: () => loginHint,
2571
2691
  modeFlags: () => modeFlags,
2692
+ modeRank: () => modeRank,
2572
2693
  offeredLevels: () => offeredLevels2,
2573
2694
  outputArgs: () => outputArgs,
2574
2695
  parseAuthStatus: () => parseAuthStatus,
2575
2696
  parseCaps: () => parseCaps,
2576
2697
  parseResult: () => parseResult,
2698
+ peerClass: () => peerClass,
2577
2699
  recentlyWritten: () => recentlyWritten,
2700
+ relaunchMode: () => relaunchMode,
2701
+ resolveHome: () => resolveHome,
2578
2702
  runClaude: () => runClaude,
2579
2703
  sessionFolder: () => sessionFolder
2580
2704
  });
2581
2705
  import { execFile, execFileSync as execFileSync3 } from "node:child_process";
2582
- import { closeSync as closeSync2, openSync as openSync2, readdirSync as readdirSync4, readFileSync as readFileSync5, readSync as readSync2, statSync as statSync5 } from "node:fs";
2706
+ import { closeSync as closeSync2, openSync as openSync2, readdirSync as readdirSync4, readFileSync as readFileSync6, readSync as readSync2, statSync as statSync5 } from "node:fs";
2583
2707
  import os5 from "node:os";
2584
2708
  import path10 from "node:path";
2585
2709
 
@@ -2593,10 +2717,10 @@ var MODEL_RE = /^[A-Za-z0-9][A-Za-z0-9._:/@[\]-]*$/;
2593
2717
  var START_PERMISSIONS = ["read", "safe", "edits", "auto"];
2594
2718
  var levelRank = (l) => START_PERMISSIONS.indexOf(l);
2595
2719
  var FIELDS = {
2596
- prompt: ["text", "files"],
2720
+ prompt: ["text", "files", "reply_to"],
2597
2721
  "permission.answer": ["for", "decision"],
2598
2722
  cancel: [],
2599
- start: ["text", "cwd", "files", "permission", "model"],
2723
+ start: ["text", "cwd", "files", "permission", "model", "reply_to"],
2600
2724
  "key.add": ["key", "name"],
2601
2725
  pool: ["pool_action", "pool_account", "pool_order", "pool_code"]
2602
2726
  };
@@ -2668,6 +2792,8 @@ var LEVEL_MODE = {
2668
2792
  };
2669
2793
  function modeFlags(level, caps) {
2670
2794
  if (level === "inherit") return { args: [] };
2795
+ if (caps.unread)
2796
+ return { error: `this machine couldn't read its Claude Code's options (${caps.unread}); try again in a minute` };
2671
2797
  if (level === "auto" && !caps.modes.includes("auto"))
2672
2798
  return { error: "this machine is in auto mode, but its Claude Code has no auto permission mode" };
2673
2799
  if (level !== "safe") {
@@ -2687,6 +2813,7 @@ function modeFlags(level, caps) {
2687
2813
  }
2688
2814
  var ceilingLevel = (mode) => mode === "inherit" ? "safe" : mode;
2689
2815
  function offeredLevels2(mode, caps) {
2816
+ if (caps.unread) return [];
2690
2817
  return START_PERMISSIONS.filter(
2691
2818
  (l) => levelRank(l) <= levelRank(ceilingLevel(mode)) && (l === "safe" || caps.modes.includes(LEVEL_MODE[l]))
2692
2819
  );
@@ -2712,7 +2839,7 @@ function claudeArgs(o) {
2712
2839
  };
2713
2840
  }
2714
2841
  function claudeEnv(configDir, env = process.env) {
2715
- const e = { ...env };
2842
+ const e = withoutLineage(env);
2716
2843
  for (const k of [
2717
2844
  "CLAUDECODE",
2718
2845
  "CLAUDE_CODE_SESSION_ID",
@@ -2746,11 +2873,12 @@ function parseAuthStatus(stdout) {
2746
2873
  }
2747
2874
  }
2748
2875
  function claudeLogin2(bin, configDir, env = process.env) {
2876
+ const c = probe(bin, ["auth", "status", "--json"]);
2749
2877
  return new Promise((resolve) => {
2750
2878
  execFile(
2751
- bin,
2752
- ["auth", "status", "--json"],
2753
- { encoding: "utf8", timeout: 2e4, env: claudeEnv(configDir, env), maxBuffer: 256 * 1024 },
2879
+ c.file,
2880
+ c.args,
2881
+ { encoding: "utf8", timeout: 2e4, env: claudeEnv(configDir, env), maxBuffer: 256 * 1024, shell: c.shell },
2754
2882
  // `auth status` may exit non-zero when signed out; its JSON still says so.
2755
2883
  (_err, stdout) => resolve(parseAuthStatus(stdout))
2756
2884
  );
@@ -2768,41 +2896,73 @@ function loginHint(configDir, platform = process.platform) {
2768
2896
  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.`;
2769
2897
  return `Claude Code (${shown}) isn't signed in on this machine: run ${login} there.`;
2770
2898
  }
2771
- function detectCaps(bin) {
2772
- let help = "";
2899
+ function detectCaps(bin, timeoutMs = 3e4) {
2900
+ const c = probe(bin, ["--help"]);
2773
2901
  try {
2774
- help = execFileSync3(bin, ["--help"], {
2775
- encoding: "utf8",
2776
- timeout: 3e4,
2777
- env: claudeEnv(),
2778
- stdio: ["ignore", "pipe", "pipe"],
2779
- maxBuffer: 4 * 1024 * 1024
2780
- });
2902
+ return parseCaps(
2903
+ execFileSync3(c.file, c.args, {
2904
+ encoding: "utf8",
2905
+ timeout: timeoutMs,
2906
+ env: claudeEnv(),
2907
+ stdio: ["ignore", "pipe", "pipe"],
2908
+ maxBuffer: 4 * 1024 * 1024,
2909
+ shell: c.shell
2910
+ })
2911
+ );
2781
2912
  } catch (e) {
2782
- help = String(e?.stdout || "");
2913
+ const err = e;
2914
+ const caps = parseCaps(String(err?.stdout || ""));
2915
+ if (caps.hasMode) return caps;
2916
+ const why = err?.code === "ETIMEDOUT" ? `timed out after ${timeoutMs / 1e3} s` : "failed";
2917
+ return { ...caps, unread: `\`${path10.basename(bin)} --help\` ${why}` };
2783
2918
  }
2784
- return parseCaps(help);
2785
2919
  }
2786
- function findTranscript(session, dirs) {
2787
- if (!UUID_RE2.test(session)) return null;
2788
- for (const dir of dirs) {
2789
- let projects = [];
2920
+ function transcriptIn(session, dir) {
2921
+ let projects = [];
2922
+ try {
2923
+ projects = readdirSync4(path10.join(dir, "projects"), { withFileTypes: true });
2924
+ } catch {
2925
+ return null;
2926
+ }
2927
+ for (const p of projects) {
2928
+ if (!p.isDirectory()) continue;
2929
+ const file = path10.join(dir, "projects", p.name, `${session}.jsonl`);
2790
2930
  try {
2791
- projects = readdirSync4(path10.join(dir, "projects"), { withFileTypes: true });
2931
+ if (statSync5(file).isFile()) return { file, configDir: dir, project: p.name };
2792
2932
  } catch {
2793
- continue;
2794
- }
2795
- for (const p of projects) {
2796
- if (!p.isDirectory()) continue;
2797
- const file = path10.join(dir, "projects", p.name, `${session}.jsonl`);
2798
- try {
2799
- if (statSync5(file).isFile()) return { file, configDir: dir, project: p.name };
2800
- } catch {
2801
- }
2802
2933
  }
2803
2934
  }
2804
2935
  return null;
2805
2936
  }
2937
+ function findTranscript(session, dirs) {
2938
+ if (!UUID_RE2.test(session)) return null;
2939
+ for (const dir of dirs) {
2940
+ const t = transcriptIn(session, dir);
2941
+ if (t) return t;
2942
+ }
2943
+ return null;
2944
+ }
2945
+ function findTranscripts(session, dirs) {
2946
+ if (!UUID_RE2.test(session)) return [];
2947
+ const out = [];
2948
+ for (const dir of dirs) {
2949
+ const t = transcriptIn(session, dir);
2950
+ if (t) out.push(t);
2951
+ }
2952
+ return out;
2953
+ }
2954
+ var homeName = (t) => path10.basename(t.configDir);
2955
+ function resolveHome(session, dirs) {
2956
+ const all = findTranscripts(session, dirs);
2957
+ if (!all.length) return null;
2958
+ if (all.length === 1) return { transcript: all[0] };
2959
+ const live = all.filter((t) => liveRecords(session, t.configDir).length > 0);
2960
+ if (live.length === 1) return { transcript: live[0] };
2961
+ return {
2962
+ ambiguous: [...new Set(all.map(homeName))].sort(),
2963
+ live: [...new Set(live.map(homeName))].sort()
2964
+ };
2965
+ }
2806
2966
  var projectName = (cwd) => cwd.replace(/[^a-zA-Z0-9]/g, "-");
2807
2967
  function sessionFolder(t, cap2 = 8 * 1024 * 1024) {
2808
2968
  let first = null;
@@ -2874,22 +3034,105 @@ function pidAlive(pid) {
2874
3034
  return e.code === "EPERM";
2875
3035
  }
2876
3036
  }
2877
- function liveProcess(session, configDir) {
2878
- let names = [];
3037
+ var registryRow = (file, configDir, j) => {
3038
+ const pid = Number(j.pid ?? path10.basename(file).slice(0, -5));
3039
+ const str2 = (v) => typeof v === "string" && v ? v : null;
3040
+ let keyFile = null;
2879
3041
  try {
2880
- names = readdirSync4(path10.join(configDir, "sessions")).filter((n) => /^\d+\.json$/.test(n));
3042
+ const want = new RegExp(`^${pid}\\.[0-9a-f]{64}\\.key$`);
3043
+ const hit = readdirSync4(path10.dirname(file)).find((n) => want.test(n));
3044
+ if (hit) keyFile = path10.join(path10.dirname(file), hit);
2881
3045
  } catch {
2882
- return false;
2883
3046
  }
2884
- for (const n of names) {
3047
+ return {
3048
+ configDir,
3049
+ file,
3050
+ pid,
3051
+ socket: str2(j.messagingSocketPath),
3052
+ peerProtocol: typeof j.peerProtocol === "number" ? j.peerProtocol : null,
3053
+ procStart: str2(j.procStart),
3054
+ status: str2(j.status),
3055
+ kind: str2(j.kind),
3056
+ entrypoint: str2(j.entrypoint),
3057
+ keyFile
3058
+ };
3059
+ };
3060
+ function liveRecords(session, dirs) {
3061
+ const out = [];
3062
+ for (const dir of typeof dirs === "string" ? [dirs] : dirs) {
3063
+ let names = [];
2885
3064
  try {
2886
- const j = JSON.parse(readFileSync5(path10.join(configDir, "sessions", n), "utf8"));
2887
- if (String(j.sessionId) !== session) continue;
2888
- if (pidAlive(Number(j.pid ?? n.slice(0, -5)))) return true;
3065
+ names = readdirSync4(path10.join(dir, "sessions")).filter((n) => /^\d+\.json$/.test(n));
2889
3066
  } catch {
3067
+ continue;
3068
+ }
3069
+ for (const n of names) {
3070
+ const file = path10.join(dir, "sessions", n);
3071
+ let j;
3072
+ try {
3073
+ j = JSON.parse(readFileSync6(file, "utf8"));
3074
+ } catch {
3075
+ continue;
3076
+ }
3077
+ if (String(j.sessionId) !== session) continue;
3078
+ const row = registryRow(file, dir, j);
3079
+ if (pidAlive(row.pid)) out.push(row);
2890
3080
  }
2891
3081
  }
2892
- return false;
3082
+ return out;
3083
+ }
3084
+ function liveRecord(session, dirs) {
3085
+ const all = liveRecords(session, dirs);
3086
+ return all.length === 1 ? all[0] : null;
3087
+ }
3088
+ function liveProcess(session, configDir) {
3089
+ return liveRecords(session, configDir).length > 0;
3090
+ }
3091
+ var MODE_RANK = {
3092
+ plan: 0,
3093
+ default: 1,
3094
+ manual: 1,
3095
+ dontAsk: 1,
3096
+ acceptEdits: 2,
3097
+ auto: 3,
3098
+ bypassPermissions: 4
3099
+ };
3100
+ var CLAUDE_MODES = Object.keys(MODE_RANK);
3101
+ var modeRank = (m) => m && m in MODE_RANK ? MODE_RANK[m] : -1;
3102
+ function hookMode(payload) {
3103
+ if (!payload || typeof payload !== "object") return null;
3104
+ const p = payload;
3105
+ const v = typeof p.permission_mode === "string" ? p.permission_mode : p.permissionMode;
3106
+ return typeof v === "string" && CLAUDE_MODES.includes(v) ? v : null;
3107
+ }
3108
+ function peerClass(mode) {
3109
+ if (mode === "bypassPermissions") return "bypass";
3110
+ if (mode === "default" || mode === "manual" || mode === "dontAsk" || mode === "acceptEdits") return "prompting";
3111
+ return null;
3112
+ }
3113
+ var NO_HOME_SETTINGS = { defaultMode: null, disableAutoMode: false, disableBypass: false };
3114
+ function homeSettings(configDir, home = os5.homedir()) {
3115
+ const file = path10.join(configDir ?? path10.join(home, ".claude"), "settings.json");
3116
+ let j;
3117
+ try {
3118
+ j = JSON.parse(readFileSync6(file, "utf8"));
3119
+ } catch {
3120
+ return { ...NO_HOME_SETTINGS };
3121
+ }
3122
+ if (!j || typeof j !== "object" || Array.isArray(j)) return { ...NO_HOME_SETTINGS };
3123
+ const p = j.permissions && typeof j.permissions === "object" ? j.permissions : {};
3124
+ const off = (k) => j[k] === "disable" || p[k] === "disable";
3125
+ return {
3126
+ defaultMode: typeof p.defaultMode === "string" && CLAUDE_MODES.includes(p.defaultMode) ? p.defaultMode : null,
3127
+ disableAutoMode: off("disableAutoMode"),
3128
+ disableBypass: off("disableBypassPermissionsMode")
3129
+ };
3130
+ }
3131
+ function relaunchMode(recorded, home) {
3132
+ if (recorded === "plan") return recorded;
3133
+ if ((recorded === "dontAsk" || recorded === "acceptEdits") && modeRank(home.defaultMode) >= modeRank(recorded))
3134
+ return recorded;
3135
+ return null;
2893
3136
  }
2894
3137
  function recentlyWritten(file, ourLastEnd, now = Date.now()) {
2895
3138
  let m;
@@ -2931,7 +3174,7 @@ function parseResult(stdout) {
2931
3174
  var isUuid = (s) => typeof s === "string" && UUID_RE2.test(s);
2932
3175
 
2933
3176
  // src/adapters/codex.ts
2934
- import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync as readdirSync5, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "node:fs";
3177
+ import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync as readdirSync5, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "node:fs";
2935
3178
  import path11 from "node:path";
2936
3179
 
2937
3180
  // src/adapters/codex-config.ts
@@ -3088,7 +3331,7 @@ function installNotify(cmd, env) {
3088
3331
  const file = configFile2(env);
3089
3332
  let text = "";
3090
3333
  try {
3091
- text = readFileSync6(file, "utf8");
3334
+ text = readFileSync7(file, "utf8");
3092
3335
  } catch {
3093
3336
  }
3094
3337
  const argv = cmd([NAME3, "notify"]).argv;
@@ -3134,7 +3377,7 @@ function uninstallNotify(env) {
3134
3377
  const file = configFile2(env);
3135
3378
  let text;
3136
3379
  try {
3137
- text = readFileSync6(file, "utf8");
3380
+ text = readFileSync7(file, "utf8");
3138
3381
  } catch {
3139
3382
  return { file, changed: false };
3140
3383
  }
@@ -3206,7 +3449,7 @@ var codex = {
3206
3449
  ];
3207
3450
  let text = "";
3208
3451
  try {
3209
- text = readFileSync6(configFile2(env), "utf8");
3452
+ text = readFileSync7(configFile2(env), "utf8");
3210
3453
  } catch {
3211
3454
  }
3212
3455
  const n = readNotify(text);
@@ -3333,7 +3576,7 @@ var codex = {
3333
3576
  readTranscript(file, fromLine) {
3334
3577
  let text = "";
3335
3578
  try {
3336
- text = readFileSync6(file, "utf8");
3579
+ text = readFileSync7(file, "utf8");
3337
3580
  } catch {
3338
3581
  return { turns: [], line: fromLine };
3339
3582
  }
@@ -3450,7 +3693,7 @@ __export(codex_control_exports, {
3450
3693
  reading: () => reading2
3451
3694
  });
3452
3695
  import { execFile as execFile2 } from "node:child_process";
3453
- import { readFileSync as readFileSync7 } from "node:fs";
3696
+ import { readFileSync as readFileSync8 } from "node:fs";
3454
3697
  import os6 from "node:os";
3455
3698
  import path12 from "node:path";
3456
3699
  var LABEL2 = "Codex";
@@ -3496,7 +3739,7 @@ function codexArgs(o) {
3496
3739
  function codexModels(env = process.env, home = os6.homedir()) {
3497
3740
  try {
3498
3741
  const file = path12.join(env.CODEX_HOME || path12.join(home, ".codex"), "models_cache.json");
3499
- const j = JSON.parse(readFileSync7(file, "utf8"));
3742
+ const j = JSON.parse(readFileSync8(file, "utf8"));
3500
3743
  if (!Array.isArray(j.models)) return [];
3501
3744
  const out = [];
3502
3745
  for (const m of j.models)
@@ -3512,11 +3755,12 @@ var codexEnv = (env = process.env) => jobEnv(env);
3512
3755
  var LOGIN_HINT = "Codex isn't signed in on this machine: run `codex login --device-auth` there.";
3513
3756
  function codexLogin(bin, env = process.env) {
3514
3757
  if (env.CODEX_API_KEY) return Promise.resolve({ loggedIn: true, method: "api key" });
3758
+ const c = probe(bin, ["login", "status"]);
3515
3759
  return new Promise((resolve) => {
3516
3760
  execFile2(
3517
- bin,
3518
- ["login", "status"],
3519
- { encoding: "utf8", timeout: 2e4, env: codexEnv(env), maxBuffer: 256 * 1024 },
3761
+ c.file,
3762
+ c.args,
3763
+ { encoding: "utf8", timeout: 2e4, env: codexEnv(env), maxBuffer: 256 * 1024, shell: c.shell },
3520
3764
  (err) => {
3521
3765
  if (!err) return resolve({ loggedIn: true });
3522
3766
  const code = err.code;
@@ -4021,7 +4265,7 @@ function devinAccount(env = process.env) {
4021
4265
 
4022
4266
  // src/adapters/gemini-cli.ts
4023
4267
  import { execFileSync as execFileSync5 } from "node:child_process";
4024
- import { existsSync as existsSync7, readFileSync as readFileSync8 } from "node:fs";
4268
+ import { existsSync as existsSync7, readFileSync as readFileSync9 } from "node:fs";
4025
4269
  import path14 from "node:path";
4026
4270
  var NAME5 = "gemini-cli";
4027
4271
  var GEMINI_EVENTS = [
@@ -4043,14 +4287,14 @@ function geminiVersion(platform = process.platform, env = process.env) {
4043
4287
  const win = platform === "win32";
4044
4288
  const bin = win ? findBinary({ names: ["gemini.exe", "gemini.cmd"], dirs: [path14.dirname(process.execPath)], env }) : "gemini";
4045
4289
  if (!bin) throw new Error("no gemini on PATH");
4046
- const o = {
4290
+ const c = probe(bin, ["--version"]);
4291
+ return execFileSync5(c.file, c.args, {
4047
4292
  encoding: "utf8",
4048
4293
  timeout: 4e3,
4049
4294
  stdio: ["ignore", "pipe", "ignore"],
4050
- env
4051
- };
4052
- const out = /\.cmd$/i.test(bin) ? execFileSync5(`"${bin}" --version`, { ...o, shell: true }) : execFileSync5(bin, ["--version"], o);
4053
- return out.trim();
4295
+ env,
4296
+ shell: c.shell
4297
+ }).trim();
4054
4298
  }
4055
4299
  function geminiHooksActive(settingsFile) {
4056
4300
  const tools = readJson(settingsFile).tools;
@@ -4175,7 +4419,7 @@ var geminiCli = {
4175
4419
  readTranscript(file, fromLine) {
4176
4420
  let text = "";
4177
4421
  try {
4178
- text = readFileSync8(file, "utf8");
4422
+ text = readFileSync9(file, "utf8");
4179
4423
  } catch {
4180
4424
  return { turns: [], line: fromLine };
4181
4425
  }
@@ -4224,12 +4468,12 @@ function drivableHarnesses(env = process.env) {
4224
4468
  }
4225
4469
 
4226
4470
  // src/config.ts
4227
- import { chmodSync, existsSync as existsSync8, mkdirSync as mkdirSync4, readFileSync as readFileSync9, writeFileSync as writeFileSync5 } from "node:fs";
4471
+ import { chmodSync, existsSync as existsSync8, mkdirSync as mkdirSync4, readFileSync as readFileSync10, writeFileSync as writeFileSync5 } from "node:fs";
4228
4472
  import path15 from "node:path";
4229
4473
  var DEFAULT_CONFIG = { sinks: [], harnesses: {}, update_check: true, keep_days: 30 };
4230
4474
  function readConfig(file = configFile()) {
4231
4475
  try {
4232
- const j = JSON.parse(readFileSync9(file, "utf8"));
4476
+ const j = JSON.parse(readFileSync10(file, "utf8"));
4233
4477
  return {
4234
4478
  ...DEFAULT_CONFIG,
4235
4479
  ...j,
@@ -4306,22 +4550,6 @@ function makeEvent(a, ctx) {
4306
4550
  };
4307
4551
  }
4308
4552
 
4309
- // src/launched-by.ts
4310
- var LAUNCHED_BY_ENV = "SESSIONPIPE_LAUNCHED_BY";
4311
- var LAUNCHED_BY = /^[a-z][a-z0-9-]{0,39}:[A-Za-z0-9._:-]{1,128}$/;
4312
- function launchedBy(env) {
4313
- const v = env[LAUNCHED_BY_ENV]?.trim();
4314
- return v && LAUNCHED_BY.test(v) ? v : void 0;
4315
- }
4316
- function readHook(adapter, input) {
4317
- const r = adapter.fromHook(input);
4318
- if (r && !r.session.parent_id) {
4319
- const launcher = launchedBy(input.env);
4320
- if (launcher && launcher !== `${adapter.name}:${r.session.id}`) r.session.launched_by = launcher;
4321
- }
4322
- return r;
4323
- }
4324
-
4325
4553
  // src/outbox.ts
4326
4554
  import {
4327
4555
  appendFileSync,
@@ -4330,7 +4558,7 @@ import {
4330
4558
  mkdirSync as mkdirSync5,
4331
4559
  openSync as openSync3,
4332
4560
  readdirSync as readdirSync6,
4333
- readFileSync as readFileSync10,
4561
+ readFileSync as readFileSync11,
4334
4562
  readSync as readSync3,
4335
4563
  renameSync,
4336
4564
  statSync as statSync6,
@@ -4360,7 +4588,7 @@ var Outbox = class {
4360
4588
  const f = this.seqFile(ref);
4361
4589
  let n = -1;
4362
4590
  try {
4363
- const t = readFileSync10(f, "utf8").trim();
4591
+ const t = readFileSync11(f, "utf8").trim();
4364
4592
  if (/^\d+$/.test(t)) n = Number(t);
4365
4593
  } catch {
4366
4594
  }
@@ -4372,7 +4600,7 @@ var Outbox = class {
4372
4600
  lastSeqInFile(ref) {
4373
4601
  const f = this.file(ref);
4374
4602
  if (!existsSync9(f)) return -1;
4375
- const lines = readFileSync10(f, "utf8").trim().split("\n");
4603
+ const lines = readFileSync11(f, "utf8").trim().split("\n");
4376
4604
  for (let i = lines.length - 1; i >= 0; i--) {
4377
4605
  try {
4378
4606
  const j = JSON.parse(lines[i]);
@@ -4495,7 +4723,7 @@ var Cursors = class {
4495
4723
  }
4496
4724
  read() {
4497
4725
  try {
4498
- return JSON.parse(readFileSync10(this.file(), "utf8"));
4726
+ return JSON.parse(readFileSync11(this.file(), "utf8"));
4499
4727
  } catch {
4500
4728
  return {};
4501
4729
  }
@@ -4519,7 +4747,7 @@ function takeNamedLock(root, name, staleMs = 5 * 6e4) {
4519
4747
  mkdirSync5(dir, { recursive: true, mode: 448 });
4520
4748
  const f = path16.join(dir, `${name}.lock`);
4521
4749
  try {
4522
- if (Date.now() - statSync6(f).mtimeMs > staleMs || !processAlive(Number(readFileSync10(f, "utf8")))) unlinkSync3(f);
4750
+ if (Date.now() - statSync6(f).mtimeMs > staleMs || !processAlive(Number(readFileSync11(f, "utf8")))) unlinkSync3(f);
4523
4751
  else return null;
4524
4752
  } catch {
4525
4753
  }
@@ -4760,6 +4988,8 @@ var TYPE_TIER = {
4760
4988
  "subagent.ended": 1,
4761
4989
  "context.compacted": 1,
4762
4990
  "limits.observed": 1,
4991
+ "delegation.started": 0,
4992
+ "delegation.ended": 0,
4763
4993
  native: 3
4764
4994
  };
4765
4995
  var DATA_ALLOW = {
@@ -4770,8 +5000,8 @@ var DATA_ALLOW = {
4770
5000
  "session.forgotten": { 0: [] },
4771
5001
  "turn.started": { 0: ["turn_id", "submitted_by", "injected"], 1: ["prompt_chars"] },
4772
5002
  "turn.ended": { 0: ["turn_id", "reason", "ms", "background"], 1: ["error"] },
4773
- "turn.transcript": { 2: ["turn", "user", "assistant", "at"] },
4774
- "turn.progress": { 2: ["turn", "user", "assistant", "at"] },
5003
+ "turn.transcript": { 2: ["turn", "user", "assistant", "at", "injected"] },
5004
+ "turn.progress": { 2: ["turn", "user", "assistant", "at", "injected"] },
4775
5005
  "tool.started": { 1: ["tool", "call_id", "turn_id", "agent_id"], 3: ["input"] },
4776
5006
  "tool.ended": { 1: ["tool", "call_id", "turn_id", "agent_id", "ms", "ok", "error"], 3: ["input", "output"] },
4777
5007
  "files.changed": { 1: ["paths"] },
@@ -4780,7 +5010,9 @@ var DATA_ALLOW = {
4780
5010
  "subagent.started": { 1: ["agent_id", "agent_type"] },
4781
5011
  "subagent.ended": { 1: ["agent_id", "agent_type"] },
4782
5012
  "context.compacted": { 1: ["trigger"] },
4783
- "limits.observed": { 1: ["windows", "plan", "account_id"] }
5013
+ "limits.observed": { 1: ["windows", "plan", "account_id"] },
5014
+ "delegation.started": { 0: ["ask_id", "via", "harness", "to"], 1: ["text"] },
5015
+ "delegation.ended": { 0: ["ask_id", "to", "ok", "ms", "error", "relayed"], 2: ["reply"] }
4784
5016
  };
4785
5017
  var SESSION_ALLOW = [
4786
5018
  "id",
@@ -4796,7 +5028,8 @@ var SESSION_ALLOW = [
4796
5028
  "account_id",
4797
5029
  "attended",
4798
5030
  "parent_id",
4799
- "launched_by"
5031
+ "launched_by",
5032
+ "delegated_by"
4800
5033
  ];
4801
5034
  var MAX_SIDE = 2e4;
4802
5035
  var MAX_IO = 64 * 1024;
@@ -4835,6 +5068,8 @@ function filterEvent(event, tier, pii, opts = {}) {
4835
5068
  if (data.input !== void 0) data.input = cap(data.input, MAX_IO);
4836
5069
  if (data.output !== void 0) data.output = cap(data.output, MAX_IO);
4837
5070
  if (typeof data.message === "string") data.message = cap(data.message, 200);
5071
+ if (event.type === "delegation.started" && typeof data.text === "string") data.text = cap(data.text, 200);
5072
+ if (event.type === "delegation.ended" && typeof data.reply === "string") data.reply = cap(data.reply, MAX_SIDE);
4838
5073
  const rulesets = [];
4839
5074
  let out = { ...event, tier, session, data, privacy: { rulesets, pii } };
4840
5075
  if (tier > 0) {
@@ -4914,13 +5149,16 @@ var PROTOCOL_VERSION = 1;
4914
5149
  export {
4915
5150
  ADAPTERS,
4916
5151
  AG_STALE_PRETOOL,
5152
+ ASK_CHAIN_ENV,
4917
5153
  Cursors,
4918
5154
  DEFAULT_CONFIG,
5155
+ DELEGATED_BY_ENV,
4919
5156
  DEVIN_EVENTS,
4920
5157
  FileSink,
4921
5158
  HELLO_HARNESSES_MAX,
4922
5159
  JOB_TIMEOUT_MS,
4923
5160
  LAUNCHED_BY_ENV,
5161
+ LINEAGE_ENV,
4924
5162
  Outbox,
4925
5163
  PROTOCOL_VERSION,
4926
5164
  REDACTED,
@@ -4942,6 +5180,7 @@ export {
4942
5180
  codex,
4943
5181
  codex_control_exports as codexControl,
4944
5182
  configFile,
5183
+ delegatedBy,
4945
5184
  deniedNote,
4946
5185
  devin,
4947
5186
  devinConfigDir,
@@ -4962,6 +5201,7 @@ export {
4962
5201
  makeEvent,
4963
5202
  notClaudeCode,
4964
5203
  pairTurns,
5204
+ peerAsk,
4965
5205
  processAlive,
4966
5206
  readAntigravity,
4967
5207
  readClaude,
@@ -4984,6 +5224,7 @@ export {
4984
5224
  tilde,
4985
5225
  ulid,
4986
5226
  untilde,
5227
+ withoutLineage,
4987
5228
  writeConfig
4988
5229
  };
4989
5230
  //# sourceMappingURL=index.js.map