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.
- package/dist/index.js +1295 -225
- 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.
|
|
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
|
-
|
|
5591
|
-
|
|
5592
|
-
|
|
5593
|
-
|
|
5594
|
-
|
|
5595
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
|
97392
|
-
import { basename as
|
|
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
|
|
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 =
|
|
98289
|
+
real = realpathSync7(cwd);
|
|
97403
98290
|
} catch {}
|
|
97404
|
-
return
|
|
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
|
|
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 =
|
|
98338
|
+
const dir = join52(projectsDir(), dirName);
|
|
97452
98339
|
let names;
|
|
97453
98340
|
try {
|
|
97454
|
-
names =
|
|
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 =
|
|
98347
|
+
const file = join52(dir, n);
|
|
97461
98348
|
try {
|
|
97462
|
-
const st =
|
|
98349
|
+
const st = statSync17(file);
|
|
97463
98350
|
if (st.size === 0)
|
|
97464
98351
|
continue;
|
|
97465
98352
|
const row = {
|
|
97466
|
-
id:
|
|
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)" :
|
|
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 =
|
|
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 =
|
|
98528
|
+
fd = openSync13(file, "r");
|
|
97642
98529
|
const buf = Buffer.allocUnsafe(len);
|
|
97643
|
-
const n =
|
|
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
|
-
|
|
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 =
|
|
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
|
|
98726
|
+
lstatSync as lstatSync7,
|
|
97840
98727
|
mkdirSync as mkdirSync24,
|
|
97841
98728
|
readFileSync as readFileSync42,
|
|
97842
|
-
readdirSync as
|
|
97843
|
-
realpathSync as
|
|
98729
|
+
readdirSync as readdirSync10,
|
|
98730
|
+
realpathSync as realpathSync8,
|
|
97844
98731
|
rmSync as rmSync12,
|
|
97845
|
-
statSync as
|
|
98732
|
+
statSync as statSync18,
|
|
97846
98733
|
writeFileSync as writeFileSync22
|
|
97847
98734
|
} from "fs";
|
|
97848
|
-
import { basename as
|
|
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
|
|
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 ??
|
|
98901
|
+
run_id: status.runId ?? basename6(path),
|
|
98015
98902
|
path,
|
|
98016
|
-
kind: status.kind ?? (
|
|
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
|
|
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(
|
|
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 =
|
|
99180
|
+
realRoot = realpathSync8(root);
|
|
98246
99181
|
} catch {
|
|
98247
99182
|
realRoot = resolve5(root);
|
|
98248
99183
|
}
|
|
98249
99184
|
let real;
|
|
98250
99185
|
try {
|
|
98251
|
-
real =
|
|
99186
|
+
real = realpathSync8(resolve5(root, path));
|
|
98252
99187
|
} catch {
|
|
98253
99188
|
return { skip: "missing" };
|
|
98254
99189
|
}
|
|
98255
|
-
if (real !== realRoot && !real.startsWith(realRoot +
|
|
99190
|
+
if (real !== realRoot && !real.startsWith(realRoot + sep2))
|
|
98256
99191
|
return { skip: "outside the working directory" };
|
|
98257
99192
|
try {
|
|
98258
|
-
const st =
|
|
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
|
-
|
|
99246
|
+
lstatSync7(existing);
|
|
98312
99247
|
break;
|
|
98313
99248
|
} catch {
|
|
98314
|
-
const parent =
|
|
99249
|
+
const parent = dirname21(existing);
|
|
98315
99250
|
if (parent === existing)
|
|
98316
99251
|
break;
|
|
98317
|
-
tail.unshift(
|
|
99252
|
+
tail.unshift(basename6(existing));
|
|
98318
99253
|
existing = parent;
|
|
98319
99254
|
}
|
|
98320
99255
|
}
|
|
98321
99256
|
let real;
|
|
98322
99257
|
try {
|
|
98323
|
-
real =
|
|
99258
|
+
real = join53(realpathSync8(existing), ...tail);
|
|
98324
99259
|
} catch {
|
|
98325
99260
|
throw outside();
|
|
98326
99261
|
}
|
|
98327
|
-
const cwd =
|
|
98328
|
-
if (real !== cwd && !real.startsWith(cwd +
|
|
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(
|
|
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(
|
|
98356
|
-
mkdirSync24(
|
|
99290
|
+
mkdirSync24(join53(sessionPath, "work"), { recursive: true });
|
|
99291
|
+
mkdirSync24(join53(sessionPath, "errors"), { recursive: true });
|
|
98357
99292
|
if (input !== undefined) {
|
|
98358
|
-
writeFileSync22(
|
|
98359
|
-
} else if (!existsSync36(
|
|
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(
|
|
99311
|
+
mkdirSync24(join53(sessionPath, "work", anonId), { recursive: true });
|
|
98377
99312
|
}
|
|
98378
|
-
writeFileSync22(
|
|
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(
|
|
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 `${
|
|
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(
|
|
98455
|
-
const statusPath =
|
|
98456
|
-
const inputContent = readFileSync42(
|
|
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) =>
|
|
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(
|
|
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(
|
|
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(
|
|
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:
|
|
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 =
|
|
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(
|
|
99888
|
+
responses[id] = readFileSync42(join53(sessionPath, file), "utf-8");
|
|
98950
99889
|
}
|
|
98951
|
-
const input = readFileSync42(
|
|
99890
|
+
const input = readFileSync42(join53(sessionPath, "input.md"), "utf-8");
|
|
98952
99891
|
const judgePrompt = buildJudgePrompt(input, responses);
|
|
98953
|
-
writeFileSync22(
|
|
99892
|
+
writeFileSync22(join53(sessionPath, "judge-prompt.md"), judgePrompt, "utf-8");
|
|
98954
99893
|
const judgeModels = opts.judges ?? getDefaultJudgeModels(sessionPath);
|
|
98955
|
-
const judgePath =
|
|
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(
|
|
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(
|
|
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(
|
|
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 =
|
|
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(
|
|
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(
|
|
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
|
|
99184
|
-
import { join as
|
|
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 =
|
|
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 (!
|
|
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
|
|
100224
|
+
closeSync as closeSync14,
|
|
99286
100225
|
createWriteStream,
|
|
99287
100226
|
existsSync as existsSync38,
|
|
99288
100227
|
mkdirSync as mkdirSync25,
|
|
99289
|
-
openSync as
|
|
100228
|
+
openSync as openSync14,
|
|
99290
100229
|
readFileSync as readFileSync44,
|
|
99291
|
-
readSync as
|
|
100230
|
+
readSync as readSync7,
|
|
99292
100231
|
renameSync as renameSync10,
|
|
99293
100232
|
rmSync as rmSync13,
|
|
99294
|
-
statSync as
|
|
100233
|
+
statSync as statSync20,
|
|
99295
100234
|
writeFileSync as writeFileSync23
|
|
99296
100235
|
} from "fs";
|
|
99297
|
-
import { join as
|
|
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 =
|
|
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 =
|
|
99408
|
-
|
|
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
|
-
|
|
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
|
|
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(
|
|
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 =
|
|
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(
|
|
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(
|
|
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(
|
|
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",
|
|
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 ??
|
|
100622
|
+
const sessionDir = opts.sessionDir ?? join55(this.sessionsDir, sessionId);
|
|
99682
100623
|
mkdirSync25(sessionDir, { recursive: true });
|
|
99683
100624
|
if (opts.prompt)
|
|
99684
|
-
writeFileSync23(
|
|
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(
|
|
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 ??
|
|
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(
|
|
100682
|
+
outputLogStream: createWriteStream(join55(sessionDir, "output.log")),
|
|
99742
100683
|
sessionDir,
|
|
99743
100684
|
cwd,
|
|
99744
100685
|
tokenFile,
|
|
99745
|
-
eventLogPath:
|
|
100686
|
+
eventLogPath: join55(sessionDir, "events.jsonl"),
|
|
99746
100687
|
eventLogBytes: 0,
|
|
99747
|
-
upstreamErrorLogPath:
|
|
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:
|
|
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(
|
|
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(
|
|
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 !==
|
|
101238
|
+
if (dir !== join55(root, sessionId))
|
|
100228
101239
|
return null;
|
|
100229
|
-
if (!dir.startsWith(root +
|
|
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 =
|
|
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(
|
|
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(
|
|
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(
|
|
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 =
|
|
100292
|
-
const upstreamErrorLogPath =
|
|
100293
|
-
const outputLogPath =
|
|
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(
|
|
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
|
|
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(
|
|
101831
|
+
const root = mkdtempSync3(join56(tmpdir2(), "claudish-run-prompt-"));
|
|
100814
101832
|
try {
|
|
100815
|
-
const 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 =
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
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 '
|
|
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 '
|
|
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 =
|
|
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(
|
|
103514
|
+
sessionData[file] = readFileSync45(join57(sp, file), "utf-8");
|
|
102472
103515
|
} catch {}
|
|
102473
103516
|
}
|
|
102474
103517
|
try {
|
|
102475
|
-
const errorDir =
|
|
103518
|
+
const errorDir = join57(sp, "errors");
|
|
102476
103519
|
if (existsSync39(errorDir)) {
|
|
102477
|
-
for (const f of
|
|
103520
|
+
for (const f of readdirSync11(errorDir)) {
|
|
102478
103521
|
if (f.endsWith(".log")) {
|
|
102479
103522
|
try {
|
|
102480
|
-
sessionData[`errors/${f}`] = readFileSync45(
|
|
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
|
|
103530
|
+
for (const f of readdirSync11(sp)) {
|
|
102488
103531
|
if (f.startsWith("response-") && f.endsWith(".md")) {
|
|
102489
103532
|
try {
|
|
102490
|
-
const content = readFileSync45(
|
|
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 =
|
|
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 =
|
|
103154
|
-
CLAUDISH_CACHE_DIR =
|
|
103155
|
-
OPENROUTER_MODELS_CACHE_PATH =
|
|
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
|
|
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
|
|
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 =
|
|
104430
|
+
projects = readdirSync12(root);
|
|
103361
104431
|
} catch {
|
|
103362
104432
|
return files;
|
|
103363
104433
|
}
|
|
103364
104434
|
for (const project of projects) {
|
|
103365
|
-
const dir =
|
|
104435
|
+
const dir = join58(root, project);
|
|
103366
104436
|
try {
|
|
103367
|
-
if (!
|
|
104437
|
+
if (!statSync21(dir).isDirectory())
|
|
103368
104438
|
continue;
|
|
103369
|
-
for (const f of
|
|
104439
|
+
for (const f of readdirSync12(dir)) {
|
|
103370
104440
|
if (f.endsWith(".jsonl"))
|
|
103371
|
-
files.push(
|
|
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 ??
|
|
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 ??
|
|
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
|
|
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) :
|
|
104595
|
+
const dir = dirArg ? resolve9(process.cwd(), dirArg) : join59(homedir39(), ".claudish", "hooks");
|
|
103526
104596
|
const files = [
|
|
103527
|
-
[
|
|
103528
|
-
[
|
|
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
|
|
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 =
|
|
103882
|
-
const prompt = readFileSync49(
|
|
103883
|
-
const rawPrompt = readFileSync49(
|
|
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 =
|
|
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
|
|
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(
|
|
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(
|
|
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
|
|
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 =
|
|
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
|
|
114423
|
-
import { join as
|
|
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,
|
|
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
|
|
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
|
|
116813
|
+
closeSync as closeSync15,
|
|
115744
116814
|
existsSync as existsSync46,
|
|
115745
116815
|
mkdirSync as mkdirSync28,
|
|
115746
116816
|
mkdtempSync as mkdtempSync4,
|
|
115747
|
-
openSync as
|
|
116817
|
+
openSync as openSync15,
|
|
115748
116818
|
readFileSync as readFileSync51,
|
|
115749
|
-
readdirSync as
|
|
116819
|
+
readdirSync as readdirSync13,
|
|
115750
116820
|
rmSync as rmSync15,
|
|
115751
|
-
statSync as
|
|
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
|
|
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(
|
|
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
|
-
|
|
115929
|
-
...env.CLAUDE_CONFIG_DIR ? [
|
|
115930
|
-
|
|
115931
|
-
|
|
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
|
|
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 =
|
|
115985
|
-
const scriptPath =
|
|
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(
|
|
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 =
|
|
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 =
|
|
117247
|
+
const full = join64(dir, name);
|
|
116178
117248
|
try {
|
|
116179
|
-
if (
|
|
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
|
-
|
|
116207
|
-
|
|
116208
|
-
|
|
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 =
|
|
117319
|
+
const claudishDir = join64(homeDir, ".claudish");
|
|
116250
117320
|
try {
|
|
116251
117321
|
mkdirSync28(claudishDir, { recursive: true });
|
|
116252
117322
|
} catch {}
|
|
116253
|
-
const tempPath =
|
|
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() ?
|
|
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 =
|
|
117725
|
+
const fd = openSync15("/dev/fd/0", "r+");
|
|
116656
117726
|
if (isatty(fd)) {
|
|
116657
117727
|
ttyFd = fd;
|
|
116658
117728
|
} else {
|
|
116659
|
-
|
|
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(
|
|
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
|
-
|
|
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 ?
|
|
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
|
-
|
|
116835
|
-
|
|
116836
|
-
|
|
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
|
-
|
|
116848
|
-
|
|
116849
|
-
|
|
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
|
-
|
|
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
|
|
118030
|
+
import { join as join65 } from "path";
|
|
116961
118031
|
function getClaudishDir() {
|
|
116962
|
-
const dir =
|
|
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
|
|
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
|
|
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 =
|
|
117590
|
-
fd =
|
|
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 =
|
|
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
|
-
|
|
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
|
|
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(
|
|
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 =
|
|
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 =
|
|
121086
|
+
const responsePath = join66(sessionPath, `response-${anonId}.md`);
|
|
120017
121087
|
try {
|
|
120018
121088
|
const raw = readFileSync53(responsePath, "utf-8").trim();
|
|
120019
121089
|
try {
|