claudish 14.12.0 → 14.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.
Files changed (2) hide show
  1. package/dist/index.js +1295 -225
  2. package/package.json +5 -5
package/dist/index.js CHANGED
@@ -5491,7 +5491,7 @@ var init_onepassword_config = __esm(() => {
5491
5491
  });
5492
5492
 
5493
5493
  // src/version.ts
5494
- var VERSION = "14.12.0";
5494
+ var VERSION = "14.13.0";
5495
5495
 
5496
5496
  // src/redact.ts
5497
5497
  function isPlaceholder(value) {
@@ -5587,22 +5587,134 @@ function redactForClient(text) {
5587
5587
  function redactSecrets(text) {
5588
5588
  if (!text)
5589
5589
  return "";
5590
- let out = redactSigningValues(text);
5591
- out = out.replace(ASSIGNMENT_RE, (match, lhs, value) => isPlaceholder(value) ? match : `${lhs}${MASK}`);
5592
- for (const { re } of TOKEN_PATTERNS) {
5593
- out = out.replace(re, (match) => {
5594
- const scheme = /^(Bearer|Basic|Token)\s/.exec(match);
5595
- return scheme ? `${scheme[1]} ${MASK}` : MASK;
5596
- });
5590
+ return redactPatterns(redactSigningValues(text));
5591
+ }
5592
+ function applyParts(match, parts) {
5593
+ if (parts === null)
5594
+ return match;
5595
+ let out = "";
5596
+ for (const p of parts)
5597
+ out += typeof p === "string" ? p : match.slice(p[0], p[1]);
5598
+ return out;
5599
+ }
5600
+ function redactPatterns(text) {
5601
+ if (!text)
5602
+ return text;
5603
+ let out = text;
5604
+ for (const rule of PATTERN_RULES) {
5605
+ out = out.replace(rule.re, (match, ...rest) => applyParts(match, rule.parts(match, rest.slice(0, -2))));
5597
5606
  }
5598
5607
  return out;
5599
5608
  }
5609
+
5610
+ class SegmentBuilder {
5611
+ segments = [];
5612
+ push(length, origin) {
5613
+ if (length <= 0)
5614
+ return;
5615
+ const last = this.segments[this.segments.length - 1];
5616
+ const bothInserted = last !== undefined && last.origin < 0 && origin < 0;
5617
+ const contiguousOriginal = last !== undefined && last.origin >= 0 && origin >= 0 && last.origin + last.length === origin;
5618
+ if (last && (bothInserted || contiguousOriginal))
5619
+ last.length += length;
5620
+ else
5621
+ this.segments.push({ length, origin });
5622
+ }
5623
+ }
5624
+
5625
+ class SegmentCursor {
5626
+ segs;
5627
+ idx = 0;
5628
+ segStart = 0;
5629
+ constructor(segs) {
5630
+ this.segs = segs;
5631
+ }
5632
+ copy(from, to, into) {
5633
+ let pos = from;
5634
+ while (pos < to) {
5635
+ let seg = this.segs[this.idx];
5636
+ while (this.segStart + seg.length <= pos) {
5637
+ this.segStart += seg.length;
5638
+ seg = this.segs[++this.idx];
5639
+ }
5640
+ const take = Math.min(to, this.segStart + seg.length) - pos;
5641
+ into.push(take, seg.origin < 0 ? -1 : seg.origin + (pos - this.segStart));
5642
+ pos += take;
5643
+ }
5644
+ }
5645
+ }
5646
+ function redactPatternsMapped(text) {
5647
+ let cur = text;
5648
+ let segs = text ? [{ length: text.length, origin: 0 }] : [];
5649
+ for (const rule of PATTERN_RULES) {
5650
+ if (!cur)
5651
+ break;
5652
+ const out = new SegmentBuilder;
5653
+ const src = new SegmentCursor(segs);
5654
+ let built = "";
5655
+ let last = 0;
5656
+ let hit = false;
5657
+ for (const m of cur.matchAll(rule.re)) {
5658
+ const parts = rule.parts(m[0], m.slice(1));
5659
+ if (parts === null)
5660
+ continue;
5661
+ hit = true;
5662
+ const at = m.index;
5663
+ built += cur.slice(last, at);
5664
+ src.copy(last, at, out);
5665
+ for (const p of parts) {
5666
+ if (typeof p === "string") {
5667
+ built += p;
5668
+ out.push(p.length, -1);
5669
+ } else {
5670
+ built += cur.slice(at + p[0], at + p[1]);
5671
+ src.copy(at + p[0], at + p[1], out);
5672
+ }
5673
+ }
5674
+ last = at + m[0].length;
5675
+ }
5676
+ if (!hit)
5677
+ continue;
5678
+ built += cur.slice(last);
5679
+ src.copy(last, cur.length, out);
5680
+ cur = built;
5681
+ segs = out.segments;
5682
+ }
5683
+ return { text: cur, segments: segs };
5684
+ }
5685
+ function signingValueRanges(text) {
5686
+ if (liveSigningValues.size === 0 || !text)
5687
+ return [];
5688
+ if (!sortedSigningValues) {
5689
+ sortedSigningValues = [...liveSigningValues.keys()].sort((a, b) => b.length - a.length);
5690
+ }
5691
+ const found = [];
5692
+ for (const value of sortedSigningValues) {
5693
+ let at = text.indexOf(value);
5694
+ while (at !== -1) {
5695
+ found.push([at, at + value.length]);
5696
+ at = text.indexOf(value, at + 1);
5697
+ }
5698
+ }
5699
+ if (found.length === 0)
5700
+ return [];
5701
+ found.sort((a, b) => a[0] - b[0] || a[1] - b[1]);
5702
+ const merged = [];
5703
+ for (const [s, e] of found) {
5704
+ const last = merged[merged.length - 1];
5705
+ if (last && s <= last[1])
5706
+ last[1] = Math.max(last[1], e);
5707
+ else
5708
+ merged.push([s, e]);
5709
+ }
5710
+ return merged;
5711
+ }
5600
5712
  function sanitizeForReport(text) {
5601
5713
  if (!text)
5602
5714
  return "";
5603
5715
  return redactSecrets(text).replace(/\/Users\/[^/\s]+/g, "/Users/***").replace(/\/home\/[^/\s]+/g, "/home/***").replace(/[A-Z]:\\Users\\[^\\\s]+/gi, "C:\\Users\\***").replace(/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g, "***@***.***");
5604
5716
  }
5605
- var MASK = "***REDACTED***", PLACEHOLDER, ASSIGNMENT_RE, TOKEN_PATTERNS, SIGNING_MASK = "[redacted]", SIGNING_VALUE_MIN_LENGTH = 8, liveSigningValues, sortedSigningValues = null;
5717
+ var MASK = "***REDACTED***", PLACEHOLDER, ASSIGNMENT_RE, TOKEN_PATTERNS, SIGNING_MASK = "[redacted]", SIGNING_VALUE_MIN_LENGTH = 8, liveSigningValues, sortedSigningValues = null, PATTERN_RULES;
5606
5718
  var init_redact = __esm(() => {
5607
5719
  PLACEHOLDER = /^(['"`]?)(your[-_a-z0-9]*|<[^>]*>|\.\.\.|x{3,}|\*{3,}|REDACTED|CHANGEME|TODO|\$\{[^}]*\}|)(['"`]?)$/i;
5608
5720
  ASSIGNMENT_RE = /(["']?\b[A-Z][A-Z0-9_]*(?:_API_KEY|_KEY|_TOKEN|_SECRET|_PASSWORD)\b["']?\s*[:=]\s*)(["'`]?\$\{[^}]*\}["'`]?|["'`]?[^\s"'`,}]+["'`]?)/g;
@@ -5617,6 +5729,19 @@ var init_redact = __esm(() => {
5617
5729
  { name: "bearer", re: /\b(?:Bearer|Basic|Token)\s+[A-Za-z0-9_.\-+/=]{12,}/g }
5618
5730
  ];
5619
5731
  liveSigningValues = new Map;
5732
+ PATTERN_RULES = [
5733
+ {
5734
+ re: ASSIGNMENT_RE,
5735
+ parts: (_m, [lhs = "", value = ""]) => isPlaceholder(value) ? null : [[0, lhs.length], MASK]
5736
+ },
5737
+ ...TOKEN_PATTERNS.map(({ re }) => ({
5738
+ re,
5739
+ parts: (m) => {
5740
+ const scheme = /^(Bearer|Basic|Token)\s/.exec(m);
5741
+ return scheme ? [[0, scheme[1].length], ` ${MASK}`] : [MASK];
5742
+ }
5743
+ }))
5744
+ ];
5620
5745
  });
5621
5746
 
5622
5747
  // src/logger.ts
@@ -73038,7 +73163,7 @@ function isTerminalState(s) {
73038
73163
  function contractMeta() {
73039
73164
  return { contract_version: CONTRACT_VERSION, capabilities: [...CAPABILITIES] };
73040
73165
  }
73041
- var CONTRACT_VERSION = 1, CAPABILITIES, SLOT_STATES, TERMINAL_STATES, FAILURE_REASONS, ContractErrorException;
73166
+ var CONTRACT_VERSION = 1, CAPABILITIES, SLOT_STATES, TERMINAL_STATES, FAILURE_REASONS, MAX_TRANSCRIPT_LINES = 200, TRANSCRIPT_LINE_MAX_CHARS = 8000, TRANSCRIPT_LINE_MAX_JSON_BYTES = 16000, TRANSCRIPT_ANSWER_MAX_BYTES = 32000, ContractErrorException;
73042
73167
  var init_contract = __esm(() => {
73043
73168
  CAPABILITIES = [
73044
73169
  "list",
@@ -73049,7 +73174,8 @@ var init_contract = __esm(() => {
73049
73174
  "capture_spans",
73050
73175
  "resize",
73051
73176
  "allowance",
73052
- "scroll"
73177
+ "scroll",
73178
+ "transcript"
73053
73179
  ];
73054
73180
  SLOT_STATES = [
73055
73181
  "STARTING",
@@ -94445,7 +94571,7 @@ class TranscriptFollower {
94445
94571
  let any = false;
94446
94572
  for (const { offset, line } of this.main.read()) {
94447
94573
  any = true;
94448
- const rec = parse8(line);
94574
+ const rec = parseRecord3(line);
94449
94575
  if (rec)
94450
94576
  applyRecord(this.state, rec, offset + 1);
94451
94577
  }
@@ -94474,7 +94600,7 @@ class TranscriptFollower {
94474
94600
  }
94475
94601
  for (const { line } of tail.read()) {
94476
94602
  any = true;
94477
- const rec = parse8(line);
94603
+ const rec = parseRecord3(line);
94478
94604
  if (rec)
94479
94605
  applySubagentRecord(this.state, rec, this.main.offset + 1);
94480
94606
  }
@@ -94536,7 +94662,7 @@ function lastHumanPromptOffset(path) {
94536
94662
  const tail = new Tail(path);
94537
94663
  let found = null;
94538
94664
  for (const { offset, line } of tail.read()) {
94539
- const rec = parse8(line);
94665
+ const rec = parseRecord3(line);
94540
94666
  if (rec && isHumanPrompt(rec))
94541
94667
  found = offset;
94542
94668
  }
@@ -94549,7 +94675,7 @@ function fileSize(p) {
94549
94675
  return 0;
94550
94676
  }
94551
94677
  }
94552
- function parse8(line) {
94678
+ function parseRecord3(line) {
94553
94679
  if (!line.trim())
94554
94680
  return null;
94555
94681
  try {
@@ -94686,6 +94812,7 @@ class PaneSessionImpl {
94686
94812
  reaping = false;
94687
94813
  fastReap = false;
94688
94814
  reapPromise = null;
94815
+ verifiedReap = Promise.withResolvers();
94689
94816
  reconnecting = false;
94690
94817
  depth = 0;
94691
94818
  stepFrom = null;
@@ -96932,6 +97059,9 @@ ${screenTailOf(this.screen)}`
96932
97059
  reaped() {
96933
97060
  return this.reapPromise ?? this.terminal.then(() => this.reapPromise ?? Promise.resolve());
96934
97061
  }
97062
+ reapVerified() {
97063
+ return this.verifiedReap.promise;
97064
+ }
96935
97065
  panePid() {
96936
97066
  return this.panePidValue;
96937
97067
  }
@@ -97058,6 +97188,7 @@ ${screenTailOf(this.screen)}`
97058
97188
  if (groupGone && magGone) {
97059
97189
  removePaneFiles(this.root, this.files());
97060
97190
  this.reapFailed = false;
97191
+ this.verifiedReap.resolve();
97061
97192
  await this.endWatcher();
97062
97193
  unregisterPane(this.paneId);
97063
97194
  return;
@@ -97351,6 +97482,759 @@ var init_pane_session = __esm(() => {
97351
97482
  };
97352
97483
  });
97353
97484
 
97485
+ // src/pane/transcript-lines.ts
97486
+ function sanitiseText(s) {
97487
+ if (!s)
97488
+ return "";
97489
+ return s.replace(OSC_RE, "").replace(STRING_SEQ_RE, "").replace(CSI_RE, "").replace(CSI8_RE, "").replace(ESC2_RE, "").replace(/\r\n/g, `
97490
+ `).replace(CONTROL_RE, "").replace(LONE_SURROGATE_RE, "\uFFFD").trimEnd();
97491
+ }
97492
+ function jsonUnitBytes(s, i) {
97493
+ const c = s.charCodeAt(i);
97494
+ if (c === 34 || c === 92)
97495
+ return 2;
97496
+ if (c < 32)
97497
+ return c === 8 || c === 9 || c === 10 || c === 12 || c === 13 ? 2 : 6;
97498
+ if (c < 128)
97499
+ return 1;
97500
+ if (c < 2048)
97501
+ return 2;
97502
+ if (c >= 55296 && c <= 56319) {
97503
+ const n = s.charCodeAt(i + 1);
97504
+ return n >= 56320 && n <= 57343 ? 2 : 6;
97505
+ }
97506
+ if (c >= 56320 && c <= 57343) {
97507
+ const p = s.charCodeAt(i - 1);
97508
+ return i > 0 && p >= 55296 && p <= 56319 ? 2 : 6;
97509
+ }
97510
+ return 3;
97511
+ }
97512
+ function isHigh(c) {
97513
+ return c >= 55296 && c <= 56319;
97514
+ }
97515
+ function isLow(c) {
97516
+ return c >= 56320 && c <= 57343;
97517
+ }
97518
+ function nextCut(s, pos, maxChars, maxJsonBytes) {
97519
+ const contentBytes = Math.max(1, maxJsonBytes - 2);
97520
+ let end = pos;
97521
+ let bytes = 0;
97522
+ const limit = Math.min(s.length, pos + Math.max(1, maxChars));
97523
+ while (end < limit) {
97524
+ const b = jsonUnitBytes(s, end);
97525
+ if (bytes + b > contentBytes)
97526
+ break;
97527
+ bytes += b;
97528
+ end++;
97529
+ }
97530
+ if (end === s.length)
97531
+ return end;
97532
+ if (end > pos && isHigh(s.charCodeAt(end - 1)) && isLow(s.charCodeAt(end)))
97533
+ end--;
97534
+ if (end <= pos)
97535
+ return Math.min(s.length, pos + (isHigh(s.charCodeAt(pos)) ? 2 : 1));
97536
+ let space = -1;
97537
+ for (let i = end - 1;i >= pos; i--) {
97538
+ const c = s.charCodeAt(i);
97539
+ if (c === 10)
97540
+ return i + 1;
97541
+ if (space === -1 && (c === 32 || c === 9))
97542
+ space = i;
97543
+ }
97544
+ return space !== -1 ? space + 1 : end;
97545
+ }
97546
+ function str4(v) {
97547
+ return typeof v === "string" && v.trim() !== "" ? v : undefined;
97548
+ }
97549
+ function firstStringInput(input) {
97550
+ for (const v of Object.values(input)) {
97551
+ const s = str4(v);
97552
+ if (s !== undefined)
97553
+ return s;
97554
+ }
97555
+ return;
97556
+ }
97557
+ function collapseWhitespace(s) {
97558
+ return s.replace(/\s+/g, " ").trim();
97559
+ }
97560
+ function ellipsisCut(s, max) {
97561
+ if (s.length <= max)
97562
+ return s.length;
97563
+ let end = max - 1;
97564
+ if (end > 0 && isHigh(s.charCodeAt(end - 1)))
97565
+ end--;
97566
+ return end;
97567
+ }
97568
+ function toolCall(name, input) {
97569
+ const n = collapseWhitespace(sanitiseText(typeof name === "string" ? name : "")) || "tool";
97570
+ const rec = input && typeof input === "object" && !Array.isArray(input) ? input : {};
97571
+ const raw = TOOL_ARG[n]?.(rec) ?? firstStringInput(rec);
97572
+ const arg = raw === undefined ? "" : sanitiseText(raw);
97573
+ return arg ? { kind: "tool", text: n, arg } : { kind: "tool", text: n };
97574
+ }
97575
+ function line(kind, raw) {
97576
+ const text = sanitiseText(raw);
97577
+ return text ? [{ kind, text }] : [];
97578
+ }
97579
+ function userContentText(content) {
97580
+ if (typeof content === "string")
97581
+ return content;
97582
+ if (!Array.isArray(content))
97583
+ return "";
97584
+ const parts = [];
97585
+ for (const b of content) {
97586
+ if (b?.type === "text" && typeof b.text === "string")
97587
+ parts.push(b.text);
97588
+ else if (b?.type === "image")
97589
+ parts.push("[image]");
97590
+ else if (b?.type === "document")
97591
+ parts.push("[document]");
97592
+ }
97593
+ return parts.join(`
97594
+ `);
97595
+ }
97596
+ function isTaskNotificationText(text, origin) {
97597
+ return origin?.kind === "task-notification" || text.includes("<task-notification>");
97598
+ }
97599
+ function promptTextLines(text, origin, fallback) {
97600
+ if (text.startsWith(INTERRUPT_PREFIX))
97601
+ return line("system", text);
97602
+ if (isTaskNotificationText(text, origin)) {
97603
+ const m = SUMMARY_RE.exec(text);
97604
+ return line("system", m ? m[1] ?? "" : text);
97605
+ }
97606
+ if (isLocalCommandText(text) || COMMAND_TAG_RE.test(text)) {
97607
+ if (CAVEAT_RE.test(text))
97608
+ return [];
97609
+ const name = COMMAND_NAME_RE.exec(text);
97610
+ if (name) {
97611
+ let cmd = (name[1] ?? "").trim();
97612
+ if (cmd && !cmd.startsWith("/"))
97613
+ cmd = `/${cmd}`;
97614
+ const args = (COMMAND_ARGS_RE.exec(text)?.[1] ?? "").trim();
97615
+ return line("user", args ? `${cmd} ${args}` : cmd);
97616
+ }
97617
+ const out = STDOUT_RE.exec(text);
97618
+ return out ? line("system", out[2] ?? "") : [];
97619
+ }
97620
+ return fallback ? line(fallback, text) : [];
97621
+ }
97622
+ function assistantLines(r) {
97623
+ const content = r.message?.content;
97624
+ const blocks = typeof content === "string" ? [{ type: "text", text: content }] : Array.isArray(content) ? content : [];
97625
+ if (r.isApiErrorMessage === true) {
97626
+ const text = blocks.filter((b) => b?.type === "text" && typeof b.text === "string").map((b) => b.text).join(`
97627
+ `);
97628
+ return line("error", text);
97629
+ }
97630
+ const out = [];
97631
+ for (const b of blocks) {
97632
+ if (b?.type === "text" && typeof b.text === "string") {
97633
+ if (!b.text.trim())
97634
+ continue;
97635
+ out.push(...line(parseInBandApiError(b.text) ? "error" : "assistant", b.text));
97636
+ } else if (b?.type === "tool_use" || b?.type === "server_tool_use") {
97637
+ out.push(toolCall(b.name, b.input));
97638
+ }
97639
+ }
97640
+ return out;
97641
+ }
97642
+ function userLines(r) {
97643
+ if (r.isCompactSummary === true || r.isMeta === true)
97644
+ return [];
97645
+ const content = r.message?.content;
97646
+ if (Array.isArray(content) && content.some((b) => b?.type === "tool_result")) {
97647
+ const file = r.toolUseResult?.file;
97648
+ const isTaskFile = typeof file?.filePath === "string" && TASK_FILE_RE.test(file.filePath) && typeof file.content === "string";
97649
+ const out = [];
97650
+ let taskFileShown = false;
97651
+ for (const b of content) {
97652
+ if (b?.type === "tool_result") {
97653
+ if (isTaskFile && !taskFileShown) {
97654
+ taskFileShown = true;
97655
+ out.push(...line("task_file", file.content));
97656
+ }
97657
+ } else if (b?.type === "text" && typeof b.text === "string" && b.text.startsWith(INTERRUPT_PREFIX)) {
97658
+ out.push(...line("system", b.text));
97659
+ }
97660
+ }
97661
+ return out;
97662
+ }
97663
+ return promptTextLines(userContentText(content), r.origin, "user");
97664
+ }
97665
+ function systemLines(r) {
97666
+ const subtype = typeof r.subtype === "string" ? r.subtype : "";
97667
+ const content = typeof r.content === "string" ? r.content : undefined;
97668
+ switch (subtype) {
97669
+ case "compact_boundary":
97670
+ return line("system", content ?? "Conversation compacted");
97671
+ case "local_command":
97672
+ return content === undefined ? [] : promptTextLines(content, null, null);
97673
+ case "scheduled_task_fire":
97674
+ case "agents_killed":
97675
+ case "model_refusal_fallback":
97676
+ return line("system", content ?? SYSTEM_WORDS[subtype] ?? subtype);
97677
+ case "api_error": {
97678
+ const e = r.error;
97679
+ const text = str4(e?.formatted) ?? str4(e?.message) ?? content ?? "API error";
97680
+ return line("error", text);
97681
+ }
97682
+ case "model_refusal_no_fallback":
97683
+ return line("error", content ?? "model refused, no fallback");
97684
+ }
97685
+ if (r.level === "error")
97686
+ return line("error", content ?? subtype.replace(/_/g, " "));
97687
+ return [];
97688
+ }
97689
+ function renderEntry(rec) {
97690
+ if (!rec || typeof rec !== "object" || Array.isArray(rec))
97691
+ return [];
97692
+ const r = rec;
97693
+ if (r.isSidechain === true)
97694
+ return [];
97695
+ switch (r.type) {
97696
+ case "assistant":
97697
+ return assistantLines(r);
97698
+ case "user":
97699
+ return userLines(r);
97700
+ case "attachment": {
97701
+ const a = r.attachment;
97702
+ if (a?.type !== "queued_command" || typeof a.prompt !== "string")
97703
+ return [];
97704
+ return promptTextLines(a.prompt, a.origin ?? r.origin, "user");
97705
+ }
97706
+ case "system":
97707
+ return systemLines(r);
97708
+ case "continued-in": {
97709
+ const id = typeof r.continuedInSessionId === "string" ? r.continuedInSessionId : "";
97710
+ return line("system", `conversation continued in session ${id}`.trimEnd());
97711
+ }
97712
+ default:
97713
+ return [];
97714
+ }
97715
+ }
97716
+ var TOOL_ARG_MAX_CHARS = 160, OSC_RE, STRING_SEQ_RE, CSI_RE, CSI8_RE, ESC2_RE, CONTROL_RE, LONE_SURROGATE_RE, TOOL_ARG, TASK_FILE_RE, COMMAND_NAME_RE, COMMAND_ARGS_RE, CAVEAT_RE, COMMAND_TAG_RE, SUMMARY_RE, SYSTEM_WORDS;
97717
+ var init_transcript_lines = __esm(() => {
97718
+ init_transcript_follower();
97719
+ OSC_RE = /\x1b\][\s\S]*?(?:\x07|\x1b\\)/g;
97720
+ STRING_SEQ_RE = /\x1b[PX^_][\s\S]*?\x1b\\/g;
97721
+ CSI_RE = /\x1b\[[\x30-\x3f]*[\x20-\x2f]*[\x40-\x7e]/g;
97722
+ CSI8_RE = /\x9b[\x30-\x3f]*[\x20-\x2f]*[\x40-\x7e]/g;
97723
+ ESC2_RE = /\x1b[\x20-\x7e]/g;
97724
+ CONTROL_RE = /[\x00-\x08\x0b-\x1f\x7f-\x9f]/g;
97725
+ LONE_SURROGATE_RE = /[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g;
97726
+ TOOL_ARG = {
97727
+ Bash: (i) => str4(i.command),
97728
+ Monitor: (i) => str4(i.description) ?? str4(i.command),
97729
+ Read: (i) => str4(i.file_path),
97730
+ Write: (i) => str4(i.file_path),
97731
+ Edit: (i) => str4(i.file_path),
97732
+ MultiEdit: (i) => str4(i.file_path),
97733
+ NotebookEdit: (i) => str4(i.notebook_path),
97734
+ Glob: (i) => str4(i.pattern),
97735
+ Grep: (i) => str4(i.pattern),
97736
+ Agent: (i) => str4(i.description) ?? str4(i.subagent_type),
97737
+ Task: (i) => str4(i.description) ?? str4(i.subagent_type),
97738
+ Skill: (i) => str4(i.skill),
97739
+ WebFetch: (i) => str4(i.url),
97740
+ WebSearch: (i) => str4(i.query),
97741
+ ToolSearch: (i) => str4(i.query),
97742
+ SendMessage: (i) => str4(i.to) ?? str4(i.recipient),
97743
+ AskUserQuestion: (i) => Array.isArray(i.questions) ? str4(i.questions[0]?.question) : undefined,
97744
+ TaskStop: (i) => str4(i.task_id),
97745
+ TaskCreate: (i) => str4(i.subject),
97746
+ TaskUpdate: (i) => str4(i.taskId),
97747
+ CronCreate: (i) => str4(i.cron),
97748
+ ScheduleWakeup: (i) => str4(i.reason)
97749
+ };
97750
+ TASK_FILE_RE = /\/launch-[A-Za-z0-9]{6}\/turn-\d+\.md$/;
97751
+ COMMAND_NAME_RE = /<command-name>([\s\S]*?)<\/command-name>/;
97752
+ COMMAND_ARGS_RE = /<command-args>([\s\S]*?)<\/command-args>/;
97753
+ CAVEAT_RE = /^\s*<local-command-caveat>/;
97754
+ COMMAND_TAG_RE = /^\s*<command-(?:message|args)>[\s\S]*<command-name>/;
97755
+ SUMMARY_RE = /<summary>([\s\S]*?)<\/summary>/;
97756
+ SYSTEM_WORDS = {
97757
+ scheduled_task_fire: "scheduled task fired",
97758
+ agents_killed: "agents killed",
97759
+ model_refusal_fallback: "model refusal: fell back"
97760
+ };
97761
+ });
97762
+
97763
+ // src/pane/transcript-redaction.ts
97764
+ function isHigh2(c) {
97765
+ return c >= 55296 && c <= 56319;
97766
+ }
97767
+ function isLow2(c) {
97768
+ return c >= 56320 && c <= 57343;
97769
+ }
97770
+ function firstRangeEndingAfter(ranges, pos) {
97771
+ let lo = 0;
97772
+ let hi = ranges.length;
97773
+ while (lo < hi) {
97774
+ const mid = lo + hi >> 1;
97775
+ if (ranges[mid][1] <= pos)
97776
+ lo = mid + 1;
97777
+ else
97778
+ hi = mid;
97779
+ }
97780
+ return lo;
97781
+ }
97782
+ function redactCarrying(t0, find = signingValueRanges) {
97783
+ const ranges = find(t0);
97784
+ const { text: plain, segments } = redactPatternsMapped(t0);
97785
+ if (ranges.length === 0)
97786
+ return { plain, masked: [] };
97787
+ const masked = [];
97788
+ const add = (from, to) => {
97789
+ const splitsStart = from > 0 && isLow2(plain.charCodeAt(from)) && isHigh2(plain.charCodeAt(from - 1));
97790
+ const splitsEnd = to < plain.length && isLow2(plain.charCodeAt(to)) && isHigh2(plain.charCodeAt(to - 1));
97791
+ const s = splitsStart ? from - 1 : from;
97792
+ const e = splitsEnd ? to + 1 : to;
97793
+ const last = masked[masked.length - 1];
97794
+ if (last && s <= last[1])
97795
+ last[1] = Math.max(last[1], e);
97796
+ else
97797
+ masked.push([s, e]);
97798
+ };
97799
+ let p = 0;
97800
+ for (const seg of segments) {
97801
+ if (seg.origin >= 0) {
97802
+ const o = seg.origin;
97803
+ const oEnd = o + seg.length;
97804
+ for (let i = firstRangeEndingAfter(ranges, o);i < ranges.length; i++) {
97805
+ const [s, e] = ranges[i];
97806
+ if (s >= oEnd)
97807
+ break;
97808
+ add(p + Math.max(s, o) - o, p + Math.min(e, oEnd) - o);
97809
+ }
97810
+ }
97811
+ p += seg.length;
97812
+ }
97813
+ return { plain, masked };
97814
+ }
97815
+ function maskedSlice(r, a, b) {
97816
+ let out = "";
97817
+ if (r.masked.length > 0) {
97818
+ let at = a;
97819
+ for (let i = firstRangeEndingAfter(r.masked, a);i < r.masked.length; i++) {
97820
+ const [s, e] = r.masked[i];
97821
+ if (s >= b)
97822
+ break;
97823
+ const from = Math.max(s, a);
97824
+ const to = Math.min(e, b);
97825
+ out += r.plain.slice(at, from) + r.plain.slice(from, to).replace(/\S/g, SIGNING_UNIT_MASK);
97826
+ at = to;
97827
+ }
97828
+ out += r.plain.slice(at, b);
97829
+ } else
97830
+ out = r.plain.slice(a, b);
97831
+ return out.replace(LONE_SURROGATE_RE2, "\uFFFD");
97832
+ }
97833
+ function toolText(r, find) {
97834
+ const name = redactCarrying(r.text, find);
97835
+ const nameMasked = maskedSlice(name, 0, name.plain.length);
97836
+ if (!r.arg)
97837
+ return { plain: `${name.plain}()`, masked: `${nameMasked}()` };
97838
+ const arg = redactCarrying(r.arg, find);
97839
+ const plain = collapseWhitespace(arg.plain);
97840
+ const masked = collapseWhitespace(maskedSlice(arg, 0, arg.plain.length));
97841
+ const end = ellipsisCut(plain, TOOL_ARG_MAX_CHARS);
97842
+ const cut = end < plain.length;
97843
+ const tail = cut ? "\u2026" : "";
97844
+ return {
97845
+ plain: `${name.plain}(${plain.slice(0, end)}${tail})`,
97846
+ masked: `${nameMasked}(${masked.slice(0, end)}${tail})`
97847
+ };
97848
+ }
97849
+ function* finishLine(r, find) {
97850
+ let plain;
97851
+ let slice;
97852
+ if (r.kind === "tool") {
97853
+ const t = toolText(r, find);
97854
+ plain = t.plain;
97855
+ slice = (a, b) => t.masked.slice(a, b);
97856
+ } else {
97857
+ const red = redactCarrying(r.text, find);
97858
+ plain = red.plain;
97859
+ slice = (a, b) => maskedSlice(red, a, b);
97860
+ }
97861
+ let first = true;
97862
+ for (let pos = 0;pos < plain.length; ) {
97863
+ const a = pos;
97864
+ const b = nextCut(plain, pos, TRANSCRIPT_LINE_MAX_CHARS, TRANSCRIPT_LINE_MAX_JSON_BYTES);
97865
+ pos = b;
97866
+ const cont = !first;
97867
+ first = false;
97868
+ yield {
97869
+ kind: r.kind,
97870
+ cont,
97871
+ build: () => cont ? { kind: r.kind, text: slice(a, b), cont: true } : { kind: r.kind, text: slice(a, b) }
97872
+ };
97873
+ }
97874
+ }
97875
+ function* entryLines(raw, find = signingValueRanges) {
97876
+ const rec = parseRecord3(raw);
97877
+ if (!rec || Array.isArray(rec))
97878
+ return;
97879
+ for (const r of renderEntry(rec))
97880
+ yield* finishLine(r, find);
97881
+ }
97882
+ var SIGNING_UNIT_MASK = "*", LONE_SURROGATE_RE2;
97883
+ var init_transcript_redaction = __esm(() => {
97884
+ init_redact();
97885
+ init_contract();
97886
+ init_transcript_follower();
97887
+ init_transcript_lines();
97888
+ LONE_SURROGATE_RE2 = /[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g;
97889
+ });
97890
+
97891
+ // src/pane/transcript-page.ts
97892
+ import {
97893
+ constants as constants4,
97894
+ closeSync as closeSync12,
97895
+ fstatSync as fstatSync5,
97896
+ lstatSync as lstatSync6,
97897
+ openSync as openSync12,
97898
+ readSync as readSync5,
97899
+ readdirSync as readdirSync8,
97900
+ readlinkSync,
97901
+ realpathSync as realpathSync6,
97902
+ statSync as statSync16
97903
+ } from "fs";
97904
+ import { basename as basename4, dirname as dirname20, join as join51, sep } from "path";
97905
+ function isUuid(v) {
97906
+ return typeof v === "string" && UUID_RE.test(v);
97907
+ }
97908
+ function id8Of(uuid) {
97909
+ return uuid.replace(/-/g, "").slice(0, 8).toLowerCase();
97910
+ }
97911
+ function decodeCursor(s) {
97912
+ const m = typeof s === "string" ? CURSOR_RE.exec(s) : null;
97913
+ if (!m)
97914
+ throw new ContractErrorException("invalid_args", "'since' is not a transcript cursor");
97915
+ const offset = Number(m[1]);
97916
+ const k = Number(m[2]);
97917
+ if (!Number.isSafeInteger(offset) || !Number.isSafeInteger(k))
97918
+ throw new ContractErrorException("invalid_args", "'since' is not a transcript cursor");
97919
+ return { offset, k, id8: m[3] };
97920
+ }
97921
+ function encodeCursor(c) {
97922
+ return `${CURSOR_PREFIX}.${c.offset}.${c.k}.${c.id8}`;
97923
+ }
97924
+
97925
+ class LineScanner {
97926
+ fd;
97927
+ size;
97928
+ chunk = Buffer.alloc(0);
97929
+ chunkStart;
97930
+ idx = 0;
97931
+ constructor(fd, size, start) {
97932
+ this.fd = fd;
97933
+ this.size = size;
97934
+ this.chunkStart = start;
97935
+ }
97936
+ get position() {
97937
+ return this.chunkStart + this.idx;
97938
+ }
97939
+ next(collect, maxBytes) {
97940
+ const start = this.position;
97941
+ const parts = [];
97942
+ let len = 0;
97943
+ let keep = collect;
97944
+ for (;; ) {
97945
+ if (this.idx >= this.chunk.length) {
97946
+ const at = this.chunkStart + this.chunk.length;
97947
+ if (at >= this.size)
97948
+ return null;
97949
+ const n = Math.min(READ_CHUNK, this.size - at);
97950
+ const b = Buffer.alloc(n);
97951
+ const got = readSync5(this.fd, b, 0, n, at);
97952
+ if (got <= 0)
97953
+ return null;
97954
+ this.chunk = got < n ? b.subarray(0, got) : b;
97955
+ this.chunkStart = at;
97956
+ this.idx = 0;
97957
+ }
97958
+ const nl = this.chunk.indexOf(10, this.idx);
97959
+ const stop = nl === -1 ? this.chunk.length : nl;
97960
+ if (keep) {
97961
+ const piece = this.chunk.subarray(this.idx, stop);
97962
+ len += piece.length;
97963
+ if (len > maxBytes) {
97964
+ keep = false;
97965
+ parts.length = 0;
97966
+ } else
97967
+ parts.push(piece);
97968
+ }
97969
+ if (nl === -1) {
97970
+ this.idx = this.chunk.length;
97971
+ continue;
97972
+ }
97973
+ this.idx = nl + 1;
97974
+ return { start, end: this.chunkStart + nl, data: keep ? Buffer.concat(parts) : null };
97975
+ }
97976
+ }
97977
+ }
97978
+ function lineCost(l) {
97979
+ return Buffer.byteLength(JSON.stringify(l)) + 1;
97980
+ }
97981
+ function absentPage(from) {
97982
+ return { lines: [], next: from, more: false, at: [] };
97983
+ }
97984
+ function closeQuietly(fd) {
97985
+ try {
97986
+ closeSync12(fd);
97987
+ } catch {}
97988
+ }
97989
+ function readTranscriptPageFd(fd, from, limits) {
97990
+ const lim = { ...DEFAULT_PAGE_LIMITS, ...limits };
97991
+ const st = fstatSync5(fd);
97992
+ if (!st.isFile())
97993
+ return absentPage(from);
97994
+ const size = st.size;
97995
+ if (from.offset > size)
97996
+ throw new ContractErrorException("invalid_args", "'since' is past the end of the transcript");
97997
+ if (from.offset > 0) {
97998
+ const b = Buffer.alloc(1);
97999
+ const got = readSync5(fd, b, 0, 1, from.offset - 1);
98000
+ if (got !== 1 || b[0] !== 10)
98001
+ throw new ContractErrorException("invalid_args", "'since' is not at a line start");
98002
+ }
98003
+ const scanner = new LineScanner(fd, size, from.offset);
98004
+ const lines = [];
98005
+ const at = [];
98006
+ let bytes = lim.envelopeBytes;
98007
+ let next = { ...from };
98008
+ let more = false;
98009
+ for (;; ) {
98010
+ if (scanner.position - from.offset >= lim.scanBytes) {
98011
+ if (scanner.next(false, 0))
98012
+ more = true;
98013
+ break;
98014
+ }
98015
+ const l = scanner.next(true, lim.maxEntryBytes);
98016
+ if (!l)
98017
+ break;
98018
+ const startK = l.start === from.offset ? from.k : 0;
98019
+ if (l.data) {
98020
+ let j = 0;
98021
+ for (const pending of entryLines(l.data.toString("utf-8"))) {
98022
+ if (j < startK) {
98023
+ j++;
98024
+ continue;
98025
+ }
98026
+ const stop = { lines, next: { offset: l.start, k: j, id8: from.id8 }, more: true, at };
98027
+ if (lines.length >= lim.maxLines)
98028
+ return stop;
98029
+ const line = pending.build();
98030
+ const cost = lineCost(line);
98031
+ if (lines.length > 0 && bytes + cost > lim.maxAnswerBytes)
98032
+ return stop;
98033
+ lines.push(line);
98034
+ at.push({ offset: l.start, k: j, id8: from.id8 });
98035
+ bytes += cost;
98036
+ j++;
98037
+ }
98038
+ }
98039
+ next = { offset: l.end + 1, k: 0, id8: from.id8 };
98040
+ }
98041
+ return { lines, next, more, at };
98042
+ }
98043
+ function locateTranscriptByUuid(projectsRoot, uuid) {
98044
+ if (!isUuid(uuid))
98045
+ return null;
98046
+ let dirs;
98047
+ try {
98048
+ dirs = readdirSync8(projectsRoot);
98049
+ } catch {
98050
+ return null;
98051
+ }
98052
+ let hit = null;
98053
+ for (const d of dirs) {
98054
+ const p = join51(projectsRoot, d, `${uuid}.jsonl`);
98055
+ try {
98056
+ if (!statSync16(p).isFile())
98057
+ continue;
98058
+ } catch {
98059
+ continue;
98060
+ }
98061
+ if (hit !== null)
98062
+ return null;
98063
+ hit = p;
98064
+ }
98065
+ return hit;
98066
+ }
98067
+ function writerLiveness(pid) {
98068
+ if (typeof pid !== "number" || !Number.isSafeInteger(pid) || pid <= 0)
98069
+ return "unknown";
98070
+ try {
98071
+ process.kill(pid, 0);
98072
+ return "alive";
98073
+ } catch (e) {
98074
+ const code = e?.code;
98075
+ if (code === "ESRCH")
98076
+ return "dead";
98077
+ if (code === "EPERM")
98078
+ return "alive";
98079
+ return "unknown";
98080
+ }
98081
+ }
98082
+ function fileAgeMs(path, now = Date.now()) {
98083
+ try {
98084
+ return now - statSync16(path).mtimeMs;
98085
+ } catch {
98086
+ return null;
98087
+ }
98088
+ }
98089
+ function transcriptQuiet(path, now, graceMs) {
98090
+ if (path === null)
98091
+ return true;
98092
+ const age = fileAgeMs(path, now);
98093
+ return age === null || age >= graceMs;
98094
+ }
98095
+ function logRejection(path, why) {
98096
+ if (loggedRejections.has(path) || loggedRejections.size >= MAX_LOGGED_REJECTIONS)
98097
+ return;
98098
+ loggedRejections.add(path);
98099
+ log(`[transcript] refused transcript path ${path}: ${why}`);
98100
+ }
98101
+ function isEnoent(e) {
98102
+ const code = e?.code;
98103
+ return code === "ENOENT" || code === "ENOTDIR";
98104
+ }
98105
+ function errText(e) {
98106
+ return e instanceof Error ? e.message : String(e);
98107
+ }
98108
+ function openContainedTranscript(path, uuid, projectsRoot) {
98109
+ if (basename4(path) !== `${uuid}.jsonl`) {
98110
+ logRejection(path, "basename is not the session uuid");
98111
+ return null;
98112
+ }
98113
+ let real;
98114
+ let root;
98115
+ try {
98116
+ real = realpathSync6(path);
98117
+ root = realpathSync6(projectsRoot);
98118
+ } catch (e) {
98119
+ if (!isEnoent(e))
98120
+ logRejection(path, errText(e));
98121
+ return null;
98122
+ }
98123
+ if (!real.startsWith(root + sep)) {
98124
+ logRejection(path, "outside the Claude Code projects directory");
98125
+ return null;
98126
+ }
98127
+ let fd;
98128
+ try {
98129
+ fd = openSync12(real, OPEN_FLAGS);
98130
+ } catch (e) {
98131
+ if (e?.code === "ELOOP")
98132
+ logRejection(path, "the transcript became a symlink");
98133
+ else if (!isEnoent(e))
98134
+ logRejection(path, errText(e));
98135
+ return null;
98136
+ }
98137
+ try {
98138
+ const st = fstatSync5(fd);
98139
+ if (!st.isFile()) {
98140
+ logRejection(path, "not a regular file");
98141
+ closeQuietly(fd);
98142
+ return null;
98143
+ }
98144
+ const parent = dirname20(real);
98145
+ const named = lstatSync6(real);
98146
+ if (realpathSync6(parent) !== parent || !named.isFile() || named.dev !== st.dev || named.ino !== st.ino || !kernelPathMatches(fd, real)) {
98147
+ logRejection(path, "the opened file is not the one under the projects directory");
98148
+ closeQuietly(fd);
98149
+ return null;
98150
+ }
98151
+ return fd;
98152
+ } catch (e) {
98153
+ if (!isEnoent(e))
98154
+ logRejection(path, errText(e));
98155
+ closeQuietly(fd);
98156
+ return null;
98157
+ }
98158
+ }
98159
+ function kernelPathMatches(fd, real) {
98160
+ if (process.platform !== "linux")
98161
+ return true;
98162
+ let kernel;
98163
+ try {
98164
+ kernel = readlinkSync(`/proc/self/fd/${fd}`);
98165
+ } catch {
98166
+ return true;
98167
+ }
98168
+ return kernel === real;
98169
+ }
98170
+ function envelopeBytesOf(address) {
98171
+ return Buffer.byteLength(JSON.stringify({ ...address, cursor: WORST_CASE_CURSOR, lines: [], more: false, final: false }));
98172
+ }
98173
+ function transcriptAnswer(src, since, address, limits) {
98174
+ const uuid = isUuid(src.sessionUuid) ? src.sessionUuid.toLowerCase() : null;
98175
+ const id8 = uuid ? id8Of(uuid) : NO_UUID_ID8;
98176
+ if (since && since.id8 !== id8)
98177
+ throw new ContractErrorException("invalid_args", "the cursor belongs to another transcript");
98178
+ const from = since ?? { offset: 0, k: 0, id8 };
98179
+ const lim = { envelopeBytes: envelopeBytesOf(address), ...limits };
98180
+ const maxBytes = lim.maxAnswerBytes ?? DEFAULT_PAGE_LIMITS.maxAnswerBytes;
98181
+ let page;
98182
+ if (!uuid) {
98183
+ if (from.offset !== 0 || from.k !== 0)
98184
+ throw new ContractErrorException("invalid_args", "the cursor belongs to another transcript");
98185
+ page = absentPage(from);
98186
+ } else {
98187
+ const fd = src.transcriptPath === null ? null : openContainedTranscript(src.transcriptPath, uuid, src.projectsRoot);
98188
+ if (fd === null)
98189
+ page = absentPage(from);
98190
+ else {
98191
+ try {
98192
+ page = readTranscriptPageFd(fd, from, lim);
98193
+ } finally {
98194
+ closeQuietly(fd);
98195
+ }
98196
+ }
98197
+ }
98198
+ const build = () => ({
98199
+ ...address,
98200
+ cursor: encodeCursor(page.next),
98201
+ lines: page.lines,
98202
+ more: page.more,
98203
+ final: src.writerGone && !page.more
98204
+ });
98205
+ let answer = build();
98206
+ while (page.lines.length > 1 && Buffer.byteLength(JSON.stringify(answer)) > maxBytes) {
98207
+ const last = page.at[page.lines.length - 1];
98208
+ page = {
98209
+ lines: page.lines.slice(0, -1),
98210
+ at: page.at.slice(0, -1),
98211
+ next: last,
98212
+ more: true
98213
+ };
98214
+ answer = build();
98215
+ }
98216
+ return answer;
98217
+ }
98218
+ var NO_UUID_ID8 = "00000000", CURSOR_PREFIX = "v1", CURSOR_RE, UUID_RE, WORST_CASE_CURSOR, DEFAULT_PAGE_LIMITS, READ_CHUNK, OPEN_FLAGS, TRANSCRIPT_GRACE_MS = 30000, loggedRejections, MAX_LOGGED_REJECTIONS = 256;
98219
+ var init_transcript_page = __esm(() => {
98220
+ init_logger();
98221
+ init_contract();
98222
+ init_transcript_redaction();
98223
+ CURSOR_RE = /^v1\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.([0-9a-f]{8})$/;
98224
+ UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
98225
+ WORST_CASE_CURSOR = `${CURSOR_PREFIX}.${"9".repeat(16)}.${"9".repeat(16)}.${"f".repeat(8)}`;
98226
+ DEFAULT_PAGE_LIMITS = {
98227
+ maxLines: MAX_TRANSCRIPT_LINES,
98228
+ maxAnswerBytes: TRANSCRIPT_ANSWER_MAX_BYTES,
98229
+ envelopeBytes: 0,
98230
+ scanBytes: 16 * 1024 * 1024,
98231
+ maxEntryBytes: 32 * 1024 * 1024
98232
+ };
98233
+ READ_CHUNK = 1024 * 1024;
98234
+ OPEN_FLAGS = constants4.O_RDONLY | (constants4.O_NOFOLLOW ?? 0) | (constants4.O_NONBLOCK ?? 0);
98235
+ loggedRejections = new Set;
98236
+ });
98237
+
97354
98238
  // src/pane/types.ts
97355
98239
  function chainAnswerOf(t) {
97356
98240
  return [...t.chainAnswers, t.answer].filter((a) => a !== "").join(`
@@ -97368,6 +98252,9 @@ var init_pane = __esm(() => {
97368
98252
  init_pane_registry();
97369
98253
  init_pane_session();
97370
98254
  init_scroll_input();
98255
+ init_transcript_lines();
98256
+ init_transcript_redaction();
98257
+ init_transcript_page();
97371
98258
  init_contract();
97372
98259
  });
97373
98260
 
@@ -97388,10 +98275,10 @@ __export(exports_session_discovery, {
97388
98275
  transcriptPathFor: () => transcriptPathFor
97389
98276
  });
97390
98277
  import { execFile as execFile4, execFileSync as execFileSync3 } from "child_process";
97391
- import { closeSync as closeSync12, openSync as openSync12, readSync as readSync5, readdirSync as readdirSync8, realpathSync as realpathSync6, statSync as statSync16 } from "fs";
97392
- import { basename as basename4, join as join51 } from "path";
98278
+ import { closeSync as closeSync13, openSync as openSync13, readSync as readSync6, readdirSync as readdirSync9, realpathSync as realpathSync7, statSync as statSync17 } from "fs";
98279
+ import { basename as basename5, join as join52 } from "path";
97393
98280
  function projectsDir(env = process.env) {
97394
- return join51(claudeConfigDir(env), "projects");
98281
+ return join52(claudeConfigDir(env), "projects");
97395
98282
  }
97396
98283
  function slugForPath(absPath) {
97397
98284
  return projectDirNameFor(absPath);
@@ -97399,9 +98286,9 @@ function slugForPath(absPath) {
97399
98286
  function transcriptPathFor(cwd, sessionUuid, projectsRoot = projectsDir()) {
97400
98287
  let real = cwd;
97401
98288
  try {
97402
- real = realpathSync6(cwd);
98289
+ real = realpathSync7(cwd);
97403
98290
  } catch {}
97404
- return join51(projectsRoot, slugForPath(real), `${sessionUuid}.jsonl`);
98291
+ return join52(projectsRoot, slugForPath(real), `${sessionUuid}.jsonl`);
97405
98292
  }
97406
98293
  function isAgentSession(row) {
97407
98294
  return row.entrypoint !== undefined && row.entrypoint !== "cli";
@@ -97442,28 +98329,28 @@ function getRepoContext(cwd = process.cwd()) {
97442
98329
  }
97443
98330
  function projectDirs() {
97444
98331
  try {
97445
- return readdirSync8(projectsDir(), { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
98332
+ return readdirSync9(projectsDir(), { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
97446
98333
  } catch {
97447
98334
  return [];
97448
98335
  }
97449
98336
  }
97450
98337
  function sessionsIn2(dirName) {
97451
- const dir = join51(projectsDir(), dirName);
98338
+ const dir = join52(projectsDir(), dirName);
97452
98339
  let names;
97453
98340
  try {
97454
- names = readdirSync8(dir).filter((n) => n.endsWith(".jsonl"));
98341
+ names = readdirSync9(dir).filter((n) => n.endsWith(".jsonl"));
97455
98342
  } catch {
97456
98343
  return [];
97457
98344
  }
97458
98345
  const rows = [];
97459
98346
  for (const n of names) {
97460
- const file = join51(dir, n);
98347
+ const file = join52(dir, n);
97461
98348
  try {
97462
- const st = statSync16(file);
98349
+ const st = statSync17(file);
97463
98350
  if (st.size === 0)
97464
98351
  continue;
97465
98352
  const row = {
97466
- id: basename4(n, ".jsonl"),
98353
+ id: basename5(n, ".jsonl"),
97467
98354
  file,
97468
98355
  mtimeMs: st.mtimeMs,
97469
98356
  birthtimeMs: st.birthtimeMs,
@@ -97565,7 +98452,7 @@ function discoverWorktreeGroups(repo) {
97565
98452
  if (hit) {
97566
98453
  path = hit.path;
97567
98454
  live = true;
97568
- name = hit.path === repo.root ? "(root)" : basename4(hit.path);
98455
+ name = hit.path === repo.root ? "(root)" : basename5(hit.path);
97569
98456
  } else if (at !== -1 && dir.startsWith(rootSlug)) {
97570
98457
  path = null;
97571
98458
  live = false;
@@ -97620,7 +98507,7 @@ function discoverWorktreeGroups(repo) {
97620
98507
  g.activeNow = g.sessions.some((s) => isActive(s));
97621
98508
  if (g.path) {
97622
98509
  try {
97623
- g.createdMs = statSync16(g.path).birthtimeMs;
98510
+ g.createdMs = statSync17(g.path).birthtimeMs;
97624
98511
  } catch {}
97625
98512
  }
97626
98513
  if (!g.createdMs && g.sessions.length > 0) {
@@ -97638,16 +98525,16 @@ function readChunk(file, pos, len) {
97638
98525
  return "";
97639
98526
  let fd = null;
97640
98527
  try {
97641
- fd = openSync12(file, "r");
98528
+ fd = openSync13(file, "r");
97642
98529
  const buf = Buffer.allocUnsafe(len);
97643
- const n = readSync5(fd, buf, 0, len, pos);
98530
+ const n = readSync6(fd, buf, 0, len, pos);
97644
98531
  return buf.subarray(0, n).toString("utf-8");
97645
98532
  } catch {
97646
98533
  return "";
97647
98534
  } finally {
97648
98535
  if (fd !== null) {
97649
98536
  try {
97650
- closeSync12(fd);
98537
+ closeSync13(fd);
97651
98538
  } catch {}
97652
98539
  }
97653
98540
  }
@@ -97708,7 +98595,7 @@ function hydrateSession(row) {
97708
98595
  row.hydrated = true;
97709
98596
  if (isActive(row)) {
97710
98597
  try {
97711
- row.sizeBytes = statSync16(row.file).size;
98598
+ row.sizeBytes = statSync17(row.file).size;
97712
98599
  } catch {}
97713
98600
  }
97714
98601
  const head = parseRecords(readChunk(row.file, 0, Math.min(HEAD_BYTES, row.sizeBytes)), false);
@@ -97836,16 +98723,16 @@ var init_session_discovery = __esm(() => {
97836
98723
  import { randomBytes as randomBytes13, randomUUID as randomUUID8 } from "crypto";
97837
98724
  import {
97838
98725
  existsSync as existsSync36,
97839
- lstatSync as lstatSync6,
98726
+ lstatSync as lstatSync7,
97840
98727
  mkdirSync as mkdirSync24,
97841
98728
  readFileSync as readFileSync42,
97842
- readdirSync as readdirSync9,
97843
- realpathSync as realpathSync7,
98729
+ readdirSync as readdirSync10,
98730
+ realpathSync as realpathSync8,
97844
98731
  rmSync as rmSync12,
97845
- statSync as statSync17,
98732
+ statSync as statSync18,
97846
98733
  writeFileSync as writeFileSync22
97847
98734
  } from "fs";
97848
- import { basename as basename5, dirname as dirname20, join as join52, resolve as resolve5, sep } from "path";
98735
+ import { basename as basename6, dirname as dirname21, join as join53, resolve as resolve5, sep as sep2 } from "path";
97849
98736
  function slotTerminal(e) {
97850
98737
  if (!e.session)
97851
98738
  return true;
@@ -97914,7 +98801,7 @@ function notableAnomalies(snap) {
97914
98801
  return snap.anomalies.filter((a) => a.startsWith("background_shell_open") || a.startsWith("turn_end_record_missing") || a.startsWith("agent_count_mismatch") || a.startsWith("prompt_not_read"));
97915
98802
  }
97916
98803
  function upstreamLogPath(path, id) {
97917
- return join52(path, "errors", `${id}-upstream.jsonl`);
98804
+ return join53(path, "errors", `${id}-upstream.jsonl`);
97918
98805
  }
97919
98806
  function neverSpawnedRow(e, m) {
97920
98807
  return {
@@ -98011,9 +98898,9 @@ function teamRunRowFromDisk(path, status) {
98011
98898
  const settled = slots.length > 0 && slots.every((s) => isTerminalState(s.state));
98012
98899
  const completions = Object.values(status.models ?? {}).map((m) => m?.completedAt ? Date.parse(m.completedAt) : Number.NaN).filter(Number.isFinite);
98013
98900
  return {
98014
- run_id: status.runId ?? basename5(path),
98901
+ run_id: status.runId ?? basename6(path),
98015
98902
  path,
98016
- kind: status.kind ?? (basename5(path) === "judging" ? "judge" : "run"),
98903
+ kind: status.kind ?? (basename6(path) === "judging" ? "judge" : "run"),
98017
98904
  started_at: status.startedAt,
98018
98905
  finished_at: settled ? new Date(completions.length ? Math.max(...completions) : statusMtimeMs(path) ?? Date.parse(status.startedAt)).toISOString() : null,
98019
98906
  state: settled ? "SETTLED" : "ACTIVE",
@@ -98023,14 +98910,14 @@ function teamRunRowFromDisk(path, status) {
98023
98910
  }
98024
98911
  function statusMtimeMs(path) {
98025
98912
  try {
98026
- return statSync17(join52(path, "status.json")).mtimeMs;
98913
+ return statSync18(join53(path, "status.json")).mtimeMs;
98027
98914
  } catch {
98028
98915
  return null;
98029
98916
  }
98030
98917
  }
98031
98918
  function readManifestOrNull(path) {
98032
98919
  try {
98033
- return JSON.parse(readFileSync42(join52(path, "manifest.json"), "utf-8"));
98920
+ return JSON.parse(readFileSync42(join53(path, "manifest.json"), "utf-8"));
98034
98921
  } catch {
98035
98922
  return null;
98036
98923
  }
@@ -98110,6 +98997,54 @@ async function scrollTeamSlot(path, slot, req, runId) {
98110
98997
  const r = await e.session.scroll(req);
98111
98998
  return { run_id: run.runId, path: run.path, slot, seq: r.seq };
98112
98999
  }
99000
+ function transcriptTeamSlot(path, slot, since, runId, opts = {}) {
99001
+ const graceMs = opts.graceMs ?? TRANSCRIPT_GRACE_MS;
99002
+ const run = addressRun(path, runId);
99003
+ if (run) {
99004
+ const e = run.slots.get(slot);
99005
+ if (!e)
99006
+ throw new ContractErrorException("unknown_slot", `run ${run.runId} has no slot ${JSON.stringify(slot)}`);
99007
+ const m = run.status.models[e.id];
99008
+ const writerGone = e.session ? isTerminalState(e.session.snapshot().state) && e.reapVerified === true : m?.state === undefined || isTerminalState(m.state);
99009
+ const src = {
99010
+ transcriptPath: m?.transcriptPath || null,
99011
+ sessionUuid: m?.sessionUuid || null,
99012
+ writerGone,
99013
+ projectsRoot: run.projectsRoot
99014
+ };
99015
+ return transcriptAnswer(src, since, { run_id: run.runId, path: run.path, slot });
99016
+ }
99017
+ if (path === undefined)
99018
+ throw unknownRun(path, runId);
99019
+ const resolved = resolve5(path);
99020
+ let status;
99021
+ try {
99022
+ status = getStatus(resolved);
99023
+ } catch {
99024
+ throw unknownRun(path, runId);
99025
+ }
99026
+ if (!status || typeof status !== "object" || !status.models || typeof status.models !== "object")
99027
+ throw unknownRun(path, runId);
99028
+ const fileRunId = typeof status.runId === "string" && status.runId ? status.runId : basename6(resolved);
99029
+ if (runId !== undefined && runId !== fileRunId)
99030
+ throw unknownRun(path, runId);
99031
+ const m = Object.hasOwn(status.models, slot) ? status.models[slot] : undefined;
99032
+ if (!m || typeof m !== "object")
99033
+ throw new ContractErrorException("unknown_slot", `run ${fileRunId} has no slot ${JSON.stringify(slot)}`);
99034
+ const now = Date.now();
99035
+ const endedMs = typeof m.completedAt === "string" ? Date.parse(m.completedAt) : Number.NaN;
99036
+ const mtime = statusMtimeMs(resolved);
99037
+ const age = Number.isFinite(endedMs) ? now - endedMs : mtime === null ? null : now - mtime;
99038
+ const terminal = typeof m.state === "string" && SLOT_STATES.includes(m.state) && isTerminalState(m.state);
99039
+ const transcriptPath = typeof m.transcriptPath === "string" && m.transcriptPath ? m.transcriptPath : null;
99040
+ const src = {
99041
+ transcriptPath,
99042
+ sessionUuid: typeof m.sessionUuid === "string" && m.sessionUuid ? m.sessionUuid : null,
99043
+ writerGone: terminal && age !== null && age >= graceMs && transcriptQuiet(transcriptPath, now, graceMs),
99044
+ projectsRoot: projectsDir(opts.parentEnv ?? process.env)
99045
+ };
99046
+ return transcriptAnswer(src, since, { run_id: fileRunId, path: resolved, slot });
99047
+ }
98113
99048
  function teamLiveMaps(path, runId) {
98114
99049
  const run = addressRun(path, runId);
98115
99050
  if (!run)
@@ -98242,20 +99177,20 @@ function answerFailureDetail(v, answer, requirePattern, outcomes) {
98242
99177
  function readWrittenFile(path, root) {
98243
99178
  let realRoot;
98244
99179
  try {
98245
- realRoot = realpathSync7(root);
99180
+ realRoot = realpathSync8(root);
98246
99181
  } catch {
98247
99182
  realRoot = resolve5(root);
98248
99183
  }
98249
99184
  let real;
98250
99185
  try {
98251
- real = realpathSync7(resolve5(root, path));
99186
+ real = realpathSync8(resolve5(root, path));
98252
99187
  } catch {
98253
99188
  return { skip: "missing" };
98254
99189
  }
98255
- if (real !== realRoot && !real.startsWith(realRoot + sep))
99190
+ if (real !== realRoot && !real.startsWith(realRoot + sep2))
98256
99191
  return { skip: "outside the working directory" };
98257
99192
  try {
98258
- const st = statSync17(real);
99193
+ const st = statSync18(real);
98259
99194
  if (!st.isFile())
98260
99195
  return { skip: "not a regular file" };
98261
99196
  if (st.size > MAX_FILE_ANSWER_BYTES)
@@ -98308,24 +99243,24 @@ function validateSessionPath(sessionPath) {
98308
99243
  const tail = [];
98309
99244
  for (;; ) {
98310
99245
  try {
98311
- lstatSync6(existing);
99246
+ lstatSync7(existing);
98312
99247
  break;
98313
99248
  } catch {
98314
- const parent = dirname20(existing);
99249
+ const parent = dirname21(existing);
98315
99250
  if (parent === existing)
98316
99251
  break;
98317
- tail.unshift(basename5(existing));
99252
+ tail.unshift(basename6(existing));
98318
99253
  existing = parent;
98319
99254
  }
98320
99255
  }
98321
99256
  let real;
98322
99257
  try {
98323
- real = join52(realpathSync7(existing), ...tail);
99258
+ real = join53(realpathSync8(existing), ...tail);
98324
99259
  } catch {
98325
99260
  throw outside();
98326
99261
  }
98327
- const cwd = realpathSync7(process.cwd());
98328
- if (real !== cwd && !real.startsWith(cwd + sep)) {
99262
+ const cwd = realpathSync8(process.cwd());
99263
+ if (real !== cwd && !real.startsWith(cwd + sep2)) {
98329
99264
  throw outside();
98330
99265
  }
98331
99266
  return resolved;
@@ -98349,14 +99284,14 @@ function setupSession(sessionPath, models, input) {
98349
99284
  if (models.length === 0) {
98350
99285
  throw new Error("At least one model is required");
98351
99286
  }
98352
- if (existsSync36(join52(sessionPath, "manifest.json"))) {
99287
+ if (existsSync36(join53(sessionPath, "manifest.json"))) {
98353
99288
  throw new Error(`Session already exists at ${sessionPath}. Use a new directory path or delete the existing session first.`);
98354
99289
  }
98355
- mkdirSync24(join52(sessionPath, "work"), { recursive: true });
98356
- mkdirSync24(join52(sessionPath, "errors"), { recursive: true });
99290
+ mkdirSync24(join53(sessionPath, "work"), { recursive: true });
99291
+ mkdirSync24(join53(sessionPath, "errors"), { recursive: true });
98357
99292
  if (input !== undefined) {
98358
- writeFileSync22(join52(sessionPath, "input.md"), input, "utf-8");
98359
- } else if (!existsSync36(join52(sessionPath, "input.md"))) {
99293
+ writeFileSync22(join53(sessionPath, "input.md"), input, "utf-8");
99294
+ } else if (!existsSync36(join53(sessionPath, "input.md"))) {
98360
99295
  throw new Error(`No input.md found at ${sessionPath} and no input provided`);
98361
99296
  }
98362
99297
  const ids = models.map((_, i) => String(i + 1).padStart(2, "0"));
@@ -98373,9 +99308,9 @@ function setupSession(sessionPath, models, input) {
98373
99308
  model: models[i],
98374
99309
  assignedAt: now
98375
99310
  };
98376
- mkdirSync24(join52(sessionPath, "work", anonId), { recursive: true });
99311
+ mkdirSync24(join53(sessionPath, "work", anonId), { recursive: true });
98377
99312
  }
98378
- writeFileSync22(join52(sessionPath, "manifest.json"), JSON.stringify(manifest, null, 2), "utf-8");
99313
+ writeFileSync22(join53(sessionPath, "manifest.json"), JSON.stringify(manifest, null, 2), "utf-8");
98379
99314
  const status = {
98380
99315
  startedAt: now,
98381
99316
  models: Object.fromEntries(Object.keys(manifest.models).map((id) => [
@@ -98390,7 +99325,7 @@ function setupSession(sessionPath, models, input) {
98390
99325
  }
98391
99326
  ]))
98392
99327
  };
98393
- writeFileAtomic(join52(sessionPath, "status.json"), JSON.stringify(status, null, 2));
99328
+ writeFileAtomic(join53(sessionPath, "status.json"), JSON.stringify(status, null, 2));
98394
99329
  return manifest;
98395
99330
  }
98396
99331
  function assertValidRequirePattern(pattern) {
@@ -98425,7 +99360,7 @@ async function preflightTeamRun(opts) {
98425
99360
  throw new Error(`pane_limit: ${live} live panes for this user, ${opts.slots} more would exceed ${MAX_LIVE_PANES}`);
98426
99361
  }
98427
99362
  function mintRunId(path, startMs) {
98428
- return `${basename5(path)}-${startMs.toString(36)}-${randomBytes13(3).toString("hex")}`;
99363
+ return `${basename6(path)}-${startMs.toString(36)}-${randomBytes13(3).toString("hex")}`;
98429
99364
  }
98430
99365
  function startModels(sessionPath, opts = {}) {
98431
99366
  const flight = { aborted: false };
@@ -98451,9 +99386,9 @@ async function startModelsIn(flight, sessionPath, opts) {
98451
99386
  throw new Error(`invalid_args: ${flagCheck.message}`);
98452
99387
  const agentRequested = requestedAgentOf(claudeFlags);
98453
99388
  const path = resolve5(sessionPath);
98454
- const manifest = JSON.parse(readFileSync42(join52(path, "manifest.json"), "utf-8"));
98455
- const statusPath = join52(path, "status.json");
98456
- const inputContent = readFileSync42(join52(path, "input.md"), "utf-8");
99389
+ const manifest = JSON.parse(readFileSync42(join53(path, "manifest.json"), "utf-8"));
99390
+ const statusPath = join53(path, "status.json");
99391
+ const inputContent = readFileSync42(join53(path, "input.md"), "utf-8");
98457
99392
  const readAvailable = !flagsRemoveRead(claudeFlags);
98458
99393
  const refusal = deliveryRefusal(inputContent, readAvailable);
98459
99394
  if (refusal)
@@ -98493,7 +99428,7 @@ async function startModelsIn(flight, sessionPath, opts) {
98493
99428
  mkdirSync24(statsDir(path), { recursive: true });
98494
99429
  const entries = new Map;
98495
99430
  let run = null;
98496
- const errorLogPathOf = (id) => join52(path, "errors", `${id}.log`);
99431
+ const errorLogPathOf = (id) => join53(path, "errors", `${id}.log`);
98497
99432
  const upstreamLogOf = (id) => upstreamLogPath(path, id);
98498
99433
  const flagsOf = (e) => {
98499
99434
  const effort = opts.efforts?.[e.model];
@@ -98503,7 +99438,7 @@ async function startModelsIn(flight, sessionPath, opts) {
98503
99438
  function writeResponse(e, text) {
98504
99439
  e.answer = text;
98505
99440
  try {
98506
- writeFileSync22(join52(path, `response-${e.id}.md`), text, "utf-8");
99441
+ writeFileSync22(join53(path, `response-${e.id}.md`), text, "utf-8");
98507
99442
  } catch (err) {
98508
99443
  persistErrorLog(errorLogPathOf(e.id), `response file not written: ${String(err)}`, []);
98509
99444
  }
@@ -98513,7 +99448,7 @@ async function startModelsIn(flight, sessionPath, opts) {
98513
99448
  if (e.answer !== null || !e.session)
98514
99449
  return;
98515
99450
  try {
98516
- writeFileSync22(join52(path, `response-${e.id}.md`), e.session.chainText(), "utf-8");
99451
+ writeFileSync22(join53(path, `response-${e.id}.md`), e.session.chainText(), "utf-8");
98517
99452
  } catch (err) {
98518
99453
  persistErrorLog(errorLogPathOf(e.id), `response file not written: ${String(err)}`, []);
98519
99454
  }
@@ -98710,7 +99645,7 @@ async function startModelsIn(flight, sessionPath, opts) {
98710
99645
  }
98711
99646
  async function startSlot(e, uuid, transcriptPath) {
98712
99647
  let session;
98713
- e.inboxPath = createHookInbox(join52(path, "work"), `${e.id}.hook-inbox.jsonl`);
99648
+ e.inboxPath = createHookInbox(join53(path, "work"), `${e.id}.hook-inbox.jsonl`);
98714
99649
  const binding = bindingFor(e, uuid);
98715
99650
  e.binding = binding;
98716
99651
  try {
@@ -98750,6 +99685,9 @@ async function startModelsIn(flight, sessionPath, opts) {
98750
99685
  return;
98751
99686
  }
98752
99687
  e.session = session;
99688
+ session.reapVerified?.().then(() => {
99689
+ e.reapVerified = true;
99690
+ });
98753
99691
  session.reaped().then(() => {
98754
99692
  if (!binding && e.inboxPath)
98755
99693
  reportOwnerWithoutHooks(e.inboxPath, hookEngine, { model: e.model, sessionId: uuid });
@@ -98889,7 +99827,8 @@ async function startModelsIn(flight, sessionPath, opts) {
98889
99827
  finishedAt: null,
98890
99828
  status: statusCache,
98891
99829
  slots: entries,
98892
- settled
99830
+ settled,
99831
+ projectsRoot: projects
98893
99832
  };
98894
99833
  teamRuns.set(runId, run);
98895
99834
  newestRunByPath.set(path, runId);
@@ -98925,7 +99864,7 @@ async function startModelsIn(flight, sessionPath, opts) {
98925
99864
  done.catch((err) => log(`[team] run failed at ${path}: ${err instanceof Error ? err.message : String(err)}`));
98926
99865
  return {
98927
99866
  runId,
98928
- teamSessionId: basename5(path),
99867
+ teamSessionId: basename6(path),
98929
99868
  sessionPath: path,
98930
99869
  slots: Object.fromEntries(Object.entries(manifest.models).map(([anonId, entry]) => [entry.model, anonId])),
98931
99870
  done
@@ -98939,20 +99878,20 @@ async function judgeResponses(sessionPath, opts = {}) {
98939
99878
  const active = activeRunAt(resolve5(sessionPath));
98940
99879
  if (active)
98941
99880
  throw new Error(`invalid_args: the team run at ${resolve5(sessionPath)} is still ACTIVE (run_id ${active.runId}); judge it once it has settled`);
98942
- const responseFiles = readdirSync9(sessionPath).filter((f) => f.startsWith("response-") && f.endsWith(".md")).sort();
99881
+ const responseFiles = readdirSync10(sessionPath).filter((f) => f.startsWith("response-") && f.endsWith(".md")).sort();
98943
99882
  if (responseFiles.length < 2) {
98944
99883
  throw new Error(`Need at least 2 responses to judge, found ${responseFiles.length}`);
98945
99884
  }
98946
99885
  const responses = {};
98947
99886
  for (const file of responseFiles) {
98948
99887
  const id = file.replace(/^response-/, "").replace(/\.md$/, "");
98949
- responses[id] = readFileSync42(join52(sessionPath, file), "utf-8");
99888
+ responses[id] = readFileSync42(join53(sessionPath, file), "utf-8");
98950
99889
  }
98951
- const input = readFileSync42(join52(sessionPath, "input.md"), "utf-8");
99890
+ const input = readFileSync42(join53(sessionPath, "input.md"), "utf-8");
98952
99891
  const judgePrompt = buildJudgePrompt(input, responses);
98953
- writeFileSync22(join52(sessionPath, "judge-prompt.md"), judgePrompt, "utf-8");
99892
+ writeFileSync22(join53(sessionPath, "judge-prompt.md"), judgePrompt, "utf-8");
98954
99893
  const judgeModels = opts.judges ?? getDefaultJudgeModels(sessionPath);
98955
- const judgePath = join52(sessionPath, "judging");
99894
+ const judgePath = join53(sessionPath, "judging");
98956
99895
  mkdirSync24(judgePath, { recursive: true });
98957
99896
  setupSession(judgePath, judgeModels, judgePrompt);
98958
99897
  await runModels(judgePath, {
@@ -98964,11 +99903,11 @@ async function judgeResponses(sessionPath, opts = {}) {
98964
99903
  });
98965
99904
  const votes = parseJudgeVotes(judgePath, Object.keys(responses));
98966
99905
  const verdict = aggregateVerdict(votes, Object.keys(responses));
98967
- writeFileSync22(join52(sessionPath, "verdict.md"), formatVerdict(verdict, sessionPath), "utf-8");
99906
+ writeFileSync22(join53(sessionPath, "verdict.md"), formatVerdict(verdict, sessionPath), "utf-8");
98968
99907
  return verdict;
98969
99908
  }
98970
99909
  function getStatus(sessionPath) {
98971
- return JSON.parse(readFileSync42(join52(sessionPath, "status.json"), "utf-8"));
99910
+ return JSON.parse(readFileSync42(join53(sessionPath, "status.json"), "utf-8"));
98972
99911
  }
98973
99912
  function readTeamStatus(sessionPath) {
98974
99913
  try {
@@ -99008,7 +99947,7 @@ function fisherYatesShuffle(arr) {
99008
99947
  return arr;
99009
99948
  }
99010
99949
  function getDefaultJudgeModels(sessionPath) {
99011
- const manifest = JSON.parse(readFileSync42(join52(sessionPath, "manifest.json"), "utf-8"));
99950
+ const manifest = JSON.parse(readFileSync42(join53(sessionPath, "manifest.json"), "utf-8"));
99012
99951
  return Object.values(manifest.models).map((e) => e.model);
99013
99952
  }
99014
99953
  function buildJudgePrompt(input, responses) {
@@ -99066,12 +100005,12 @@ function buildJudgePrompt(input, responses) {
99066
100005
  }
99067
100006
  function parseJudgeVotes(judgePath, responseIds) {
99068
100007
  const votes = [];
99069
- const responseFiles = readdirSync9(judgePath).filter((f) => f.startsWith("response-") && f.endsWith(".md")).sort();
100008
+ const responseFiles = readdirSync10(judgePath).filter((f) => f.startsWith("response-") && f.endsWith(".md")).sort();
99070
100009
  for (const file of responseFiles) {
99071
100010
  const judgeId = file.replace(/^response-/, "").replace(/\.md$/, "");
99072
100011
  let content;
99073
100012
  try {
99074
- content = readFileSync42(join52(judgePath, file), "utf-8");
100013
+ content = readFileSync42(join53(judgePath, file), "utf-8");
99075
100014
  } catch (err) {
99076
100015
  log(`[team] judge answer ${file} unreadable: ${err instanceof Error ? err.message : String(err)}`);
99077
100016
  continue;
@@ -99124,7 +100063,7 @@ function aggregateVerdict(votes, responseIds) {
99124
100063
  function formatVerdict(verdict, sessionPath) {
99125
100064
  let manifest = null;
99126
100065
  try {
99127
- manifest = JSON.parse(readFileSync42(join52(sessionPath, "manifest.json"), "utf-8"));
100066
+ manifest = JSON.parse(readFileSync42(join53(sessionPath, "manifest.json"), "utf-8"));
99128
100067
  } catch {}
99129
100068
  let output = `# Team Verdict
99130
100069
 
@@ -99180,8 +100119,8 @@ var init_team_orchestrator = __esm(() => {
99180
100119
  });
99181
100120
 
99182
100121
  // src/mcp-session/inherited-settings.ts
99183
- import { existsSync as existsSync37, readFileSync as readFileSync43, statSync as statSync18 } from "fs";
99184
- import { join as join53, resolve as resolve6 } from "path";
100122
+ import { existsSync as existsSync37, readFileSync as readFileSync43, statSync as statSync19 } from "fs";
100123
+ import { join as join54, resolve as resolve6 } from "path";
99185
100124
  function isPlainObject7(value) {
99186
100125
  return typeof value === "object" && value !== null && !Array.isArray(value);
99187
100126
  }
@@ -99235,7 +100174,7 @@ function readSettingsValue(value) {
99235
100174
  function buildInheritedSettingsLayer(setupFrom, callerFlags) {
99236
100175
  const layers = [];
99237
100176
  for (const name of ["settings.json", "settings.local.json"]) {
99238
- const file = join53(setupFrom, ".claude", name);
100177
+ const file = join54(setupFrom, ".claude", name);
99239
100178
  if (!existsSync37(file))
99240
100179
  continue;
99241
100180
  layers.push(parseLayer(readFileSync43(file, "utf-8"), file));
@@ -99268,7 +100207,7 @@ function resolveSetupFrom(arg, workDir, serverCwd) {
99268
100207
  let setupFrom = serverCwd;
99269
100208
  if (typeof arg === "string") {
99270
100209
  setupFrom = resolve6(arg);
99271
- if (!statSync18(setupFrom, { throwIfNoEntry: false })?.isDirectory()) {
100210
+ if (!statSync19(setupFrom, { throwIfNoEntry: false })?.isDirectory()) {
99272
100211
  throw new Error(`invalid_args: setup_from ${setupFrom} is not an existing directory`);
99273
100212
  }
99274
100213
  } else if (arg !== undefined) {
@@ -99282,19 +100221,19 @@ var init_inherited_settings = () => {};
99282
100221
  import { randomUUID as randomUUID9 } from "crypto";
99283
100222
  import {
99284
100223
  appendFileSync as appendFileSync7,
99285
- closeSync as closeSync13,
100224
+ closeSync as closeSync14,
99286
100225
  createWriteStream,
99287
100226
  existsSync as existsSync38,
99288
100227
  mkdirSync as mkdirSync25,
99289
- openSync as openSync13,
100228
+ openSync as openSync14,
99290
100229
  readFileSync as readFileSync44,
99291
- readSync as readSync6,
100230
+ readSync as readSync7,
99292
100231
  renameSync as renameSync10,
99293
100232
  rmSync as rmSync13,
99294
- statSync as statSync19,
100233
+ statSync as statSync20,
99295
100234
  writeFileSync as writeFileSync23
99296
100235
  } from "fs";
99297
- import { join as join54, resolve as resolve7, sep as sep2 } from "path";
100236
+ import { join as join55, resolve as resolve7, sep as sep3 } from "path";
99298
100237
  function normaliseTimeoutSeconds(raw) {
99299
100238
  const n = typeof raw === "string" && raw.trim() !== "" ? Number(raw) : raw;
99300
100239
  if (typeof n !== "number" || !Number.isFinite(n))
@@ -99398,21 +100337,21 @@ function anomalyKey(a) {
99398
100337
  function readTailText(path, maxBytes) {
99399
100338
  let fd = null;
99400
100339
  try {
99401
- const size = statSync19(path).size;
100340
+ const size = statSync20(path).size;
99402
100341
  if (size === 0)
99403
100342
  return { text: "", truncated: false };
99404
100343
  const start = Math.max(0, size - maxBytes);
99405
100344
  const length = size - start;
99406
100345
  const buf = Buffer.alloc(length);
99407
- fd = openSync13(path, "r");
99408
- readSync6(fd, buf, 0, length, start);
100346
+ fd = openSync14(path, "r");
100347
+ readSync7(fd, buf, 0, length, start);
99409
100348
  return { text: buf.toString("utf-8"), truncated: start > 0 };
99410
100349
  } catch {
99411
100350
  return null;
99412
100351
  } finally {
99413
100352
  if (fd !== null) {
99414
100353
  try {
99415
- closeSync13(fd);
100354
+ closeSync14(fd);
99416
100355
  } catch {}
99417
100356
  }
99418
100357
  }
@@ -99429,7 +100368,7 @@ function readTailLines2(path, maxBytes) {
99429
100368
  }
99430
100369
  function fileSize2(path) {
99431
100370
  try {
99432
- return statSync19(path).size;
100371
+ return statSync20(path).size;
99433
100372
  } catch {
99434
100373
  return 0;
99435
100374
  }
@@ -99464,7 +100403,7 @@ function dropLeadingFragment(tail) {
99464
100403
  return firstBreak === -1 ? tail.text : tail.text.slice(firstBreak + 1);
99465
100404
  }
99466
100405
  function diskAccounting(sessionDir) {
99467
- const stats = readTokenStatsAt(join54(sessionDir, "tokens.json"));
100406
+ const stats = readTokenStatsAt(join55(sessionDir, "tokens.json"));
99468
100407
  return {
99469
100408
  tokensIn: stats?.billed_input_tokens ?? null,
99470
100409
  tokensOut: stats?.output_tokens ?? null,
@@ -99504,6 +100443,7 @@ class SessionManager {
99504
100443
  scrollbackCapacity;
99505
100444
  sessionsDir;
99506
100445
  terminalRetentionMs;
100446
+ transcriptGraceMs;
99507
100447
  parentEnv;
99508
100448
  paneTimings;
99509
100449
  starting = 0;
@@ -99523,6 +100463,7 @@ class SessionManager {
99523
100463
  this.maxSessions = options?.maxSessions ?? DEFAULT_MAX_SESSIONS;
99524
100464
  this.scrollbackCapacity = options?.scrollbackCapacity ?? DEFAULT_SCROLLBACK;
99525
100465
  this.terminalRetentionMs = options?.terminalRetentionMs ?? TERMINAL_RETENTION_MS;
100466
+ this.transcriptGraceMs = options?.transcriptGraceMs ?? TRANSCRIPT_GRACE_MS;
99526
100467
  this.sessionsDir = options?.sessionsDir ?? sessionsDirFrom(process.env);
99527
100468
  this.parentEnv = options?.parentEnv ?? process.env;
99528
100469
  this.paneTimings = options?.paneTimings;
@@ -99547,7 +100488,7 @@ class SessionManager {
99547
100488
  let dir = "";
99548
100489
  for (let attempt = 0;; attempt++) {
99549
100490
  id = `team-${randomUUID9().replace(/-/g, "").slice(0, 8)}`;
99550
- dir = join54(this.sessionsDir, id);
100491
+ dir = join55(this.sessionsDir, id);
99551
100492
  try {
99552
100493
  mkdirSync25(dir);
99553
100494
  break;
@@ -99579,9 +100520,9 @@ class SessionManager {
99579
100520
  if (dir === null)
99580
100521
  throw new Error("not a record id");
99581
100522
  const completedAt = new Date().toISOString();
99582
- const startedAt = this.teamRunStarts.get(record) ?? metaString(readJsonObject(join54(dir, "spawn.json"), META_READ_LIMIT)?.startedAt) ?? completedAt;
100523
+ const startedAt = this.teamRunStarts.get(record) ?? metaString(readJsonObject(join55(dir, "spawn.json"), META_READ_LIMIT)?.startedAt) ?? completedAt;
99583
100524
  const elapsedMs = Date.parse(completedAt) - Date.parse(startedAt);
99584
- writeJsonAtomic(join54(dir, "meta.json"), {
100525
+ writeJsonAtomic(join55(dir, "meta.json"), {
99585
100526
  kind: "team",
99586
100527
  status: outcome.status,
99587
100528
  startedAt,
@@ -99613,7 +100554,7 @@ class SessionManager {
99613
100554
  startedAt,
99614
100555
  ...rest
99615
100556
  };
99616
- writeJsonAtomic(join54(dir, "spawn.json"), body);
100557
+ writeJsonAtomic(join55(dir, "spawn.json"), body);
99617
100558
  }
99618
100559
  async createSession(opts) {
99619
100560
  if (this.activeSessions + this.starting >= this.maxSessions) {
@@ -99653,7 +100594,7 @@ class SessionManager {
99653
100594
  throw err;
99654
100595
  }
99655
100596
  if (inherited.layer) {
99656
- flags = [...inherited.restFlags, "--settings", join54(entry.sessionDir, INHERITED_SETTINGS)];
100597
+ flags = [...inherited.restFlags, "--settings", join55(entry.sessionDir, INHERITED_SETTINGS)];
99657
100598
  }
99658
100599
  this.sessions.set(entry.info.sessionId, entry);
99659
100600
  uncount();
@@ -99678,10 +100619,10 @@ class SessionManager {
99678
100619
  const timeout = normaliseTimeoutSeconds(opts.timeoutSeconds);
99679
100620
  const agentRequested = requestedAgentOf(opts.claudishFlags ?? []);
99680
100621
  const startedAt = new Date().toISOString();
99681
- const sessionDir = opts.sessionDir ?? join54(this.sessionsDir, sessionId);
100622
+ const sessionDir = opts.sessionDir ?? join55(this.sessionsDir, sessionId);
99682
100623
  mkdirSync25(sessionDir, { recursive: true });
99683
100624
  if (opts.prompt)
99684
- writeFileSync23(join54(sessionDir, "prompt.md"), opts.prompt, "utf-8");
100625
+ writeFileSync23(join55(sessionDir, "prompt.md"), opts.prompt, "utf-8");
99685
100626
  this.writeSpawnRecord(sessionDir, {
99686
100627
  kind: "session",
99687
100628
  sessionId,
@@ -99694,13 +100635,13 @@ class SessionManager {
99694
100635
  ...agentRequested ? { agentRequested } : {}
99695
100636
  });
99696
100637
  if (inheritedLayer) {
99697
- writeFileSync23(join54(sessionDir, INHERITED_SETTINGS), `${JSON.stringify(inheritedLayer, null, 2)}
100638
+ writeFileSync23(join55(sessionDir, INHERITED_SETTINGS), `${JSON.stringify(inheritedLayer, null, 2)}
99698
100639
  `, { encoding: "utf-8", mode: 384 });
99699
100640
  }
99700
100641
  const cwd = opts.cwd ?? process.cwd();
99701
100642
  const parentEnv = opts.parentEnv ?? this.parentEnv;
99702
100643
  const spawnModel = opts.spawnModel ?? null;
99703
- const tokenFile = opts.tokenFile ?? join54(sessionDir, "tokens.json");
100644
+ const tokenFile = opts.tokenFile ?? join55(sessionDir, "tokens.json");
99704
100645
  return {
99705
100646
  info: {
99706
100647
  sessionId,
@@ -99738,18 +100679,18 @@ class SessionManager {
99738
100679
  },
99739
100680
  session: null,
99740
100681
  scrollback: new ScrollbackBuffer(this.scrollbackCapacity),
99741
- outputLogStream: createWriteStream(join54(sessionDir, "output.log")),
100682
+ outputLogStream: createWriteStream(join55(sessionDir, "output.log")),
99742
100683
  sessionDir,
99743
100684
  cwd,
99744
100685
  tokenFile,
99745
- eventLogPath: join54(sessionDir, "events.jsonl"),
100686
+ eventLogPath: join55(sessionDir, "events.jsonl"),
99746
100687
  eventLogBytes: 0,
99747
- upstreamErrorLogPath: join54(sessionDir, "upstream-errors.jsonl"),
100688
+ upstreamErrorLogPath: join55(sessionDir, "upstream-errors.jsonl"),
99748
100689
  eventRing: [],
99749
100690
  proseBytes: 0,
99750
100691
  answeredTurns: new Map,
99751
100692
  inboxPath: null,
99752
- waitLogPath: join54(sessionDir, "waits.jsonl"),
100693
+ waitLogPath: join55(sessionDir, "waits.jsonl"),
99753
100694
  waitLogBytes: 0,
99754
100695
  waitSince: null,
99755
100696
  ended: false,
@@ -99757,7 +100698,9 @@ class SessionManager {
99757
100698
  lastLoggedTool: null,
99758
100699
  assistantIdsLogged: 0,
99759
100700
  anomaliesLogged: new Set,
99760
- requestedSize: null
100701
+ requestedSize: null,
100702
+ reapVerified: false,
100703
+ projectsRoot: projectsDir(parentEnv)
99761
100704
  };
99762
100705
  }
99763
100706
  async startPane(entry, opts, p) {
@@ -99812,6 +100755,9 @@ class SessionManager {
99812
100755
  session.resize(asked.cols, asked.rows).catch(() => {
99813
100756
  return;
99814
100757
  });
100758
+ session.reapVerified?.().then(() => {
100759
+ entry.reapVerified = true;
100760
+ });
99815
100761
  session.reaped().then(() => {
99816
100762
  if (!binding && entry.inboxPath)
99817
100763
  reportOwnerWithoutHooks(entry.inboxPath, engine, {
@@ -99939,7 +100885,7 @@ class SessionManager {
99939
100885
  try {
99940
100886
  const text = screenLines.join(`
99941
100887
  `).replace(/\s+$/, "");
99942
- writeFileSync23(join54(entry.sessionDir, "screen.txt"), `${redactSecrets(text)}
100888
+ writeFileSync23(join55(entry.sessionDir, "screen.txt"), `${redactSecrets(text)}
99943
100889
  `, "utf-8");
99944
100890
  } catch {}
99945
100891
  }
@@ -99953,7 +100899,7 @@ class SessionManager {
99953
100899
  entry.waitSince = null;
99954
100900
  }
99955
100901
  try {
99956
- writeJsonAtomic(join54(entry.sessionDir, "meta.json"), toMetaRecord(entry.info, entry.cwd));
100902
+ writeJsonAtomic(join55(entry.sessionDir, "meta.json"), toMetaRecord(entry.info, entry.cwd));
99957
100903
  } catch (err) {
99958
100904
  process.stderr.write(`[claudish] session ${entry.info.sessionId}: could not write meta.json: ` + `${err instanceof Error ? err.message : String(err)}
99959
100905
  `);
@@ -100167,6 +101113,71 @@ class SessionManager {
100167
101113
  return sinceSeq === 0 ? { unchanged: true, seq: 0, final: true } : blankFinalCapture();
100168
101114
  return entry.session.capture(sinceSeq, { spans: spans === true });
100169
101115
  }
101116
+ transcriptSession(sessionId, since) {
101117
+ const address = { session_id: sessionId };
101118
+ const entry = this.sessions.get(sessionId);
101119
+ const src = entry ? this.liveTranscriptSource(entry) : this.diskTranscriptSource(sessionId);
101120
+ if (!src)
101121
+ throw unknownSession(sessionId);
101122
+ return transcriptAnswer(src, since, address);
101123
+ }
101124
+ liveTranscriptSource(entry) {
101125
+ const writerGone = entry.session ? isTerminalState(entry.session.snapshot().state) && entry.reapVerified : isTerminalState(entry.info.state);
101126
+ return {
101127
+ transcriptPath: entry.info.transcriptPath || null,
101128
+ sessionUuid: entry.info.claudeSessionId || null,
101129
+ writerGone,
101130
+ projectsRoot: entry.projectsRoot
101131
+ };
101132
+ }
101133
+ diskTranscriptSource(sessionId) {
101134
+ const sessionDir = this.diskSessionDir(sessionId);
101135
+ if (sessionDir === null)
101136
+ return null;
101137
+ try {
101138
+ if (!statSync20(sessionDir).isDirectory())
101139
+ return null;
101140
+ } catch {
101141
+ return null;
101142
+ }
101143
+ const spawnPath = join55(sessionDir, "spawn.json");
101144
+ const metaPath = join55(sessionDir, "meta.json");
101145
+ const spawnRecord = readJsonObject(spawnPath, META_READ_LIMIT);
101146
+ if (spawnRecord?.kind === "team")
101147
+ return null;
101148
+ const meta = readJsonObject(metaPath, META_READ_LIMIT);
101149
+ if (meta?.kind === "team")
101150
+ return null;
101151
+ const projectsRoot = projectsDir(this.parentEnv);
101152
+ const uuid = metaString(meta?.claudeSessionId) ?? metaString(spawnRecord?.claudeSessionId) ?? null;
101153
+ const now = Date.now();
101154
+ let writerGone;
101155
+ let transcriptPath = null;
101156
+ if (meta !== null) {
101157
+ const completedAt = metaString(meta.completedAt);
101158
+ const endedMs = completedAt ? Date.parse(completedAt) : Number.NaN;
101159
+ const age = Number.isFinite(endedMs) ? now - endedMs : fileAgeMs(metaPath, now);
101160
+ if (uuid) {
101161
+ const stored = metaString(meta.transcriptPath);
101162
+ const cwd = metaString(meta.cwd);
101163
+ if (stored && existsSync38(stored))
101164
+ transcriptPath = stored;
101165
+ else if (cwd)
101166
+ transcriptPath = transcriptPathFor(cwd, uuid, projectsRoot);
101167
+ else
101168
+ transcriptPath = locateTranscriptByUuid(projectsRoot, uuid);
101169
+ }
101170
+ writerGone = rawStateTerminal(meta) && age !== null && age >= this.transcriptGraceMs && transcriptQuiet(transcriptPath, now, this.transcriptGraceMs);
101171
+ } else {
101172
+ const pid = spawnRecord?.mcpPid;
101173
+ const liveness = pid === process.pid ? "dead" : writerLiveness(pid);
101174
+ if (uuid)
101175
+ transcriptPath = locateTranscriptByUuid(projectsRoot, uuid);
101176
+ const quiet = (transcriptPath !== null ? fileAgeMs(transcriptPath, now) : null) ?? fileAgeMs(spawnPath, now);
101177
+ writerGone = liveness === "dead" && quiet !== null && quiet >= this.transcriptGraceMs;
101178
+ }
101179
+ return { transcriptPath, sessionUuid: uuid, writerGone, projectsRoot };
101180
+ }
100170
101181
  listSessions(includeCompleted = false) {
100171
101182
  const sessions = [];
100172
101183
  for (const entry of this.sessions.values()) {
@@ -100224,9 +101235,9 @@ class SessionManager {
100224
101235
  return null;
100225
101236
  const root = resolve7(this.sessionsDir);
100226
101237
  const dir = resolve7(root, sessionId);
100227
- if (dir !== join54(root, sessionId))
101238
+ if (dir !== join55(root, sessionId))
100228
101239
  return null;
100229
- if (!dir.startsWith(root + sep2))
101240
+ if (!dir.startsWith(root + sep3))
100230
101241
  return null;
100231
101242
  return dir;
100232
101243
  }
@@ -100236,17 +101247,17 @@ class SessionManager {
100236
101247
  return null;
100237
101248
  let dirMtimeMs;
100238
101249
  try {
100239
- const stat = statSync19(sessionDir);
101250
+ const stat = statSync20(sessionDir);
100240
101251
  if (!stat.isDirectory())
100241
101252
  return null;
100242
101253
  dirMtimeMs = stat.mtimeMs;
100243
101254
  } catch {
100244
101255
  return null;
100245
101256
  }
100246
- const spawnRecord = readJsonObject(join54(sessionDir, "spawn.json"), META_READ_LIMIT);
101257
+ const spawnRecord = readJsonObject(join55(sessionDir, "spawn.json"), META_READ_LIMIT);
100247
101258
  if (spawnRecord?.kind === "team")
100248
101259
  return null;
100249
- const meta = readJsonObject(join54(sessionDir, "meta.json"), META_READ_LIMIT);
101260
+ const meta = readJsonObject(join55(sessionDir, "meta.json"), META_READ_LIMIT);
100250
101261
  if (meta?.kind === "team")
100251
101262
  return null;
100252
101263
  const partial = meta === null;
@@ -100268,7 +101279,7 @@ class SessionManager {
100268
101279
  return stored;
100269
101280
  }
100270
101281
  diskOutput(record, tailLines) {
100271
- const tail = readTailText(join54(record.sessionDir, "output.log"), OUTPUT_TAIL_BYTES);
101282
+ const tail = readTailText(join55(record.sessionDir, "output.log"), OUTPUT_TAIL_BYTES);
100272
101283
  const buffer = new ScrollbackBuffer(this.scrollbackCapacity);
100273
101284
  if (tail?.text)
100274
101285
  buffer.append(dropLeadingFragment(tail));
@@ -100288,12 +101299,12 @@ class SessionManager {
100288
101299
  }
100289
101300
  diskDiagnostics(record, limit) {
100290
101301
  const { sessionDir, info } = record;
100291
- const eventLogPath = join54(sessionDir, "events.jsonl");
100292
- const upstreamErrorLogPath = join54(sessionDir, "upstream-errors.jsonl");
100293
- const outputLogPath = join54(sessionDir, "output.log");
101302
+ const eventLogPath = join55(sessionDir, "events.jsonl");
101303
+ const upstreamErrorLogPath = join55(sessionDir, "upstream-errors.jsonl");
101304
+ const outputLogPath = join55(sessionDir, "output.log");
100294
101305
  const events = readTailLines2(eventLogPath, EVENT_TAIL_BYTES);
100295
101306
  const outputTail = readTailText(outputLogPath, OUTPUT_TAIL_BYTES);
100296
- const screen = readTailText(join54(sessionDir, "screen.txt"), SCREEN_READ_BYTES);
101307
+ const screen = readTailText(join55(sessionDir, "screen.txt"), SCREEN_READ_BYTES);
100297
101308
  return {
100298
101309
  sessionId: info.sessionId,
100299
101310
  state: info.state,
@@ -100489,6 +101500,13 @@ function diskState(meta) {
100489
101500
  return { state, reason: "timeout" };
100490
101501
  return { state, reason: null };
100491
101502
  }
101503
+ function rawStateTerminal(meta) {
101504
+ const raw = meta.state;
101505
+ if (typeof raw === "string" && SLOT_STATES.includes(raw))
101506
+ return isTerminalState(raw);
101507
+ const status = typeof meta.status === "string" ? STATE_OF_10_4_STATUS[meta.status] : undefined;
101508
+ return status !== undefined && isTerminalState(status);
101509
+ }
100492
101510
  function diskInfo(d) {
100493
101511
  const { meta, spawnRecord, measured } = d;
100494
101512
  const startedAt = metaString(meta?.startedAt) ?? metaString(spawnRecord?.startedAt) ?? new Date(d.dirMtimeMs).toISOString();
@@ -100594,7 +101612,7 @@ var NESTED_MCP_TOOL_NAMES, NESTED_INSTRUCTIONS = `Claudish MCP server provides a
100594
101612
 
100595
101613
  ## Nested mode \u2014 no sessions
100596
101614
 
100597
- This server runs inside a session that a claudish MCP server started (CLAUDISH_PANE_CHILD=1). One level of nesting is the limit, so these tools are not registered: create_session, team, send_input, get_output, cancel_session, list_sessions, get_diagnostics, capture_session, resize_session, scroll_session, preflight, report_error. No channel notifications are sent.
101615
+ This server runs inside a session that a claudish MCP server started (CLAUDISH_PANE_CHILD=1). One level of nesting is the limit, so these tools are not registered: create_session, team, send_input, get_output, cancel_session, list_sessions, get_diagnostics, capture_session, resize_session, scroll_session, transcript_session, preflight, report_error. No channel notifications are sent.
100598
101616
 
100599
101617
  - run_prompt or compare_models: a one-shot answer from another model.
100600
101618
  - list_models, search_models: find a model.
@@ -100692,7 +101710,7 @@ var init_progress_heartbeat = __esm(() => {
100692
101710
  import { spawn as spawn2 } from "child_process";
100693
101711
  import { mkdirSync as mkdirSync26, mkdtempSync as mkdtempSync3, rmSync as rmSync14, writeFileSync as writeFileSync24 } from "fs";
100694
101712
  import { tmpdir as tmpdir2 } from "os";
100695
- import { join as join55 } from "path";
101713
+ import { join as join56 } from "path";
100696
101714
  function claudeCodePrintArgv(model, systemPromptFile) {
100697
101715
  const head = [PRINT_FLAGS[0], "--model", model, ...PRINT_FLAGS.slice(1)];
100698
101716
  return systemPromptFile !== undefined ? [...head, "--system-prompt-file", systemPromptFile] : [...head, "--system-prompt", ""];
@@ -100810,13 +101828,13 @@ async function runPromptViaClaudeCode(model, prompt, systemPrompt, opts = {}) {
100810
101828
  } catch (e) {
100811
101829
  throw new Error(`cannot start claudish (${parentEnv.CLAUDISH_BIN?.trim() || "claudish"}): ${e instanceof Error ? e.message : String(e)}`);
100812
101830
  }
100813
- const root = mkdtempSync3(join55(tmpdir2(), "claudish-run-prompt-"));
101831
+ const root = mkdtempSync3(join56(tmpdir2(), "claudish-run-prompt-"));
100814
101832
  try {
100815
- const cwd = join55(root, "cwd");
101833
+ const cwd = join56(root, "cwd");
100816
101834
  mkdirSync26(cwd, { mode: 448 });
100817
101835
  let systemPromptFile;
100818
101836
  if (typeof systemPrompt === "string" && systemPrompt.length > 0) {
100819
- systemPromptFile = join55(root, "system-prompt.txt");
101837
+ systemPromptFile = join56(root, "system-prompt.txt");
100820
101838
  writeFileSync24(systemPromptFile, systemPrompt, { mode: 384 });
100821
101839
  }
100822
101840
  const argv = claudeCodePrintArgv(model, systemPromptFile);
@@ -101116,9 +102134,9 @@ var init_preflight_chain = __esm(() => {
101116
102134
  });
101117
102135
 
101118
102136
  // src/mcp-server.ts
101119
- import { existsSync as existsSync39, readFileSync as readFileSync45, readdirSync as readdirSync10 } from "fs";
102137
+ import { existsSync as existsSync39, readFileSync as readFileSync45, readdirSync as readdirSync11 } from "fs";
101120
102138
  import { homedir as homedir37 } from "os";
101121
- import { dirname as dirname21, join as join56, resolve as resolve8 } from "path";
102139
+ import { dirname as dirname22, join as join57, resolve as resolve8 } from "path";
101122
102140
  import { fileURLToPath as fileURLToPath3 } from "url";
101123
102141
  async function loadAllModels(forceRefresh = false) {
101124
102142
  if (!forceRefresh && existsSync39(OPENROUTER_MODELS_CACHE_PATH)) {
@@ -101707,6 +102725,20 @@ function scrollArgs(args) {
101707
102725
  throw new ContractErrorException("invalid_args", "'by' must not be 0");
101708
102726
  return { by };
101709
102727
  }
102728
+ function transcriptSince(raw) {
102729
+ const since = givenString(raw, "since");
102730
+ return since === undefined ? undefined : decodeCursor(since);
102731
+ }
102732
+ function givenString(raw, name) {
102733
+ if (raw === undefined)
102734
+ return;
102735
+ if (typeof raw !== "string" || !raw)
102736
+ throw new ContractErrorException("invalid_args", `'${name}' must be a non-empty string`);
102737
+ return raw;
102738
+ }
102739
+ function grouped(n) {
102740
+ return n.toLocaleString("en-US");
102741
+ }
101710
102742
  function teamStatusAnswer(path, runId) {
101711
102743
  let row = null;
101712
102744
  if (runId) {
@@ -101745,13 +102777,14 @@ async function teamContractVerb(mode, args) {
101745
102777
  if (mode === "list")
101746
102778
  return contractAnswer(listTeamRuns());
101747
102779
  const path = contractPath(args.path, mode);
101748
- const runId = optionalString(args.run_id, "run_id");
102780
+ const runId = mode === "transcript" ? givenString(args.run_id, "run_id") : optionalString(args.run_id, "run_id");
101749
102781
  const slot = optionalString(args.slot, "slot");
101750
- const since = optionalInteger(args.since_seq, "since_seq");
102782
+ const sinceSeq = optionalInteger(args.since_seq, "since_seq");
101751
102783
  const spans = optionalBoolean(args.spans, "spans");
101752
102784
  const cols = mode === "resize" ? requiredInteger(args.cols, "cols", mode) : undefined;
101753
102785
  const rows = mode === "resize" ? requiredInteger(args.rows, "rows", mode) : undefined;
101754
102786
  const scroll = mode === "scroll" ? scrollArgs(args) : undefined;
102787
+ const since = mode === "transcript" ? transcriptSince(args.since) : undefined;
101755
102788
  switch (mode) {
101756
102789
  case "status":
101757
102790
  return contractAnswer(teamStatusAnswer(path, runId));
@@ -101760,7 +102793,7 @@ async function teamContractVerb(mode, args) {
101760
102793
  case "capture": {
101761
102794
  if (slot === undefined)
101762
102795
  throw new ContractErrorException("invalid_args", "'slot' is required for mode 'capture'");
101763
- return contractAnswer(captureTeamSlot(path, slot, since, spans, runId));
102796
+ return contractAnswer(captureTeamSlot(path, slot, sinceSeq, spans, runId));
101764
102797
  }
101765
102798
  case "resize": {
101766
102799
  if (slot === undefined)
@@ -101772,6 +102805,11 @@ async function teamContractVerb(mode, args) {
101772
102805
  throw new ContractErrorException("invalid_args", "'slot' is required for mode 'scroll'");
101773
102806
  return contractAnswer(await scrollTeamSlot(path, slot, scroll, runId));
101774
102807
  }
102808
+ case "transcript": {
102809
+ if (slot === undefined)
102810
+ throw new ContractErrorException("invalid_args", "'slot' is required for mode 'transcript'");
102811
+ return compactAnswer(transcriptTeamSlot(path, slot, since, runId));
102812
+ }
101775
102813
  }
101776
102814
  } catch (e) {
101777
102815
  return contractErrorAnswer(e);
@@ -102119,7 +103157,7 @@ This searched OpenRouter's listing only. Subscription wire ids and catalog alias
102119
103157
  });
102120
103158
  tools.push({
102121
103159
  name: "team",
102122
- description: "Run AI models on a task with anonymized outputs and optional blind judging. Every slot is an interactive Claude Code session in its own headless magmux pane. " + "Modes: 'run' (START the models and return once each has taken its prompt \u2014 it does " + "NOT wait for answers), 'status' (per-slot state; read `run.slots`), 'list' (every run this server holds), 'capture' (one slot's screen), 'resize' (set one slot's terminal size), 'scroll' (scroll one slot's transcript view), 'cancel' (stop one slot or the whole run), 'judge'(blind-vote on existing outputs), 'run-and-judge' (the blocking pipeline). Poll 'status' (read run.slots) or 'list'. NO SLOT IS EVER KILLED ON A TIMER once its prompt is accepted. A team slot is a full Claude Code session and may work for a long time; a slot inside a build or test suite emits nothing for minutes and is working, not stuck. Judge the silence against the task you set, and use 'cancel' if you decide a slot is wedged. A slot that stops on a question it cannot answer is FAILED 'blocked' (team has no way to answer it). JUDGING A WORKING SLOT: read live_output_bytes_by_slot, idle_seconds_by_slot and activity_by_slot (or each row's idle_seconds and " + "activity). Do NOT use 'outputSize' \u2014 it is the size of the final answer, written " + "only when a slot finishes, so it is 0 for every working slot no matter how much that slot has produced.",
103160
+ description: "Run AI models on a task with anonymized outputs and optional blind judging. Every slot is an interactive Claude Code session in its own headless magmux pane. " + "Modes: 'run' (START the models and return once each has taken its prompt \u2014 it does " + "NOT wait for answers), 'status' (per-slot state; read `run.slots`), 'list' (every run this server holds), 'capture' (one slot's screen), 'resize' (set one slot's terminal size), 'scroll' (scroll one slot's transcript view), 'transcript' (one slot's conversation as text lines), 'cancel' (stop one slot or the whole run), 'judge'(blind-vote on existing outputs), 'run-and-judge' (the blocking pipeline). Poll 'status' (read run.slots) or 'list'. NO SLOT IS EVER KILLED ON A TIMER once its prompt is accepted. A team slot is a full Claude Code session and may work for a long time; a slot inside a build or test suite emits nothing for minutes and is working, not stuck. Judge the silence against the task you set, and use 'cancel' if you decide a slot is wedged. A slot that stops on a question it cannot answer is FAILED 'blocked' (team has no way to answer it). JUDGING A WORKING SLOT: read live_output_bytes_by_slot, idle_seconds_by_slot and activity_by_slot (or each row's idle_seconds and " + "activity). Do NOT use 'outputSize' \u2014 it is the size of the final answer, written " + "only when a slot finishes, so it is 0 for every working slot no matter how much that slot has produced.",
102123
103161
  inputSchema: {
102124
103162
  type: "object",
102125
103163
  properties: {
@@ -102134,13 +103172,14 @@ This searched OpenRouter's listing only. Subscription wire ids and catalog alias
102134
103172
  "list",
102135
103173
  "capture",
102136
103174
  "resize",
102137
- "scroll"
103175
+ "scroll",
103176
+ "transcript"
102138
103177
  ],
102139
- description: "Operation mode. 'run' STARTS the models and returns once every slot has taken " + "its prompt (or failed to) \u2014 it does not wait for answers. Poll 'status' (read " + "`run.slots`) or 'list' for progress, then 'judge' once the slots have finished. 'capture' returns one slot's screen; 'resize' sets its terminal size; 'scroll' scrolls its transcript view (`by` or `to`). 'run-and-judge' is the blocking pipeline and holds the call open for the whole run. 'cancel' stops one slot or the whole run."
103178
+ description: "Operation mode. 'run' STARTS the models and returns once every slot has taken " + "its prompt (or failed to) \u2014 it does not wait for answers. Poll 'status' (read " + "`run.slots`) or 'list' for progress, then 'judge' once the slots have finished. 'capture' returns one slot's screen; 'resize' sets its terminal size; 'scroll' scrolls its transcript view (`by` or `to`). 'transcript' reads one slot's conversation as text lines (`since` pages; a `cont` chain may continue in the next answer after `cursor`, in order, never reordered or lost). 'run-and-judge' is the blocking pipeline and holds the call open for the whole run. 'cancel' stops one slot or the whole run."
102140
103179
  },
102141
103180
  slot: {
102142
103181
  type: "string",
102143
- description: "For 'cancel', 'capture', 'resize' and 'scroll': the anonymised slot id (e.g. '02') from the slot map 'run' returned. 'capture', 'resize' and 'scroll' require it; for 'cancel', omit it to stop every slot in the run."
103182
+ description: "For 'cancel', 'capture', 'resize', 'scroll' and 'transcript': the anonymised slot id (e.g. '02') from the slot map 'run' returned. 'capture', 'resize', 'scroll' and 'transcript' require it; for 'cancel', omit it to stop every slot in the run."
102144
103183
  },
102145
103184
  path: {
102146
103185
  type: "string",
@@ -102148,11 +103187,15 @@ This searched OpenRouter's listing only. Subscription wire ids and catalog alias
102148
103187
  },
102149
103188
  run_id: {
102150
103189
  type: "string",
102151
- description: "For 'status', 'cancel', 'capture', 'resize' and 'scroll': the run_id a 'run' answer returned. It addresses THAT run while this server retains it, even after a newer run reused the path. Omit it to address the newest run at 'path'."
103190
+ description: "For 'status', 'cancel', 'capture', 'resize', 'scroll' and 'transcript': the run_id a 'run' answer returned. It addresses THAT run while this server retains it, even after a newer run reused the path. Omit it to address the newest run at 'path'."
102152
103191
  },
102153
103192
  since_seq: {
102154
103193
  type: "number",
102155
- description: "For 'capture': the `seq` of the screen you already hold. When it is still current the answer is `{unchanged:true}`; any other value returns the full screen."
103194
+ description: "For 'capture' only: the `seq` of the screen you already hold. When it is still current the answer is `{unchanged:true}`; any other value returns the full screen."
103195
+ },
103196
+ since: {
103197
+ type: "string",
103198
+ description: `For 'transcript' only (capture uses since_seq): the \`cursor\` of a previous answer; returns only the lines after it. Omit to read from the start. Answer {run_id, path, slot, cursor, lines, more, final}, at most ${MAX_TRANSCRIPT_LINES} lines and ${grouped(TRANSCRIPT_ANSWER_MAX_BYTES)} bytes per call; a \`cont\` chain may continue in the next answer after \`cursor\`, in order, never reordered or lost.`
102156
103199
  },
102157
103200
  spans: {
102158
103201
  type: "boolean",
@@ -102231,7 +103274,7 @@ This searched OpenRouter's listing only. Subscription wire ids and catalog alias
102231
103274
  heartbeat: true,
102232
103275
  handler: async (args, ctx) => {
102233
103276
  const mode = args.mode;
102234
- if (mode === "list" || mode === "status" || mode === "cancel" || mode === "capture" || mode === "resize" || mode === "scroll") {
103277
+ if (mode === "list" || mode === "status" || mode === "cancel" || mode === "capture" || mode === "resize" || mode === "scroll" || mode === "transcript") {
102235
103278
  return teamContractVerb(mode, args);
102236
103279
  }
102237
103280
  try {
@@ -102285,7 +103328,7 @@ This searched OpenRouter's listing only. Subscription wire ids and catalog alias
102285
103328
  const promptText = () => {
102286
103329
  if (input !== undefined)
102287
103330
  return input;
102288
- const existing = join56(resolved, "input.md");
103331
+ const existing = join57(resolved, "input.md");
102289
103332
  return existsSync39(existing) ? readFileSync45(existing, "utf-8") : undefined;
102290
103333
  };
102291
103334
  const choice = modelChoiceStop([
@@ -102468,26 +103511,26 @@ This searched OpenRouter's listing only. Subscription wire ids and catalog alias
102468
103511
  const sp = session_path;
102469
103512
  for (const file of ["status.json", "manifest.json", "input.md"]) {
102470
103513
  try {
102471
- sessionData[file] = readFileSync45(join56(sp, file), "utf-8");
103514
+ sessionData[file] = readFileSync45(join57(sp, file), "utf-8");
102472
103515
  } catch {}
102473
103516
  }
102474
103517
  try {
102475
- const errorDir = join56(sp, "errors");
103518
+ const errorDir = join57(sp, "errors");
102476
103519
  if (existsSync39(errorDir)) {
102477
- for (const f of readdirSync10(errorDir)) {
103520
+ for (const f of readdirSync11(errorDir)) {
102478
103521
  if (f.endsWith(".log")) {
102479
103522
  try {
102480
- sessionData[`errors/${f}`] = readFileSync45(join56(errorDir, f), "utf-8");
103523
+ sessionData[`errors/${f}`] = readFileSync45(join57(errorDir, f), "utf-8");
102481
103524
  } catch {}
102482
103525
  }
102483
103526
  }
102484
103527
  }
102485
103528
  } catch {}
102486
103529
  try {
102487
- for (const f of readdirSync10(sp)) {
103530
+ for (const f of readdirSync11(sp)) {
102488
103531
  if (f.startsWith("response-") && f.endsWith(".md")) {
102489
103532
  try {
102490
- const content = readFileSync45(join56(sp, f), "utf-8");
103533
+ const content = readFileSync45(join57(sp, f), "utf-8");
102491
103534
  sessionData[f] = content.slice(0, 200) + (content.length > 200 ? "... (truncated)" : "");
102492
103535
  } catch {}
102493
103536
  }
@@ -102496,7 +103539,7 @@ This searched OpenRouter's listing only. Subscription wire ids and catalog alias
102496
103539
  }
102497
103540
  let version = "unknown";
102498
103541
  try {
102499
- const pkgPath = join56(__dirname3, "../package.json");
103542
+ const pkgPath = join57(__dirname3, "../package.json");
102500
103543
  if (existsSync39(pkgPath)) {
102501
103544
  version = JSON.parse(readFileSync45(pkgPath, "utf-8")).version;
102502
103545
  }
@@ -102964,6 +104007,33 @@ Report manually at https://github.com/anthropics/claudish/issues${autoSendHint}`
102964
104007
  }
102965
104008
  }
102966
104009
  });
104010
+ tools.push({
104011
+ name: "transcript_session",
104012
+ description: `Read a session's conversation as plain text lines, from its Claude Code transcript: prompts (\`user\`), replies (\`assistant\`), one line per tool call (\`tool\`, e.g. \`Bash(git status)\`), the content of a prompt delivered as a file (\`task_file\`), notices (\`system\`) and errors (\`error\`). Works while the session runs and after it finished. Page with \`since\`: pass the previous answer's \`cursor\` to get only newer lines; the cursor can move even when no line is returned. At most ${MAX_TRANSCRIPT_LINES} lines (and ${grouped(TRANSCRIPT_ANSWER_MAX_BYTES)} bytes) per call; \`more: true\` means call again now. A text over ${grouped(TRANSCRIPT_LINE_MAX_CHARS)} characters is split into lines marked \`cont: true\`; a \`cont\` chain may continue in the next answer after \`cursor\`, in order, never reordered or lost. \`final: true\` means the session finished, its process exited and every line has been returned; until then keep polling. Read only: it never sends anything to the session. The answer is {session_id, cursor, lines:[{kind, text, cont?}], more, final}. Errors are a JSON ContractError: \`unknown_session\`, \`invalid_args\` (a malformed cursor, or one from another session).`,
104013
+ inputSchema: {
104014
+ type: "object",
104015
+ properties: {
104016
+ session_id: { type: "string", description: "Session ID from create_session" },
104017
+ since: {
104018
+ type: "string",
104019
+ description: "The `cursor` of a previous answer; returns only the lines after it. Omit to read from the start. Opaque; valid for this session only."
104020
+ }
104021
+ },
104022
+ required: ["session_id"]
104023
+ },
104024
+ group: "session",
104025
+ handler: async (args) => {
104026
+ try {
104027
+ const sessionId = optionalString(args.session_id, "session_id");
104028
+ if (sessionId === undefined)
104029
+ throw new ContractErrorException("invalid_args", "'session_id' is required");
104030
+ const since = transcriptSince(args.since);
104031
+ return compactAnswer(sessionManager.transcriptSession(sessionId, since));
104032
+ } catch (e) {
104033
+ return contractErrorAnswer(e);
104034
+ }
104035
+ }
104036
+ });
102967
104037
  return tools;
102968
104038
  }
102969
104039
  function resolveToolGroups(mode) {
@@ -103107,7 +104177,7 @@ Each session is an interactive Claude Code running the model in its own headless
103107
104177
  3. send_input may be called at any time; it is queued until the session is idle.
103108
104178
  4. COMPLETED: call get_output for the answer.
103109
104179
  5. FAILED or TIMEOUT \u2014 or COMPLETED with an empty or surprising answer \u2014 call get_diagnostics. It returns the child's final screen, the upstream error bodies, the recent state records, the resolved model chain and the paths to the full records. It needs no re-run and no debug flag.
103110
- 6. capture_session reads a session's current screen; resize_session sets its terminal size (any time, mid-turn included); scroll_session scrolls the child's transcript view (by rows, or back to the live bottom); cancel_session stops it. Nothing stops a session for being idle: a child inside a long build is silent and working.
104180
+ 6. capture_session reads a session's current screen; resize_session sets its terminal size (any time, mid-turn included); scroll_session scrolls the child's transcript view (by rows, or back to the live bottom); transcript_session reads the conversation as text lines (also after the session finished); cancel_session stops it. Nothing stops a session for being idle: a child inside a long build is silent and working.
103111
104181
 
103112
104182
  The session_id from create_session is the key for every session tool.`, proxyInstance = null, proxyStarting = null, SCORE_TIE_EPSILON = 0.000001, NEXT_STEP, KEEP_EFFORT = "default", USER_REQUIRED_MODEL_DESCRIPTION, sanitize2, TOOL_MODES;
103113
104183
  var init_mcp_server = __esm(() => {
@@ -103150,9 +104220,9 @@ var init_mcp_server = __esm(() => {
103150
104220
  init_team_orchestrator();
103151
104221
  init_preflight_chain();
103152
104222
  __filename3 = fileURLToPath3(import.meta.url);
103153
- __dirname3 = dirname21(__filename3);
103154
- CLAUDISH_CACHE_DIR = join56(homedir37(), ".claudish");
103155
- OPENROUTER_MODELS_CACHE_PATH = join56(CLAUDISH_CACHE_DIR, "openrouter-models.json");
104223
+ __dirname3 = dirname22(__filename3);
104224
+ CLAUDISH_CACHE_DIR = join57(homedir37(), ".claudish");
104225
+ OPENROUTER_MODELS_CACHE_PATH = join57(CLAUDISH_CACHE_DIR, "openrouter-models.json");
103156
104226
  NEXT_STEP = {
103157
104227
  cancelled: "you stopped this slot; nothing is wrong with it. Re-run it if you still want its vote",
103158
104228
  timeout: "grid mode only \u2014 magmux ended the pane. The orchestrator has no deadline",
@@ -103281,9 +104351,9 @@ var init_serve_command = __esm(() => {
103281
104351
  var HOOKS_AMBIENT_DTS = '// claudish hook types \u2014 written by `claudish hooks init`. Do not edit: run\n// `claudish hooks init` from a newer claudish to refresh it.\n// With this file beside your hook, `import type { HookDefinition } from\n// "claudish/hooks"` type-checks with nothing installed.\ndeclare module "claudish/hooks" {\n /**\n * claudish/hooks \u2014 the public hook contract.\n *\n * Everything a hook author can import lives here, and nothing else does. This\n * file imports nothing from the rest of claudish and has no side effects, so it\n * can be built on its own into `dist/hooks.js` + `dist/hooks.d.ts` and served to\n * user modules as the `claudish/hooks` virtual module (see `loader.ts`).\n *\n * Every internal hooks module takes its types from here, so the contract has a\n * single source.\n *\n * ## Process isolation\n *\n * A hook module\'s top-level state is PER PROCESS. In the interactive launcher,\n * proxy events (`request` \u2026 `responseEnd`, raw) and session events\n * (`sessionStart` \u2026 `sessionEnd`) run in one process. In `create_session` and\n * team children they do not: proxy events run in the pane child, and session\n * events run in the MCP server or the CLI process that drives the pane. Pass\n * per-turn data from a proxy event to `sessionIdle` with `annotateTurn`, never\n * with module variables.\n *\n * ## Execution: a hook chain\n *\n * The hooks of one event run one after another, in effective order. Each hook\'s\n * actions are applied before the next hook is called, so `ctx.request`,\n * `ctx.tool.args` and `ctx.response` show what the earlier hooks did, and\n * `ctx.prior` lists it. A warn-severity or failing hook changes nothing for the\n * next one. Each event has a budget for all its hooks together; a hook\'s timeout\n * is cut to what is left of it.\n *\n * ## Output\n *\n * Log with `ctx.log`. `console.*` and `process.stdout`/`stderr` writes made by a\n * hook are routed to claudish\'s debug log, never to the terminal or the MCP\n * protocol stream.\n */\n export const HOOK_API_VERSION = 1;\n export type Severity = "off" | "warn" | "fix";\n export type HookOrder = "pre" | "normal" | "post";\n /** native = RouteInfo.native true; foreign = RouteInfo.native false; all = both */\n export type RouteScope = "foreign" | "native" | "all";\n export type HostMode = "interactive" | "pane-child" | "serve" | "mcp" | "other";\n export type JsonValue = string | number | boolean | null | ReadonlyArray<JsonValue> | {\n readonly [k: string]: JsonValue;\n };\n export interface HarnessFacts {\n readonly planModeActive: boolean;\n readonly planFilePath?: string;\n readonly planDir?: string;\n /** Claude Code session id from request metadata */\n readonly sessionId?: string;\n /** empty = no listing found, not "no skills" */\n readonly skills?: ReadonlyArray<{\n name: string;\n description: string;\n }>;\n }\n export interface RouteInfo {\n /** bare model name the handler serves, e.g. "glm-5" */\n readonly modelId: string;\n /** claudish provider name, e.g. "openrouter"; "anthropic" for native */\n readonly provider: string;\n /**\n * true when the request is served by the native Anthropic handler, OR when\n * !forceForeignModel && (/^claude[-.]/i.test(modelId) || provider === "anthropic")\n * where forceForeignModel is the provider profile\'s opt-in override. This is the exact\n * rule the plan-mode built-in has always used (a claude-* uid on an aggregator that sets\n * forceForeignModel is foreign).\n */\n readonly native: boolean;\n }\n export interface ClaudeTool {\n name: string;\n description?: string;\n input_schema?: unknown;\n }\n /** A Claude-format content block. Only `text` blocks are editable by closed actions. */\n export type ClaudeContentBlock = {\n type: "text";\n text: string;\n } | {\n type: "tool_use";\n id: string;\n name: string;\n input: unknown;\n } | {\n type: "tool_result";\n tool_use_id: string;\n content?: unknown;\n is_error?: boolean;\n } | {\n type: "thinking";\n thinking: string;\n signature?: string;\n } | {\n type: string;\n [k: string]: unknown;\n };\n export interface ClaudeMessage {\n role: "user" | "assistant";\n content: string | ReadonlyArray<ClaudeContentBlock>;\n }\n /**\n * In `request`, the request as the earlier steps of the hook chain left it. On\n * the response side and in `claims()`, the request chain\'s final view.\n */\n export interface RequestFacts {\n /** as Claude Code asked for it; frozen */\n readonly model: string;\n /** frozen */\n readonly stream: boolean;\n /** system prompt flattened to text, after earlier steps */\n readonly systemText: string;\n /** read-only view after earlier steps; change it through actions */\n readonly messages: ReadonlyArray<ClaudeMessage>;\n /**\n * read-only view after earlier steps; `description` includes earlier description actions as\n * stage B will apply them to the Claude-format tool (the provider copy may be summarised)\n */\n readonly tools: ReadonlyArray<ClaudeTool>;\n }\n /** Which session a proxy event belongs to, when claudish knows. */\n export interface ProxySessionRef {\n readonly kind: SessionKind;\n readonly id: string;\n readonly role: "slot" | "judge" | null;\n }\n /** One action an earlier step of this hook chain applied or handed on. */\n export interface PriorAction {\n readonly hookId: string;\n readonly action: HookAction;\n }\n interface BaseContext {\n readonly hookId: string;\n /** "warn" or "fix" (an "off" hook is never called) */\n readonly severity: Severity;\n /** writes to claudish\'s debug log; never to the terminal */\n log(message: string): void;\n /**\n * this step\'s own signal: aborted when its step timeout elapses (its own timeout, or the\n * event budget left, whichever is shorter) or when the dispatch is aborted. Never shared\n * with another hook\'s step.\n */\n readonly signal: AbortSignal;\n /**\n * actions earlier steps of this hook chain applied (or, for responseEnd/sessionStart/\n * sessionIdle, handed on), in order. Empty for the first step and in claims()/the toolCall\n * predicate. Never lists warn actions or anything a warn-severity hook returned.\n */\n readonly prior: ReadonlyArray<PriorAction>;\n }\n export interface ProxyContext extends BaseContext {\n readonly host: {\n mode: HostMode;\n };\n readonly route: RouteInfo;\n readonly request: RequestFacts;\n readonly harness: HarnessFacts;\n readonly session: ProxySessionRef | null;\n }\n export interface RequestContext extends ProxyContext {\n readonly event: "request";\n }\n export interface ToolCallContext extends ProxyContext {\n readonly event: "toolCall";\n readonly tool: {\n id: string;\n name: string;\n /** the arguments after every repair earlier hooks in the chain applied */\n args: Readonly<Record<string, unknown>>;\n /** the argument JSON exactly as the model produced it; never rewritten */\n rawArgs: string;\n };\n }\n export type ResponseBlock = {\n type: "text";\n text: string;\n } | {\n type: "thinking";\n thinking: string;\n } | {\n type: "tool_use";\n id: string;\n name: string;\n input: unknown;\n } | {\n type: "other";\n blockType: string;\n };\n export interface ResponseView {\n blocks: ReadonlyArray<ResponseBlock>;\n /** every text block, joined with "\\n\\n" */\n text: string;\n /** every thinking block, joined with "\\n\\n" */\n reasoning: string;\n toolNames: ReadonlyArray<string>;\n /** "tool_use" = an intermediate step of a tool loop; the turn continues after the tool runs */\n stopReason: string | null;\n }\n /** response = the held response after every text action earlier hooks in the chain applied */\n export interface ResponseRewriteContext extends ProxyContext {\n readonly event: "responseRewrite";\n readonly response: ResponseView;\n }\n /** response = the final bytes the client received: after any rewrite AND any raw `response` replacement */\n export interface ResponseEndContext extends ProxyContext {\n readonly event: "responseEnd";\n readonly response: ResponseView;\n }\n export type SessionKind = "interactive" | "channel" | "team";\n export interface SessionInfo {\n kind: SessionKind;\n /** team only; null otherwise */\n role: "slot" | "judge" | null;\n /** channel: the 8-hex session_id \xB7 team: "<run_id>:<slot id>" \xB7 interactive: the Claude Code session id */\n id: string;\n claudeSessionId: string | null;\n /** as the caller asked for it */\n model: string;\n cwd: string;\n /** a one-shot session stays "one-shot" through a hook-extended turn chain */\n shape: "interactive" | "one-shot";\n injection: "available" | "unavailable";\n }\n export interface SessionStartContext extends BaseContext {\n readonly event: "sessionStart";\n readonly session: SessionInfo;\n }\n export interface TurnAnnotation {\n hookId: string;\n key: string;\n value: JsonValue;\n }\n export interface SessionIdleContext extends BaseContext {\n readonly event: "sessionIdle";\n readonly session: SessionInfo;\n readonly turn: {\n /** 1-based within the session; a re-woken turn keeps its index */\n index: number;\n /** this turn\'s assistant text */\n answer: string;\n stopReason: string | null;\n apiError: boolean;\n /** this turn was started by an injection */\n hookCaused: boolean;\n /** annotateTurn actions from this turn\'s responses, in arrival order */\n annotations: ReadonlyArray<TurnAnnotation>;\n };\n readonly consecutiveHookTurns: number;\n /** caller/human input is queued; an injection returned now will be dropped as stale */\n readonly callerInputPending: boolean;\n }\n export interface SessionBlockedContext extends BaseContext {\n readonly event: "sessionBlocked";\n readonly session: SessionInfo;\n readonly block: {\n kind: "question" | "permission";\n text: string;\n };\n }\n export interface SessionEndContext extends BaseContext {\n readonly event: "sessionEnd";\n readonly session: SessionInfo;\n readonly outcome: {\n state: string;\n reason: string | null;\n exitCode: number | null;\n };\n }\n type TextMatch = {\n find: string;\n regex?: boolean;\n flags?: string;\n };\n /** default ["user"] */\n type MessageRoles = {\n roles?: ReadonlyArray<"user" | "assistant">;\n };\n export type InjectSystemNote = {\n type: "injectSystemNote";\n text: string;\n };\n export type AppendUserText = {\n type: "appendUserText";\n text: string;\n };\n export type ReplaceMessageText = {\n type: "replaceMessageText";\n replace: string;\n } & TextMatch & MessageRoles;\n export type RemoveMessageText = {\n type: "removeMessageText";\n } & TextMatch & MessageRoles;\n export type RewriteToolDescription = {\n type: "rewriteToolDescription";\n tool: string;\n append: string;\n };\n export type ReplaceToolDescription = {\n type: "replaceToolDescription";\n tool: string;\n description: string;\n };\n export type ReplaceToolSchema = {\n type: "replaceToolSchema";\n tool: string;\n inputSchema: {\n type: "object";\n [k: string]: unknown;\n };\n };\n export type RemoveTool = {\n type: "removeTool";\n tool: string;\n };\n export type RepairToolArgs = {\n type: "repairToolArgs";\n args: Record<string, unknown>;\n reason: string;\n };\n export type AppendText = {\n type: "appendText";\n text: string;\n };\n export type PrependText = {\n type: "prependText";\n text: string;\n };\n export type ReplaceText = {\n type: "replaceText";\n replace: string;\n } & TextMatch;\n export type RemoveText = {\n type: "removeText";\n } & TextMatch;\n export type InjectUserTurn = {\n type: "injectUserTurn";\n text: string;\n };\n export type AnnotateTurn = {\n type: "annotateTurn";\n key: string;\n value: JsonValue;\n };\n export type Warn = {\n type: "warn";\n message: string;\n };\n export type RequestAction = InjectSystemNote | AppendUserText | ReplaceMessageText | RemoveMessageText | RewriteToolDescription | ReplaceToolDescription | ReplaceToolSchema | RemoveTool | Warn;\n export type ToolCallAction = RepairToolArgs | Warn;\n export type RewriteAction = AppendText | PrependText | ReplaceText | RemoveText | Warn;\n export type ResponseEndAction = InjectSystemNote | InjectUserTurn | AnnotateTurn | Warn;\n export type SessionAction = InjectUserTurn | Warn;\n export type ObserveAction = Warn;\n export type HookAction = RequestAction | ToolCallAction | RewriteAction | ResponseEndAction | SessionAction;\n export type HookResult<A> = A[] | void | Promise<A[] | void>;\n export interface HookHandlers {\n request?(ctx: RequestContext): HookResult<RequestAction>;\n toolCall?: {\n /** tool names whose completed arguments this hook sees; "*" = every tool (costs streaming of all tool input) */\n tools: ReadonlyArray<string>;\n /** synchronous, per request; default true. false = not armed for this request */\n armed?(ctx: RequestContext): boolean;\n /** true = see the call after it was forwarded; can only warn; never holds the stream */\n observe?: boolean;\n handle(ctx: ToolCallContext): HookResult<ToolCallAction>;\n };\n responseRewrite?: {\n /** synchronous, per request, evaluated before the response starts; default true */\n claims?(ctx: RequestContext): boolean;\n handle(ctx: ResponseRewriteContext): HookResult<RewriteAction>;\n };\n responseEnd?(ctx: ResponseEndContext): HookResult<ResponseEndAction>;\n sessionStart?(ctx: SessionStartContext): HookResult<SessionAction>;\n sessionIdle?(ctx: SessionIdleContext): HookResult<SessionAction>;\n sessionBlocked?(ctx: SessionBlockedContext): HookResult<ObserveAction>;\n sessionEnd?(ctx: SessionEndContext): HookResult<ObserveAction>;\n }\n export interface RawProviderRequestContext {\n readonly hookId: string;\n readonly route: RouteInfo;\n claudeRequest: any;\n messages: any[];\n tools: any[];\n }\n export interface RawProviderChunkContext {\n readonly hookId: string;\n readonly route: RouteInfo;\n /** A clone of the parsed provider event. Whatever this field holds when the call returns is\n * what the parser reads from then on, so it may be edited in place or replaced. */\n chunk: any;\n /** The delta object inside `chunk` at call time (Responses wire: `chunk` itself). Edit it in\n * place; assigning a new object to this field has no effect. */\n delta: any;\n /** per-stream, shared across this hook\'s chunk calls; never cloned */\n metadata: Map<string, unknown>;\n }\n export interface RawProviderStreamEndContext {\n readonly hookId: string;\n readonly route: RouteInfo;\n metadata: Map<string, unknown>;\n }\n export interface RawResponseContext {\n readonly hookId: string;\n readonly route: RouteInfo;\n readonly request: RequestFacts;\n /** A replay of the current Claude-format response (SSE, or JSON for stream:false), read at the\n * hook\'s own pace. claudish drains the original itself, so leaving this unread delays nothing. */\n response: Response;\n }\n export interface RawMiddleware {\n /**\n * Receives clones. They replace the live request only when this call returns (or its promise\n * resolves) within its timeout. A throw, a rejection or a timeout discards every change.\n */\n providerRequest?(ctx: RawProviderRequestContext): void | Promise<void>;\n /**\n * MUST be synchronous. Called on the parser\'s loop, on a clone of the chunk. On a normal return\n * of anything but a thenable, the clone (whatever `ctx.chunk` holds then) is committed: it\n * becomes the object the parser reads, deletions included. On a throw nothing is committed.\n * A declared `async` function is rejected at load. Any other thenable return is an invalid\n * invocation: the clone is discarded even if the hook mutated it before returning, the call is\n * journaled `invalid` (`async_return`) and counts toward the circuit breaker, and a rejection\n * handler is attached, so a rejection is journaled `error` and never becomes an\n * unhandledRejection.\n */\n providerChunk?(ctx: RawProviderChunkContext): void;\n providerStreamEnd?(ctx: RawProviderStreamEndContext): void | Promise<void>;\n /**\n * Return a replacement Response, or nothing to keep the current one. A replacement is read\n * whole and forwarded only when it completes cleanly; on any failure the original is forwarded.\n * The client never sees a partial replacement. A replacement must close within this hook\'s\n * timeout after the later of this call\'s return and the end of the original; the original\'s end\n * never depends on whether `ctx.response` is read.\n */\n response?(ctx: RawResponseContext): Response | void | Promise<Response | void>;\n }\n interface HookBase {\n apiVersion: 1;\n id: string;\n description?: string;\n /** default "fix" for user hooks */\n defaultSeverity?: Severity;\n /** default "normal" */\n order?: HookOrder;\n /** globs on RouteInfo.modelId; default "*"; proxy events only */\n models?: string | ReadonlyArray<string>;\n /** default "all"; proxy events only */\n routes?: RouteScope;\n /** per call; clamped to the event\'s maximum and to the event budget left when the hook\'s turn comes */\n timeoutMs?: number;\n on?: HookHandlers;\n }\n export interface SafeHook extends HookBase {\n unsafe?: never;\n raw?: never;\n }\n export interface UnsafeHook extends HookBase {\n unsafe: true;\n raw: RawMiddleware;\n }\n /**\n * One hook. Module-level state is per process: in `create_session` and team\n * children, proxy events run in the pane child and session events in the MCP\n * server or CLI process. Pass per-turn data from proxy events to `sessionIdle`\n * with `annotateTurn`, never with module variables.\n */\n export type HookDefinition = SafeHook | UnsafeHook;\n /** Identity function: gives editor type-checking and inference. */\n export function defineHook<const H extends HookDefinition>(hook: H): H;\n /** Action constructors (plain data; equivalent to writing the object literal). */\n export const act: {\n injectSystemNote(text: string): InjectSystemNote;\n appendUserText(text: string): AppendUserText;\n replaceMessageText(find: string, replace: string, opts?: {\n regex?: boolean;\n flags?: string;\n roles?: ReadonlyArray<"user" | "assistant">;\n }): ReplaceMessageText;\n removeMessageText(find: string, opts?: {\n regex?: boolean;\n flags?: string;\n roles?: ReadonlyArray<"user" | "assistant">;\n }): RemoveMessageText;\n rewriteToolDescription(tool: string, append: string): RewriteToolDescription;\n replaceToolDescription(tool: string, description: string): ReplaceToolDescription;\n replaceToolSchema(tool: string, inputSchema: {\n type: "object";\n [k: string]: unknown;\n }): ReplaceToolSchema;\n removeTool(tool: string): RemoveTool;\n repairToolArgs(args: Record<string, unknown>, reason: string): RepairToolArgs;\n appendText(text: string): AppendText;\n prependText(text: string): PrependText;\n replaceText(find: string, replace: string, opts?: {\n regex?: boolean;\n flags?: string;\n }): ReplaceText;\n removeText(find: string, opts?: {\n regex?: boolean;\n flags?: string;\n }): RemoveText;\n injectUserTurn(text: string): InjectUserTurn;\n annotateTurn(key: string, value: JsonValue): AnnotateTurn;\n warn(message: string): Warn;\n };\n}\n';
103282
104352
 
103283
104353
  // src/hooks/observer/corpus.ts
103284
- import { appendFileSync as appendFileSync8, readFileSync as readFileSync47, readdirSync as readdirSync11, statSync as statSync20 } from "fs";
104354
+ import { appendFileSync as appendFileSync8, readFileSync as readFileSync47, readdirSync as readdirSync12, statSync as statSync21 } from "fs";
103285
104355
  import { homedir as homedir38 } from "os";
103286
- import { join as join57 } from "path";
104356
+ import { join as join58 } from "path";
103287
104357
  function directoryOf2(filePath) {
103288
104358
  const slash = filePath.lastIndexOf("/");
103289
104359
  return slash > 0 ? filePath.slice(0, slash) : undefined;
@@ -103357,31 +104427,31 @@ function listTranscripts(root) {
103357
104427
  const files = [];
103358
104428
  let projects;
103359
104429
  try {
103360
- projects = readdirSync11(root);
104430
+ projects = readdirSync12(root);
103361
104431
  } catch {
103362
104432
  return files;
103363
104433
  }
103364
104434
  for (const project of projects) {
103365
- const dir = join57(root, project);
104435
+ const dir = join58(root, project);
103366
104436
  try {
103367
- if (!statSync20(dir).isDirectory())
104437
+ if (!statSync21(dir).isDirectory())
103368
104438
  continue;
103369
- for (const f of readdirSync11(dir)) {
104439
+ for (const f of readdirSync12(dir)) {
103370
104440
  if (f.endsWith(".jsonl"))
103371
- files.push(join57(dir, f));
104441
+ files.push(join58(dir, f));
103372
104442
  }
103373
104443
  } catch {}
103374
104444
  }
103375
104445
  return files;
103376
104446
  }
103377
104447
  function buildCorpus(options = {}) {
103378
- const root = options.projectsRoot ?? join57(homedir38(), ".claude", "projects");
104448
+ const root = options.projectsRoot ?? join58(homedir38(), ".claude", "projects");
103379
104449
  const files = listTranscripts(root);
103380
104450
  const records = [];
103381
104451
  for (const f of files)
103382
104452
  records.push(...replayTranscript(f));
103383
104453
  if (options.write && records.length > 0) {
103384
- const outputPath = options.outputPath ?? join57(homedir38(), ".claudish", "behavior-divergences.jsonl");
104454
+ const outputPath = options.outputPath ?? join58(homedir38(), ".claudish", "behavior-divergences.jsonl");
103385
104455
  try {
103386
104456
  appendFileSync8(outputPath, `${records.map((r) => JSON.stringify(r)).join(`
103387
104457
  `)}
@@ -103399,7 +104469,7 @@ var init_corpus = __esm(() => {
103399
104469
  // src/hooks-command.ts
103400
104470
  import { existsSync as existsSync41, mkdirSync as mkdirSync27, readFileSync as readFileSync48, writeFileSync as writeFileSync25 } from "fs";
103401
104471
  import { homedir as homedir39 } from "os";
103402
- import { join as join58, resolve as resolve9 } from "path";
104472
+ import { join as join59, resolve as resolve9 } from "path";
103403
104473
  function severityColor(sev) {
103404
104474
  if (sev === "fix")
103405
104475
  return green(sev);
@@ -103522,10 +104592,10 @@ async function check(file, json) {
103522
104592
  return valid ? 0 : 1;
103523
104593
  }
103524
104594
  function init(dirArg) {
103525
- const dir = dirArg ? resolve9(process.cwd(), dirArg) : join58(homedir39(), ".claudish", "hooks");
104595
+ const dir = dirArg ? resolve9(process.cwd(), dirArg) : join59(homedir39(), ".claudish", "hooks");
103526
104596
  const files = [
103527
- [join58(dir, "example-hook.ts"), EXAMPLE_HOOK],
103528
- [join58(dir, "claudish-hooks.d.ts"), HOOKS_AMBIENT_DTS]
104597
+ [join59(dir, "example-hook.ts"), EXAMPLE_HOOK],
104598
+ [join59(dir, "claudish-hooks.d.ts"), HOOKS_AMBIENT_DTS]
103529
104599
  ];
103530
104600
  const existing = files.filter(([path]) => existsSync41(path)).map(([path]) => path);
103531
104601
  if (existing.length > 0) {
@@ -103728,7 +104798,7 @@ var init_hooks_command = __esm(() => {
103728
104798
  import { spawn as spawn3 } from "child_process";
103729
104799
  import { existsSync as existsSync42, readFileSync as readFileSync49, writeFileSync as writeFileSync26 } from "fs";
103730
104800
  import { connect as netConnect2 } from "net";
103731
- import { join as join59 } from "path";
104801
+ import { join as join60 } from "path";
103732
104802
  import { setTimeout as wait } from "timers/promises";
103733
104803
  function paneRouteLine(explanation) {
103734
104804
  const origin = describeRouteExplanation(explanation);
@@ -103878,9 +104948,9 @@ async function runWithGrid(sessionPath, models, input, opts) {
103878
104948
  const keep = opts?.keep ?? false;
103879
104949
  const manifest = setupSession(sessionPath, models, input);
103880
104950
  const startedAt = new Date().toISOString();
103881
- const gridfilePath = join59(sessionPath, "gridfile.txt");
103882
- const prompt = readFileSync49(join59(sessionPath, "input.md"), "utf-8").replace(/'/g, "'\\''").replace(/\n/g, " ");
103883
- const rawPrompt = readFileSync49(join59(sessionPath, "input.md"), "utf-8");
104951
+ const gridfilePath = join60(sessionPath, "gridfile.txt");
104952
+ const prompt = readFileSync49(join60(sessionPath, "input.md"), "utf-8").replace(/'/g, "'\\''").replace(/\n/g, " ");
104953
+ const rawPrompt = readFileSync49(join60(sessionPath, "input.md"), "utf-8");
103884
104954
  const usedBannerColors = new Set;
103885
104955
  const routeLines = mode === "interactive" ? new Map : await resolvePaneRouteLines(Object.values(manifest.models).map((entry) => entry.model));
103886
104956
  const gridLines = Object.entries(manifest.models).map(([anonId]) => {
@@ -103912,7 +104982,7 @@ async function runWithGrid(sessionPath, models, input, opts) {
103912
104982
  });
103913
104983
  const [{ results }] = await Promise.all([subscription, procExit]);
103914
104984
  const status = buildTeamStatus(manifest, startedAt, results?.panes ?? null);
103915
- const statusPath = join59(sessionPath, "status.json");
104985
+ const statusPath = join60(sessionPath, "status.json");
103916
104986
  writeFileSync26(statusPath, JSON.stringify(status, null, 2), "utf-8");
103917
104987
  return status;
103918
104988
  }
@@ -103933,7 +105003,7 @@ var init_team_grid = __esm(() => {
103933
105003
 
103934
105004
  // src/team-cli.ts
103935
105005
  import { readFileSync as readFileSync50 } from "fs";
103936
- import { join as join60 } from "path";
105006
+ import { join as join61 } from "path";
103937
105007
  async function runPaneTeam(sessionPath, models, input, opts) {
103938
105008
  installPaneShutdownHooks({ exitAfter: true });
103939
105009
  try {
@@ -104078,7 +105148,7 @@ async function teamCommand(args) {
104078
105148
  }
104079
105149
  case "judge": {
104080
105150
  await judgeResponses(sessionPath, { judges });
104081
- console.log(readFileSync50(join60(sessionPath, "verdict.md"), "utf-8"));
105151
+ console.log(readFileSync50(join61(sessionPath, "verdict.md"), "utf-8"));
104082
105152
  break;
104083
105153
  }
104084
105154
  case "run-and-judge": {
@@ -104094,7 +105164,7 @@ async function teamCommand(args) {
104094
105164
  });
104095
105165
  printStatus(status);
104096
105166
  await judgeResponses(sessionPath, { judges });
104097
- console.log(readFileSync50(join60(sessionPath, "verdict.md"), "utf-8"));
105167
+ console.log(readFileSync50(join61(sessionPath, "verdict.md"), "utf-8"));
104098
105168
  break;
104099
105169
  }
104100
105170
  case "status": {
@@ -106022,7 +107092,7 @@ var init_account_command = __esm(() => {
106022
107092
  import { spawnSync as spawnSync2 } from "child_process";
106023
107093
  import { existsSync as existsSync43, unlinkSync as unlinkSync12 } from "fs";
106024
107094
  import { homedir as homedir40 } from "os";
106025
- import { join as join61 } from "path";
107095
+ import { join as join62 } from "path";
106026
107096
  async function defaultSuggestModel() {
106027
107097
  try {
106028
107098
  const token = await getValidAntigravityAccessToken();
@@ -106141,7 +107211,7 @@ No session detected yet. Starting the Antigravity CLI interactively \u2014
106141
107211
  async logout(deps) {
106142
107212
  deleteSharedAntigravityToken(deps);
106143
107213
  try {
106144
- const tokenFile = join61(homedir40(), ".gemini", "antigravity-cli", "antigravity-oauth-token");
107214
+ const tokenFile = join62(homedir40(), ".gemini", "antigravity-cli", "antigravity-oauth-token");
106145
107215
  if (existsSync43(tokenFile))
106146
107216
  unlinkSync12(tokenFile);
106147
107217
  } catch {}
@@ -114419,8 +115489,8 @@ var init_own_pane = __esm(() => {
114419
115489
  });
114420
115490
 
114421
115491
  // src/hooks/session/interactive-driver.ts
114422
- import { existsSync as existsSync44, statSync as statSync21 } from "fs";
114423
- import { join as join62 } from "path";
115492
+ import { existsSync as existsSync44, statSync as statSync22 } from "fs";
115493
+ import { join as join63 } from "path";
114424
115494
  function nextDriverPhase(phase, ev) {
114425
115495
  const row = EDGES[phase];
114426
115496
  return row[ev] ?? null;
@@ -114638,7 +115708,7 @@ class InteractiveDriver {
114638
115708
  }
114639
115709
  follow(sid, offset) {
114640
115710
  const path = transcriptPathFor(this.o.cwd, sid, this.o.projectsRoot);
114641
- this.follower = new TranscriptFollower(path, join62(path.replace(/\.jsonl$/, ""), "subagents"));
115711
+ this.follower = new TranscriptFollower(path, join63(path.replace(/\.jsonl$/, ""), "subagents"));
114642
115712
  this.sessionId = sid;
114643
115713
  this.settledTurn = null;
114644
115714
  this.lastPollAt = Date.now();
@@ -115183,7 +116253,7 @@ function assertNeverPhase(p) {
115183
116253
  }
115184
116254
  function fileSizeOf(path) {
115185
116255
  try {
115186
- return statSync21(path).size;
116256
+ return statSync22(path).size;
115187
116257
  } catch {
115188
116258
  return 0;
115189
116259
  }
@@ -115740,20 +116810,20 @@ import { spawn as spawn4, spawnSync as spawnSync4 } from "child_process";
115740
116810
  import { randomUUID as randomUUID11 } from "crypto";
115741
116811
  import {
115742
116812
  chmodSync as chmodSync5,
115743
- closeSync as closeSync14,
116813
+ closeSync as closeSync15,
115744
116814
  existsSync as existsSync46,
115745
116815
  mkdirSync as mkdirSync28,
115746
116816
  mkdtempSync as mkdtempSync4,
115747
- openSync as openSync14,
116817
+ openSync as openSync15,
115748
116818
  readFileSync as readFileSync51,
115749
- readdirSync as readdirSync12,
116819
+ readdirSync as readdirSync13,
115750
116820
  rmSync as rmSync15,
115751
- statSync as statSync22,
116821
+ statSync as statSync23,
115752
116822
  unlinkSync as unlinkSync13,
115753
116823
  writeFileSync as writeFileSync27
115754
116824
  } from "fs";
115755
116825
  import { homedir as homedir42, tmpdir as tmpdir3 } from "os";
115756
- import { dirname as dirname22, join as join63 } from "path";
116826
+ import { dirname as dirname23, join as join64 } from "path";
115757
116827
  import { isatty } from "tty";
115758
116828
  function releaseTerminalIsolation() {
115759
116829
  if (!restoreTerminal)
@@ -115825,7 +116895,7 @@ function hasResolvableAnthropicAuth(deps = {}) {
115825
116895
  if (isRealAnthropicEnvCredential(env, "ANTHROPIC_API_KEY") || isRealAnthropicEnvCredential(env, "ANTHROPIC_AUTH_TOKEN")) {
115826
116896
  return true;
115827
116897
  }
115828
- if (fileExists(join63(homedir42(), ".claude", ".credentials.json")))
116898
+ if (fileExists(join64(homedir42(), ".claude", ".credentials.json")))
115829
116899
  return true;
115830
116900
  return keychainProbe();
115831
116901
  }
@@ -115925,10 +116995,10 @@ function readableClaudeSettingsLayers(config, env, deps) {
115925
116995
  const cwd = deps.cwd ?? process.cwd();
115926
116996
  const files = [
115927
116997
  managedSettingsPath(),
115928
- join63(home, ".claude", "settings.json"),
115929
- ...env.CLAUDE_CONFIG_DIR ? [join63(env.CLAUDE_CONFIG_DIR, "settings.json")] : [],
115930
- join63(cwd, ".claude", "settings.json"),
115931
- join63(cwd, ".claude", "settings.local.json")
116998
+ join64(home, ".claude", "settings.json"),
116999
+ ...env.CLAUDE_CONFIG_DIR ? [join64(env.CLAUDE_CONFIG_DIR, "settings.json")] : [],
117000
+ join64(cwd, ".claude", "settings.json"),
117001
+ join64(cwd, ".claude", "settings.local.json")
115932
117002
  ];
115933
117003
  const layers = [];
115934
117004
  for (const file of files) {
@@ -115960,7 +117030,7 @@ function readSettingsSource(source, readFile) {
115960
117030
  }
115961
117031
  function managedSettingsPath() {
115962
117032
  if (isWindows2()) {
115963
- return join63(process.env.PROGRAMDATA || "C:\\ProgramData", "ClaudeCode", "managed-settings.json");
117033
+ return join64(process.env.PROGRAMDATA || "C:\\ProgramData", "ClaudeCode", "managed-settings.json");
115964
117034
  }
115965
117035
  if (process.platform === "darwin") {
115966
117036
  return "/Library/Application Support/ClaudeCode/managed-settings.json";
@@ -115981,8 +117051,8 @@ function isWindows2() {
115981
117051
  }
115982
117052
  function createStatusLineScript(tokenFilePath) {
115983
117053
  const homeDir = process.env.HOME || process.env.USERPROFILE || tmpdir3();
115984
- const claudishDir = join63(homeDir, ".claudish");
115985
- const scriptPath = join63(claudishDir, `status-${process.pid}-${Date.now()}.js`);
117054
+ const claudishDir = join64(homeDir, ".claudish");
117055
+ const scriptPath = join64(claudishDir, `status-${process.pid}-${Date.now()}.js`);
115986
117056
  const escapedTokenPath = tokenFilePath.replace(/\\/g, "\\\\");
115987
117057
  const light = getThemeMode() === "light";
115988
117058
  const cyanCode = light ? "38;2;14;116;144" : "96";
@@ -116142,7 +117212,7 @@ process.stdin.on('end', () => {
116142
117212
  }
116143
117213
  function initializeTokenFile(tokenFilePath) {
116144
117214
  try {
116145
- mkdirSync28(dirname22(tokenFilePath), { recursive: true });
117215
+ mkdirSync28(dirname23(tokenFilePath), { recursive: true });
116146
117216
  writeFileSync27(tokenFilePath, JSON.stringify({
116147
117217
  input_tokens: 0,
116148
117218
  output_tokens: 0,
@@ -116162,7 +117232,7 @@ function cleanupStaleTokenFiles(dir, now = Date.now(), maxAgeMs = STALE_TOKEN_FI
116162
117232
  let removed = 0;
116163
117233
  let entries;
116164
117234
  try {
116165
- entries = readdirSync12(dir);
117235
+ entries = readdirSync13(dir);
116166
117236
  } catch {
116167
117237
  return 0;
116168
117238
  }
@@ -116174,9 +117244,9 @@ function cleanupStaleTokenFiles(dir, now = Date.now(), maxAgeMs = STALE_TOKEN_FI
116174
117244
  if (!name.startsWith("tokens-") || !name.endsWith(".json"))
116175
117245
  continue;
116176
117246
  scanned++;
116177
- const full = join63(dir, name);
117247
+ const full = join64(dir, name);
116178
117248
  try {
116179
- if (statSync22(full).mtimeMs >= cutoff)
117249
+ if (statSync23(full).mtimeMs >= cutoff)
116180
117250
  continue;
116181
117251
  unlinkSync13(full);
116182
117252
  removed++;
@@ -116203,9 +117273,9 @@ function parseSettingsArgSafe(value) {
116203
117273
  }
116204
117274
  function userSettingsFileCandidates(cwd) {
116205
117275
  return [
116206
- join63(homedir42(), ".claude", "settings.json"),
116207
- join63(cwd, ".claude", "settings.json"),
116208
- join63(cwd, ".claude", "settings.local.json")
117276
+ join64(homedir42(), ".claude", "settings.json"),
117277
+ join64(cwd, ".claude", "settings.json"),
117278
+ join64(cwd, ".claude", "settings.local.json")
116209
117279
  ];
116210
117280
  }
116211
117281
  function discoverUserStatusLineCommand(claudeArgs = [], cwd = process.cwd()) {
@@ -116246,11 +117316,11 @@ function buildChainedStatusCommand(userCommand, claudishBody, claudishSegment) {
116246
117316
  }
116247
117317
  function createTempSettingsFile(_modelDisplay, port, proxyAuthMode, userStatusLineCommand) {
116248
117318
  const homeDir = process.env.HOME || process.env.USERPROFILE || tmpdir3();
116249
- const claudishDir = join63(homeDir, ".claudish");
117319
+ const claudishDir = join64(homeDir, ".claudish");
116250
117320
  try {
116251
117321
  mkdirSync28(claudishDir, { recursive: true });
116252
117322
  } catch {}
116253
- const tempPath = join63(claudishDir, `settings-${process.pid}-${Date.now()}.json`);
117323
+ const tempPath = join64(claudishDir, `settings-${process.pid}-${Date.now()}.json`);
116254
117324
  const assignedFile = assignedTokenFile(process.env) !== null;
116255
117325
  const tokenFilePath = resolveTokenFilePath(port);
116256
117326
  cleanupStaleTokenFiles(claudishDir);
@@ -116641,7 +117711,7 @@ async function runClaudeWithProxy(config, proxyUrl, onCleanup) {
116641
117711
  console.error(`
116642
117712
  Or set CLAUDE_PATH to your custom installation:`);
116643
117713
  const home = homedir42();
116644
- const localPath = isWindows2() ? join63(home, ".claude", "local", "claude.exe") : join63(home, ".claude", "local", "claude");
117714
+ const localPath = isWindows2() ? join64(home, ".claude", "local", "claude.exe") : join64(home, ".claude", "local", "claude");
116645
117715
  console.error(` export CLAUDE_PATH=${localPath}`);
116646
117716
  process.exit(1);
116647
117717
  }
@@ -116652,11 +117722,11 @@ Or set CLAUDE_PATH to your custom installation:`);
116652
117722
  const childWantsTty = config.interactive && !process.stdout.isTTY && Boolean(process.stdin.isTTY);
116653
117723
  if (childWantsTty) {
116654
117724
  try {
116655
- const fd = openSync14("/dev/fd/0", "r+");
117725
+ const fd = openSync15("/dev/fd/0", "r+");
116656
117726
  if (isatty(fd)) {
116657
117727
  ttyFd = fd;
116658
117728
  } else {
116659
- closeSync14(fd);
117729
+ closeSync15(fd);
116660
117730
  }
116661
117731
  } catch {
116662
117732
  ttyFd = undefined;
@@ -116676,7 +117746,7 @@ Or set CLAUDE_PATH to your custom installation:`);
116676
117746
  let driverLaunch = null;
116677
117747
  if (surfaces.driver) {
116678
117748
  const minted = sessionFlags.sessionId || sessionFlags.resumes ? null : randomUUID11();
116679
- const turnDir = mkdtempSync4(join63(tmpdir3(), "claudish-turns-"));
117749
+ const turnDir = mkdtempSync4(join64(tmpdir3(), "claudish-turns-"));
116680
117750
  chmodSync5(turnDir, 448);
116681
117751
  claudeArgs.unshift(...driverLeadingArgs({ mintedSessionId: minted, turnDir }));
116682
117752
  const sink = new InProcessSink;
@@ -116759,7 +117829,7 @@ Or set CLAUDE_PATH to your custom installation:`);
116759
117829
  const fdToClose = ttyFd;
116760
117830
  proc.on("spawn", () => {
116761
117831
  try {
116762
- closeSync14(fdToClose);
117832
+ closeSync15(fdToClose);
116763
117833
  } catch {}
116764
117834
  });
116765
117835
  }
@@ -116825,15 +117895,15 @@ async function findClaudeBinary() {
116825
117895
  }
116826
117896
  }
116827
117897
  const home = homedir42();
116828
- const localPath = isWindows ? join63(home, ".claude", "local", "claude.exe") : join63(home, ".claude", "local", "claude");
117898
+ const localPath = isWindows ? join64(home, ".claude", "local", "claude.exe") : join64(home, ".claude", "local", "claude");
116829
117899
  if (existsSync46(localPath)) {
116830
117900
  return localPath;
116831
117901
  }
116832
117902
  if (isWindows) {
116833
117903
  const windowsPaths = [
116834
- join63(home, "AppData", "Roaming", "npm", "claude.cmd"),
116835
- join63(home, ".npm-global", "claude.cmd"),
116836
- join63(home, "node_modules", ".bin", "claude.cmd")
117904
+ join64(home, "AppData", "Roaming", "npm", "claude.cmd"),
117905
+ join64(home, ".npm-global", "claude.cmd"),
117906
+ join64(home, "node_modules", ".bin", "claude.cmd")
116837
117907
  ];
116838
117908
  for (const path of windowsPaths) {
116839
117909
  if (existsSync46(path)) {
@@ -116844,11 +117914,11 @@ async function findClaudeBinary() {
116844
117914
  const commonPaths = [
116845
117915
  "/usr/local/bin/claude",
116846
117916
  "/opt/homebrew/bin/claude",
116847
- join63(home, ".npm-global/bin/claude"),
116848
- join63(home, ".local/bin/claude"),
116849
- join63(home, "node_modules/.bin/claude"),
117917
+ join64(home, ".npm-global/bin/claude"),
117918
+ join64(home, ".local/bin/claude"),
117919
+ join64(home, "node_modules/.bin/claude"),
116850
117920
  "/data/data/com.termux/files/usr/bin/claude",
116851
- join63(home, "../usr/bin/claude")
117921
+ join64(home, "../usr/bin/claude")
116852
117922
  ];
116853
117923
  for (const path of commonPaths) {
116854
117924
  if (existsSync46(path)) {
@@ -116957,16 +118027,16 @@ var init_claude_runner = __esm(() => {
116957
118027
  // src/diag-output.ts
116958
118028
  import { createWriteStream as createWriteStream2, mkdirSync as mkdirSync29, unlinkSync as unlinkSync14, writeFileSync as writeFileSync28 } from "fs";
116959
118029
  import { homedir as homedir43 } from "os";
116960
- import { join as join64 } from "path";
118030
+ import { join as join65 } from "path";
116961
118031
  function getClaudishDir() {
116962
- const dir = join64(homedir43(), ".claudish");
118032
+ const dir = join65(homedir43(), ".claudish");
116963
118033
  try {
116964
118034
  mkdirSync29(dir, { recursive: true });
116965
118035
  } catch {}
116966
118036
  return dir;
116967
118037
  }
116968
118038
  function getDiagLogPath() {
116969
- return join64(getClaudishDir(), `diag-${process.pid}.log`);
118039
+ return join65(getClaudishDir(), `diag-${process.pid}.log`);
116970
118040
  }
116971
118041
 
116972
118042
  class LogFileDiagOutput {
@@ -117531,7 +118601,7 @@ var init_advisor_startup = __esm(() => {
117531
118601
  });
117532
118602
 
117533
118603
  // src/session/conversation.ts
117534
- import { closeSync as closeSync15, openSync as openSync15, readSync as readSync7, statSync as statSync23 } from "fs";
118604
+ import { closeSync as closeSync16, openSync as openSync16, readSync as readSync8, statSync as statSync24 } from "fs";
117535
118605
  import { StringDecoder } from "string_decoder";
117536
118606
  function looksLikeTurn(line) {
117537
118607
  const assistant = line.includes('"type":"assistant"');
@@ -117586,8 +118656,8 @@ function readConversation(file, opts = {}) {
117586
118656
  };
117587
118657
  let fd = null;
117588
118658
  try {
117589
- const size = statSync23(file).size;
117590
- fd = openSync15(file, "r");
118659
+ const size = statSync24(file).size;
118660
+ fd = openSync16(file, "r");
117591
118661
  const buf = Buffer.allocUnsafe(chunkBytes);
117592
118662
  const decoder = new StringDecoder("utf-8");
117593
118663
  let pending = "";
@@ -117619,7 +118689,7 @@ function readConversation(file, opts = {}) {
117619
118689
  take({ role: raw.role, text, elided });
117620
118690
  };
117621
118691
  while (pos < size) {
117622
- const n = readSync7(fd, buf, 0, Math.min(chunkBytes, size - pos), pos);
118692
+ const n = readSync8(fd, buf, 0, Math.min(chunkBytes, size - pos), pos);
117623
118693
  if (n <= 0)
117624
118694
  break;
117625
118695
  pos += n;
@@ -117643,7 +118713,7 @@ function readConversation(file, opts = {}) {
117643
118713
  } catch {} finally {
117644
118714
  if (fd !== null) {
117645
118715
  try {
117646
- closeSync15(fd);
118716
+ closeSync16(fd);
117647
118717
  } catch {}
117648
118718
  }
117649
118719
  }
@@ -119628,7 +120698,7 @@ init_op_source();
119628
120698
  init_startup_trace();
119629
120699
  init_picker_cancelled();
119630
120700
  import { existsSync as existsSync47, readFileSync as readFileSync53 } from "fs";
119631
- import { join as join65, resolve as resolve11 } from "path";
120701
+ import { join as join66, resolve as resolve11 } from "path";
119632
120702
 
119633
120703
  // src/tui/runtime/should-open-picker.ts
119634
120704
  function noModelConfigured(gate) {
@@ -119645,7 +120715,7 @@ function requiresExplicitModel(gate, tty) {
119645
120715
  var autoloadDropped = [...droppedAtLaunch, ...takeLauncherDropped(process.env)];
119646
120716
  var dotenvText;
119647
120717
  try {
119648
- dotenvText = readFileSync53(join65(process.cwd(), ".env"));
120718
+ dotenvText = readFileSync53(join66(process.cwd(), ".env"));
119649
120719
  } catch {}
119650
120720
  if (dotenvText !== undefined) {
119651
120721
  await Promise.resolve().then(() => init_dotenv_allowlist());
@@ -119993,7 +121063,7 @@ async function runCli() {
119993
121063
  process.exit(1);
119994
121064
  }
119995
121065
  const mode = cliConfig.teamMode ?? "default";
119996
- const sessionPath = join65(process.cwd(), `.claudish-team-${Date.now()}`);
121066
+ const sessionPath = join66(process.cwd(), `.claudish-team-${Date.now()}`);
119997
121067
  if (mode === "json") {
119998
121068
  await Promise.resolve().then(() => init_pane());
119999
121069
  await Promise.resolve().then(() => init_team_orchestrator());
@@ -120013,7 +121083,7 @@ async function runCli() {
120013
121083
  const status = await runModels(sessionPath);
120014
121084
  const result = { ...status, responses: {} };
120015
121085
  for (const anonId of Object.keys(status.models)) {
120016
- const responsePath = join65(sessionPath, `response-${anonId}.md`);
121086
+ const responsePath = join66(sessionPath, `response-${anonId}.md`);
120017
121087
  try {
120018
121088
  const raw = readFileSync53(responsePath, "utf-8").trim();
120019
121089
  try {