claudish 14.11.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 +1531 -232
- 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",
|
|
@@ -73048,7 +73173,9 @@ var init_contract = __esm(() => {
|
|
|
73048
73173
|
"capture_since_seq",
|
|
73049
73174
|
"capture_spans",
|
|
73050
73175
|
"resize",
|
|
73051
|
-
"allowance"
|
|
73176
|
+
"allowance",
|
|
73177
|
+
"scroll",
|
|
73178
|
+
"transcript"
|
|
73052
73179
|
];
|
|
73053
73180
|
SLOT_STATES = [
|
|
73054
73181
|
"STARTING",
|
|
@@ -93473,6 +93600,39 @@ var init_screen_classifier = __esm(() => {
|
|
|
93473
93600
|
NON_ANSWER_ROW = /^\s+\u23BF|^\s{2}(Ran|Read|Searched|Wrote|Edited|Listed|Updated) \d+ |^\s*[\u00B7\u2722\u2733\u2736\u273B\u273D*\u25CF\u2217]\s|^\s*\(ctrl\+/;
|
|
93474
93601
|
});
|
|
93475
93602
|
|
|
93603
|
+
// src/pane/scroll-input.ts
|
|
93604
|
+
function isLiveBottom(pos) {
|
|
93605
|
+
return pos.atBottom && pos.lastDirection === null;
|
|
93606
|
+
}
|
|
93607
|
+
function scrollRowsOf(by) {
|
|
93608
|
+
if (!Number.isFinite(by))
|
|
93609
|
+
return 0;
|
|
93610
|
+
return Math.min(Math.abs(Math.trunc(by)), MAX_SCROLL_ROWS);
|
|
93611
|
+
}
|
|
93612
|
+
function planScroll(pos, req) {
|
|
93613
|
+
if ("to" in req)
|
|
93614
|
+
return { bytes: CTRL_END, events: 1, next: LIVE_BOTTOM };
|
|
93615
|
+
const n = scrollRowsOf(req.by);
|
|
93616
|
+
if (n === 0)
|
|
93617
|
+
return { bytes: "", events: 0, next: pos };
|
|
93618
|
+
const dir = req.by < 0 ? "up" : "down";
|
|
93619
|
+
const extra = pos.atBottom || pos.lastDirection !== dir ? 1 : 0;
|
|
93620
|
+
const events = n + extra;
|
|
93621
|
+
return {
|
|
93622
|
+
bytes: (dir === "up" ? WHEEL_UP : WHEEL_DOWN).repeat(events),
|
|
93623
|
+
events,
|
|
93624
|
+
next: { atBottom: false, lastDirection: dir }
|
|
93625
|
+
};
|
|
93626
|
+
}
|
|
93627
|
+
var MAX_SCROLL_ROWS = 200, WHEEL_UP = "\x1B[<64;1;1M", WHEEL_DOWN = "\x1B[<65;1;1M", CTRL_END = "\x1B[1;5F", LIVE_BOTTOM, UNKNOWN_POSITION;
|
|
93628
|
+
var init_scroll_input = __esm(() => {
|
|
93629
|
+
LIVE_BOTTOM = Object.freeze({ atBottom: true, lastDirection: null });
|
|
93630
|
+
UNKNOWN_POSITION = Object.freeze({
|
|
93631
|
+
atBottom: false,
|
|
93632
|
+
lastDirection: null
|
|
93633
|
+
});
|
|
93634
|
+
});
|
|
93635
|
+
|
|
93476
93636
|
// src/pane/session-registry.ts
|
|
93477
93637
|
import { readFileSync as readFileSync40, readdirSync as readdirSync6 } from "fs";
|
|
93478
93638
|
import { join as join48 } from "path";
|
|
@@ -94411,7 +94571,7 @@ class TranscriptFollower {
|
|
|
94411
94571
|
let any = false;
|
|
94412
94572
|
for (const { offset, line } of this.main.read()) {
|
|
94413
94573
|
any = true;
|
|
94414
|
-
const rec =
|
|
94574
|
+
const rec = parseRecord3(line);
|
|
94415
94575
|
if (rec)
|
|
94416
94576
|
applyRecord(this.state, rec, offset + 1);
|
|
94417
94577
|
}
|
|
@@ -94440,7 +94600,7 @@ class TranscriptFollower {
|
|
|
94440
94600
|
}
|
|
94441
94601
|
for (const { line } of tail.read()) {
|
|
94442
94602
|
any = true;
|
|
94443
|
-
const rec =
|
|
94603
|
+
const rec = parseRecord3(line);
|
|
94444
94604
|
if (rec)
|
|
94445
94605
|
applySubagentRecord(this.state, rec, this.main.offset + 1);
|
|
94446
94606
|
}
|
|
@@ -94502,7 +94662,7 @@ function lastHumanPromptOffset(path) {
|
|
|
94502
94662
|
const tail = new Tail(path);
|
|
94503
94663
|
let found = null;
|
|
94504
94664
|
for (const { offset, line } of tail.read()) {
|
|
94505
|
-
const rec =
|
|
94665
|
+
const rec = parseRecord3(line);
|
|
94506
94666
|
if (rec && isHumanPrompt(rec))
|
|
94507
94667
|
found = offset;
|
|
94508
94668
|
}
|
|
@@ -94515,7 +94675,7 @@ function fileSize(p) {
|
|
|
94515
94675
|
return 0;
|
|
94516
94676
|
}
|
|
94517
94677
|
}
|
|
94518
|
-
function
|
|
94678
|
+
function parseRecord3(line) {
|
|
94519
94679
|
if (!line.trim())
|
|
94520
94680
|
return null;
|
|
94521
94681
|
try {
|
|
@@ -94617,6 +94777,9 @@ class PaneSessionImpl {
|
|
|
94617
94777
|
resizeQuietFrom = 0;
|
|
94618
94778
|
resizeQuietUntil = 0;
|
|
94619
94779
|
activityChangedAt = 0;
|
|
94780
|
+
scrollPos = LIVE_BOTTOM;
|
|
94781
|
+
scrollEndsInFlight = 0;
|
|
94782
|
+
scrollEndConfirmedAt = 0;
|
|
94620
94783
|
gate = null;
|
|
94621
94784
|
gateTrusted = false;
|
|
94622
94785
|
gateInFlight = false;
|
|
@@ -94649,6 +94812,7 @@ class PaneSessionImpl {
|
|
|
94649
94812
|
reaping = false;
|
|
94650
94813
|
fastReap = false;
|
|
94651
94814
|
reapPromise = null;
|
|
94815
|
+
verifiedReap = Promise.withResolvers();
|
|
94652
94816
|
reconnecting = false;
|
|
94653
94817
|
depth = 0;
|
|
94654
94818
|
stepFrom = null;
|
|
@@ -95407,8 +95571,8 @@ ${gate}`
|
|
|
95407
95571
|
await this.pause(adm, TICK_MS);
|
|
95408
95572
|
continue;
|
|
95409
95573
|
}
|
|
95410
|
-
if (!this.
|
|
95411
|
-
await this.
|
|
95574
|
+
if (!this.trustedConnection() || this.needsAdmissionLiveBottom(adm)) {
|
|
95575
|
+
await this.connectionStep(adm);
|
|
95412
95576
|
continue;
|
|
95413
95577
|
}
|
|
95414
95578
|
if (await this.beforeSend(adm) !== "send")
|
|
@@ -95420,8 +95584,22 @@ ${gate}`
|
|
|
95420
95584
|
adm.inFlight = false;
|
|
95421
95585
|
}
|
|
95422
95586
|
}
|
|
95587
|
+
needsAdmissionLiveBottom(adm) {
|
|
95588
|
+
if (adm.liveBottomClient === this.client)
|
|
95589
|
+
return false;
|
|
95590
|
+
adm.liveBottomClient = this.client;
|
|
95591
|
+
return !isLiveBottom(this.scrollPos);
|
|
95592
|
+
}
|
|
95593
|
+
trustedConnection() {
|
|
95594
|
+
return !!this.client && !this.client.isClosed && this.gateTrusted;
|
|
95595
|
+
}
|
|
95596
|
+
connectionStep(adm) {
|
|
95597
|
+
if (!this.trustedConnection())
|
|
95598
|
+
return this.awaitTrustedClient(adm);
|
|
95599
|
+
return this.returnToLiveBottom(Math.max(0, adm.deadline - Date.now()));
|
|
95600
|
+
}
|
|
95423
95601
|
awaitTrustedClient(adm) {
|
|
95424
|
-
return this.waitFor(() => !this.admitting(adm) ||
|
|
95602
|
+
return this.waitFor(() => !this.admitting(adm) || this.trustedConnection(), Math.max(0, adm.deadline - Date.now()));
|
|
95425
95603
|
}
|
|
95426
95604
|
async attemptOnce(adm) {
|
|
95427
95605
|
const c = this.client;
|
|
@@ -95846,6 +96024,7 @@ ${screenTailOf(this.screen)}` });
|
|
|
95846
96024
|
accept() {
|
|
95847
96025
|
this.admission = null;
|
|
95848
96026
|
this.initialDelivery = false;
|
|
96027
|
+
this.scrollPos = LIVE_BOTTOM;
|
|
95849
96028
|
const now = Date.now();
|
|
95850
96029
|
this.lastChatSeenAt = now;
|
|
95851
96030
|
this.turnAcceptedAt = now;
|
|
@@ -95922,6 +96101,7 @@ ${screenTailOf(this.screen)}`
|
|
|
95922
96101
|
const d = this.currentDelivery;
|
|
95923
96102
|
this.turnSource = "screen";
|
|
95924
96103
|
this.anomaly("degraded_mode");
|
|
96104
|
+
this.returnToLiveBottom(SCROLL_REPLY_MS);
|
|
95925
96105
|
this.screenTurn = {
|
|
95926
96106
|
echo: d && d.mode !== "refused" ? d.line : "",
|
|
95927
96107
|
acceptedAt: at,
|
|
@@ -96399,11 +96579,21 @@ ${screenTailOf(this.screen)}`
|
|
|
96399
96579
|
const g = this.gateNow();
|
|
96400
96580
|
if (!g || !g.ready || g.dialog !== null || workingAboveBox(this.screen) === true)
|
|
96401
96581
|
return false;
|
|
96402
|
-
|
|
96582
|
+
let quiet = this.aboveBoxQuietMs() ?? Date.now() - this.screen.changedAt;
|
|
96583
|
+
if (this.scrollEndConfirmedAt > 0)
|
|
96584
|
+
quiet = Math.min(quiet, Date.now() - this.scrollEndConfirmedAt);
|
|
96403
96585
|
if (view.availability === "off" && quiet >= this.secondaryQuietMs)
|
|
96404
96586
|
return true;
|
|
96405
96587
|
return (!!answer || !!apiError) && quiet >= 2 * this.t("screenSettleQuietMs", SCREEN_SETTLE_QUIET_MS);
|
|
96406
96588
|
}
|
|
96589
|
+
screenViewLive() {
|
|
96590
|
+
if (this.scrollEndsInFlight > 0)
|
|
96591
|
+
return false;
|
|
96592
|
+
if (isLiveBottom(this.scrollPos))
|
|
96593
|
+
return true;
|
|
96594
|
+
this.returnToLiveBottom(SCROLL_REPLY_MS);
|
|
96595
|
+
return false;
|
|
96596
|
+
}
|
|
96407
96597
|
evalScreenTurn(view) {
|
|
96408
96598
|
if (this.witnessed(view.current)) {
|
|
96409
96599
|
this.turnSource = "transcript";
|
|
@@ -96418,7 +96608,7 @@ ${screenTailOf(this.screen)}`
|
|
|
96418
96608
|
const answer = screenAnswer(this.screen, st.echo);
|
|
96419
96609
|
const apiError = this.screenApiError(st, answer);
|
|
96420
96610
|
this.activity = "thinking";
|
|
96421
|
-
if (!this.screenSettleReady(view, answer, apiError))
|
|
96611
|
+
if (!this.screenViewLive() || !this.screenSettleReady(view, answer, apiError))
|
|
96422
96612
|
return;
|
|
96423
96613
|
this.follower.poll();
|
|
96424
96614
|
if (this.witnessed(this.follower.view().current))
|
|
@@ -96749,6 +96939,69 @@ ${screenTailOf(this.screen)}`
|
|
|
96749
96939
|
answer({ ...set, changed: true, seq: this.screen.seq });
|
|
96750
96940
|
}
|
|
96751
96941
|
}
|
|
96942
|
+
async scroll(req) {
|
|
96943
|
+
const refusal = this.scrollRefusal();
|
|
96944
|
+
if (refusal)
|
|
96945
|
+
throw new ContractErrorException("scroll_unavailable", refusal);
|
|
96946
|
+
const c = this.client;
|
|
96947
|
+
if (!c || c.isClosed)
|
|
96948
|
+
throw new ContractErrorException("scroll_unavailable", "the pane's socket is reconnecting");
|
|
96949
|
+
const plan = planScroll(this.scrollPos, req);
|
|
96950
|
+
if (plan.bytes === "")
|
|
96951
|
+
return { seq: this.screen.seq };
|
|
96952
|
+
this.scrollPos = plan.next;
|
|
96953
|
+
const seq0 = this.screen.seq;
|
|
96954
|
+
this.extendResizeQuiet();
|
|
96955
|
+
const r = await c.request({ type: "send", pane: 0, text: plan.bytes, enter: false, noGate: true }, SCROLL_REPLY_MS);
|
|
96956
|
+
if (!r.ok) {
|
|
96957
|
+
this.scrollPos = UNKNOWN_POSITION;
|
|
96958
|
+
if (r.code === "client_lost" || r.code === "client_timeout")
|
|
96959
|
+
this.extendResizeQuiet();
|
|
96960
|
+
throw new ContractErrorException("scroll_unavailable", `magmux did not confirm the scroll: ${r.code}${r.error ? `: ${r.error}` : ""}`);
|
|
96961
|
+
}
|
|
96962
|
+
this.extendResizeQuiet();
|
|
96963
|
+
await this.waitFor(() => isTerminalPhase(this.phase) || this.screen.seq !== seq0, this.t("scrollScreenMs", SCROLL_SCREEN_MS));
|
|
96964
|
+
return { seq: this.screen.seq };
|
|
96965
|
+
}
|
|
96966
|
+
screenTurnActive() {
|
|
96967
|
+
if (this.turnSource !== "screen")
|
|
96968
|
+
return false;
|
|
96969
|
+
return this.phase === "RUNNING" || this.phase === "QUESTION" || this.phase === "PERMISSION";
|
|
96970
|
+
}
|
|
96971
|
+
scrollRefusal() {
|
|
96972
|
+
if (isTerminalPhase(this.phase) || this.final)
|
|
96973
|
+
return "the session has finished";
|
|
96974
|
+
if (this.phase === "BOOTING" || this.screen.seq === 0)
|
|
96975
|
+
return "the session is still starting";
|
|
96976
|
+
if (this.phase === "ADMITTING")
|
|
96977
|
+
return "a prompt is being delivered to the session";
|
|
96978
|
+
if (this.screenTurnActive())
|
|
96979
|
+
return "the session's turn is being read from its screen (no transcript)";
|
|
96980
|
+
if (!this.client || this.client.isClosed || !this.gateTrusted)
|
|
96981
|
+
return "the pane's socket is reconnecting";
|
|
96982
|
+
return null;
|
|
96983
|
+
}
|
|
96984
|
+
async returnToLiveBottom(ms) {
|
|
96985
|
+
if (isLiveBottom(this.scrollPos))
|
|
96986
|
+
return;
|
|
96987
|
+
const c = this.client;
|
|
96988
|
+
if (!c || c.isClosed || !this.gateTrusted || ms <= 0)
|
|
96989
|
+
return;
|
|
96990
|
+
this.scrollPos = LIVE_BOTTOM;
|
|
96991
|
+
this.extendResizeQuiet();
|
|
96992
|
+
this.scrollEndsInFlight++;
|
|
96993
|
+
try {
|
|
96994
|
+
const r = await c.request({ type: "send", pane: 0, text: CTRL_END, enter: false, noGate: true }, Math.min(SCROLL_REPLY_MS, ms));
|
|
96995
|
+
if (r.ok) {
|
|
96996
|
+
this.scrollEndConfirmedAt = Date.now();
|
|
96997
|
+
} else {
|
|
96998
|
+
this.scrollPos = UNKNOWN_POSITION;
|
|
96999
|
+
this.anomaly(`scroll_end_unconfirmed:${r.code}`);
|
|
97000
|
+
}
|
|
97001
|
+
} finally {
|
|
97002
|
+
this.scrollEndsInFlight--;
|
|
97003
|
+
}
|
|
97004
|
+
}
|
|
96752
97005
|
turnAnswer(index) {
|
|
96753
97006
|
const done = this.answers.get(index);
|
|
96754
97007
|
if (done !== undefined)
|
|
@@ -96806,6 +97059,9 @@ ${screenTailOf(this.screen)}`
|
|
|
96806
97059
|
reaped() {
|
|
96807
97060
|
return this.reapPromise ?? this.terminal.then(() => this.reapPromise ?? Promise.resolve());
|
|
96808
97061
|
}
|
|
97062
|
+
reapVerified() {
|
|
97063
|
+
return this.verifiedReap.promise;
|
|
97064
|
+
}
|
|
96809
97065
|
panePid() {
|
|
96810
97066
|
return this.panePidValue;
|
|
96811
97067
|
}
|
|
@@ -96932,6 +97188,7 @@ ${screenTailOf(this.screen)}`
|
|
|
96932
97188
|
if (groupGone && magGone) {
|
|
96933
97189
|
removePaneFiles(this.root, this.files());
|
|
96934
97190
|
this.reapFailed = false;
|
|
97191
|
+
this.verifiedReap.resolve();
|
|
96935
97192
|
await this.endWatcher();
|
|
96936
97193
|
unregisterPane(this.paneId);
|
|
96937
97194
|
return;
|
|
@@ -97001,7 +97258,8 @@ function newAdmission(a) {
|
|
|
97001
97258
|
clearAfter: 0,
|
|
97002
97259
|
lostType: null,
|
|
97003
97260
|
last: null,
|
|
97004
|
-
noted: new Set
|
|
97261
|
+
noted: new Set,
|
|
97262
|
+
liveBottomClient: null
|
|
97005
97263
|
};
|
|
97006
97264
|
}
|
|
97007
97265
|
function markClearFirst(adm) {
|
|
@@ -97168,7 +97426,7 @@ async function startPaneSession(o) {
|
|
|
97168
97426
|
await session.start({ magmuxBinary: magmux.binary, env: p.magmuxEnv, cwd: p.cwd });
|
|
97169
97427
|
return session;
|
|
97170
97428
|
}
|
|
97171
|
-
var BOOT_TIMEOUT_MS = 90000, ADMIT_TIMEOUT_MS = 30000, DEGRADED_ENTRY_MS = 1e4, SCREEN_SETTLE_QUIET_MS = 3000, PANEL_SETTLE_MS = 500, SEND_CLIENT_SLACK_MS = 2000, MIN_READY_TIMEOUT_MS = 1000, BUSY_RETRY_MS = 250, MISMATCH_SETTLE_MS = 1000, CLEAR_WAIT_MS = 1000, BOOT_STATIC_MS = 5000, TICK_MS = 250, WATCH_FPS = 4, POLL_BACKSTOP_MS = 1000, TURN_END_MISSING_FACTOR = 3, RESIZE_REPLY_MS = 1e4, RESIZE_SCREEN_MS = 1000, GATE_POLL_MS = 250, GATE_REPLY_MS = 1000, GATE_STALE_MS2 = 1000, GATE_GRACE_MS, GATE_DEAD_BACKSTOP_MS = 1000, EXIT_CODE_QUERY_MS = 2000, EXIT_CODE_RETRY_MS = 50, GATE_KINDLESS_REASONS, RUNTIME_DIALOG_KINDS, REGISTRY_READ_MS = 250, REGISTRY_DISAGREE_MS = 2000, REGISTRY_GATE_BOOT_DISAGREE_MS = 5000, REGISTRY_MISSING_MS = 5000, MAX_ANOMALY_KEYS = 64, KEPT_ANSWERS = 4, DEFAULT_MAX_CONSECUTIVE = 3, EVENT_REASON, VERDICT_EVENT, PaneStartError;
|
|
97429
|
+
var BOOT_TIMEOUT_MS = 90000, ADMIT_TIMEOUT_MS = 30000, DEGRADED_ENTRY_MS = 1e4, SCREEN_SETTLE_QUIET_MS = 3000, PANEL_SETTLE_MS = 500, SEND_CLIENT_SLACK_MS = 2000, MIN_READY_TIMEOUT_MS = 1000, BUSY_RETRY_MS = 250, MISMATCH_SETTLE_MS = 1000, CLEAR_WAIT_MS = 1000, BOOT_STATIC_MS = 5000, TICK_MS = 250, WATCH_FPS = 4, POLL_BACKSTOP_MS = 1000, TURN_END_MISSING_FACTOR = 3, RESIZE_REPLY_MS = 1e4, RESIZE_SCREEN_MS = 1000, SCROLL_REPLY_MS = 5000, SCROLL_SCREEN_MS = 1000, GATE_POLL_MS = 250, GATE_REPLY_MS = 1000, GATE_STALE_MS2 = 1000, GATE_GRACE_MS, GATE_DEAD_BACKSTOP_MS = 1000, EXIT_CODE_QUERY_MS = 2000, EXIT_CODE_RETRY_MS = 50, GATE_KINDLESS_REASONS, RUNTIME_DIALOG_KINDS, REGISTRY_READ_MS = 250, REGISTRY_DISAGREE_MS = 2000, REGISTRY_GATE_BOOT_DISAGREE_MS = 5000, REGISTRY_MISSING_MS = 5000, MAX_ANOMALY_KEYS = 64, KEPT_ANSWERS = 4, DEFAULT_MAX_CONSECUTIVE = 3, EVENT_REASON, VERDICT_EVENT, PaneStartError;
|
|
97172
97430
|
var init_pane_session = __esm(() => {
|
|
97173
97431
|
init_queue();
|
|
97174
97432
|
init_parent_session();
|
|
@@ -97185,6 +97443,7 @@ var init_pane_session = __esm(() => {
|
|
|
97185
97443
|
init_prompt_delivery();
|
|
97186
97444
|
init_screen_classifier();
|
|
97187
97445
|
init_screen_model();
|
|
97446
|
+
init_scroll_input();
|
|
97188
97447
|
init_session_registry();
|
|
97189
97448
|
init_settle_rule();
|
|
97190
97449
|
init_slot_state();
|
|
@@ -97223,6 +97482,759 @@ var init_pane_session = __esm(() => {
|
|
|
97223
97482
|
};
|
|
97224
97483
|
});
|
|
97225
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
|
+
|
|
97226
98238
|
// src/pane/types.ts
|
|
97227
98239
|
function chainAnswerOf(t) {
|
|
97228
98240
|
return [...t.chainAnswers, t.answer].filter((a) => a !== "").join(`
|
|
@@ -97239,6 +98251,10 @@ var init_pane = __esm(() => {
|
|
|
97239
98251
|
init_pane_launch();
|
|
97240
98252
|
init_pane_registry();
|
|
97241
98253
|
init_pane_session();
|
|
98254
|
+
init_scroll_input();
|
|
98255
|
+
init_transcript_lines();
|
|
98256
|
+
init_transcript_redaction();
|
|
98257
|
+
init_transcript_page();
|
|
97242
98258
|
init_contract();
|
|
97243
98259
|
});
|
|
97244
98260
|
|
|
@@ -97259,10 +98275,10 @@ __export(exports_session_discovery, {
|
|
|
97259
98275
|
transcriptPathFor: () => transcriptPathFor
|
|
97260
98276
|
});
|
|
97261
98277
|
import { execFile as execFile4, execFileSync as execFileSync3 } from "child_process";
|
|
97262
|
-
import { closeSync as
|
|
97263
|
-
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";
|
|
97264
98280
|
function projectsDir(env = process.env) {
|
|
97265
|
-
return
|
|
98281
|
+
return join52(claudeConfigDir(env), "projects");
|
|
97266
98282
|
}
|
|
97267
98283
|
function slugForPath(absPath) {
|
|
97268
98284
|
return projectDirNameFor(absPath);
|
|
@@ -97270,9 +98286,9 @@ function slugForPath(absPath) {
|
|
|
97270
98286
|
function transcriptPathFor(cwd, sessionUuid, projectsRoot = projectsDir()) {
|
|
97271
98287
|
let real = cwd;
|
|
97272
98288
|
try {
|
|
97273
|
-
real =
|
|
98289
|
+
real = realpathSync7(cwd);
|
|
97274
98290
|
} catch {}
|
|
97275
|
-
return
|
|
98291
|
+
return join52(projectsRoot, slugForPath(real), `${sessionUuid}.jsonl`);
|
|
97276
98292
|
}
|
|
97277
98293
|
function isAgentSession(row) {
|
|
97278
98294
|
return row.entrypoint !== undefined && row.entrypoint !== "cli";
|
|
@@ -97313,28 +98329,28 @@ function getRepoContext(cwd = process.cwd()) {
|
|
|
97313
98329
|
}
|
|
97314
98330
|
function projectDirs() {
|
|
97315
98331
|
try {
|
|
97316
|
-
return
|
|
98332
|
+
return readdirSync9(projectsDir(), { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
97317
98333
|
} catch {
|
|
97318
98334
|
return [];
|
|
97319
98335
|
}
|
|
97320
98336
|
}
|
|
97321
98337
|
function sessionsIn2(dirName) {
|
|
97322
|
-
const dir =
|
|
98338
|
+
const dir = join52(projectsDir(), dirName);
|
|
97323
98339
|
let names;
|
|
97324
98340
|
try {
|
|
97325
|
-
names =
|
|
98341
|
+
names = readdirSync9(dir).filter((n) => n.endsWith(".jsonl"));
|
|
97326
98342
|
} catch {
|
|
97327
98343
|
return [];
|
|
97328
98344
|
}
|
|
97329
98345
|
const rows = [];
|
|
97330
98346
|
for (const n of names) {
|
|
97331
|
-
const file =
|
|
98347
|
+
const file = join52(dir, n);
|
|
97332
98348
|
try {
|
|
97333
|
-
const st =
|
|
98349
|
+
const st = statSync17(file);
|
|
97334
98350
|
if (st.size === 0)
|
|
97335
98351
|
continue;
|
|
97336
98352
|
const row = {
|
|
97337
|
-
id:
|
|
98353
|
+
id: basename5(n, ".jsonl"),
|
|
97338
98354
|
file,
|
|
97339
98355
|
mtimeMs: st.mtimeMs,
|
|
97340
98356
|
birthtimeMs: st.birthtimeMs,
|
|
@@ -97436,7 +98452,7 @@ function discoverWorktreeGroups(repo) {
|
|
|
97436
98452
|
if (hit) {
|
|
97437
98453
|
path = hit.path;
|
|
97438
98454
|
live = true;
|
|
97439
|
-
name = hit.path === repo.root ? "(root)" :
|
|
98455
|
+
name = hit.path === repo.root ? "(root)" : basename5(hit.path);
|
|
97440
98456
|
} else if (at !== -1 && dir.startsWith(rootSlug)) {
|
|
97441
98457
|
path = null;
|
|
97442
98458
|
live = false;
|
|
@@ -97491,7 +98507,7 @@ function discoverWorktreeGroups(repo) {
|
|
|
97491
98507
|
g.activeNow = g.sessions.some((s) => isActive(s));
|
|
97492
98508
|
if (g.path) {
|
|
97493
98509
|
try {
|
|
97494
|
-
g.createdMs =
|
|
98510
|
+
g.createdMs = statSync17(g.path).birthtimeMs;
|
|
97495
98511
|
} catch {}
|
|
97496
98512
|
}
|
|
97497
98513
|
if (!g.createdMs && g.sessions.length > 0) {
|
|
@@ -97509,16 +98525,16 @@ function readChunk(file, pos, len) {
|
|
|
97509
98525
|
return "";
|
|
97510
98526
|
let fd = null;
|
|
97511
98527
|
try {
|
|
97512
|
-
fd =
|
|
98528
|
+
fd = openSync13(file, "r");
|
|
97513
98529
|
const buf = Buffer.allocUnsafe(len);
|
|
97514
|
-
const n =
|
|
98530
|
+
const n = readSync6(fd, buf, 0, len, pos);
|
|
97515
98531
|
return buf.subarray(0, n).toString("utf-8");
|
|
97516
98532
|
} catch {
|
|
97517
98533
|
return "";
|
|
97518
98534
|
} finally {
|
|
97519
98535
|
if (fd !== null) {
|
|
97520
98536
|
try {
|
|
97521
|
-
|
|
98537
|
+
closeSync13(fd);
|
|
97522
98538
|
} catch {}
|
|
97523
98539
|
}
|
|
97524
98540
|
}
|
|
@@ -97579,7 +98595,7 @@ function hydrateSession(row) {
|
|
|
97579
98595
|
row.hydrated = true;
|
|
97580
98596
|
if (isActive(row)) {
|
|
97581
98597
|
try {
|
|
97582
|
-
row.sizeBytes =
|
|
98598
|
+
row.sizeBytes = statSync17(row.file).size;
|
|
97583
98599
|
} catch {}
|
|
97584
98600
|
}
|
|
97585
98601
|
const head = parseRecords(readChunk(row.file, 0, Math.min(HEAD_BYTES, row.sizeBytes)), false);
|
|
@@ -97707,16 +98723,16 @@ var init_session_discovery = __esm(() => {
|
|
|
97707
98723
|
import { randomBytes as randomBytes13, randomUUID as randomUUID8 } from "crypto";
|
|
97708
98724
|
import {
|
|
97709
98725
|
existsSync as existsSync36,
|
|
97710
|
-
lstatSync as
|
|
98726
|
+
lstatSync as lstatSync7,
|
|
97711
98727
|
mkdirSync as mkdirSync24,
|
|
97712
98728
|
readFileSync as readFileSync42,
|
|
97713
|
-
readdirSync as
|
|
97714
|
-
realpathSync as
|
|
98729
|
+
readdirSync as readdirSync10,
|
|
98730
|
+
realpathSync as realpathSync8,
|
|
97715
98731
|
rmSync as rmSync12,
|
|
97716
|
-
statSync as
|
|
98732
|
+
statSync as statSync18,
|
|
97717
98733
|
writeFileSync as writeFileSync22
|
|
97718
98734
|
} from "fs";
|
|
97719
|
-
import { basename as
|
|
98735
|
+
import { basename as basename6, dirname as dirname21, join as join53, resolve as resolve5, sep as sep2 } from "path";
|
|
97720
98736
|
function slotTerminal(e) {
|
|
97721
98737
|
if (!e.session)
|
|
97722
98738
|
return true;
|
|
@@ -97785,7 +98801,7 @@ function notableAnomalies(snap) {
|
|
|
97785
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"));
|
|
97786
98802
|
}
|
|
97787
98803
|
function upstreamLogPath(path, id) {
|
|
97788
|
-
return
|
|
98804
|
+
return join53(path, "errors", `${id}-upstream.jsonl`);
|
|
97789
98805
|
}
|
|
97790
98806
|
function neverSpawnedRow(e, m) {
|
|
97791
98807
|
return {
|
|
@@ -97882,9 +98898,9 @@ function teamRunRowFromDisk(path, status) {
|
|
|
97882
98898
|
const settled = slots.length > 0 && slots.every((s) => isTerminalState(s.state));
|
|
97883
98899
|
const completions = Object.values(status.models ?? {}).map((m) => m?.completedAt ? Date.parse(m.completedAt) : Number.NaN).filter(Number.isFinite);
|
|
97884
98900
|
return {
|
|
97885
|
-
run_id: status.runId ??
|
|
98901
|
+
run_id: status.runId ?? basename6(path),
|
|
97886
98902
|
path,
|
|
97887
|
-
kind: status.kind ?? (
|
|
98903
|
+
kind: status.kind ?? (basename6(path) === "judging" ? "judge" : "run"),
|
|
97888
98904
|
started_at: status.startedAt,
|
|
97889
98905
|
finished_at: settled ? new Date(completions.length ? Math.max(...completions) : statusMtimeMs(path) ?? Date.parse(status.startedAt)).toISOString() : null,
|
|
97890
98906
|
state: settled ? "SETTLED" : "ACTIVE",
|
|
@@ -97894,14 +98910,14 @@ function teamRunRowFromDisk(path, status) {
|
|
|
97894
98910
|
}
|
|
97895
98911
|
function statusMtimeMs(path) {
|
|
97896
98912
|
try {
|
|
97897
|
-
return
|
|
98913
|
+
return statSync18(join53(path, "status.json")).mtimeMs;
|
|
97898
98914
|
} catch {
|
|
97899
98915
|
return null;
|
|
97900
98916
|
}
|
|
97901
98917
|
}
|
|
97902
98918
|
function readManifestOrNull(path) {
|
|
97903
98919
|
try {
|
|
97904
|
-
return JSON.parse(readFileSync42(
|
|
98920
|
+
return JSON.parse(readFileSync42(join53(path, "manifest.json"), "utf-8"));
|
|
97905
98921
|
} catch {
|
|
97906
98922
|
return null;
|
|
97907
98923
|
}
|
|
@@ -97967,6 +98983,68 @@ async function resizeTeamSlot(path, slot, cols, rows, runId) {
|
|
|
97967
98983
|
return { ...where, cols: DEFAULT_PANE_COLS, rows: DEFAULT_PANE_ROWS, changed: false, seq: 0 };
|
|
97968
98984
|
return { ...where, ...await e.session.resize(cols, rows) };
|
|
97969
98985
|
}
|
|
98986
|
+
async function scrollTeamSlot(path, slot, req, runId) {
|
|
98987
|
+
const run = addressRun(path, runId);
|
|
98988
|
+
if (!run)
|
|
98989
|
+
throw unknownRun(path, runId);
|
|
98990
|
+
const e = run.slots.get(slot);
|
|
98991
|
+
if (!e)
|
|
98992
|
+
throw new ContractErrorException("unknown_slot", `run ${run.runId} has no slot ${JSON.stringify(slot)}`);
|
|
98993
|
+
if (!e.session) {
|
|
98994
|
+
const state = run.status.models[e.id]?.state;
|
|
98995
|
+
throw new ContractErrorException("scroll_unavailable", state === undefined || isTerminalState(state) ? "the slot has finished" : "the slot is still starting");
|
|
98996
|
+
}
|
|
98997
|
+
const r = await e.session.scroll(req);
|
|
98998
|
+
return { run_id: run.runId, path: run.path, slot, seq: r.seq };
|
|
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
|
+
}
|
|
97970
99048
|
function teamLiveMaps(path, runId) {
|
|
97971
99049
|
const run = addressRun(path, runId);
|
|
97972
99050
|
if (!run)
|
|
@@ -98099,20 +99177,20 @@ function answerFailureDetail(v, answer, requirePattern, outcomes) {
|
|
|
98099
99177
|
function readWrittenFile(path, root) {
|
|
98100
99178
|
let realRoot;
|
|
98101
99179
|
try {
|
|
98102
|
-
realRoot =
|
|
99180
|
+
realRoot = realpathSync8(root);
|
|
98103
99181
|
} catch {
|
|
98104
99182
|
realRoot = resolve5(root);
|
|
98105
99183
|
}
|
|
98106
99184
|
let real;
|
|
98107
99185
|
try {
|
|
98108
|
-
real =
|
|
99186
|
+
real = realpathSync8(resolve5(root, path));
|
|
98109
99187
|
} catch {
|
|
98110
99188
|
return { skip: "missing" };
|
|
98111
99189
|
}
|
|
98112
|
-
if (real !== realRoot && !real.startsWith(realRoot +
|
|
99190
|
+
if (real !== realRoot && !real.startsWith(realRoot + sep2))
|
|
98113
99191
|
return { skip: "outside the working directory" };
|
|
98114
99192
|
try {
|
|
98115
|
-
const st =
|
|
99193
|
+
const st = statSync18(real);
|
|
98116
99194
|
if (!st.isFile())
|
|
98117
99195
|
return { skip: "not a regular file" };
|
|
98118
99196
|
if (st.size > MAX_FILE_ANSWER_BYTES)
|
|
@@ -98165,24 +99243,24 @@ function validateSessionPath(sessionPath) {
|
|
|
98165
99243
|
const tail = [];
|
|
98166
99244
|
for (;; ) {
|
|
98167
99245
|
try {
|
|
98168
|
-
|
|
99246
|
+
lstatSync7(existing);
|
|
98169
99247
|
break;
|
|
98170
99248
|
} catch {
|
|
98171
|
-
const parent =
|
|
99249
|
+
const parent = dirname21(existing);
|
|
98172
99250
|
if (parent === existing)
|
|
98173
99251
|
break;
|
|
98174
|
-
tail.unshift(
|
|
99252
|
+
tail.unshift(basename6(existing));
|
|
98175
99253
|
existing = parent;
|
|
98176
99254
|
}
|
|
98177
99255
|
}
|
|
98178
99256
|
let real;
|
|
98179
99257
|
try {
|
|
98180
|
-
real =
|
|
99258
|
+
real = join53(realpathSync8(existing), ...tail);
|
|
98181
99259
|
} catch {
|
|
98182
99260
|
throw outside();
|
|
98183
99261
|
}
|
|
98184
|
-
const cwd =
|
|
98185
|
-
if (real !== cwd && !real.startsWith(cwd +
|
|
99262
|
+
const cwd = realpathSync8(process.cwd());
|
|
99263
|
+
if (real !== cwd && !real.startsWith(cwd + sep2)) {
|
|
98186
99264
|
throw outside();
|
|
98187
99265
|
}
|
|
98188
99266
|
return resolved;
|
|
@@ -98206,14 +99284,14 @@ function setupSession(sessionPath, models, input) {
|
|
|
98206
99284
|
if (models.length === 0) {
|
|
98207
99285
|
throw new Error("At least one model is required");
|
|
98208
99286
|
}
|
|
98209
|
-
if (existsSync36(
|
|
99287
|
+
if (existsSync36(join53(sessionPath, "manifest.json"))) {
|
|
98210
99288
|
throw new Error(`Session already exists at ${sessionPath}. Use a new directory path or delete the existing session first.`);
|
|
98211
99289
|
}
|
|
98212
|
-
mkdirSync24(
|
|
98213
|
-
mkdirSync24(
|
|
99290
|
+
mkdirSync24(join53(sessionPath, "work"), { recursive: true });
|
|
99291
|
+
mkdirSync24(join53(sessionPath, "errors"), { recursive: true });
|
|
98214
99292
|
if (input !== undefined) {
|
|
98215
|
-
writeFileSync22(
|
|
98216
|
-
} else if (!existsSync36(
|
|
99293
|
+
writeFileSync22(join53(sessionPath, "input.md"), input, "utf-8");
|
|
99294
|
+
} else if (!existsSync36(join53(sessionPath, "input.md"))) {
|
|
98217
99295
|
throw new Error(`No input.md found at ${sessionPath} and no input provided`);
|
|
98218
99296
|
}
|
|
98219
99297
|
const ids = models.map((_, i) => String(i + 1).padStart(2, "0"));
|
|
@@ -98230,9 +99308,9 @@ function setupSession(sessionPath, models, input) {
|
|
|
98230
99308
|
model: models[i],
|
|
98231
99309
|
assignedAt: now
|
|
98232
99310
|
};
|
|
98233
|
-
mkdirSync24(
|
|
99311
|
+
mkdirSync24(join53(sessionPath, "work", anonId), { recursive: true });
|
|
98234
99312
|
}
|
|
98235
|
-
writeFileSync22(
|
|
99313
|
+
writeFileSync22(join53(sessionPath, "manifest.json"), JSON.stringify(manifest, null, 2), "utf-8");
|
|
98236
99314
|
const status = {
|
|
98237
99315
|
startedAt: now,
|
|
98238
99316
|
models: Object.fromEntries(Object.keys(manifest.models).map((id) => [
|
|
@@ -98247,7 +99325,7 @@ function setupSession(sessionPath, models, input) {
|
|
|
98247
99325
|
}
|
|
98248
99326
|
]))
|
|
98249
99327
|
};
|
|
98250
|
-
writeFileAtomic(
|
|
99328
|
+
writeFileAtomic(join53(sessionPath, "status.json"), JSON.stringify(status, null, 2));
|
|
98251
99329
|
return manifest;
|
|
98252
99330
|
}
|
|
98253
99331
|
function assertValidRequirePattern(pattern) {
|
|
@@ -98282,7 +99360,7 @@ async function preflightTeamRun(opts) {
|
|
|
98282
99360
|
throw new Error(`pane_limit: ${live} live panes for this user, ${opts.slots} more would exceed ${MAX_LIVE_PANES}`);
|
|
98283
99361
|
}
|
|
98284
99362
|
function mintRunId(path, startMs) {
|
|
98285
|
-
return `${
|
|
99363
|
+
return `${basename6(path)}-${startMs.toString(36)}-${randomBytes13(3).toString("hex")}`;
|
|
98286
99364
|
}
|
|
98287
99365
|
function startModels(sessionPath, opts = {}) {
|
|
98288
99366
|
const flight = { aborted: false };
|
|
@@ -98308,9 +99386,9 @@ async function startModelsIn(flight, sessionPath, opts) {
|
|
|
98308
99386
|
throw new Error(`invalid_args: ${flagCheck.message}`);
|
|
98309
99387
|
const agentRequested = requestedAgentOf(claudeFlags);
|
|
98310
99388
|
const path = resolve5(sessionPath);
|
|
98311
|
-
const manifest = JSON.parse(readFileSync42(
|
|
98312
|
-
const statusPath =
|
|
98313
|
-
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");
|
|
98314
99392
|
const readAvailable = !flagsRemoveRead(claudeFlags);
|
|
98315
99393
|
const refusal = deliveryRefusal(inputContent, readAvailable);
|
|
98316
99394
|
if (refusal)
|
|
@@ -98350,7 +99428,7 @@ async function startModelsIn(flight, sessionPath, opts) {
|
|
|
98350
99428
|
mkdirSync24(statsDir(path), { recursive: true });
|
|
98351
99429
|
const entries = new Map;
|
|
98352
99430
|
let run = null;
|
|
98353
|
-
const errorLogPathOf = (id) =>
|
|
99431
|
+
const errorLogPathOf = (id) => join53(path, "errors", `${id}.log`);
|
|
98354
99432
|
const upstreamLogOf = (id) => upstreamLogPath(path, id);
|
|
98355
99433
|
const flagsOf = (e) => {
|
|
98356
99434
|
const effort = opts.efforts?.[e.model];
|
|
@@ -98360,7 +99438,7 @@ async function startModelsIn(flight, sessionPath, opts) {
|
|
|
98360
99438
|
function writeResponse(e, text) {
|
|
98361
99439
|
e.answer = text;
|
|
98362
99440
|
try {
|
|
98363
|
-
writeFileSync22(
|
|
99441
|
+
writeFileSync22(join53(path, `response-${e.id}.md`), text, "utf-8");
|
|
98364
99442
|
} catch (err) {
|
|
98365
99443
|
persistErrorLog(errorLogPathOf(e.id), `response file not written: ${String(err)}`, []);
|
|
98366
99444
|
}
|
|
@@ -98370,7 +99448,7 @@ async function startModelsIn(flight, sessionPath, opts) {
|
|
|
98370
99448
|
if (e.answer !== null || !e.session)
|
|
98371
99449
|
return;
|
|
98372
99450
|
try {
|
|
98373
|
-
writeFileSync22(
|
|
99451
|
+
writeFileSync22(join53(path, `response-${e.id}.md`), e.session.chainText(), "utf-8");
|
|
98374
99452
|
} catch (err) {
|
|
98375
99453
|
persistErrorLog(errorLogPathOf(e.id), `response file not written: ${String(err)}`, []);
|
|
98376
99454
|
}
|
|
@@ -98567,7 +99645,7 @@ async function startModelsIn(flight, sessionPath, opts) {
|
|
|
98567
99645
|
}
|
|
98568
99646
|
async function startSlot(e, uuid, transcriptPath) {
|
|
98569
99647
|
let session;
|
|
98570
|
-
e.inboxPath = createHookInbox(
|
|
99648
|
+
e.inboxPath = createHookInbox(join53(path, "work"), `${e.id}.hook-inbox.jsonl`);
|
|
98571
99649
|
const binding = bindingFor(e, uuid);
|
|
98572
99650
|
e.binding = binding;
|
|
98573
99651
|
try {
|
|
@@ -98607,6 +99685,9 @@ async function startModelsIn(flight, sessionPath, opts) {
|
|
|
98607
99685
|
return;
|
|
98608
99686
|
}
|
|
98609
99687
|
e.session = session;
|
|
99688
|
+
session.reapVerified?.().then(() => {
|
|
99689
|
+
e.reapVerified = true;
|
|
99690
|
+
});
|
|
98610
99691
|
session.reaped().then(() => {
|
|
98611
99692
|
if (!binding && e.inboxPath)
|
|
98612
99693
|
reportOwnerWithoutHooks(e.inboxPath, hookEngine, { model: e.model, sessionId: uuid });
|
|
@@ -98746,7 +99827,8 @@ async function startModelsIn(flight, sessionPath, opts) {
|
|
|
98746
99827
|
finishedAt: null,
|
|
98747
99828
|
status: statusCache,
|
|
98748
99829
|
slots: entries,
|
|
98749
|
-
settled
|
|
99830
|
+
settled,
|
|
99831
|
+
projectsRoot: projects
|
|
98750
99832
|
};
|
|
98751
99833
|
teamRuns.set(runId, run);
|
|
98752
99834
|
newestRunByPath.set(path, runId);
|
|
@@ -98782,7 +99864,7 @@ async function startModelsIn(flight, sessionPath, opts) {
|
|
|
98782
99864
|
done.catch((err) => log(`[team] run failed at ${path}: ${err instanceof Error ? err.message : String(err)}`));
|
|
98783
99865
|
return {
|
|
98784
99866
|
runId,
|
|
98785
|
-
teamSessionId:
|
|
99867
|
+
teamSessionId: basename6(path),
|
|
98786
99868
|
sessionPath: path,
|
|
98787
99869
|
slots: Object.fromEntries(Object.entries(manifest.models).map(([anonId, entry]) => [entry.model, anonId])),
|
|
98788
99870
|
done
|
|
@@ -98796,20 +99878,20 @@ async function judgeResponses(sessionPath, opts = {}) {
|
|
|
98796
99878
|
const active = activeRunAt(resolve5(sessionPath));
|
|
98797
99879
|
if (active)
|
|
98798
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`);
|
|
98799
|
-
const responseFiles =
|
|
99881
|
+
const responseFiles = readdirSync10(sessionPath).filter((f) => f.startsWith("response-") && f.endsWith(".md")).sort();
|
|
98800
99882
|
if (responseFiles.length < 2) {
|
|
98801
99883
|
throw new Error(`Need at least 2 responses to judge, found ${responseFiles.length}`);
|
|
98802
99884
|
}
|
|
98803
99885
|
const responses = {};
|
|
98804
99886
|
for (const file of responseFiles) {
|
|
98805
99887
|
const id = file.replace(/^response-/, "").replace(/\.md$/, "");
|
|
98806
|
-
responses[id] = readFileSync42(
|
|
99888
|
+
responses[id] = readFileSync42(join53(sessionPath, file), "utf-8");
|
|
98807
99889
|
}
|
|
98808
|
-
const input = readFileSync42(
|
|
99890
|
+
const input = readFileSync42(join53(sessionPath, "input.md"), "utf-8");
|
|
98809
99891
|
const judgePrompt = buildJudgePrompt(input, responses);
|
|
98810
|
-
writeFileSync22(
|
|
99892
|
+
writeFileSync22(join53(sessionPath, "judge-prompt.md"), judgePrompt, "utf-8");
|
|
98811
99893
|
const judgeModels = opts.judges ?? getDefaultJudgeModels(sessionPath);
|
|
98812
|
-
const judgePath =
|
|
99894
|
+
const judgePath = join53(sessionPath, "judging");
|
|
98813
99895
|
mkdirSync24(judgePath, { recursive: true });
|
|
98814
99896
|
setupSession(judgePath, judgeModels, judgePrompt);
|
|
98815
99897
|
await runModels(judgePath, {
|
|
@@ -98821,11 +99903,11 @@ async function judgeResponses(sessionPath, opts = {}) {
|
|
|
98821
99903
|
});
|
|
98822
99904
|
const votes = parseJudgeVotes(judgePath, Object.keys(responses));
|
|
98823
99905
|
const verdict = aggregateVerdict(votes, Object.keys(responses));
|
|
98824
|
-
writeFileSync22(
|
|
99906
|
+
writeFileSync22(join53(sessionPath, "verdict.md"), formatVerdict(verdict, sessionPath), "utf-8");
|
|
98825
99907
|
return verdict;
|
|
98826
99908
|
}
|
|
98827
99909
|
function getStatus(sessionPath) {
|
|
98828
|
-
return JSON.parse(readFileSync42(
|
|
99910
|
+
return JSON.parse(readFileSync42(join53(sessionPath, "status.json"), "utf-8"));
|
|
98829
99911
|
}
|
|
98830
99912
|
function readTeamStatus(sessionPath) {
|
|
98831
99913
|
try {
|
|
@@ -98865,7 +99947,7 @@ function fisherYatesShuffle(arr) {
|
|
|
98865
99947
|
return arr;
|
|
98866
99948
|
}
|
|
98867
99949
|
function getDefaultJudgeModels(sessionPath) {
|
|
98868
|
-
const manifest = JSON.parse(readFileSync42(
|
|
99950
|
+
const manifest = JSON.parse(readFileSync42(join53(sessionPath, "manifest.json"), "utf-8"));
|
|
98869
99951
|
return Object.values(manifest.models).map((e) => e.model);
|
|
98870
99952
|
}
|
|
98871
99953
|
function buildJudgePrompt(input, responses) {
|
|
@@ -98923,12 +100005,12 @@ function buildJudgePrompt(input, responses) {
|
|
|
98923
100005
|
}
|
|
98924
100006
|
function parseJudgeVotes(judgePath, responseIds) {
|
|
98925
100007
|
const votes = [];
|
|
98926
|
-
const responseFiles =
|
|
100008
|
+
const responseFiles = readdirSync10(judgePath).filter((f) => f.startsWith("response-") && f.endsWith(".md")).sort();
|
|
98927
100009
|
for (const file of responseFiles) {
|
|
98928
100010
|
const judgeId = file.replace(/^response-/, "").replace(/\.md$/, "");
|
|
98929
100011
|
let content;
|
|
98930
100012
|
try {
|
|
98931
|
-
content = readFileSync42(
|
|
100013
|
+
content = readFileSync42(join53(judgePath, file), "utf-8");
|
|
98932
100014
|
} catch (err) {
|
|
98933
100015
|
log(`[team] judge answer ${file} unreadable: ${err instanceof Error ? err.message : String(err)}`);
|
|
98934
100016
|
continue;
|
|
@@ -98981,7 +100063,7 @@ function aggregateVerdict(votes, responseIds) {
|
|
|
98981
100063
|
function formatVerdict(verdict, sessionPath) {
|
|
98982
100064
|
let manifest = null;
|
|
98983
100065
|
try {
|
|
98984
|
-
manifest = JSON.parse(readFileSync42(
|
|
100066
|
+
manifest = JSON.parse(readFileSync42(join53(sessionPath, "manifest.json"), "utf-8"));
|
|
98985
100067
|
} catch {}
|
|
98986
100068
|
let output = `# Team Verdict
|
|
98987
100069
|
|
|
@@ -99037,8 +100119,8 @@ var init_team_orchestrator = __esm(() => {
|
|
|
99037
100119
|
});
|
|
99038
100120
|
|
|
99039
100121
|
// src/mcp-session/inherited-settings.ts
|
|
99040
|
-
import { existsSync as existsSync37, readFileSync as readFileSync43, statSync as
|
|
99041
|
-
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";
|
|
99042
100124
|
function isPlainObject7(value) {
|
|
99043
100125
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
99044
100126
|
}
|
|
@@ -99092,7 +100174,7 @@ function readSettingsValue(value) {
|
|
|
99092
100174
|
function buildInheritedSettingsLayer(setupFrom, callerFlags) {
|
|
99093
100175
|
const layers = [];
|
|
99094
100176
|
for (const name of ["settings.json", "settings.local.json"]) {
|
|
99095
|
-
const file =
|
|
100177
|
+
const file = join54(setupFrom, ".claude", name);
|
|
99096
100178
|
if (!existsSync37(file))
|
|
99097
100179
|
continue;
|
|
99098
100180
|
layers.push(parseLayer(readFileSync43(file, "utf-8"), file));
|
|
@@ -99125,7 +100207,7 @@ function resolveSetupFrom(arg, workDir, serverCwd) {
|
|
|
99125
100207
|
let setupFrom = serverCwd;
|
|
99126
100208
|
if (typeof arg === "string") {
|
|
99127
100209
|
setupFrom = resolve6(arg);
|
|
99128
|
-
if (!
|
|
100210
|
+
if (!statSync19(setupFrom, { throwIfNoEntry: false })?.isDirectory()) {
|
|
99129
100211
|
throw new Error(`invalid_args: setup_from ${setupFrom} is not an existing directory`);
|
|
99130
100212
|
}
|
|
99131
100213
|
} else if (arg !== undefined) {
|
|
@@ -99139,19 +100221,19 @@ var init_inherited_settings = () => {};
|
|
|
99139
100221
|
import { randomUUID as randomUUID9 } from "crypto";
|
|
99140
100222
|
import {
|
|
99141
100223
|
appendFileSync as appendFileSync7,
|
|
99142
|
-
closeSync as
|
|
100224
|
+
closeSync as closeSync14,
|
|
99143
100225
|
createWriteStream,
|
|
99144
100226
|
existsSync as existsSync38,
|
|
99145
100227
|
mkdirSync as mkdirSync25,
|
|
99146
|
-
openSync as
|
|
100228
|
+
openSync as openSync14,
|
|
99147
100229
|
readFileSync as readFileSync44,
|
|
99148
|
-
readSync as
|
|
100230
|
+
readSync as readSync7,
|
|
99149
100231
|
renameSync as renameSync10,
|
|
99150
100232
|
rmSync as rmSync13,
|
|
99151
|
-
statSync as
|
|
100233
|
+
statSync as statSync20,
|
|
99152
100234
|
writeFileSync as writeFileSync23
|
|
99153
100235
|
} from "fs";
|
|
99154
|
-
import { join as
|
|
100236
|
+
import { join as join55, resolve as resolve7, sep as sep3 } from "path";
|
|
99155
100237
|
function normaliseTimeoutSeconds(raw) {
|
|
99156
100238
|
const n = typeof raw === "string" && raw.trim() !== "" ? Number(raw) : raw;
|
|
99157
100239
|
if (typeof n !== "number" || !Number.isFinite(n))
|
|
@@ -99255,21 +100337,21 @@ function anomalyKey(a) {
|
|
|
99255
100337
|
function readTailText(path, maxBytes) {
|
|
99256
100338
|
let fd = null;
|
|
99257
100339
|
try {
|
|
99258
|
-
const size =
|
|
100340
|
+
const size = statSync20(path).size;
|
|
99259
100341
|
if (size === 0)
|
|
99260
100342
|
return { text: "", truncated: false };
|
|
99261
100343
|
const start = Math.max(0, size - maxBytes);
|
|
99262
100344
|
const length = size - start;
|
|
99263
100345
|
const buf = Buffer.alloc(length);
|
|
99264
|
-
fd =
|
|
99265
|
-
|
|
100346
|
+
fd = openSync14(path, "r");
|
|
100347
|
+
readSync7(fd, buf, 0, length, start);
|
|
99266
100348
|
return { text: buf.toString("utf-8"), truncated: start > 0 };
|
|
99267
100349
|
} catch {
|
|
99268
100350
|
return null;
|
|
99269
100351
|
} finally {
|
|
99270
100352
|
if (fd !== null) {
|
|
99271
100353
|
try {
|
|
99272
|
-
|
|
100354
|
+
closeSync14(fd);
|
|
99273
100355
|
} catch {}
|
|
99274
100356
|
}
|
|
99275
100357
|
}
|
|
@@ -99286,7 +100368,7 @@ function readTailLines2(path, maxBytes) {
|
|
|
99286
100368
|
}
|
|
99287
100369
|
function fileSize2(path) {
|
|
99288
100370
|
try {
|
|
99289
|
-
return
|
|
100371
|
+
return statSync20(path).size;
|
|
99290
100372
|
} catch {
|
|
99291
100373
|
return 0;
|
|
99292
100374
|
}
|
|
@@ -99321,7 +100403,7 @@ function dropLeadingFragment(tail) {
|
|
|
99321
100403
|
return firstBreak === -1 ? tail.text : tail.text.slice(firstBreak + 1);
|
|
99322
100404
|
}
|
|
99323
100405
|
function diskAccounting(sessionDir) {
|
|
99324
|
-
const stats = readTokenStatsAt(
|
|
100406
|
+
const stats = readTokenStatsAt(join55(sessionDir, "tokens.json"));
|
|
99325
100407
|
return {
|
|
99326
100408
|
tokensIn: stats?.billed_input_tokens ?? null,
|
|
99327
100409
|
tokensOut: stats?.output_tokens ?? null,
|
|
@@ -99361,6 +100443,7 @@ class SessionManager {
|
|
|
99361
100443
|
scrollbackCapacity;
|
|
99362
100444
|
sessionsDir;
|
|
99363
100445
|
terminalRetentionMs;
|
|
100446
|
+
transcriptGraceMs;
|
|
99364
100447
|
parentEnv;
|
|
99365
100448
|
paneTimings;
|
|
99366
100449
|
starting = 0;
|
|
@@ -99380,6 +100463,7 @@ class SessionManager {
|
|
|
99380
100463
|
this.maxSessions = options?.maxSessions ?? DEFAULT_MAX_SESSIONS;
|
|
99381
100464
|
this.scrollbackCapacity = options?.scrollbackCapacity ?? DEFAULT_SCROLLBACK;
|
|
99382
100465
|
this.terminalRetentionMs = options?.terminalRetentionMs ?? TERMINAL_RETENTION_MS;
|
|
100466
|
+
this.transcriptGraceMs = options?.transcriptGraceMs ?? TRANSCRIPT_GRACE_MS;
|
|
99383
100467
|
this.sessionsDir = options?.sessionsDir ?? sessionsDirFrom(process.env);
|
|
99384
100468
|
this.parentEnv = options?.parentEnv ?? process.env;
|
|
99385
100469
|
this.paneTimings = options?.paneTimings;
|
|
@@ -99404,7 +100488,7 @@ class SessionManager {
|
|
|
99404
100488
|
let dir = "";
|
|
99405
100489
|
for (let attempt = 0;; attempt++) {
|
|
99406
100490
|
id = `team-${randomUUID9().replace(/-/g, "").slice(0, 8)}`;
|
|
99407
|
-
dir =
|
|
100491
|
+
dir = join55(this.sessionsDir, id);
|
|
99408
100492
|
try {
|
|
99409
100493
|
mkdirSync25(dir);
|
|
99410
100494
|
break;
|
|
@@ -99436,9 +100520,9 @@ class SessionManager {
|
|
|
99436
100520
|
if (dir === null)
|
|
99437
100521
|
throw new Error("not a record id");
|
|
99438
100522
|
const completedAt = new Date().toISOString();
|
|
99439
|
-
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;
|
|
99440
100524
|
const elapsedMs = Date.parse(completedAt) - Date.parse(startedAt);
|
|
99441
|
-
writeJsonAtomic(
|
|
100525
|
+
writeJsonAtomic(join55(dir, "meta.json"), {
|
|
99442
100526
|
kind: "team",
|
|
99443
100527
|
status: outcome.status,
|
|
99444
100528
|
startedAt,
|
|
@@ -99470,7 +100554,7 @@ class SessionManager {
|
|
|
99470
100554
|
startedAt,
|
|
99471
100555
|
...rest
|
|
99472
100556
|
};
|
|
99473
|
-
writeJsonAtomic(
|
|
100557
|
+
writeJsonAtomic(join55(dir, "spawn.json"), body);
|
|
99474
100558
|
}
|
|
99475
100559
|
async createSession(opts) {
|
|
99476
100560
|
if (this.activeSessions + this.starting >= this.maxSessions) {
|
|
@@ -99510,7 +100594,7 @@ class SessionManager {
|
|
|
99510
100594
|
throw err;
|
|
99511
100595
|
}
|
|
99512
100596
|
if (inherited.layer) {
|
|
99513
|
-
flags = [...inherited.restFlags, "--settings",
|
|
100597
|
+
flags = [...inherited.restFlags, "--settings", join55(entry.sessionDir, INHERITED_SETTINGS)];
|
|
99514
100598
|
}
|
|
99515
100599
|
this.sessions.set(entry.info.sessionId, entry);
|
|
99516
100600
|
uncount();
|
|
@@ -99535,10 +100619,10 @@ class SessionManager {
|
|
|
99535
100619
|
const timeout = normaliseTimeoutSeconds(opts.timeoutSeconds);
|
|
99536
100620
|
const agentRequested = requestedAgentOf(opts.claudishFlags ?? []);
|
|
99537
100621
|
const startedAt = new Date().toISOString();
|
|
99538
|
-
const sessionDir = opts.sessionDir ??
|
|
100622
|
+
const sessionDir = opts.sessionDir ?? join55(this.sessionsDir, sessionId);
|
|
99539
100623
|
mkdirSync25(sessionDir, { recursive: true });
|
|
99540
100624
|
if (opts.prompt)
|
|
99541
|
-
writeFileSync23(
|
|
100625
|
+
writeFileSync23(join55(sessionDir, "prompt.md"), opts.prompt, "utf-8");
|
|
99542
100626
|
this.writeSpawnRecord(sessionDir, {
|
|
99543
100627
|
kind: "session",
|
|
99544
100628
|
sessionId,
|
|
@@ -99551,13 +100635,13 @@ class SessionManager {
|
|
|
99551
100635
|
...agentRequested ? { agentRequested } : {}
|
|
99552
100636
|
});
|
|
99553
100637
|
if (inheritedLayer) {
|
|
99554
|
-
writeFileSync23(
|
|
100638
|
+
writeFileSync23(join55(sessionDir, INHERITED_SETTINGS), `${JSON.stringify(inheritedLayer, null, 2)}
|
|
99555
100639
|
`, { encoding: "utf-8", mode: 384 });
|
|
99556
100640
|
}
|
|
99557
100641
|
const cwd = opts.cwd ?? process.cwd();
|
|
99558
100642
|
const parentEnv = opts.parentEnv ?? this.parentEnv;
|
|
99559
100643
|
const spawnModel = opts.spawnModel ?? null;
|
|
99560
|
-
const tokenFile = opts.tokenFile ??
|
|
100644
|
+
const tokenFile = opts.tokenFile ?? join55(sessionDir, "tokens.json");
|
|
99561
100645
|
return {
|
|
99562
100646
|
info: {
|
|
99563
100647
|
sessionId,
|
|
@@ -99595,18 +100679,18 @@ class SessionManager {
|
|
|
99595
100679
|
},
|
|
99596
100680
|
session: null,
|
|
99597
100681
|
scrollback: new ScrollbackBuffer(this.scrollbackCapacity),
|
|
99598
|
-
outputLogStream: createWriteStream(
|
|
100682
|
+
outputLogStream: createWriteStream(join55(sessionDir, "output.log")),
|
|
99599
100683
|
sessionDir,
|
|
99600
100684
|
cwd,
|
|
99601
100685
|
tokenFile,
|
|
99602
|
-
eventLogPath:
|
|
100686
|
+
eventLogPath: join55(sessionDir, "events.jsonl"),
|
|
99603
100687
|
eventLogBytes: 0,
|
|
99604
|
-
upstreamErrorLogPath:
|
|
100688
|
+
upstreamErrorLogPath: join55(sessionDir, "upstream-errors.jsonl"),
|
|
99605
100689
|
eventRing: [],
|
|
99606
100690
|
proseBytes: 0,
|
|
99607
100691
|
answeredTurns: new Map,
|
|
99608
100692
|
inboxPath: null,
|
|
99609
|
-
waitLogPath:
|
|
100693
|
+
waitLogPath: join55(sessionDir, "waits.jsonl"),
|
|
99610
100694
|
waitLogBytes: 0,
|
|
99611
100695
|
waitSince: null,
|
|
99612
100696
|
ended: false,
|
|
@@ -99614,7 +100698,9 @@ class SessionManager {
|
|
|
99614
100698
|
lastLoggedTool: null,
|
|
99615
100699
|
assistantIdsLogged: 0,
|
|
99616
100700
|
anomaliesLogged: new Set,
|
|
99617
|
-
requestedSize: null
|
|
100701
|
+
requestedSize: null,
|
|
100702
|
+
reapVerified: false,
|
|
100703
|
+
projectsRoot: projectsDir(parentEnv)
|
|
99618
100704
|
};
|
|
99619
100705
|
}
|
|
99620
100706
|
async startPane(entry, opts, p) {
|
|
@@ -99669,6 +100755,9 @@ class SessionManager {
|
|
|
99669
100755
|
session.resize(asked.cols, asked.rows).catch(() => {
|
|
99670
100756
|
return;
|
|
99671
100757
|
});
|
|
100758
|
+
session.reapVerified?.().then(() => {
|
|
100759
|
+
entry.reapVerified = true;
|
|
100760
|
+
});
|
|
99672
100761
|
session.reaped().then(() => {
|
|
99673
100762
|
if (!binding && entry.inboxPath)
|
|
99674
100763
|
reportOwnerWithoutHooks(entry.inboxPath, engine, {
|
|
@@ -99796,7 +100885,7 @@ class SessionManager {
|
|
|
99796
100885
|
try {
|
|
99797
100886
|
const text = screenLines.join(`
|
|
99798
100887
|
`).replace(/\s+$/, "");
|
|
99799
|
-
writeFileSync23(
|
|
100888
|
+
writeFileSync23(join55(entry.sessionDir, "screen.txt"), `${redactSecrets(text)}
|
|
99800
100889
|
`, "utf-8");
|
|
99801
100890
|
} catch {}
|
|
99802
100891
|
}
|
|
@@ -99810,7 +100899,7 @@ class SessionManager {
|
|
|
99810
100899
|
entry.waitSince = null;
|
|
99811
100900
|
}
|
|
99812
100901
|
try {
|
|
99813
|
-
writeJsonAtomic(
|
|
100902
|
+
writeJsonAtomic(join55(entry.sessionDir, "meta.json"), toMetaRecord(entry.info, entry.cwd));
|
|
99814
100903
|
} catch (err) {
|
|
99815
100904
|
process.stderr.write(`[claudish] session ${entry.info.sessionId}: could not write meta.json: ` + `${err instanceof Error ? err.message : String(err)}
|
|
99816
100905
|
`);
|
|
@@ -100007,6 +101096,15 @@ class SessionManager {
|
|
|
100007
101096
|
const r = await entry.session.resize(cols, rows);
|
|
100008
101097
|
return { session_id: sessionId, ...r };
|
|
100009
101098
|
}
|
|
101099
|
+
async scrollSession(sessionId, req) {
|
|
101100
|
+
const entry = this.liveEntry(sessionId);
|
|
101101
|
+
if (!entry)
|
|
101102
|
+
throw unknownSession(sessionId);
|
|
101103
|
+
if (!entry.session)
|
|
101104
|
+
throw new ContractErrorException("scroll_unavailable", isTerminalState(entry.info.state) ? "the session has finished" : "the session is still starting");
|
|
101105
|
+
const r = await entry.session.scroll(req);
|
|
101106
|
+
return { session_id: sessionId, seq: r.seq };
|
|
101107
|
+
}
|
|
100010
101108
|
captureSession(sessionId, sinceSeq, spans) {
|
|
100011
101109
|
const entry = this.liveEntry(sessionId);
|
|
100012
101110
|
if (!entry)
|
|
@@ -100015,6 +101113,71 @@ class SessionManager {
|
|
|
100015
101113
|
return sinceSeq === 0 ? { unchanged: true, seq: 0, final: true } : blankFinalCapture();
|
|
100016
101114
|
return entry.session.capture(sinceSeq, { spans: spans === true });
|
|
100017
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
|
+
}
|
|
100018
101181
|
listSessions(includeCompleted = false) {
|
|
100019
101182
|
const sessions = [];
|
|
100020
101183
|
for (const entry of this.sessions.values()) {
|
|
@@ -100072,9 +101235,9 @@ class SessionManager {
|
|
|
100072
101235
|
return null;
|
|
100073
101236
|
const root = resolve7(this.sessionsDir);
|
|
100074
101237
|
const dir = resolve7(root, sessionId);
|
|
100075
|
-
if (dir !==
|
|
101238
|
+
if (dir !== join55(root, sessionId))
|
|
100076
101239
|
return null;
|
|
100077
|
-
if (!dir.startsWith(root +
|
|
101240
|
+
if (!dir.startsWith(root + sep3))
|
|
100078
101241
|
return null;
|
|
100079
101242
|
return dir;
|
|
100080
101243
|
}
|
|
@@ -100084,17 +101247,17 @@ class SessionManager {
|
|
|
100084
101247
|
return null;
|
|
100085
101248
|
let dirMtimeMs;
|
|
100086
101249
|
try {
|
|
100087
|
-
const stat =
|
|
101250
|
+
const stat = statSync20(sessionDir);
|
|
100088
101251
|
if (!stat.isDirectory())
|
|
100089
101252
|
return null;
|
|
100090
101253
|
dirMtimeMs = stat.mtimeMs;
|
|
100091
101254
|
} catch {
|
|
100092
101255
|
return null;
|
|
100093
101256
|
}
|
|
100094
|
-
const spawnRecord = readJsonObject(
|
|
101257
|
+
const spawnRecord = readJsonObject(join55(sessionDir, "spawn.json"), META_READ_LIMIT);
|
|
100095
101258
|
if (spawnRecord?.kind === "team")
|
|
100096
101259
|
return null;
|
|
100097
|
-
const meta = readJsonObject(
|
|
101260
|
+
const meta = readJsonObject(join55(sessionDir, "meta.json"), META_READ_LIMIT);
|
|
100098
101261
|
if (meta?.kind === "team")
|
|
100099
101262
|
return null;
|
|
100100
101263
|
const partial = meta === null;
|
|
@@ -100116,7 +101279,7 @@ class SessionManager {
|
|
|
100116
101279
|
return stored;
|
|
100117
101280
|
}
|
|
100118
101281
|
diskOutput(record, tailLines) {
|
|
100119
|
-
const tail = readTailText(
|
|
101282
|
+
const tail = readTailText(join55(record.sessionDir, "output.log"), OUTPUT_TAIL_BYTES);
|
|
100120
101283
|
const buffer = new ScrollbackBuffer(this.scrollbackCapacity);
|
|
100121
101284
|
if (tail?.text)
|
|
100122
101285
|
buffer.append(dropLeadingFragment(tail));
|
|
@@ -100136,12 +101299,12 @@ class SessionManager {
|
|
|
100136
101299
|
}
|
|
100137
101300
|
diskDiagnostics(record, limit) {
|
|
100138
101301
|
const { sessionDir, info } = record;
|
|
100139
|
-
const eventLogPath =
|
|
100140
|
-
const upstreamErrorLogPath =
|
|
100141
|
-
const outputLogPath =
|
|
101302
|
+
const eventLogPath = join55(sessionDir, "events.jsonl");
|
|
101303
|
+
const upstreamErrorLogPath = join55(sessionDir, "upstream-errors.jsonl");
|
|
101304
|
+
const outputLogPath = join55(sessionDir, "output.log");
|
|
100142
101305
|
const events = readTailLines2(eventLogPath, EVENT_TAIL_BYTES);
|
|
100143
101306
|
const outputTail = readTailText(outputLogPath, OUTPUT_TAIL_BYTES);
|
|
100144
|
-
const screen = readTailText(
|
|
101307
|
+
const screen = readTailText(join55(sessionDir, "screen.txt"), SCREEN_READ_BYTES);
|
|
100145
101308
|
return {
|
|
100146
101309
|
sessionId: info.sessionId,
|
|
100147
101310
|
state: info.state,
|
|
@@ -100337,6 +101500,13 @@ function diskState(meta) {
|
|
|
100337
101500
|
return { state, reason: "timeout" };
|
|
100338
101501
|
return { state, reason: null };
|
|
100339
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
|
+
}
|
|
100340
101510
|
function diskInfo(d) {
|
|
100341
101511
|
const { meta, spawnRecord, measured } = d;
|
|
100342
101512
|
const startedAt = metaString(meta?.startedAt) ?? metaString(spawnRecord?.startedAt) ?? new Date(d.dirMtimeMs).toISOString();
|
|
@@ -100442,7 +101612,7 @@ var NESTED_MCP_TOOL_NAMES, NESTED_INSTRUCTIONS = `Claudish MCP server provides a
|
|
|
100442
101612
|
|
|
100443
101613
|
## Nested mode \u2014 no sessions
|
|
100444
101614
|
|
|
100445
|
-
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, 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.
|
|
100446
101616
|
|
|
100447
101617
|
- run_prompt or compare_models: a one-shot answer from another model.
|
|
100448
101618
|
- list_models, search_models: find a model.
|
|
@@ -100540,7 +101710,7 @@ var init_progress_heartbeat = __esm(() => {
|
|
|
100540
101710
|
import { spawn as spawn2 } from "child_process";
|
|
100541
101711
|
import { mkdirSync as mkdirSync26, mkdtempSync as mkdtempSync3, rmSync as rmSync14, writeFileSync as writeFileSync24 } from "fs";
|
|
100542
101712
|
import { tmpdir as tmpdir2 } from "os";
|
|
100543
|
-
import { join as
|
|
101713
|
+
import { join as join56 } from "path";
|
|
100544
101714
|
function claudeCodePrintArgv(model, systemPromptFile) {
|
|
100545
101715
|
const head = [PRINT_FLAGS[0], "--model", model, ...PRINT_FLAGS.slice(1)];
|
|
100546
101716
|
return systemPromptFile !== undefined ? [...head, "--system-prompt-file", systemPromptFile] : [...head, "--system-prompt", ""];
|
|
@@ -100658,13 +101828,13 @@ async function runPromptViaClaudeCode(model, prompt, systemPrompt, opts = {}) {
|
|
|
100658
101828
|
} catch (e) {
|
|
100659
101829
|
throw new Error(`cannot start claudish (${parentEnv.CLAUDISH_BIN?.trim() || "claudish"}): ${e instanceof Error ? e.message : String(e)}`);
|
|
100660
101830
|
}
|
|
100661
|
-
const root = mkdtempSync3(
|
|
101831
|
+
const root = mkdtempSync3(join56(tmpdir2(), "claudish-run-prompt-"));
|
|
100662
101832
|
try {
|
|
100663
|
-
const cwd =
|
|
101833
|
+
const cwd = join56(root, "cwd");
|
|
100664
101834
|
mkdirSync26(cwd, { mode: 448 });
|
|
100665
101835
|
let systemPromptFile;
|
|
100666
101836
|
if (typeof systemPrompt === "string" && systemPrompt.length > 0) {
|
|
100667
|
-
systemPromptFile =
|
|
101837
|
+
systemPromptFile = join56(root, "system-prompt.txt");
|
|
100668
101838
|
writeFileSync24(systemPromptFile, systemPrompt, { mode: 384 });
|
|
100669
101839
|
}
|
|
100670
101840
|
const argv = claudeCodePrintArgv(model, systemPromptFile);
|
|
@@ -100964,9 +102134,9 @@ var init_preflight_chain = __esm(() => {
|
|
|
100964
102134
|
});
|
|
100965
102135
|
|
|
100966
102136
|
// src/mcp-server.ts
|
|
100967
|
-
import { existsSync as existsSync39, readFileSync as readFileSync45, readdirSync as
|
|
102137
|
+
import { existsSync as existsSync39, readFileSync as readFileSync45, readdirSync as readdirSync11 } from "fs";
|
|
100968
102138
|
import { homedir as homedir37 } from "os";
|
|
100969
|
-
import { dirname as
|
|
102139
|
+
import { dirname as dirname22, join as join57, resolve as resolve8 } from "path";
|
|
100970
102140
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
100971
102141
|
async function loadAllModels(forceRefresh = false) {
|
|
100972
102142
|
if (!forceRefresh && existsSync39(OPENROUTER_MODELS_CACHE_PATH)) {
|
|
@@ -101539,6 +102709,36 @@ function requiredInteger(raw, name, mode) {
|
|
|
101539
102709
|
throw new ContractErrorException("invalid_args", `'${name}' is required${mode ? ` for mode '${mode}'` : ""}`);
|
|
101540
102710
|
return n;
|
|
101541
102711
|
}
|
|
102712
|
+
function scrollArgs(args) {
|
|
102713
|
+
const by = optionalInteger(args.by, "by");
|
|
102714
|
+
const to = args.to;
|
|
102715
|
+
if (to !== undefined && to !== null && to !== "end")
|
|
102716
|
+
throw new ContractErrorException("invalid_args", `'to' must be "end"`);
|
|
102717
|
+
const hasTo = to === "end";
|
|
102718
|
+
if (by !== undefined && hasTo)
|
|
102719
|
+
throw new ContractErrorException("invalid_args", "give exactly one of 'by' and 'to', not both");
|
|
102720
|
+
if (hasTo)
|
|
102721
|
+
return { to: "end" };
|
|
102722
|
+
if (by === undefined)
|
|
102723
|
+
throw new ContractErrorException("invalid_args", "one of 'by' and 'to' is required");
|
|
102724
|
+
if (by === 0)
|
|
102725
|
+
throw new ContractErrorException("invalid_args", "'by' must not be 0");
|
|
102726
|
+
return { by };
|
|
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
|
+
}
|
|
101542
102742
|
function teamStatusAnswer(path, runId) {
|
|
101543
102743
|
let row = null;
|
|
101544
102744
|
if (runId) {
|
|
@@ -101577,12 +102777,14 @@ async function teamContractVerb(mode, args) {
|
|
|
101577
102777
|
if (mode === "list")
|
|
101578
102778
|
return contractAnswer(listTeamRuns());
|
|
101579
102779
|
const path = contractPath(args.path, mode);
|
|
101580
|
-
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");
|
|
101581
102781
|
const slot = optionalString(args.slot, "slot");
|
|
101582
|
-
const
|
|
102782
|
+
const sinceSeq = optionalInteger(args.since_seq, "since_seq");
|
|
101583
102783
|
const spans = optionalBoolean(args.spans, "spans");
|
|
101584
102784
|
const cols = mode === "resize" ? requiredInteger(args.cols, "cols", mode) : undefined;
|
|
101585
102785
|
const rows = mode === "resize" ? requiredInteger(args.rows, "rows", mode) : undefined;
|
|
102786
|
+
const scroll = mode === "scroll" ? scrollArgs(args) : undefined;
|
|
102787
|
+
const since = mode === "transcript" ? transcriptSince(args.since) : undefined;
|
|
101586
102788
|
switch (mode) {
|
|
101587
102789
|
case "status":
|
|
101588
102790
|
return contractAnswer(teamStatusAnswer(path, runId));
|
|
@@ -101591,13 +102793,23 @@ async function teamContractVerb(mode, args) {
|
|
|
101591
102793
|
case "capture": {
|
|
101592
102794
|
if (slot === undefined)
|
|
101593
102795
|
throw new ContractErrorException("invalid_args", "'slot' is required for mode 'capture'");
|
|
101594
|
-
return contractAnswer(captureTeamSlot(path, slot,
|
|
102796
|
+
return contractAnswer(captureTeamSlot(path, slot, sinceSeq, spans, runId));
|
|
101595
102797
|
}
|
|
101596
102798
|
case "resize": {
|
|
101597
102799
|
if (slot === undefined)
|
|
101598
102800
|
throw new ContractErrorException("invalid_args", "'slot' is required for mode 'resize'");
|
|
101599
102801
|
return contractAnswer(await resizeTeamSlot(path, slot, cols, rows, runId));
|
|
101600
102802
|
}
|
|
102803
|
+
case "scroll": {
|
|
102804
|
+
if (slot === undefined)
|
|
102805
|
+
throw new ContractErrorException("invalid_args", "'slot' is required for mode 'scroll'");
|
|
102806
|
+
return contractAnswer(await scrollTeamSlot(path, slot, scroll, runId));
|
|
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
|
+
}
|
|
101601
102813
|
}
|
|
101602
102814
|
} catch (e) {
|
|
101603
102815
|
return contractErrorAnswer(e);
|
|
@@ -101945,18 +103157,29 @@ This searched OpenRouter's listing only. Subscription wire ids and catalog alias
|
|
|
101945
103157
|
});
|
|
101946
103158
|
tools.push({
|
|
101947
103159
|
name: "team",
|
|
101948
|
-
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), '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.",
|
|
101949
103161
|
inputSchema: {
|
|
101950
103162
|
type: "object",
|
|
101951
103163
|
properties: {
|
|
101952
103164
|
mode: {
|
|
101953
103165
|
type: "string",
|
|
101954
|
-
enum: [
|
|
101955
|
-
|
|
103166
|
+
enum: [
|
|
103167
|
+
"run",
|
|
103168
|
+
"judge",
|
|
103169
|
+
"run-and-judge",
|
|
103170
|
+
"status",
|
|
103171
|
+
"cancel",
|
|
103172
|
+
"list",
|
|
103173
|
+
"capture",
|
|
103174
|
+
"resize",
|
|
103175
|
+
"scroll",
|
|
103176
|
+
"transcript"
|
|
103177
|
+
],
|
|
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."
|
|
101956
103179
|
},
|
|
101957
103180
|
slot: {
|
|
101958
103181
|
type: "string",
|
|
101959
|
-
description: "For 'cancel', 'capture' 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."
|
|
101960
103183
|
},
|
|
101961
103184
|
path: {
|
|
101962
103185
|
type: "string",
|
|
@@ -101964,11 +103187,15 @@ This searched OpenRouter's listing only. Subscription wire ids and catalog alias
|
|
|
101964
103187
|
},
|
|
101965
103188
|
run_id: {
|
|
101966
103189
|
type: "string",
|
|
101967
|
-
description: "For 'status', 'cancel', 'capture' 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'."
|
|
101968
103191
|
},
|
|
101969
103192
|
since_seq: {
|
|
101970
103193
|
type: "number",
|
|
101971
|
-
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.`
|
|
101972
103199
|
},
|
|
101973
103200
|
spans: {
|
|
101974
103201
|
type: "boolean",
|
|
@@ -101982,6 +103209,15 @@ This searched OpenRouter's listing only. Subscription wire ids and catalog alias
|
|
|
101982
103209
|
type: "integer",
|
|
101983
103210
|
description: "For 'resize' (required): the terminal height in rows, clamped to 24\u20139999 and to " + "at most 4 000 000 cells (cols \xD7 rows)."
|
|
101984
103211
|
},
|
|
103212
|
+
by: {
|
|
103213
|
+
type: "integer",
|
|
103214
|
+
description: "For 'scroll': rows to scroll the slot's transcript view; negative = toward older " + `content, positive = toward the live bottom. A target, \xB1 1 row; |by| is capped at ${MAX_SCROLL_ROWS}. Not 0. Give exactly one of 'by' and 'to'. The answer {run_id, path, slot, seq} carries the screen's seq after the scroll.`
|
|
103215
|
+
},
|
|
103216
|
+
to: {
|
|
103217
|
+
type: "string",
|
|
103218
|
+
enum: ["end"],
|
|
103219
|
+
description: `For 'scroll': "end" returns the slot's view to the live bottom. Give exactly one of 'by' and 'to'.`
|
|
103220
|
+
},
|
|
101985
103221
|
models: {
|
|
101986
103222
|
type: "array",
|
|
101987
103223
|
items: { type: "string" },
|
|
@@ -102038,7 +103274,7 @@ This searched OpenRouter's listing only. Subscription wire ids and catalog alias
|
|
|
102038
103274
|
heartbeat: true,
|
|
102039
103275
|
handler: async (args, ctx) => {
|
|
102040
103276
|
const mode = args.mode;
|
|
102041
|
-
if (mode === "list" || mode === "status" || mode === "cancel" || mode === "capture" || mode === "resize") {
|
|
103277
|
+
if (mode === "list" || mode === "status" || mode === "cancel" || mode === "capture" || mode === "resize" || mode === "scroll" || mode === "transcript") {
|
|
102042
103278
|
return teamContractVerb(mode, args);
|
|
102043
103279
|
}
|
|
102044
103280
|
try {
|
|
@@ -102092,7 +103328,7 @@ This searched OpenRouter's listing only. Subscription wire ids and catalog alias
|
|
|
102092
103328
|
const promptText = () => {
|
|
102093
103329
|
if (input !== undefined)
|
|
102094
103330
|
return input;
|
|
102095
|
-
const existing =
|
|
103331
|
+
const existing = join57(resolved, "input.md");
|
|
102096
103332
|
return existsSync39(existing) ? readFileSync45(existing, "utf-8") : undefined;
|
|
102097
103333
|
};
|
|
102098
103334
|
const choice = modelChoiceStop([
|
|
@@ -102275,26 +103511,26 @@ This searched OpenRouter's listing only. Subscription wire ids and catalog alias
|
|
|
102275
103511
|
const sp = session_path;
|
|
102276
103512
|
for (const file of ["status.json", "manifest.json", "input.md"]) {
|
|
102277
103513
|
try {
|
|
102278
|
-
sessionData[file] = readFileSync45(
|
|
103514
|
+
sessionData[file] = readFileSync45(join57(sp, file), "utf-8");
|
|
102279
103515
|
} catch {}
|
|
102280
103516
|
}
|
|
102281
103517
|
try {
|
|
102282
|
-
const errorDir =
|
|
103518
|
+
const errorDir = join57(sp, "errors");
|
|
102283
103519
|
if (existsSync39(errorDir)) {
|
|
102284
|
-
for (const f of
|
|
103520
|
+
for (const f of readdirSync11(errorDir)) {
|
|
102285
103521
|
if (f.endsWith(".log")) {
|
|
102286
103522
|
try {
|
|
102287
|
-
sessionData[`errors/${f}`] = readFileSync45(
|
|
103523
|
+
sessionData[`errors/${f}`] = readFileSync45(join57(errorDir, f), "utf-8");
|
|
102288
103524
|
} catch {}
|
|
102289
103525
|
}
|
|
102290
103526
|
}
|
|
102291
103527
|
}
|
|
102292
103528
|
} catch {}
|
|
102293
103529
|
try {
|
|
102294
|
-
for (const f of
|
|
103530
|
+
for (const f of readdirSync11(sp)) {
|
|
102295
103531
|
if (f.startsWith("response-") && f.endsWith(".md")) {
|
|
102296
103532
|
try {
|
|
102297
|
-
const content = readFileSync45(
|
|
103533
|
+
const content = readFileSync45(join57(sp, f), "utf-8");
|
|
102298
103534
|
sessionData[f] = content.slice(0, 200) + (content.length > 200 ? "... (truncated)" : "");
|
|
102299
103535
|
} catch {}
|
|
102300
103536
|
}
|
|
@@ -102303,7 +103539,7 @@ This searched OpenRouter's listing only. Subscription wire ids and catalog alias
|
|
|
102303
103539
|
}
|
|
102304
103540
|
let version = "unknown";
|
|
102305
103541
|
try {
|
|
102306
|
-
const pkgPath =
|
|
103542
|
+
const pkgPath = join57(__dirname3, "../package.json");
|
|
102307
103543
|
if (existsSync39(pkgPath)) {
|
|
102308
103544
|
version = JSON.parse(readFileSync45(pkgPath, "utf-8")).version;
|
|
102309
103545
|
}
|
|
@@ -102739,6 +103975,65 @@ Report manually at https://github.com/anthropics/claudish/issues${autoSendHint}`
|
|
|
102739
103975
|
}
|
|
102740
103976
|
}
|
|
102741
103977
|
});
|
|
103978
|
+
tools.push({
|
|
103979
|
+
name: "scroll_session",
|
|
103980
|
+
description: "Scroll a session's transcript view, as a mouse wheel over the child's Claude Code would: the next capture_session shows the child scrolled. `by` is a signed row count (negative = toward older content, positive = toward the live bottom); it is a " + `target, \xB1 1 row, and |by| is capped at ${MAX_SCROLL_ROWS} per call. \`to:"end"\` returns to the live bottom. Give exactly one of \`by\` and \`to\`; \`by:0\` is refused. It never types into the prompt, submits or answers a dialog, and a prompt claudish delivers next returns the view to the bottom first. The answer {session_id, seq} carries the screen's seq after the scroll (the first changed screen within 1 s, else the current one). Errors are a JSON ContractError: \`scroll_unavailable\` while the session is still starting, finished, delivering a prompt, reading its turn from the screen or reconnecting, or when magmux did not confirm the send (the scroll may still land). Nothing is queued.`,
|
|
103981
|
+
inputSchema: {
|
|
103982
|
+
type: "object",
|
|
103983
|
+
properties: {
|
|
103984
|
+
session_id: { type: "string", description: "Session ID from create_session" },
|
|
103985
|
+
by: {
|
|
103986
|
+
type: "integer",
|
|
103987
|
+
description: `Rows to scroll; negative = older content. Not 0; |by| capped at ${MAX_SCROLL_ROWS}; a target, \xB1 1 row`
|
|
103988
|
+
},
|
|
103989
|
+
to: {
|
|
103990
|
+
type: "string",
|
|
103991
|
+
enum: ["end"],
|
|
103992
|
+
description: '"end" returns the view to the live bottom'
|
|
103993
|
+
}
|
|
103994
|
+
},
|
|
103995
|
+
required: ["session_id"]
|
|
103996
|
+
},
|
|
103997
|
+
group: "session",
|
|
103998
|
+
handler: async (args) => {
|
|
103999
|
+
try {
|
|
104000
|
+
const sessionId = optionalString(args.session_id, "session_id");
|
|
104001
|
+
if (sessionId === undefined)
|
|
104002
|
+
throw new ContractErrorException("invalid_args", "'session_id' is required");
|
|
104003
|
+
const req = scrollArgs(args);
|
|
104004
|
+
return contractAnswer(await sessionManager.scrollSession(sessionId, req));
|
|
104005
|
+
} catch (e) {
|
|
104006
|
+
return contractErrorAnswer(e);
|
|
104007
|
+
}
|
|
104008
|
+
}
|
|
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
|
+
});
|
|
102742
104037
|
return tools;
|
|
102743
104038
|
}
|
|
102744
104039
|
function resolveToolGroups(mode) {
|
|
@@ -102882,7 +104177,7 @@ Each session is an interactive Claude Code running the model in its own headless
|
|
|
102882
104177
|
3. send_input may be called at any time; it is queued until the session is idle.
|
|
102883
104178
|
4. COMPLETED: call get_output for the answer.
|
|
102884
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.
|
|
102885
|
-
6. capture_session reads a session's current screen; resize_session sets its terminal size (any time, mid-turn included); 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.
|
|
102886
104181
|
|
|
102887
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;
|
|
102888
104183
|
var init_mcp_server = __esm(() => {
|
|
@@ -102925,9 +104220,9 @@ var init_mcp_server = __esm(() => {
|
|
|
102925
104220
|
init_team_orchestrator();
|
|
102926
104221
|
init_preflight_chain();
|
|
102927
104222
|
__filename3 = fileURLToPath3(import.meta.url);
|
|
102928
|
-
__dirname3 =
|
|
102929
|
-
CLAUDISH_CACHE_DIR =
|
|
102930
|
-
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");
|
|
102931
104226
|
NEXT_STEP = {
|
|
102932
104227
|
cancelled: "you stopped this slot; nothing is wrong with it. Re-run it if you still want its vote",
|
|
102933
104228
|
timeout: "grid mode only \u2014 magmux ended the pane. The orchestrator has no deadline",
|
|
@@ -103056,9 +104351,9 @@ var init_serve_command = __esm(() => {
|
|
|
103056
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';
|
|
103057
104352
|
|
|
103058
104353
|
// src/hooks/observer/corpus.ts
|
|
103059
|
-
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";
|
|
103060
104355
|
import { homedir as homedir38 } from "os";
|
|
103061
|
-
import { join as
|
|
104356
|
+
import { join as join58 } from "path";
|
|
103062
104357
|
function directoryOf2(filePath) {
|
|
103063
104358
|
const slash = filePath.lastIndexOf("/");
|
|
103064
104359
|
return slash > 0 ? filePath.slice(0, slash) : undefined;
|
|
@@ -103132,31 +104427,31 @@ function listTranscripts(root) {
|
|
|
103132
104427
|
const files = [];
|
|
103133
104428
|
let projects;
|
|
103134
104429
|
try {
|
|
103135
|
-
projects =
|
|
104430
|
+
projects = readdirSync12(root);
|
|
103136
104431
|
} catch {
|
|
103137
104432
|
return files;
|
|
103138
104433
|
}
|
|
103139
104434
|
for (const project of projects) {
|
|
103140
|
-
const dir =
|
|
104435
|
+
const dir = join58(root, project);
|
|
103141
104436
|
try {
|
|
103142
|
-
if (!
|
|
104437
|
+
if (!statSync21(dir).isDirectory())
|
|
103143
104438
|
continue;
|
|
103144
|
-
for (const f of
|
|
104439
|
+
for (const f of readdirSync12(dir)) {
|
|
103145
104440
|
if (f.endsWith(".jsonl"))
|
|
103146
|
-
files.push(
|
|
104441
|
+
files.push(join58(dir, f));
|
|
103147
104442
|
}
|
|
103148
104443
|
} catch {}
|
|
103149
104444
|
}
|
|
103150
104445
|
return files;
|
|
103151
104446
|
}
|
|
103152
104447
|
function buildCorpus(options = {}) {
|
|
103153
|
-
const root = options.projectsRoot ??
|
|
104448
|
+
const root = options.projectsRoot ?? join58(homedir38(), ".claude", "projects");
|
|
103154
104449
|
const files = listTranscripts(root);
|
|
103155
104450
|
const records = [];
|
|
103156
104451
|
for (const f of files)
|
|
103157
104452
|
records.push(...replayTranscript(f));
|
|
103158
104453
|
if (options.write && records.length > 0) {
|
|
103159
|
-
const outputPath = options.outputPath ??
|
|
104454
|
+
const outputPath = options.outputPath ?? join58(homedir38(), ".claudish", "behavior-divergences.jsonl");
|
|
103160
104455
|
try {
|
|
103161
104456
|
appendFileSync8(outputPath, `${records.map((r) => JSON.stringify(r)).join(`
|
|
103162
104457
|
`)}
|
|
@@ -103174,7 +104469,7 @@ var init_corpus = __esm(() => {
|
|
|
103174
104469
|
// src/hooks-command.ts
|
|
103175
104470
|
import { existsSync as existsSync41, mkdirSync as mkdirSync27, readFileSync as readFileSync48, writeFileSync as writeFileSync25 } from "fs";
|
|
103176
104471
|
import { homedir as homedir39 } from "os";
|
|
103177
|
-
import { join as
|
|
104472
|
+
import { join as join59, resolve as resolve9 } from "path";
|
|
103178
104473
|
function severityColor(sev) {
|
|
103179
104474
|
if (sev === "fix")
|
|
103180
104475
|
return green(sev);
|
|
@@ -103297,10 +104592,10 @@ async function check(file, json) {
|
|
|
103297
104592
|
return valid ? 0 : 1;
|
|
103298
104593
|
}
|
|
103299
104594
|
function init(dirArg) {
|
|
103300
|
-
const dir = dirArg ? resolve9(process.cwd(), dirArg) :
|
|
104595
|
+
const dir = dirArg ? resolve9(process.cwd(), dirArg) : join59(homedir39(), ".claudish", "hooks");
|
|
103301
104596
|
const files = [
|
|
103302
|
-
[
|
|
103303
|
-
[
|
|
104597
|
+
[join59(dir, "example-hook.ts"), EXAMPLE_HOOK],
|
|
104598
|
+
[join59(dir, "claudish-hooks.d.ts"), HOOKS_AMBIENT_DTS]
|
|
103304
104599
|
];
|
|
103305
104600
|
const existing = files.filter(([path]) => existsSync41(path)).map(([path]) => path);
|
|
103306
104601
|
if (existing.length > 0) {
|
|
@@ -103503,7 +104798,7 @@ var init_hooks_command = __esm(() => {
|
|
|
103503
104798
|
import { spawn as spawn3 } from "child_process";
|
|
103504
104799
|
import { existsSync as existsSync42, readFileSync as readFileSync49, writeFileSync as writeFileSync26 } from "fs";
|
|
103505
104800
|
import { connect as netConnect2 } from "net";
|
|
103506
|
-
import { join as
|
|
104801
|
+
import { join as join60 } from "path";
|
|
103507
104802
|
import { setTimeout as wait } from "timers/promises";
|
|
103508
104803
|
function paneRouteLine(explanation) {
|
|
103509
104804
|
const origin = describeRouteExplanation(explanation);
|
|
@@ -103653,9 +104948,9 @@ async function runWithGrid(sessionPath, models, input, opts) {
|
|
|
103653
104948
|
const keep = opts?.keep ?? false;
|
|
103654
104949
|
const manifest = setupSession(sessionPath, models, input);
|
|
103655
104950
|
const startedAt = new Date().toISOString();
|
|
103656
|
-
const gridfilePath =
|
|
103657
|
-
const prompt = readFileSync49(
|
|
103658
|
-
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");
|
|
103659
104954
|
const usedBannerColors = new Set;
|
|
103660
104955
|
const routeLines = mode === "interactive" ? new Map : await resolvePaneRouteLines(Object.values(manifest.models).map((entry) => entry.model));
|
|
103661
104956
|
const gridLines = Object.entries(manifest.models).map(([anonId]) => {
|
|
@@ -103687,7 +104982,7 @@ async function runWithGrid(sessionPath, models, input, opts) {
|
|
|
103687
104982
|
});
|
|
103688
104983
|
const [{ results }] = await Promise.all([subscription, procExit]);
|
|
103689
104984
|
const status = buildTeamStatus(manifest, startedAt, results?.panes ?? null);
|
|
103690
|
-
const statusPath =
|
|
104985
|
+
const statusPath = join60(sessionPath, "status.json");
|
|
103691
104986
|
writeFileSync26(statusPath, JSON.stringify(status, null, 2), "utf-8");
|
|
103692
104987
|
return status;
|
|
103693
104988
|
}
|
|
@@ -103708,7 +105003,7 @@ var init_team_grid = __esm(() => {
|
|
|
103708
105003
|
|
|
103709
105004
|
// src/team-cli.ts
|
|
103710
105005
|
import { readFileSync as readFileSync50 } from "fs";
|
|
103711
|
-
import { join as
|
|
105006
|
+
import { join as join61 } from "path";
|
|
103712
105007
|
async function runPaneTeam(sessionPath, models, input, opts) {
|
|
103713
105008
|
installPaneShutdownHooks({ exitAfter: true });
|
|
103714
105009
|
try {
|
|
@@ -103853,7 +105148,7 @@ async function teamCommand(args) {
|
|
|
103853
105148
|
}
|
|
103854
105149
|
case "judge": {
|
|
103855
105150
|
await judgeResponses(sessionPath, { judges });
|
|
103856
|
-
console.log(readFileSync50(
|
|
105151
|
+
console.log(readFileSync50(join61(sessionPath, "verdict.md"), "utf-8"));
|
|
103857
105152
|
break;
|
|
103858
105153
|
}
|
|
103859
105154
|
case "run-and-judge": {
|
|
@@ -103869,7 +105164,7 @@ async function teamCommand(args) {
|
|
|
103869
105164
|
});
|
|
103870
105165
|
printStatus(status);
|
|
103871
105166
|
await judgeResponses(sessionPath, { judges });
|
|
103872
|
-
console.log(readFileSync50(
|
|
105167
|
+
console.log(readFileSync50(join61(sessionPath, "verdict.md"), "utf-8"));
|
|
103873
105168
|
break;
|
|
103874
105169
|
}
|
|
103875
105170
|
case "status": {
|
|
@@ -105797,7 +107092,7 @@ var init_account_command = __esm(() => {
|
|
|
105797
107092
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
105798
107093
|
import { existsSync as existsSync43, unlinkSync as unlinkSync12 } from "fs";
|
|
105799
107094
|
import { homedir as homedir40 } from "os";
|
|
105800
|
-
import { join as
|
|
107095
|
+
import { join as join62 } from "path";
|
|
105801
107096
|
async function defaultSuggestModel() {
|
|
105802
107097
|
try {
|
|
105803
107098
|
const token = await getValidAntigravityAccessToken();
|
|
@@ -105916,7 +107211,7 @@ No session detected yet. Starting the Antigravity CLI interactively \u2014
|
|
|
105916
107211
|
async logout(deps) {
|
|
105917
107212
|
deleteSharedAntigravityToken(deps);
|
|
105918
107213
|
try {
|
|
105919
|
-
const tokenFile =
|
|
107214
|
+
const tokenFile = join62(homedir40(), ".gemini", "antigravity-cli", "antigravity-oauth-token");
|
|
105920
107215
|
if (existsSync43(tokenFile))
|
|
105921
107216
|
unlinkSync12(tokenFile);
|
|
105922
107217
|
} catch {}
|
|
@@ -114194,8 +115489,8 @@ var init_own_pane = __esm(() => {
|
|
|
114194
115489
|
});
|
|
114195
115490
|
|
|
114196
115491
|
// src/hooks/session/interactive-driver.ts
|
|
114197
|
-
import { existsSync as existsSync44, statSync as
|
|
114198
|
-
import { join as
|
|
115492
|
+
import { existsSync as existsSync44, statSync as statSync22 } from "fs";
|
|
115493
|
+
import { join as join63 } from "path";
|
|
114199
115494
|
function nextDriverPhase(phase, ev) {
|
|
114200
115495
|
const row = EDGES[phase];
|
|
114201
115496
|
return row[ev] ?? null;
|
|
@@ -114413,7 +115708,7 @@ class InteractiveDriver {
|
|
|
114413
115708
|
}
|
|
114414
115709
|
follow(sid, offset) {
|
|
114415
115710
|
const path = transcriptPathFor(this.o.cwd, sid, this.o.projectsRoot);
|
|
114416
|
-
this.follower = new TranscriptFollower(path,
|
|
115711
|
+
this.follower = new TranscriptFollower(path, join63(path.replace(/\.jsonl$/, ""), "subagents"));
|
|
114417
115712
|
this.sessionId = sid;
|
|
114418
115713
|
this.settledTurn = null;
|
|
114419
115714
|
this.lastPollAt = Date.now();
|
|
@@ -114958,7 +116253,7 @@ function assertNeverPhase(p) {
|
|
|
114958
116253
|
}
|
|
114959
116254
|
function fileSizeOf(path) {
|
|
114960
116255
|
try {
|
|
114961
|
-
return
|
|
116256
|
+
return statSync22(path).size;
|
|
114962
116257
|
} catch {
|
|
114963
116258
|
return 0;
|
|
114964
116259
|
}
|
|
@@ -115515,20 +116810,20 @@ import { spawn as spawn4, spawnSync as spawnSync4 } from "child_process";
|
|
|
115515
116810
|
import { randomUUID as randomUUID11 } from "crypto";
|
|
115516
116811
|
import {
|
|
115517
116812
|
chmodSync as chmodSync5,
|
|
115518
|
-
closeSync as
|
|
116813
|
+
closeSync as closeSync15,
|
|
115519
116814
|
existsSync as existsSync46,
|
|
115520
116815
|
mkdirSync as mkdirSync28,
|
|
115521
116816
|
mkdtempSync as mkdtempSync4,
|
|
115522
|
-
openSync as
|
|
116817
|
+
openSync as openSync15,
|
|
115523
116818
|
readFileSync as readFileSync51,
|
|
115524
|
-
readdirSync as
|
|
116819
|
+
readdirSync as readdirSync13,
|
|
115525
116820
|
rmSync as rmSync15,
|
|
115526
|
-
statSync as
|
|
116821
|
+
statSync as statSync23,
|
|
115527
116822
|
unlinkSync as unlinkSync13,
|
|
115528
116823
|
writeFileSync as writeFileSync27
|
|
115529
116824
|
} from "fs";
|
|
115530
116825
|
import { homedir as homedir42, tmpdir as tmpdir3 } from "os";
|
|
115531
|
-
import { dirname as
|
|
116826
|
+
import { dirname as dirname23, join as join64 } from "path";
|
|
115532
116827
|
import { isatty } from "tty";
|
|
115533
116828
|
function releaseTerminalIsolation() {
|
|
115534
116829
|
if (!restoreTerminal)
|
|
@@ -115600,7 +116895,7 @@ function hasResolvableAnthropicAuth(deps = {}) {
|
|
|
115600
116895
|
if (isRealAnthropicEnvCredential(env, "ANTHROPIC_API_KEY") || isRealAnthropicEnvCredential(env, "ANTHROPIC_AUTH_TOKEN")) {
|
|
115601
116896
|
return true;
|
|
115602
116897
|
}
|
|
115603
|
-
if (fileExists(
|
|
116898
|
+
if (fileExists(join64(homedir42(), ".claude", ".credentials.json")))
|
|
115604
116899
|
return true;
|
|
115605
116900
|
return keychainProbe();
|
|
115606
116901
|
}
|
|
@@ -115700,10 +116995,10 @@ function readableClaudeSettingsLayers(config, env, deps) {
|
|
|
115700
116995
|
const cwd = deps.cwd ?? process.cwd();
|
|
115701
116996
|
const files = [
|
|
115702
116997
|
managedSettingsPath(),
|
|
115703
|
-
|
|
115704
|
-
...env.CLAUDE_CONFIG_DIR ? [
|
|
115705
|
-
|
|
115706
|
-
|
|
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")
|
|
115707
117002
|
];
|
|
115708
117003
|
const layers = [];
|
|
115709
117004
|
for (const file of files) {
|
|
@@ -115735,7 +117030,7 @@ function readSettingsSource(source, readFile) {
|
|
|
115735
117030
|
}
|
|
115736
117031
|
function managedSettingsPath() {
|
|
115737
117032
|
if (isWindows2()) {
|
|
115738
|
-
return
|
|
117033
|
+
return join64(process.env.PROGRAMDATA || "C:\\ProgramData", "ClaudeCode", "managed-settings.json");
|
|
115739
117034
|
}
|
|
115740
117035
|
if (process.platform === "darwin") {
|
|
115741
117036
|
return "/Library/Application Support/ClaudeCode/managed-settings.json";
|
|
@@ -115756,8 +117051,8 @@ function isWindows2() {
|
|
|
115756
117051
|
}
|
|
115757
117052
|
function createStatusLineScript(tokenFilePath) {
|
|
115758
117053
|
const homeDir = process.env.HOME || process.env.USERPROFILE || tmpdir3();
|
|
115759
|
-
const claudishDir =
|
|
115760
|
-
const scriptPath =
|
|
117054
|
+
const claudishDir = join64(homeDir, ".claudish");
|
|
117055
|
+
const scriptPath = join64(claudishDir, `status-${process.pid}-${Date.now()}.js`);
|
|
115761
117056
|
const escapedTokenPath = tokenFilePath.replace(/\\/g, "\\\\");
|
|
115762
117057
|
const light = getThemeMode() === "light";
|
|
115763
117058
|
const cyanCode = light ? "38;2;14;116;144" : "96";
|
|
@@ -115917,7 +117212,7 @@ process.stdin.on('end', () => {
|
|
|
115917
117212
|
}
|
|
115918
117213
|
function initializeTokenFile(tokenFilePath) {
|
|
115919
117214
|
try {
|
|
115920
|
-
mkdirSync28(
|
|
117215
|
+
mkdirSync28(dirname23(tokenFilePath), { recursive: true });
|
|
115921
117216
|
writeFileSync27(tokenFilePath, JSON.stringify({
|
|
115922
117217
|
input_tokens: 0,
|
|
115923
117218
|
output_tokens: 0,
|
|
@@ -115937,7 +117232,7 @@ function cleanupStaleTokenFiles(dir, now = Date.now(), maxAgeMs = STALE_TOKEN_FI
|
|
|
115937
117232
|
let removed = 0;
|
|
115938
117233
|
let entries;
|
|
115939
117234
|
try {
|
|
115940
|
-
entries =
|
|
117235
|
+
entries = readdirSync13(dir);
|
|
115941
117236
|
} catch {
|
|
115942
117237
|
return 0;
|
|
115943
117238
|
}
|
|
@@ -115949,9 +117244,9 @@ function cleanupStaleTokenFiles(dir, now = Date.now(), maxAgeMs = STALE_TOKEN_FI
|
|
|
115949
117244
|
if (!name.startsWith("tokens-") || !name.endsWith(".json"))
|
|
115950
117245
|
continue;
|
|
115951
117246
|
scanned++;
|
|
115952
|
-
const full =
|
|
117247
|
+
const full = join64(dir, name);
|
|
115953
117248
|
try {
|
|
115954
|
-
if (
|
|
117249
|
+
if (statSync23(full).mtimeMs >= cutoff)
|
|
115955
117250
|
continue;
|
|
115956
117251
|
unlinkSync13(full);
|
|
115957
117252
|
removed++;
|
|
@@ -115978,9 +117273,9 @@ function parseSettingsArgSafe(value) {
|
|
|
115978
117273
|
}
|
|
115979
117274
|
function userSettingsFileCandidates(cwd) {
|
|
115980
117275
|
return [
|
|
115981
|
-
|
|
115982
|
-
|
|
115983
|
-
|
|
117276
|
+
join64(homedir42(), ".claude", "settings.json"),
|
|
117277
|
+
join64(cwd, ".claude", "settings.json"),
|
|
117278
|
+
join64(cwd, ".claude", "settings.local.json")
|
|
115984
117279
|
];
|
|
115985
117280
|
}
|
|
115986
117281
|
function discoverUserStatusLineCommand(claudeArgs = [], cwd = process.cwd()) {
|
|
@@ -116021,11 +117316,11 @@ function buildChainedStatusCommand(userCommand, claudishBody, claudishSegment) {
|
|
|
116021
117316
|
}
|
|
116022
117317
|
function createTempSettingsFile(_modelDisplay, port, proxyAuthMode, userStatusLineCommand) {
|
|
116023
117318
|
const homeDir = process.env.HOME || process.env.USERPROFILE || tmpdir3();
|
|
116024
|
-
const claudishDir =
|
|
117319
|
+
const claudishDir = join64(homeDir, ".claudish");
|
|
116025
117320
|
try {
|
|
116026
117321
|
mkdirSync28(claudishDir, { recursive: true });
|
|
116027
117322
|
} catch {}
|
|
116028
|
-
const tempPath =
|
|
117323
|
+
const tempPath = join64(claudishDir, `settings-${process.pid}-${Date.now()}.json`);
|
|
116029
117324
|
const assignedFile = assignedTokenFile(process.env) !== null;
|
|
116030
117325
|
const tokenFilePath = resolveTokenFilePath(port);
|
|
116031
117326
|
cleanupStaleTokenFiles(claudishDir);
|
|
@@ -116084,6 +117379,7 @@ function buildClaudishSettingsOverlay(statusLine, proxyAuthMode, paneChild = isP
|
|
|
116084
117379
|
if (paneChild) {
|
|
116085
117380
|
settings.skipDangerousModePermissionPrompt = true;
|
|
116086
117381
|
settings.editorMode = "normal";
|
|
117382
|
+
settings.wheelScrollAccelerationEnabled = false;
|
|
116087
117383
|
}
|
|
116088
117384
|
return settings;
|
|
116089
117385
|
}
|
|
@@ -116108,6 +117404,9 @@ function mergeUserSettingsIfPresent(config, tempSettingsPath, statusLine, proxyA
|
|
|
116108
117404
|
if (paneChild) {
|
|
116109
117405
|
userSettings.editorMode = "normal";
|
|
116110
117406
|
}
|
|
117407
|
+
if (paneChild) {
|
|
117408
|
+
userSettings.wheelScrollAccelerationEnabled = false;
|
|
117409
|
+
}
|
|
116111
117410
|
chmodSync5(tempSettingsPath, 384);
|
|
116112
117411
|
writeFileSync27(tempSettingsPath, JSON.stringify(userSettings, null, 2), {
|
|
116113
117412
|
encoding: "utf-8",
|
|
@@ -116412,7 +117711,7 @@ async function runClaudeWithProxy(config, proxyUrl, onCleanup) {
|
|
|
116412
117711
|
console.error(`
|
|
116413
117712
|
Or set CLAUDE_PATH to your custom installation:`);
|
|
116414
117713
|
const home = homedir42();
|
|
116415
|
-
const localPath = isWindows2() ?
|
|
117714
|
+
const localPath = isWindows2() ? join64(home, ".claude", "local", "claude.exe") : join64(home, ".claude", "local", "claude");
|
|
116416
117715
|
console.error(` export CLAUDE_PATH=${localPath}`);
|
|
116417
117716
|
process.exit(1);
|
|
116418
117717
|
}
|
|
@@ -116423,11 +117722,11 @@ Or set CLAUDE_PATH to your custom installation:`);
|
|
|
116423
117722
|
const childWantsTty = config.interactive && !process.stdout.isTTY && Boolean(process.stdin.isTTY);
|
|
116424
117723
|
if (childWantsTty) {
|
|
116425
117724
|
try {
|
|
116426
|
-
const fd =
|
|
117725
|
+
const fd = openSync15("/dev/fd/0", "r+");
|
|
116427
117726
|
if (isatty(fd)) {
|
|
116428
117727
|
ttyFd = fd;
|
|
116429
117728
|
} else {
|
|
116430
|
-
|
|
117729
|
+
closeSync15(fd);
|
|
116431
117730
|
}
|
|
116432
117731
|
} catch {
|
|
116433
117732
|
ttyFd = undefined;
|
|
@@ -116447,7 +117746,7 @@ Or set CLAUDE_PATH to your custom installation:`);
|
|
|
116447
117746
|
let driverLaunch = null;
|
|
116448
117747
|
if (surfaces.driver) {
|
|
116449
117748
|
const minted = sessionFlags.sessionId || sessionFlags.resumes ? null : randomUUID11();
|
|
116450
|
-
const turnDir = mkdtempSync4(
|
|
117749
|
+
const turnDir = mkdtempSync4(join64(tmpdir3(), "claudish-turns-"));
|
|
116451
117750
|
chmodSync5(turnDir, 448);
|
|
116452
117751
|
claudeArgs.unshift(...driverLeadingArgs({ mintedSessionId: minted, turnDir }));
|
|
116453
117752
|
const sink = new InProcessSink;
|
|
@@ -116530,7 +117829,7 @@ Or set CLAUDE_PATH to your custom installation:`);
|
|
|
116530
117829
|
const fdToClose = ttyFd;
|
|
116531
117830
|
proc.on("spawn", () => {
|
|
116532
117831
|
try {
|
|
116533
|
-
|
|
117832
|
+
closeSync15(fdToClose);
|
|
116534
117833
|
} catch {}
|
|
116535
117834
|
});
|
|
116536
117835
|
}
|
|
@@ -116596,15 +117895,15 @@ async function findClaudeBinary() {
|
|
|
116596
117895
|
}
|
|
116597
117896
|
}
|
|
116598
117897
|
const home = homedir42();
|
|
116599
|
-
const localPath = isWindows ?
|
|
117898
|
+
const localPath = isWindows ? join64(home, ".claude", "local", "claude.exe") : join64(home, ".claude", "local", "claude");
|
|
116600
117899
|
if (existsSync46(localPath)) {
|
|
116601
117900
|
return localPath;
|
|
116602
117901
|
}
|
|
116603
117902
|
if (isWindows) {
|
|
116604
117903
|
const windowsPaths = [
|
|
116605
|
-
|
|
116606
|
-
|
|
116607
|
-
|
|
117904
|
+
join64(home, "AppData", "Roaming", "npm", "claude.cmd"),
|
|
117905
|
+
join64(home, ".npm-global", "claude.cmd"),
|
|
117906
|
+
join64(home, "node_modules", ".bin", "claude.cmd")
|
|
116608
117907
|
];
|
|
116609
117908
|
for (const path of windowsPaths) {
|
|
116610
117909
|
if (existsSync46(path)) {
|
|
@@ -116615,11 +117914,11 @@ async function findClaudeBinary() {
|
|
|
116615
117914
|
const commonPaths = [
|
|
116616
117915
|
"/usr/local/bin/claude",
|
|
116617
117916
|
"/opt/homebrew/bin/claude",
|
|
116618
|
-
|
|
116619
|
-
|
|
116620
|
-
|
|
117917
|
+
join64(home, ".npm-global/bin/claude"),
|
|
117918
|
+
join64(home, ".local/bin/claude"),
|
|
117919
|
+
join64(home, "node_modules/.bin/claude"),
|
|
116621
117920
|
"/data/data/com.termux/files/usr/bin/claude",
|
|
116622
|
-
|
|
117921
|
+
join64(home, "../usr/bin/claude")
|
|
116623
117922
|
];
|
|
116624
117923
|
for (const path of commonPaths) {
|
|
116625
117924
|
if (existsSync46(path)) {
|
|
@@ -116728,16 +118027,16 @@ var init_claude_runner = __esm(() => {
|
|
|
116728
118027
|
// src/diag-output.ts
|
|
116729
118028
|
import { createWriteStream as createWriteStream2, mkdirSync as mkdirSync29, unlinkSync as unlinkSync14, writeFileSync as writeFileSync28 } from "fs";
|
|
116730
118029
|
import { homedir as homedir43 } from "os";
|
|
116731
|
-
import { join as
|
|
118030
|
+
import { join as join65 } from "path";
|
|
116732
118031
|
function getClaudishDir() {
|
|
116733
|
-
const dir =
|
|
118032
|
+
const dir = join65(homedir43(), ".claudish");
|
|
116734
118033
|
try {
|
|
116735
118034
|
mkdirSync29(dir, { recursive: true });
|
|
116736
118035
|
} catch {}
|
|
116737
118036
|
return dir;
|
|
116738
118037
|
}
|
|
116739
118038
|
function getDiagLogPath() {
|
|
116740
|
-
return
|
|
118039
|
+
return join65(getClaudishDir(), `diag-${process.pid}.log`);
|
|
116741
118040
|
}
|
|
116742
118041
|
|
|
116743
118042
|
class LogFileDiagOutput {
|
|
@@ -117302,7 +118601,7 @@ var init_advisor_startup = __esm(() => {
|
|
|
117302
118601
|
});
|
|
117303
118602
|
|
|
117304
118603
|
// src/session/conversation.ts
|
|
117305
|
-
import { closeSync as
|
|
118604
|
+
import { closeSync as closeSync16, openSync as openSync16, readSync as readSync8, statSync as statSync24 } from "fs";
|
|
117306
118605
|
import { StringDecoder } from "string_decoder";
|
|
117307
118606
|
function looksLikeTurn(line) {
|
|
117308
118607
|
const assistant = line.includes('"type":"assistant"');
|
|
@@ -117357,8 +118656,8 @@ function readConversation(file, opts = {}) {
|
|
|
117357
118656
|
};
|
|
117358
118657
|
let fd = null;
|
|
117359
118658
|
try {
|
|
117360
|
-
const size =
|
|
117361
|
-
fd =
|
|
118659
|
+
const size = statSync24(file).size;
|
|
118660
|
+
fd = openSync16(file, "r");
|
|
117362
118661
|
const buf = Buffer.allocUnsafe(chunkBytes);
|
|
117363
118662
|
const decoder = new StringDecoder("utf-8");
|
|
117364
118663
|
let pending = "";
|
|
@@ -117390,7 +118689,7 @@ function readConversation(file, opts = {}) {
|
|
|
117390
118689
|
take({ role: raw.role, text, elided });
|
|
117391
118690
|
};
|
|
117392
118691
|
while (pos < size) {
|
|
117393
|
-
const n =
|
|
118692
|
+
const n = readSync8(fd, buf, 0, Math.min(chunkBytes, size - pos), pos);
|
|
117394
118693
|
if (n <= 0)
|
|
117395
118694
|
break;
|
|
117396
118695
|
pos += n;
|
|
@@ -117414,7 +118713,7 @@ function readConversation(file, opts = {}) {
|
|
|
117414
118713
|
} catch {} finally {
|
|
117415
118714
|
if (fd !== null) {
|
|
117416
118715
|
try {
|
|
117417
|
-
|
|
118716
|
+
closeSync16(fd);
|
|
117418
118717
|
} catch {}
|
|
117419
118718
|
}
|
|
117420
118719
|
}
|
|
@@ -119399,7 +120698,7 @@ init_op_source();
|
|
|
119399
120698
|
init_startup_trace();
|
|
119400
120699
|
init_picker_cancelled();
|
|
119401
120700
|
import { existsSync as existsSync47, readFileSync as readFileSync53 } from "fs";
|
|
119402
|
-
import { join as
|
|
120701
|
+
import { join as join66, resolve as resolve11 } from "path";
|
|
119403
120702
|
|
|
119404
120703
|
// src/tui/runtime/should-open-picker.ts
|
|
119405
120704
|
function noModelConfigured(gate) {
|
|
@@ -119416,7 +120715,7 @@ function requiresExplicitModel(gate, tty) {
|
|
|
119416
120715
|
var autoloadDropped = [...droppedAtLaunch, ...takeLauncherDropped(process.env)];
|
|
119417
120716
|
var dotenvText;
|
|
119418
120717
|
try {
|
|
119419
|
-
dotenvText = readFileSync53(
|
|
120718
|
+
dotenvText = readFileSync53(join66(process.cwd(), ".env"));
|
|
119420
120719
|
} catch {}
|
|
119421
120720
|
if (dotenvText !== undefined) {
|
|
119422
120721
|
await Promise.resolve().then(() => init_dotenv_allowlist());
|
|
@@ -119764,7 +121063,7 @@ async function runCli() {
|
|
|
119764
121063
|
process.exit(1);
|
|
119765
121064
|
}
|
|
119766
121065
|
const mode = cliConfig.teamMode ?? "default";
|
|
119767
|
-
const sessionPath =
|
|
121066
|
+
const sessionPath = join66(process.cwd(), `.claudish-team-${Date.now()}`);
|
|
119768
121067
|
if (mode === "json") {
|
|
119769
121068
|
await Promise.resolve().then(() => init_pane());
|
|
119770
121069
|
await Promise.resolve().then(() => init_team_orchestrator());
|
|
@@ -119784,7 +121083,7 @@ async function runCli() {
|
|
|
119784
121083
|
const status = await runModels(sessionPath);
|
|
119785
121084
|
const result = { ...status, responses: {} };
|
|
119786
121085
|
for (const anonId of Object.keys(status.models)) {
|
|
119787
|
-
const responsePath =
|
|
121086
|
+
const responsePath = join66(sessionPath, `response-${anonId}.md`);
|
|
119788
121087
|
try {
|
|
119789
121088
|
const raw = readFileSync53(responsePath, "utf-8").trim();
|
|
119790
121089
|
try {
|