@tangle-network/agent-eval 0.123.8 → 0.125.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/CHANGELOG.md +29 -4
  2. package/dist/analyst/index.d.ts +5 -0
  3. package/dist/analyst/index.js +4 -4
  4. package/dist/benchmarks/index.js +4 -4
  5. package/dist/campaign/index.d.ts +20 -1
  6. package/dist/campaign/index.js +4 -4
  7. package/dist/{chunk-ZU3QWGZE.js → chunk-A62YMFWA.js} +89 -8
  8. package/dist/chunk-A62YMFWA.js.map +1 -0
  9. package/dist/{chunk-BUR5R4R4.js → chunk-A6GT67HT.js} +3 -3
  10. package/dist/{chunk-EEHHBAWH.js → chunk-DT7OXY3C.js} +2 -2
  11. package/dist/{chunk-PDHIOKRE.js → chunk-EQUK3RFS.js} +8 -4
  12. package/dist/chunk-EQUK3RFS.js.map +1 -0
  13. package/dist/{chunk-V7HQGZBT.js → chunk-GID26AN4.js} +2 -2
  14. package/dist/{chunk-27UXRPIQ.js → chunk-HM6V7F3M.js} +2 -2
  15. package/dist/chunk-IPYXE555.js +594 -0
  16. package/dist/chunk-IPYXE555.js.map +1 -0
  17. package/dist/chunk-LKKT3IVV.js +1644 -0
  18. package/dist/chunk-LKKT3IVV.js.map +1 -0
  19. package/dist/chunk-M7AH34KV.js +155 -0
  20. package/dist/chunk-M7AH34KV.js.map +1 -0
  21. package/dist/chunk-MAX3TN3C.js +249 -0
  22. package/dist/chunk-MAX3TN3C.js.map +1 -0
  23. package/dist/{chunk-QVGVJQMR.js → chunk-PMITBABE.js} +7 -5
  24. package/dist/{chunk-QVGVJQMR.js.map → chunk-PMITBABE.js.map} +1 -1
  25. package/dist/{chunk-J3LHTAAB.js → chunk-QOTFXW5L.js} +1 -47
  26. package/dist/chunk-QOTFXW5L.js.map +1 -0
  27. package/dist/chunk-RZTMDUO7.js +49 -0
  28. package/dist/chunk-RZTMDUO7.js.map +1 -0
  29. package/dist/chunk-VBQ3CRKH.js +291 -0
  30. package/dist/chunk-VBQ3CRKH.js.map +1 -0
  31. package/dist/{chunk-RQ5TP2TV.js → chunk-W5B3ZGP3.js} +3 -3
  32. package/dist/cli.js +13 -2
  33. package/dist/cli.js.map +1 -1
  34. package/dist/contract/index.d.ts +7 -0
  35. package/dist/contract/index.js +4 -4
  36. package/dist/index.d.ts +833 -82
  37. package/dist/index.js +61 -121
  38. package/dist/index.js.map +1 -1
  39. package/dist/openapi.json +1 -1
  40. package/dist/pipelines/index.js +4 -2
  41. package/dist/pipelines/index.js.map +1 -1
  42. package/dist/rl.d.ts +3 -0
  43. package/dist/rl.js +2 -2
  44. package/dist/rollout/index.d.ts +1061 -0
  45. package/dist/rollout/index.js +109 -0
  46. package/dist/rollout/index.js.map +1 -0
  47. package/dist/supervisor-run/index.d.ts +857 -0
  48. package/dist/supervisor-run/index.js +64 -0
  49. package/dist/supervisor-run/index.js.map +1 -0
  50. package/dist/wire/index.d.ts +3 -0
  51. package/dist/wire/index.js +2 -2
  52. package/docs/rollout.md +48 -0
  53. package/package.json +11 -1
  54. package/dist/chunk-J3LHTAAB.js.map +0 -1
  55. package/dist/chunk-PDHIOKRE.js.map +0 -1
  56. package/dist/chunk-ZU3QWGZE.js.map +0 -1
  57. /package/dist/{chunk-BUR5R4R4.js.map → chunk-A6GT67HT.js.map} +0 -0
  58. /package/dist/{chunk-EEHHBAWH.js.map → chunk-DT7OXY3C.js.map} +0 -0
  59. /package/dist/{chunk-V7HQGZBT.js.map → chunk-GID26AN4.js.map} +0 -0
  60. /package/dist/{chunk-27UXRPIQ.js.map → chunk-HM6V7F3M.js.map} +0 -0
  61. /package/dist/{chunk-RQ5TP2TV.js.map → chunk-W5B3ZGP3.js.map} +0 -0
@@ -0,0 +1,1644 @@
1
+ import {
2
+ DEFAULT_OPENCODE_DB,
3
+ findOpencodeSessionsByDirectory,
4
+ openOpencodeDb,
5
+ parseClaudeEntries,
6
+ transcriptFromEntries
7
+ } from "./chunk-VBQ3CRKH.js";
8
+ import {
9
+ ROLLOUT_SCHEMA,
10
+ isRolloutLine
11
+ } from "./chunk-MAX3TN3C.js";
12
+
13
+ // src/supervisor-run/types.ts
14
+ function unavailable(reason) {
15
+ return { unavailable: reason };
16
+ }
17
+ function isUnavailable(v) {
18
+ return typeof v === "object" && v !== null && typeof v.unavailable === "string";
19
+ }
20
+ function showMeasured(v) {
21
+ if (isUnavailable(v)) return `unavailable \u2014 ${v.unavailable}`;
22
+ if (v === null) return "null";
23
+ return String(v);
24
+ }
25
+ var NO_SOURCE_LIMITS = {
26
+ spendUsd: null,
27
+ workerVerdicts: null,
28
+ deliverables: null
29
+ };
30
+ var SUPERVISOR_RUN_SCHEMA = "tangle.supervisor-run@1";
31
+ var SUPERVISOR_RUN_ROLLUP_SCHEMA = "tangle.supervisor-run-rollup@1";
32
+
33
+ // src/supervisor-run/analyze.ts
34
+ function asRecord(v) {
35
+ return typeof v === "object" && v !== null ? v : {};
36
+ }
37
+ function num(v) {
38
+ return typeof v === "number" && Number.isFinite(v) ? v : 0;
39
+ }
40
+ function readSpend(v) {
41
+ const rec = asRecord(v);
42
+ const tok = asRecord(rec.tokens);
43
+ const cacheRead = tok.cacheRead ?? tok.cache_read;
44
+ const cacheWrite = tok.cacheWrite ?? tok.cache_write;
45
+ return {
46
+ tokens: {
47
+ input: num(tok.input),
48
+ output: num(tok.output),
49
+ cacheRead: num(cacheRead),
50
+ cacheWrite: num(cacheWrite),
51
+ hasCache: cacheRead !== void 0 || cacheWrite !== void 0
52
+ },
53
+ usd: num(rec.usd)
54
+ };
55
+ }
56
+ var NO_CACHE_COUNTERS = "the journal carries no cache-token counters for this role";
57
+ function parseJsonl(text) {
58
+ if (text === null) return [];
59
+ const out = [];
60
+ for (const line2 of text.split("\n")) {
61
+ const trimmed = line2.trim();
62
+ if (!trimmed) continue;
63
+ try {
64
+ const parsed = JSON.parse(trimmed);
65
+ if (typeof parsed === "object" && parsed !== null) out.push(parsed);
66
+ } catch {
67
+ }
68
+ }
69
+ return out;
70
+ }
71
+ function parseJson(text) {
72
+ if (text === null) return null;
73
+ try {
74
+ const parsed = JSON.parse(text);
75
+ return typeof parsed === "object" && parsed !== null ? parsed : null;
76
+ } catch {
77
+ return null;
78
+ }
79
+ }
80
+ function ms(at) {
81
+ if (typeof at !== "string") return null;
82
+ const t = Date.parse(at);
83
+ return Number.isFinite(t) ? t : null;
84
+ }
85
+ function parseSupervisorTree(src) {
86
+ const events = parseJsonl(src.journal);
87
+ const state = parseJson(src.state);
88
+ const spawns = [];
89
+ const closes = [];
90
+ let brainIn = 0;
91
+ let brainOut = 0;
92
+ let brainCacheRead = 0;
93
+ let brainCacheWrite = 0;
94
+ let brainHasCache = false;
95
+ let brainUsd = 0;
96
+ let meteredCount = 0;
97
+ let rootId = null;
98
+ for (const ev of events) {
99
+ const kind = typeof ev.kind === "string" ? ev.kind : "";
100
+ const id = typeof ev.id === "string" ? ev.id : "";
101
+ if (kind === "spawned") {
102
+ const parent = typeof ev.parent === "string" ? ev.parent : null;
103
+ const label = typeof ev.label === "string" ? ev.label : "";
104
+ if (parent === null && rootId === null) rootId = id;
105
+ spawns.push({ id, parent, label, at: ms(ev.at) });
106
+ } else if (kind === "settled") {
107
+ closes.push({
108
+ id,
109
+ kind: "settled",
110
+ status: typeof ev.status === "string" ? ev.status : null,
111
+ verdict: typeof ev.verdict === "string" ? ev.verdict : null,
112
+ at: ms(ev.at),
113
+ spend: readSpend(ev.spent),
114
+ hasSpend: asRecord(ev.spent).tokens !== void 0
115
+ });
116
+ } else if (kind === "cancelled") {
117
+ closes.push({
118
+ id,
119
+ kind: "cancelled",
120
+ status: "cancelled",
121
+ verdict: typeof ev.reason === "string" ? ev.reason : null,
122
+ at: ms(ev.at),
123
+ spend: {
124
+ tokens: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, hasCache: false },
125
+ usd: 0
126
+ },
127
+ hasSpend: false
128
+ });
129
+ } else if (kind === "metered") {
130
+ const s = readSpend(ev.spend);
131
+ brainIn += s.tokens.input;
132
+ brainOut += s.tokens.output;
133
+ brainCacheRead += s.tokens.cacheRead;
134
+ brainCacheWrite += s.tokens.cacheWrite;
135
+ brainHasCache = brainHasCache || s.tokens.hasCache;
136
+ brainUsd += s.usd;
137
+ meteredCount += 1;
138
+ }
139
+ }
140
+ const workerSpawns = spawns.filter((s) => s.id !== rootId);
141
+ const workerIds = new Set(workerSpawns.map((s) => s.id));
142
+ const workerCloses = closes.filter((c) => workerIds.has(c.id));
143
+ const workerLogs = /* @__PURE__ */ new Map();
144
+ for (const w of src.workers ?? []) {
145
+ const facts = {
146
+ started: null,
147
+ finished: false,
148
+ finishedAt: null,
149
+ passed: null,
150
+ finishedPatchBytes: null,
151
+ evidenceBytes: 0,
152
+ steersQueued: 0,
153
+ steersDelivered: 0,
154
+ questions: 0
155
+ };
156
+ for (const req of parseJsonl(w.inbox)) {
157
+ if (typeof req.message === "string" && req.message.trim().length > 0) facts.steersQueued += 1;
158
+ }
159
+ for (const ev of parseJsonl(w.events)) {
160
+ const kind = ev.kind;
161
+ if (kind === "message") {
162
+ if (ev.direction === "up") {
163
+ facts.questions += 1;
164
+ continue;
165
+ }
166
+ if (typeof ev.requestId !== "string") facts.steersQueued += 1;
167
+ if (ev.delivered === true) facts.steersDelivered += 1;
168
+ } else if (kind === "started") {
169
+ facts.started = ms(ev.at);
170
+ } else if (kind === "finished") {
171
+ facts.finished = true;
172
+ facts.finishedAt = ms(ev.at);
173
+ facts.passed = typeof ev.passed === "boolean" ? ev.passed : null;
174
+ facts.finishedPatchBytes = typeof ev.patchBytes === "number" ? ev.patchBytes : null;
175
+ facts.evidenceBytes = typeof ev.evidence === "string" ? ev.evidence.length : 0;
176
+ }
177
+ }
178
+ workerLogs.set(w.label, facts);
179
+ }
180
+ const startedAt = ms(state?.startedAt) ?? spawns[0]?.at ?? null;
181
+ const completedAt = ms(state?.completedAt) ?? [...spawns.map((s) => s.at), ...closes.map((c) => c.at)].reduce(
182
+ (acc, t) => t === null ? acc : acc === null ? t : Math.max(acc, t),
183
+ null
184
+ );
185
+ return {
186
+ rootId,
187
+ spawns,
188
+ closes,
189
+ workerSpawns,
190
+ workerCloses,
191
+ brain: {
192
+ tokensIn: brainIn,
193
+ tokensOut: brainOut,
194
+ cacheRead: brainCacheRead,
195
+ cacheWrite: brainCacheWrite,
196
+ hasCache: brainHasCache,
197
+ usd: brainUsd,
198
+ meteredCount
199
+ },
200
+ workerLogs,
201
+ startedAt,
202
+ completedAt
203
+ };
204
+ }
205
+ function analyzeSupervisorRunSources(src, now = Date.now) {
206
+ const gaps = [];
207
+ const gap = (what, reason) => {
208
+ gaps.push(`${what}: ${reason}`);
209
+ return unavailable(reason);
210
+ };
211
+ const journalMissing = src.supRunDir === null ? "no supervisor run dir under <ws>/.loops/supervisor" : "journal.jsonl absent";
212
+ const haveJournal = src.journal !== null;
213
+ const state = parseJson(src.state);
214
+ const result = parseJson(src.result);
215
+ const judge = parseJson(src.judge);
216
+ const tree = parseSupervisorTree(src);
217
+ const { rootId, workerSpawns, workerCloses, startedAt, completedAt } = tree;
218
+ const supervisorWallMs = startedAt !== null && completedAt !== null && completedAt >= startedAt ? completedAt - startedAt : !haveJournal ? gap("supervisorWallMs", journalMissing) : gap("supervisorWallMs", "no parseable start/complete timestamps in state.json or journal");
219
+ const steerRows = [];
220
+ let steerQueuedTotal = 0;
221
+ let steerDeliveredTotal = 0;
222
+ let upLegMessages = 0;
223
+ if (src.workers !== null) {
224
+ for (const w of src.workers) {
225
+ const facts = tree.workerLogs.get(w.label);
226
+ const queued = facts?.steersQueued ?? 0;
227
+ const delivered = facts?.steersDelivered ?? 0;
228
+ upLegMessages += facts?.questions ?? 0;
229
+ steerRows.push({ worker: w.label, queued, delivered });
230
+ steerQueuedTotal += queued;
231
+ steerDeliveredTotal += delivered;
232
+ }
233
+ }
234
+ const workersGapReason = src.workersMissingReason ?? "workers/ directory absent";
235
+ const steers = src.workers === null ? gap("steers", workersGapReason) : steerQueuedTotal;
236
+ const steersDelivered = src.workers === null ? unavailable(workersGapReason) : steerDeliveredTotal;
237
+ const steersByWorker = src.workers === null ? unavailable(workersGapReason) : steerRows;
238
+ const driverSteerCalls = src.driverLog === null ? gap("driverSteerCalls", "driver.log absent") : Math.max(
239
+ 0,
240
+ (src.driverLog.match(/supervisor_steer/g) ?? []).length - registrationMentions(src.driverLog)
241
+ );
242
+ const timeline = [];
243
+ for (const s of workerSpawns) if (s.at !== null) timeline.push({ at: s.at, delta: 1 });
244
+ for (const c of workerCloses) if (c.at !== null) timeline.push({ at: c.at, delta: -1 });
245
+ timeline.sort((a, b) => a.at - b.at || a.delta - b.delta);
246
+ let waves = 0;
247
+ const waveSizes = [];
248
+ let closedSinceWaveStart = true;
249
+ for (const step of timeline) {
250
+ if (step.delta === 1) {
251
+ if (closedSinceWaveStart) {
252
+ waves += 1;
253
+ waveSizes.push(0);
254
+ closedSinceWaveStart = false;
255
+ }
256
+ waveSizes[waveSizes.length - 1] = (waveSizes[waveSizes.length - 1] ?? 0) + 1;
257
+ } else {
258
+ closedSinceWaveStart = true;
259
+ }
260
+ }
261
+ let live = 0;
262
+ let maxConcurrency = 0;
263
+ let idleMs = 0;
264
+ let sumWorkerWallMs = 0;
265
+ let prev = startedAt;
266
+ for (const step of timeline) {
267
+ if (prev !== null && step.at >= prev) {
268
+ const span = step.at - prev;
269
+ if (live === 0) idleMs += span;
270
+ sumWorkerWallMs += span * live;
271
+ }
272
+ live += step.delta;
273
+ if (live > maxConcurrency) maxConcurrency = live;
274
+ prev = step.at;
275
+ }
276
+ if (prev !== null && completedAt !== null && completedAt >= prev) {
277
+ const span = completedAt - prev;
278
+ if (live === 0) idleMs += span;
279
+ sumWorkerWallMs += span * live;
280
+ }
281
+ const firstWorkerSpawnAt = workerSpawns.reduce(
282
+ (acc, s) => s.at === null ? acc : acc === null ? s.at : Math.min(acc, s.at),
283
+ null
284
+ );
285
+ const firstSettleAt = workerCloses.reduce(
286
+ (acc, c) => c.at === null ? acc : acc === null ? c.at : Math.min(acc, c.at),
287
+ null
288
+ );
289
+ const respawns = firstSettleAt === null ? 0 : workerSpawns.filter((s) => s.at !== null && s.at > firstSettleAt).length;
290
+ const labelCounts = /* @__PURE__ */ new Map();
291
+ for (const s of workerSpawns) labelCounts.set(s.label, (labelCounts.get(s.label) ?? 0) + 1);
292
+ const repeatedLabels = [...labelCounts.entries()].filter(([, n]) => n > 1).map(([l]) => l);
293
+ const parentOf = new Map(tree.spawns.map((s) => [s.id, s.parent]));
294
+ let delegationDepth = 0;
295
+ for (const s of workerSpawns) {
296
+ let d = 0;
297
+ let cur = s.id;
298
+ const seen = /* @__PURE__ */ new Set();
299
+ while (cur !== null && cur !== rootId && !seen.has(cur)) {
300
+ seen.add(cur);
301
+ d += 1;
302
+ cur = parentOf.get(cur) ?? null;
303
+ }
304
+ if (d > delegationDepth) delegationDepth = d;
305
+ }
306
+ const orchestration = {
307
+ workersSpawned: haveJournal ? workerSpawns.length : gap("workersSpawned", journalMissing),
308
+ workersSettled: haveJournal ? workerCloses.filter((c) => c.kind === "settled").length : unavailable(journalMissing),
309
+ workersCancelled: haveJournal ? workerCloses.filter((c) => c.kind === "cancelled").length : unavailable(journalMissing),
310
+ steers,
311
+ steersDelivered,
312
+ steersByWorker,
313
+ driverSteerCalls,
314
+ waves: haveJournal ? waves : unavailable(journalMissing),
315
+ waveSizes: haveJournal ? waveSizes : unavailable(journalMissing),
316
+ maxConcurrency: haveJournal ? maxConcurrency : unavailable(journalMissing),
317
+ respawns: haveJournal ? respawns : unavailable(journalMissing),
318
+ repeatedLabels: haveJournal ? repeatedLabels : unavailable(journalMissing),
319
+ delegationDepth: haveJournal ? delegationDepth : unavailable(journalMissing),
320
+ timeToFirstSpawnMs: startedAt !== null && firstWorkerSpawnAt !== null ? firstWorkerSpawnAt - startedAt : haveJournal ? unavailable("no worker spawn timestamps") : unavailable(journalMissing),
321
+ supervisorWallMs,
322
+ idleMs: isUnavailable(supervisorWallMs) ? unavailable(supervisorWallMs.unavailable) : idleMs,
323
+ idlePct: isUnavailable(supervisorWallMs) || supervisorWallMs === 0 ? isUnavailable(supervisorWallMs) ? unavailable(supervisorWallMs.unavailable) : unavailable("supervisor wall is 0ms") : round(idleMs / supervisorWallMs * 100, 1),
324
+ workerUtilization: isUnavailable(supervisorWallMs) || supervisorWallMs === 0 ? isUnavailable(supervisorWallMs) ? unavailable(supervisorWallMs.unavailable) : unavailable("supervisor wall is 0ms") : round(sumWorkerWallMs / supervisorWallMs, 3)
325
+ };
326
+ const settledByStatus = {};
327
+ const settledVerdicts = {};
328
+ for (const c of workerCloses) {
329
+ const key = c.status ?? "unknown";
330
+ settledByStatus[key] = (settledByStatus[key] ?? 0) + 1;
331
+ if (c.verdict !== null) settledVerdicts[c.verdict] = (settledVerdicts[c.verdict] ?? 0) + 1;
332
+ }
333
+ const verdictLimit = src.limits.workerVerdicts;
334
+ let accepted = 0;
335
+ let rejected = 0;
336
+ let emptyPass = 0;
337
+ let evidenceBytes = 0;
338
+ for (const w of src.workers ?? []) {
339
+ const f = tree.workerLogs.get(w.label);
340
+ if (f === void 0 || !f.finished) continue;
341
+ evidenceBytes += f.evidenceBytes;
342
+ if (f.passed === true) {
343
+ if ((f.finishedPatchBytes ?? 0) > 0) accepted += 1;
344
+ else emptyPass += 1;
345
+ } else if (f.passed === false) rejected += 1;
346
+ }
347
+ let observeThenRespawn = 0;
348
+ let respawnWithoutEvidence = 0;
349
+ const spawnTimes = workerSpawns.map((s) => s.at).filter((t) => t !== null).sort((a, b) => a - b);
350
+ const closeTimes = workerCloses.map((c) => c.at).filter((t) => t !== null).sort((a, b) => a - b);
351
+ for (let i = 1; i < spawnTimes.length; i += 1) {
352
+ const prevSpawn = spawnTimes[i - 1];
353
+ const thisSpawn = spawnTimes[i];
354
+ const sawEvidence = closeTimes.some((t) => t >= prevSpawn && t <= thisSpawn);
355
+ if (firstSettleAt !== null && thisSpawn > firstSettleAt) {
356
+ if (sawEvidence) observeThenRespawn += 1;
357
+ else respawnWithoutEvidence += 1;
358
+ }
359
+ }
360
+ const decision = {
361
+ settledByStatus: haveJournal ? settledByStatus : gap("settledByStatus", journalMissing),
362
+ settledVerdicts: verdictLimit !== null ? unavailable(verdictLimit) : haveJournal ? settledVerdicts : unavailable(journalMissing),
363
+ accepted: verdictLimit !== null ? gap("accepted", verdictLimit) : src.workers === null ? unavailable(workersGapReason) : accepted,
364
+ rejected: verdictLimit !== null ? unavailable(verdictLimit) : src.workers === null ? unavailable(workersGapReason) : rejected,
365
+ emptyPass: verdictLimit !== null || src.limits.deliverables !== null ? unavailable(verdictLimit ?? src.limits.deliverables) : src.workers === null ? unavailable(workersGapReason) : emptyPass,
366
+ observeThenRespawn: haveJournal ? observeThenRespawn : unavailable(journalMissing),
367
+ respawnWithoutEvidence: haveJournal ? respawnWithoutEvidence : unavailable(journalMissing),
368
+ reviewActions: src.workers === null ? unavailable(workersGapReason) : steerQueuedTotal + upLegMessages,
369
+ workerEvidenceBytes: src.workers === null ? unavailable(workersGapReason) : evidenceBytes
370
+ };
371
+ const journalWorkerIn = workerCloses.reduce((a, c) => a + c.spend.tokens.input, 0);
372
+ const journalWorkerOut = workerCloses.reduce((a, c) => a + c.spend.tokens.output, 0);
373
+ const journalWorkerUsd = workerCloses.reduce((a, c) => a + c.spend.usd, 0);
374
+ const labelById = new Map(tree.workerSpawns.map((s) => [s.id, s.label]));
375
+ const workerUsdByLabel = /* @__PURE__ */ new Map();
376
+ for (const c of workerCloses) {
377
+ const label = labelById.get(c.id);
378
+ if (label === void 0) continue;
379
+ workerUsdByLabel.set(label, (workerUsdByLabel.get(label) ?? 0) + c.spend.usd);
380
+ }
381
+ const sq = src.harnessWorkerTokens;
382
+ const harnessGapReason = src.harnessMissingReason ?? "harness session store unavailable and journal settled spend is 0";
383
+ const workerIn = sq !== null ? journalWorkerIn + sq.input : journalWorkerIn > 0 ? journalWorkerIn : gap("workers.tokensIn", harnessGapReason);
384
+ const workerOut = sq !== null ? journalWorkerOut + sq.output : journalWorkerOut > 0 ? journalWorkerOut : unavailable(harnessGapReason);
385
+ const stateResult = asRecord(state?.result);
386
+ const stateUsd = typeof stateResult.spentUsd === "number" ? stateResult.spentUsd : null;
387
+ const usdLimit = src.limits.spendUsd;
388
+ const totalUsd = usdLimit !== null ? gap("totalUsd", usdLimit) : stateUsd !== null ? round(stateUsd, 6) : haveJournal ? round(tree.brain.usd + journalWorkerUsd, 6) : gap("totalUsd", journalMissing);
389
+ const perWorker = (src.workers ?? []).map((w) => {
390
+ const f = tree.workerLogs.get(w.label);
391
+ return {
392
+ worker: w.label,
393
+ wallMs: f?.started != null && f.finishedAt != null ? f.finishedAt - f.started : null,
394
+ tokensIn: w.tokensIn ?? null,
395
+ tokensOut: w.tokensOut ?? null,
396
+ usd: usdLimit !== null ? null : workerUsdByLabel.get(w.label) ?? null,
397
+ patchBytes: w.patchBytes ?? f?.finishedPatchBytes ?? null,
398
+ passed: f?.passed ?? null
399
+ };
400
+ });
401
+ const walls = perWorker.map((w) => w.wallMs).filter((w) => w !== null).sort((a, b) => a - b);
402
+ const brainCalls = parseJsonl(src.brainLog);
403
+ const economics = {
404
+ brain: {
405
+ tokensIn: haveJournal ? tree.brain.tokensIn : gap("brain.tokensIn", journalMissing),
406
+ tokensOut: haveJournal ? tree.brain.tokensOut : unavailable(journalMissing),
407
+ usd: usdLimit !== null ? unavailable(usdLimit) : haveJournal ? round(tree.brain.usd, 6) : unavailable(journalMissing),
408
+ cacheRead: !haveJournal ? unavailable(journalMissing) : tree.brain.hasCache ? tree.brain.cacheRead : unavailable(NO_CACHE_COUNTERS),
409
+ cacheWrite: !haveJournal ? unavailable(journalMissing) : tree.brain.hasCache ? tree.brain.cacheWrite : unavailable(NO_CACHE_COUNTERS),
410
+ source: haveJournal ? `journal metered events (n=${tree.brain.meteredCount})` : journalMissing
411
+ },
412
+ brainTruncations: src.brainLog === null ? gap(
413
+ "brain.brainTruncations",
414
+ src.supRunDir === null ? "no supervisor run dir under <ws>/.loops/supervisor" : "brain.jsonl absent \u2014 loops predates the brain-call tap, so truncation cannot be ruled out"
415
+ ) : brainCalls.filter((c) => c.finish_reason === "length").length,
416
+ workers: {
417
+ tokensIn: workerIn,
418
+ tokensOut: workerOut,
419
+ cacheRead: sq?.cacheRead !== void 0 ? sq.cacheRead : unavailable(NO_CACHE_COUNTERS),
420
+ cacheWrite: sq?.cacheWrite !== void 0 ? sq.cacheWrite : unavailable(NO_CACHE_COUNTERS),
421
+ usd: usdLimit !== null ? unavailable(usdLimit) : haveJournal ? round(journalWorkerUsd, 6) : unavailable(journalMissing),
422
+ source: sq !== null ? `journal settled spend + ${sq.store} sessions (n=${sq.sessions})` : `journal settled spend only \u2014 ${src.harnessMissingReason ?? "harness session store unavailable"}`
423
+ },
424
+ totalUsd,
425
+ totalUsdSource: usdLimit !== null ? usdLimit : stateUsd !== null ? `state.json result.spentUsd${journalWorkerUsd === 0 ? " \u2014 brain-priced only; worker CLI inference is unpriced (see worker token counts)" : ""}` : haveJournal ? "journal metered + settled usd" : journalMissing,
426
+ costPerAcceptedPatchUsd: isUnavailable(totalUsd) ? unavailable(totalUsd.unavailable) : isUnavailable(decision.accepted) ? unavailable(decision.accepted.unavailable) : decision.accepted === 0 ? unavailable("no accepted worker patch (cost has no denominator)") : round(totalUsd / decision.accepted, 6),
427
+ workerWallMsDistribution: walls.length === 0 ? unavailable(
428
+ src.workers === null ? workersGapReason : "no worker start/finish pairs captured"
429
+ ) : {
430
+ n: walls.length,
431
+ min: walls[0],
432
+ p50: quantile(walls, 0.5),
433
+ p90: quantile(walls, 0.9),
434
+ max: walls[walls.length - 1],
435
+ sum: walls.reduce((a, b) => a + b, 0)
436
+ },
437
+ perWorker: src.workers === null ? unavailable(workersGapReason) : perWorker
438
+ };
439
+ const patchStats = src.patch === null ? gap("patch", src.limits.deliverables ?? "delivered patch file absent") : parsePatch(src.patch);
440
+ const outcome = {
441
+ supStatus: pickString(state, "status") ?? pickString(result, "sup_status") ?? gap("supStatus", "no state.json / result.json status"),
442
+ supVerdict: pickString(state, "verdict") ?? pickString(result, "sup_verdict") ?? unavailable("no state.json / result.json verdict"),
443
+ delivered: typeof stateResult.delivered === "boolean" ? stateResult.delivered : typeof result?.delivered === "boolean" ? result.delivered : unavailable("no delivered flag in state.json or result.json"),
444
+ judgeResolved: judge === null ? gap("judge", "judge.json absent") : typeof judge.resolved === "boolean" ? judge.resolved : null,
445
+ judgeScore: judge === null ? unavailable("judge.json absent") : typeof judge.score === "number" ? judge.score : null,
446
+ judgePassed: judge === null ? unavailable("judge.json absent") : typeof judge.passed === "number" ? judge.passed : null,
447
+ judgeTotal: judge === null ? unavailable("judge.json absent") : typeof judge.total === "number" ? judge.total : null,
448
+ verifyPass: typeof result?.verify_pass === "boolean" ? result.verify_pass : gap("verifyPass", "result.json absent or has no verify_pass"),
449
+ verifyRc: typeof result?.verify_rc === "number" ? result.verify_rc : unavailable("result.json absent or has no verify_rc"),
450
+ patch: patchStats,
451
+ judgeSource: src.judgeSource
452
+ };
453
+ return {
454
+ schema: SUPERVISOR_RUN_SCHEMA,
455
+ runRef: src.runRef,
456
+ instanceId: src.instanceId,
457
+ arm: src.arm,
458
+ supervisorId: rootId !== null ? rootId : unavailable(journalMissing),
459
+ generatedAt: new Date(now()).toISOString(),
460
+ orchestration,
461
+ decision,
462
+ economics,
463
+ outcome,
464
+ gaps,
465
+ traceCommand: src.traceCommand ?? "npx --yes @tangle-network/traces@latest analyze --harness opencode --cwd <worker-clone-cwd>"
466
+ };
467
+ }
468
+ function registrationMentions(driverLog) {
469
+ let n = 0;
470
+ for (const line2 of driverLog.split("\n")) {
471
+ if (line2.includes("registered tools:") && line2.includes("supervisor_steer")) n += 1;
472
+ }
473
+ return n;
474
+ }
475
+ function pickString(rec, key) {
476
+ const v = rec?.[key];
477
+ return typeof v === "string" ? v : null;
478
+ }
479
+ function round(v, digits) {
480
+ const f = 10 ** digits;
481
+ return Math.round(v * f) / f;
482
+ }
483
+ function quantile(sorted, q) {
484
+ if (sorted.length === 0) return 0;
485
+ const idx = Math.min(sorted.length - 1, Math.max(0, Math.ceil(q * sorted.length) - 1));
486
+ return sorted[idx];
487
+ }
488
+ function parsePatch(text) {
489
+ const files = /* @__PURE__ */ new Set();
490
+ const testFiles = /* @__PURE__ */ new Set();
491
+ let added = 0;
492
+ let removed = 0;
493
+ for (const line2 of text.split("\n")) {
494
+ if (line2.startsWith("+++ ")) {
495
+ const p = line2.slice(4).trim().replace(/^b\//, "");
496
+ if (p !== "/dev/null") {
497
+ files.add(p);
498
+ if (isTestPath(p)) testFiles.add(p);
499
+ }
500
+ continue;
501
+ }
502
+ if (line2.startsWith("--- ") || line2.startsWith("diff --git") || line2.startsWith("index ")) {
503
+ continue;
504
+ }
505
+ if (line2.startsWith("+")) added += 1;
506
+ else if (line2.startsWith("-")) removed += 1;
507
+ }
508
+ return {
509
+ files: files.size,
510
+ linesAdded: added,
511
+ linesRemoved: removed,
512
+ testFilesTouched: [...testFiles].sort()
513
+ };
514
+ }
515
+ function isTestPath(p) {
516
+ const base = p.split("/").pop() ?? p;
517
+ return /(^|\/)(tests?|__tests__|testing|spec)(\/|$)/.test(p) || /\.(test|spec)\.[cm]?[jt]sx?$/.test(base) || /^test_.*\.py$/.test(base) || /_test\.py$/.test(base);
518
+ }
519
+ function rollupSupervisorRuns(reports) {
520
+ const known = (vals) => vals.filter((v) => !isUnavailable(v));
521
+ const steerVals = known(reports.map((r) => r.orchestration.steers));
522
+ const waveVals = known(reports.map((r) => r.orchestration.waves));
523
+ const concVals = known(reports.map((r) => r.orchestration.maxConcurrency));
524
+ const utilVals = known(reports.map((r) => r.orchestration.workerUtilization));
525
+ const idleVals = known(reports.map((r) => r.orchestration.idlePct));
526
+ const spawnVals = known(reports.map((r) => r.orchestration.workersSpawned));
527
+ const acceptVals = known(reports.map((r) => r.decision.accepted));
528
+ const usdVals = known(reports.map((r) => r.economics.totalUsd));
529
+ const resolvedVals = known(reports.map((r) => r.outcome.judgeResolved));
530
+ const sum = (xs) => xs.reduce((a, b) => a + b, 0);
531
+ const mean = (xs) => xs.length === 0 ? unavailable("no cell reported this metric") : round(sum(xs) / xs.length, 3);
532
+ const perCell = reports.map((r) => ({
533
+ instanceId: r.instanceId,
534
+ arm: r.arm,
535
+ steers: r.orchestration.steers,
536
+ waves: r.orchestration.waves,
537
+ utilization: r.orchestration.workerUtilization,
538
+ idlePct: r.orchestration.idlePct,
539
+ resolved: r.outcome.judgeResolved,
540
+ usd: r.economics.totalUsd
541
+ }));
542
+ return {
543
+ schema: SUPERVISOR_RUN_ROLLUP_SCHEMA,
544
+ cells: reports.length,
545
+ steersTotal: steerVals.length === 0 ? unavailable("no cell reported a steer count") : sum(steerVals),
546
+ cellsWithSteers: steerVals.length === 0 ? unavailable("no cell reported a steer count") : steerVals.filter((n) => n > 0).length,
547
+ cellsWithUnavailableSteers: reports.filter((r) => isUnavailable(r.orchestration.steers)).length,
548
+ wavesMean: mean(waveVals),
549
+ maxConcurrencyMax: concVals.length === 0 ? unavailable("no cell reported concurrency") : Math.max(...concVals),
550
+ utilizationMean: mean(utilVals),
551
+ idlePctMean: mean(idleVals),
552
+ workersSpawnedTotal: spawnVals.length === 0 ? unavailable("no cell reported spawns") : sum(spawnVals),
553
+ acceptedTotal: acceptVals.length === 0 ? unavailable("no cell reported acceptance") : sum(acceptVals),
554
+ usdTotal: usdVals.length === 0 ? unavailable("no cell reported spend") : round(sum(usdVals), 6),
555
+ resolvedCount: resolvedVals.length === 0 ? unavailable("no cell reported a judge verdict") : resolvedVals.filter((v) => v === true).length,
556
+ perCell
557
+ };
558
+ }
559
+
560
+ // src/supervisor-run/claude-code-reader.ts
561
+ import { readdir, readFile } from "fs/promises";
562
+ import { basename, dirname, join } from "path";
563
+ var DEFAULT_SPAWN_TOOLS = ["Agent", "Task"];
564
+ var DEFAULT_STEER_TOOLS = ["SendMessage"];
565
+ var DEFAULT_CANCEL_TOOLS = ["TaskStop", "KillAgent"];
566
+ var SPEND_UNPRICED = "Claude Code transcripts record token usage but never a price \u2014 usd is not in the store";
567
+ var NO_VERDICTS = "Claude Code runs no per-worker verify step \u2014 a subagent reports prose, not pass/fail";
568
+ var NO_DELIVERABLES = "Claude Code retains no per-worker patch \u2014 subagents commit to git, they do not hand back a diff";
569
+ var isRecord = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
570
+ var str = (v) => typeof v === "string" ? v : null;
571
+ function blockText(content) {
572
+ if (typeof content === "string") return content;
573
+ if (!Array.isArray(content)) return "";
574
+ const parts = [];
575
+ for (const b of content) {
576
+ if (isRecord(b) && b.type === "text" && typeof b.text === "string") parts.push(b.text);
577
+ }
578
+ return parts.join("\n");
579
+ }
580
+ var tag = (xml, name) => {
581
+ const m = xml.match(new RegExp(`<${name}>([\\s\\S]*?)</${name}>`));
582
+ return m === null ? null : m[1];
583
+ };
584
+ function parseNotifications(text, at) {
585
+ const out = [];
586
+ for (const m of text.matchAll(/<task-notification>[\s\S]*?<\/task-notification>/g)) {
587
+ const xml = m[0];
588
+ const taskId = tag(xml, "task-id");
589
+ if (taskId === null) continue;
590
+ out.push({
591
+ taskId: taskId.trim(),
592
+ toolUseId: tag(xml, "tool-use-id")?.trim() ?? null,
593
+ status: tag(xml, "status")?.trim() ?? "unknown",
594
+ summary: tag(xml, "summary")?.trim() ?? null,
595
+ at
596
+ });
597
+ }
598
+ return out;
599
+ }
600
+ function threadCalls(entries) {
601
+ const uses = [];
602
+ const results = /* @__PURE__ */ new Map();
603
+ const notifications = [];
604
+ let firstAt = null;
605
+ let lastAt = null;
606
+ for (const entry of entries) {
607
+ if (entry.timestamp !== null) {
608
+ if (firstAt === null) firstAt = entry.timestamp;
609
+ lastAt = entry.timestamp;
610
+ }
611
+ const content = entry.message.content;
612
+ if (entry.type === "assistant") {
613
+ if (!Array.isArray(content)) continue;
614
+ for (const block of content) {
615
+ if (!isRecord(block) || block.type !== "tool_use") continue;
616
+ const id = str(block.id);
617
+ const name = str(block.name);
618
+ if (id === null || name === null) continue;
619
+ uses.push({
620
+ id,
621
+ name,
622
+ input: isRecord(block.input) ? block.input : {},
623
+ at: entry.timestamp
624
+ });
625
+ }
626
+ continue;
627
+ }
628
+ if (typeof content === "string") {
629
+ notifications.push(...parseNotifications(content, entry.timestamp));
630
+ continue;
631
+ }
632
+ if (!Array.isArray(content)) continue;
633
+ for (const block of content) {
634
+ if (!isRecord(block)) continue;
635
+ if (block.type === "tool_result" && typeof block.tool_use_id === "string") {
636
+ results.set(block.tool_use_id, {
637
+ id: block.tool_use_id,
638
+ at: entry.timestamp,
639
+ structured: entry.toolUseResult,
640
+ text: blockText(block.content)
641
+ });
642
+ } else if (block.type === "text" && typeof block.text === "string") {
643
+ notifications.push(...parseNotifications(block.text, entry.timestamp));
644
+ }
645
+ }
646
+ }
647
+ return { uses, results, notifications, firstAt, lastAt };
648
+ }
649
+ function spawnedAgentId(result) {
650
+ if (result === void 0) return null;
651
+ if (isRecord(result.structured)) {
652
+ const id = str(result.structured.agentId);
653
+ if (id !== null) return id;
654
+ }
655
+ return result.text.match(/agentId:\s*([A-Za-z0-9_-]+)/)?.[1] ?? null;
656
+ }
657
+ async function readChildren(dir) {
658
+ const names = await readdir(dir).catch(() => null);
659
+ if (names === null) return [];
660
+ const out = [];
661
+ for (const name of names.filter((n) => n.endsWith(".jsonl")).sort()) {
662
+ const path = join(dir, name);
663
+ const raw = await readFile(path, "utf8").catch(() => null);
664
+ if (raw === null) continue;
665
+ const entries = parseClaudeEntries(raw);
666
+ const projected = transcriptFromEntries(entries, { includeSidechain: true });
667
+ const metaRaw = await readFile(path.replace(/\.jsonl$/, ".meta.json"), "utf8").catch(() => null);
668
+ let meta = {};
669
+ if (metaRaw !== null) {
670
+ try {
671
+ const parsed = JSON.parse(metaRaw);
672
+ if (isRecord(parsed)) meta = parsed;
673
+ } catch {
674
+ meta = {};
675
+ }
676
+ }
677
+ const agentId = entries.find((e) => e.agentId !== null)?.agentId ?? name.replace(/^agent-/, "").replace(/\.jsonl$/, "");
678
+ out.push({
679
+ agentId,
680
+ path,
681
+ description: str(meta.description),
682
+ spawnToolUseId: str(meta.toolUseId),
683
+ spawnDepth: typeof meta.spawnDepth === "number" ? meta.spawnDepth : null,
684
+ entries,
685
+ tokensIn: projected.usage.tokensIn,
686
+ tokensOut: projected.usage.tokensOut,
687
+ cacheRead: projected.usage.cacheRead,
688
+ cacheWrite: projected.usage.cacheWrite,
689
+ firstAt: projected.startedAt,
690
+ lastAt: projected.endedAt,
691
+ model: projected.model,
692
+ finalReport: [...projected.messages].reverse().find((m) => m.role === "assistant" && typeof m.content === "string")?.content ?? null
693
+ });
694
+ }
695
+ return out;
696
+ }
697
+ var line = (obj) => JSON.stringify(obj);
698
+ async function readClaudeCodeSupervisorRun(opts) {
699
+ const spawnTools = new Set(opts.spawnTools ?? DEFAULT_SPAWN_TOOLS);
700
+ const steerTools = new Set(opts.steerTools ?? DEFAULT_STEER_TOOLS);
701
+ const cancelTools = new Set(opts.cancelTools ?? DEFAULT_CANCEL_TOOLS);
702
+ const raw = await readFile(opts.transcriptPath, "utf8").catch(() => null);
703
+ const sessionId = basename(opts.transcriptPath).replace(/\.jsonl$/, "");
704
+ const runRef = opts.runRef ?? opts.transcriptPath;
705
+ const limits = { spendUsd: SPEND_UNPRICED, workerVerdicts: NO_VERDICTS, deliverables: null };
706
+ const traceCommand = `npx --yes @tangle-network/traces@latest analyze --harness claude-code --session ${sessionId}`;
707
+ if (raw === null) {
708
+ return {
709
+ runRef,
710
+ instanceId: opts.instanceId ?? sessionId,
711
+ arm: opts.arm ?? null,
712
+ supRunDir: null,
713
+ journal: null,
714
+ brainLog: null,
715
+ state: null,
716
+ progress: null,
717
+ workers: null,
718
+ workersMissingReason: `session transcript unreadable at ${opts.transcriptPath}`,
719
+ result: null,
720
+ judge: null,
721
+ judgeSource: null,
722
+ patch: null,
723
+ driverLog: null,
724
+ harnessWorkerTokens: null,
725
+ harnessMissingReason: `session transcript unreadable at ${opts.transcriptPath}`,
726
+ limits: { ...limits, deliverables: NO_DELIVERABLES },
727
+ traceCommand
728
+ };
729
+ }
730
+ const allEntries = parseClaudeEntries(raw);
731
+ const main = threadCalls(allEntries.filter((e) => !e.isSidechain));
732
+ const mainTranscript = transcriptFromEntries(allEntries);
733
+ const subagentsDir = opts.subagentsDir === void 0 ? join(dirname(opts.transcriptPath), sessionId, "subagents") : opts.subagentsDir;
734
+ const children = subagentsDir === null ? [] : await readChildren(subagentsDir);
735
+ const childByAgentId = new Map(children.map((c) => [c.agentId, c]));
736
+ const childBySpawnToolUseId = new Map(
737
+ children.filter((c) => c.spawnToolUseId !== null).map((c) => [c.spawnToolUseId, c])
738
+ );
739
+ const threads = [{ id: sessionId, calls: main }];
740
+ for (const child of children) {
741
+ threads.push({ id: child.agentId, calls: threadCalls(child.entries) });
742
+ }
743
+ const spawns = [];
744
+ const steersByTarget = /* @__PURE__ */ new Map();
745
+ const cancels = [];
746
+ const settles = [];
747
+ for (const thread of threads) {
748
+ for (const use of thread.calls.uses) {
749
+ if (spawnTools.has(use.name)) {
750
+ const result = thread.calls.results.get(use.id);
751
+ const agentId = spawnedAgentId(result) ?? childBySpawnToolUseId.get(use.id)?.agentId ?? null;
752
+ if (agentId === null) continue;
753
+ const label = str(use.input.description) ?? childByAgentId.get(agentId)?.description ?? agentId;
754
+ spawns.push({
755
+ agentId,
756
+ parentId: thread.id,
757
+ label,
758
+ // The spawn is complete when the launch call is answered; the tool_use
759
+ // instant is the fallback for a call that never got a result line.
760
+ at: result?.at ?? use.at,
761
+ model: isRecord(result?.structured) ? str(result.structured.resolvedModel) : null
762
+ });
763
+ continue;
764
+ }
765
+ if (steerTools.has(use.name)) {
766
+ const target = str(use.input.to) ?? str(use.input.recipient);
767
+ if (target === null) continue;
768
+ const result = thread.calls.results.get(use.id);
769
+ const structured = isRecord(result?.structured) ? result.structured : null;
770
+ const delivered = structured?.success === true || str(structured?.resumedAgentId) === target;
771
+ const rows = steersByTarget.get(target) ?? [];
772
+ rows.push({ at: use.at, delivered });
773
+ steersByTarget.set(target, rows);
774
+ continue;
775
+ }
776
+ if (cancelTools.has(use.name)) {
777
+ const target = str(use.input.agentId) ?? str(use.input.to) ?? str(use.input.taskId);
778
+ if (target !== null) cancels.push({ agentId: target, at: use.at });
779
+ }
780
+ }
781
+ settles.push(...thread.calls.notifications);
782
+ }
783
+ const spawnedIds = new Set(spawns.map((s) => s.agentId));
784
+ const cancelledIds = new Set(
785
+ cancels.filter((c) => spawnedIds.has(c.agentId)).map((c) => c.agentId)
786
+ );
787
+ const startedAt = main.firstAt;
788
+ const completedAt = main.lastAt;
789
+ const journalLines = [
790
+ line({
791
+ kind: "spawned",
792
+ id: sessionId,
793
+ parent: null,
794
+ label: `session:${sessionId}`,
795
+ at: startedAt
796
+ })
797
+ ];
798
+ for (const s of spawns) {
799
+ journalLines.push(
800
+ line({ kind: "spawned", id: s.agentId, parent: s.parentId, label: s.label, at: s.at })
801
+ );
802
+ }
803
+ const lastNotification = /* @__PURE__ */ new Map();
804
+ for (const n of settles) {
805
+ if (!spawnedIds.has(n.taskId)) continue;
806
+ lastNotification.set(n.taskId, n);
807
+ }
808
+ for (const [agentId, n] of lastNotification) {
809
+ if (cancelledIds.has(agentId)) continue;
810
+ journalLines.push(
811
+ line({
812
+ kind: "settled",
813
+ id: agentId,
814
+ status: n.status,
815
+ verdict: n.summary,
816
+ at: n.at
817
+ // No `spent` key: Claude Code prices nothing, and a zeroed spend object
818
+ // would read as a $0 worker. `limits.spendUsd` carries the reason.
819
+ })
820
+ );
821
+ }
822
+ const lastCancel = /* @__PURE__ */ new Map();
823
+ for (const c of cancels) {
824
+ if (!spawnedIds.has(c.agentId)) continue;
825
+ lastCancel.set(c.agentId, c);
826
+ }
827
+ for (const c of lastCancel.values()) {
828
+ journalLines.push(
829
+ line({ kind: "cancelled", id: c.agentId, reason: "stopped by supervisor", at: c.at })
830
+ );
831
+ }
832
+ journalLines.push(
833
+ line({
834
+ kind: "metered",
835
+ id: sessionId,
836
+ spend: {
837
+ tokens: {
838
+ input: mainTranscript.usage.tokensIn,
839
+ output: mainTranscript.usage.tokensOut,
840
+ cacheRead: mainTranscript.usage.cacheRead,
841
+ cacheWrite: mainTranscript.usage.cacheWrite
842
+ }
843
+ },
844
+ at: completedAt
845
+ })
846
+ );
847
+ const settleAtByAgent = new Map(
848
+ [...lastNotification.entries()].map(([id, n]) => [id, n.at])
849
+ );
850
+ const spawnAtByAgent = new Map(spawns.map((s) => [s.agentId, s.at]));
851
+ const byLabel = /* @__PURE__ */ new Map();
852
+ for (const s of spawns) {
853
+ const ids = byLabel.get(s.label) ?? [];
854
+ ids.push(s.agentId);
855
+ byLabel.set(s.label, ids);
856
+ }
857
+ const workers = [];
858
+ for (const [label, agentIds] of byLabel) {
859
+ const events = [];
860
+ const inbox = [];
861
+ let tokensIn = null;
862
+ let tokensOut = null;
863
+ let cacheRead = null;
864
+ let cacheWrite = null;
865
+ let transcriptRef = null;
866
+ for (const agentId of agentIds) {
867
+ const child = childByAgentId.get(agentId) ?? null;
868
+ const startAt = child?.firstAt ?? spawnAtByAgent.get(agentId) ?? null;
869
+ const endAt = settleAtByAgent.get(agentId) ?? child?.lastAt ?? null;
870
+ if (startAt !== null) events.push(line({ kind: "started", label, at: startAt, agentId }));
871
+ for (const steer of steersByTarget.get(agentId) ?? []) {
872
+ inbox.push(
873
+ line({ id: `${agentId}:${steer.at}`, at: steer.at, worker: label, message: "steer" })
874
+ );
875
+ events.push(
876
+ line({
877
+ kind: "message",
878
+ label,
879
+ direction: "down",
880
+ at: steer.at,
881
+ requestId: `${agentId}:${steer.at}`,
882
+ delivered: steer.delivered
883
+ })
884
+ );
885
+ }
886
+ if (endAt !== null) {
887
+ events.push(
888
+ line({
889
+ kind: "finished",
890
+ label,
891
+ at: endAt,
892
+ agentId,
893
+ // No `passed` / `patchBytes`: this harness has neither. Emitting
894
+ // `passed: false` here would invent a failed worker.
895
+ // `evidence` is the child's closing message — what it handed back.
896
+ ...child?.finalReport === null || child?.finalReport === void 0 ? {} : { evidence: child.finalReport }
897
+ })
898
+ );
899
+ }
900
+ if (child !== null) {
901
+ tokensIn = (tokensIn ?? 0) + child.tokensIn;
902
+ tokensOut = (tokensOut ?? 0) + child.tokensOut;
903
+ cacheRead = (cacheRead ?? 0) + child.cacheRead;
904
+ cacheWrite = (cacheWrite ?? 0) + child.cacheWrite;
905
+ transcriptRef = child.path;
906
+ }
907
+ }
908
+ workers.push({
909
+ label,
910
+ events: events.length === 0 ? null : `${events.join("\n")}
911
+ `,
912
+ inbox: inbox.length === 0 ? null : `${inbox.join("\n")}
913
+ `,
914
+ patchBytes: null,
915
+ transcriptRef,
916
+ patchPath: null,
917
+ tokensIn,
918
+ tokensOut,
919
+ cacheRead,
920
+ cacheWrite
921
+ });
922
+ }
923
+ const joined = children.length;
924
+ const harnessWorkerTokens = joined === 0 ? null : {
925
+ store: "claude-code subagent transcripts",
926
+ sessions: joined,
927
+ input: children.reduce((a, c) => a + c.tokensIn, 0),
928
+ output: children.reduce((a, c) => a + c.tokensOut, 0),
929
+ cacheRead: children.reduce((a, c) => a + c.cacheRead, 0),
930
+ cacheWrite: children.reduce((a, c) => a + c.cacheWrite, 0)
931
+ };
932
+ const missingChildren = spawns.filter((s) => !childByAgentId.has(s.agentId)).length;
933
+ const harnessMissingReason = subagentsDir === null ? "subagent transcript join disabled" : joined === 0 ? `no subagent transcripts under ${subagentsDir}` : missingChildren === 0 ? null : `${missingChildren}/${spawns.length} spawned agents have no retained transcript under ${subagentsDir} (Claude Code prunes them; their tokens are unrecoverable)`;
934
+ const liveWorkers = spawns.filter(
935
+ (s) => !lastNotification.has(s.agentId) && !cancelledIds.has(s.agentId)
936
+ ).length;
937
+ const state = JSON.stringify({
938
+ id: sessionId,
939
+ // Derived, not asserted: a spawned agent with no notification and no stop
940
+ // is still running, which is exactly what a live transcript looks like.
941
+ status: liveWorkers > 0 ? "running" : "idle",
942
+ startedAt,
943
+ completedAt,
944
+ result: { delivered: null }
945
+ });
946
+ return {
947
+ runRef,
948
+ instanceId: opts.instanceId ?? sessionId,
949
+ arm: opts.arm ?? null,
950
+ supRunDir: subagentsDir,
951
+ journal: `${journalLines.join("\n")}
952
+ `,
953
+ brainLog: null,
954
+ state,
955
+ progress: null,
956
+ workers,
957
+ workersMissingReason: null,
958
+ result: null,
959
+ judge: null,
960
+ judgeSource: null,
961
+ patch: null,
962
+ driverLog: null,
963
+ harnessWorkerTokens,
964
+ harnessMissingReason,
965
+ limits: { ...limits, deliverables: NO_DELIVERABLES },
966
+ rootTranscriptRef: opts.transcriptPath,
967
+ traceCommand
968
+ };
969
+ }
970
+ function claudeCodeSupervisorRunReader(opts) {
971
+ return {
972
+ runRef: opts.runRef ?? opts.transcriptPath,
973
+ read: () => readClaudeCodeSupervisorRun(opts)
974
+ };
975
+ }
976
+
977
+ // src/supervisor-run/loops-reader.ts
978
+ import { appendFile, mkdir, readdir as readdir2, readFile as readFile2, writeFile } from "fs/promises";
979
+ import { basename as basename2, join as join2 } from "path";
980
+
981
+ // src/supervisor-run/render.ts
982
+ function renderSupervisorRunHeadline(r) {
983
+ const o = r.orchestration;
984
+ const steerNote = isUnavailable(o.steers) ? `unavailable \u2014 ${o.steers.unavailable}` : o.steers === 0 ? "0 (spawn\u2192wait\u2192respawn only; no mid-task steering)" : `${o.steers} queued / ${showMeasured(o.steersDelivered)} delivered`;
985
+ return [
986
+ `RUN-REPORT ${r.instanceId ?? "?"} [${r.arm ?? "?"}]`,
987
+ ` steers=${steerNote}`,
988
+ ` waves=${showMeasured(o.waves)} sizes=${isUnavailable(o.waveSizes) ? `unavailable \u2014 ${o.waveSizes.unavailable}` : `[${o.waveSizes.join(",")}]`} workers=${showMeasured(o.workersSpawned)} settled=${showMeasured(o.workersSettled)} cancelled=${showMeasured(o.workersCancelled)}`,
989
+ ` concurrency max=${showMeasured(o.maxConcurrency)} utilization=${showMeasured(o.workerUtilization)} idle=${fmtMs(o.idleMs)} (${showMeasured(o.idlePct)}%) wall=${fmtMs(o.supervisorWallMs)}`,
990
+ ` respawns=${showMeasured(o.respawns)} evidence\u2192respawn=${showMeasured(r.decision.observeThenRespawn)} blind-respawn=${showMeasured(r.decision.respawnWithoutEvidence)} depth=${showMeasured(o.delegationDepth)}`,
991
+ ` accepted=${showMeasured(r.decision.accepted)} rejected=${showMeasured(r.decision.rejected)} empty-pass=${showMeasured(r.decision.emptyPass)}`,
992
+ ` brain=$${showMeasured(r.economics.brain.usd)} total=$${showMeasured(r.economics.totalUsd)} judge.resolved=${showMeasured(r.outcome.judgeResolved)} score=${showMeasured(r.outcome.judgeScore)} verify=${showMeasured(r.outcome.verifyPass)}`,
993
+ r.gaps.length > 0 ? ` gaps(${r.gaps.length}): ${r.gaps.join("; ")}` : " gaps: none"
994
+ ].join("\n");
995
+ }
996
+ function fmtMs(v) {
997
+ if (isUnavailable(v)) return `unavailable \u2014 ${v.unavailable}`;
998
+ if (v < 1e3) return `${v}ms`;
999
+ const s = v / 1e3;
1000
+ if (s < 120) return `${round(s, 1)}s`;
1001
+ return `${round(s / 60, 1)}min`;
1002
+ }
1003
+ function renderSupervisorRunMarkdown(r) {
1004
+ const o = r.orchestration;
1005
+ const d = r.decision;
1006
+ const e = r.economics;
1007
+ const out = [];
1008
+ out.push(`# Run report \u2014 ${r.instanceId ?? "unknown instance"} [${r.arm ?? "unknown arm"}]`);
1009
+ out.push("");
1010
+ out.push("```");
1011
+ out.push(renderSupervisorRunHeadline(r));
1012
+ out.push("```");
1013
+ out.push("");
1014
+ out.push(`- Run: \`${r.runRef}\``);
1015
+ out.push(`- Supervisor: \`${showMeasured(r.supervisorId)}\``);
1016
+ out.push(`- Generated: ${r.generatedAt}`);
1017
+ out.push("");
1018
+ out.push("## Orchestration");
1019
+ out.push("");
1020
+ out.push("| Metric | Value |");
1021
+ out.push("|---|---|");
1022
+ out.push(`| Workers spawned | ${showMeasured(o.workersSpawned)} |`);
1023
+ out.push(`| Workers settled | ${showMeasured(o.workersSettled)} |`);
1024
+ out.push(`| Workers cancelled | ${showMeasured(o.workersCancelled)} |`);
1025
+ out.push(`| **Steers (mid-task messages to live workers)** | **${showMeasured(o.steers)}** |`);
1026
+ out.push(`| Steers delivered | ${showMeasured(o.steersDelivered)} |`);
1027
+ out.push(`| Outer-driver \`supervisor_steer\` calls | ${showMeasured(o.driverSteerCalls)} |`);
1028
+ out.push(`| Spawn waves | ${showMeasured(o.waves)} |`);
1029
+ out.push(
1030
+ `| Wave sizes | ${isUnavailable(o.waveSizes) ? showMeasured(o.waveSizes) : `[${o.waveSizes.join(", ")}]`} |`
1031
+ );
1032
+ out.push(`| Max concurrency | ${showMeasured(o.maxConcurrency)} |`);
1033
+ out.push(`| Respawns (spawns after first settle) | ${showMeasured(o.respawns)} |`);
1034
+ out.push(
1035
+ `| Repeated labels | ${isUnavailable(o.repeatedLabels) ? showMeasured(o.repeatedLabels) : o.repeatedLabels.length === 0 ? "none" : o.repeatedLabels.join(", ")} |`
1036
+ );
1037
+ out.push(`| Delegation depth | ${showMeasured(o.delegationDepth)} |`);
1038
+ out.push(`| Time to first spawn | ${fmtMs(o.timeToFirstSpawnMs)} |`);
1039
+ out.push(`| Supervisor wall | ${fmtMs(o.supervisorWallMs)} |`);
1040
+ out.push(`| Idle (zero live workers) | ${fmtMs(o.idleMs)} (${showMeasured(o.idlePct)}%) |`);
1041
+ out.push(
1042
+ `| Worker utilization (\u03A3 worker wall \xF7 supervisor wall) | ${showMeasured(o.workerUtilization)} |`
1043
+ );
1044
+ out.push("");
1045
+ if (!isUnavailable(o.steersByWorker) && o.steersByWorker.length > 0) {
1046
+ out.push("### Steers per worker");
1047
+ out.push("");
1048
+ out.push("| Worker | Queued | Delivered |");
1049
+ out.push("|---|---:|---:|");
1050
+ for (const s of o.steersByWorker) out.push(`| \`${s.worker}\` | ${s.queued} | ${s.delivered} |`);
1051
+ out.push("");
1052
+ } else if (isUnavailable(o.steersByWorker)) {
1053
+ out.push(`### Steers per worker
1054
+
1055
+ unavailable \u2014 ${o.steersByWorker.unavailable}
1056
+ `);
1057
+ }
1058
+ out.push("## Decision quality");
1059
+ out.push("");
1060
+ out.push("| Metric | Value |");
1061
+ out.push("|---|---|");
1062
+ out.push(`| Settled by status | ${fmtCounts(d.settledByStatus)} |`);
1063
+ out.push(`| Settled verdicts | ${fmtCounts(d.settledVerdicts)} |`);
1064
+ out.push(`| Accepted (verify green + patch bytes) | ${showMeasured(d.accepted)} |`);
1065
+ out.push(`| Rejected (verify red) | ${showMeasured(d.rejected)} |`);
1066
+ out.push(`| Empty pass (green, no patch) | ${showMeasured(d.emptyPass)} |`);
1067
+ out.push(`| Evidence \u2192 respawn sequences | ${showMeasured(d.observeThenRespawn)} |`);
1068
+ out.push(
1069
+ `| Respawn with no settled evidence in front | ${showMeasured(d.respawnWithoutEvidence)} |`
1070
+ );
1071
+ out.push(`| Review actions (steers + worker questions) | ${showMeasured(d.reviewActions)} |`);
1072
+ out.push(
1073
+ `| Worker evidence returned | ${isUnavailable(d.workerEvidenceBytes) ? showMeasured(d.workerEvidenceBytes) : `${d.workerEvidenceBytes} bytes`} |`
1074
+ );
1075
+ out.push("");
1076
+ out.push("## Economics");
1077
+ out.push("");
1078
+ out.push("| Role | Tokens in | Tokens out | Cache read | Cache write | USD | Source |");
1079
+ out.push("|---|---:|---:|---:|---:|---:|---|");
1080
+ out.push(
1081
+ `| brain | ${showMeasured(e.brain.tokensIn)} | ${showMeasured(e.brain.tokensOut)} | ${showMeasured(e.brain.cacheRead)} | ${showMeasured(e.brain.cacheWrite)} | ${showMeasured(e.brain.usd)} | ${e.brain.source} |`
1082
+ );
1083
+ out.push(
1084
+ `| workers | ${showMeasured(e.workers.tokensIn)} | ${showMeasured(e.workers.tokensOut)} | ${showMeasured(e.workers.cacheRead)} | ${showMeasured(e.workers.cacheWrite)} | ${showMeasured(e.workers.usd)} | ${e.workers.source} |`
1085
+ );
1086
+ out.push("");
1087
+ if (!isUnavailable(e.brainTruncations) && e.brainTruncations > 0) {
1088
+ out.push(
1089
+ `- **BRAIN OUTPUT TRUNCATED: ${e.brainTruncations} completion(s) hit \`finish_reason: "length"\`** \u2014 the supervisor acted on a half-written plan. Its output ceiling is too low; see \`brain.jsonl\` for the per-call \`req_max_tokens\`.`
1090
+ );
1091
+ } else {
1092
+ out.push(
1093
+ `- Brain completions truncated (finish_reason=length): ${showMeasured(e.brainTruncations)}`
1094
+ );
1095
+ }
1096
+ out.push(`- Total USD: ${showMeasured(e.totalUsd)} (source: ${e.totalUsdSource})`);
1097
+ out.push(`- Cost per accepted patch: ${showMeasured(e.costPerAcceptedPatchUsd)}`);
1098
+ if (isUnavailable(e.workerWallMsDistribution)) {
1099
+ out.push(`- Worker wall distribution: unavailable \u2014 ${e.workerWallMsDistribution.unavailable}`);
1100
+ } else {
1101
+ const w = e.workerWallMsDistribution;
1102
+ out.push(
1103
+ `- Worker wall (n=${w.n}): min ${fmtMs(w.min)} / p50 ${fmtMs(w.p50)} / p90 ${fmtMs(w.p90)} / max ${fmtMs(w.max)} / \u03A3 ${fmtMs(w.sum)}`
1104
+ );
1105
+ }
1106
+ out.push("");
1107
+ if (!isUnavailable(e.perWorker) && e.perWorker.length > 0) {
1108
+ out.push("| Worker | Wall | Tokens in | Tokens out | Patch bytes | Verify passed |");
1109
+ out.push("|---|---:|---:|---:|---:|---|");
1110
+ for (const w of e.perWorker) {
1111
+ out.push(
1112
+ `| \`${w.worker}\` | ${w.wallMs === null ? "unavailable \u2014 no start/finish pair" : fmtMs(w.wallMs)} | ${w.tokensIn ?? "unavailable \u2014 store does not attribute tokens per worker"} | ${w.tokensOut ?? "unavailable \u2014 store does not attribute tokens per worker"} | ${w.patchBytes ?? "unavailable \u2014 no worker patch file"} | ${w.passed === null ? "unavailable \u2014 no finished event" : String(w.passed)} |`
1113
+ );
1114
+ }
1115
+ out.push("");
1116
+ }
1117
+ out.push("## Outcome");
1118
+ out.push("");
1119
+ out.push("| Metric | Value |");
1120
+ out.push("|---|---|");
1121
+ out.push(`| Supervisor status | ${showMeasured(r.outcome.supStatus)} |`);
1122
+ out.push(`| Supervisor verdict | ${showMeasured(r.outcome.supVerdict)} |`);
1123
+ out.push(`| Delivered | ${showMeasured(r.outcome.delivered)} |`);
1124
+ out.push(`| Judge resolved | ${showMeasured(r.outcome.judgeResolved)} |`);
1125
+ out.push(`| Judge score | ${showMeasured(r.outcome.judgeScore)} |`);
1126
+ out.push(
1127
+ `| Judge passed / total | ${showMeasured(r.outcome.judgePassed)} / ${showMeasured(r.outcome.judgeTotal)} |`
1128
+ );
1129
+ out.push(
1130
+ `| Judge source | ${r.outcome.judgeSource ?? "unavailable \u2014 no judge.json and no ledger row"} |`
1131
+ );
1132
+ out.push(
1133
+ `| Verify gate | pass=${showMeasured(r.outcome.verifyPass)} rc=${showMeasured(r.outcome.verifyRc)} |`
1134
+ );
1135
+ if (isUnavailable(r.outcome.patch)) {
1136
+ out.push(`| Patch | unavailable \u2014 ${r.outcome.patch.unavailable} |`);
1137
+ } else {
1138
+ const p = r.outcome.patch;
1139
+ out.push(
1140
+ `| Patch | ${p.files} file(s), +${p.linesAdded}/-${p.linesRemoved}, test files touched: ${p.testFilesTouched.length === 0 ? "none" : p.testFilesTouched.join(", ")} |`
1141
+ );
1142
+ }
1143
+ out.push("");
1144
+ out.push("## Gaps");
1145
+ out.push("");
1146
+ if (r.gaps.length === 0) out.push("None \u2014 every metric above is backed by a present artifact.");
1147
+ else for (const g of r.gaps) out.push(`- ${g}`);
1148
+ out.push("");
1149
+ out.push(
1150
+ `> Harness-session view of the same run (model calls, stuck loops, tool errors): \`${r.traceCommand}\``
1151
+ );
1152
+ out.push("");
1153
+ return out.join("\n");
1154
+ }
1155
+ function fmtCounts(v) {
1156
+ if (isUnavailable(v)) return showMeasured(v);
1157
+ const entries = Object.entries(v);
1158
+ return entries.length === 0 ? "none" : entries.map(([k, n]) => `${k}=${n}`).join(", ");
1159
+ }
1160
+ function renderSupervisorRollupMarkdown(rollup, title = "Round rollup") {
1161
+ const out = [];
1162
+ out.push(`# ${title}`);
1163
+ out.push("");
1164
+ out.push(`- Cells: ${rollup.cells}`);
1165
+ out.push(
1166
+ `- **Steers across all cells: ${showMeasured(rollup.steersTotal)}** (cells with \u22651 steer: ${showMeasured(rollup.cellsWithSteers)}; cells where the steer count is unavailable: ${rollup.cellsWithUnavailableSteers})`
1167
+ );
1168
+ out.push(`- Waves per cell (mean): ${showMeasured(rollup.wavesMean)}`);
1169
+ out.push(`- Max concurrency observed: ${showMeasured(rollup.maxConcurrencyMax)}`);
1170
+ out.push(`- Worker utilization (mean): ${showMeasured(rollup.utilizationMean)}`);
1171
+ out.push(`- Idle share (mean): ${showMeasured(rollup.idlePctMean)}%`);
1172
+ out.push(
1173
+ `- Workers spawned: ${showMeasured(rollup.workersSpawnedTotal)} \xB7 accepted: ${showMeasured(rollup.acceptedTotal)}`
1174
+ );
1175
+ out.push(
1176
+ `- Spend: $${showMeasured(rollup.usdTotal)} \xB7 judged resolved: ${showMeasured(rollup.resolvedCount)}/${rollup.cells}`
1177
+ );
1178
+ out.push("");
1179
+ out.push("| Instance | Arm | Steers | Waves | Utilization | Idle % | Resolved | USD |");
1180
+ out.push("|---|---|---:|---:|---:|---:|---|---:|");
1181
+ for (const c of rollup.perCell) {
1182
+ out.push(
1183
+ `| ${c.instanceId ?? "?"} | ${c.arm ?? "?"} | ${showMeasured(c.steers)} | ${showMeasured(c.waves)} | ${showMeasured(c.utilization)} | ${showMeasured(c.idlePct)} | ${showMeasured(c.resolved)} | ${showMeasured(c.usd)} |`
1184
+ );
1185
+ }
1186
+ out.push("");
1187
+ return out.join("\n");
1188
+ }
1189
+
1190
+ // src/supervisor-run/loops-reader.ts
1191
+ async function readMaybe(path) {
1192
+ return readFile2(path, "utf8").catch(() => null);
1193
+ }
1194
+ async function findSupervisorRunDirIn(ws) {
1195
+ const root = join2(ws, ".loops", "supervisor");
1196
+ const entries = await readdir2(root, { withFileTypes: true }).catch(() => []);
1197
+ const dirs = entries.filter((e) => e.isDirectory()).map((e) => join2(root, e.name));
1198
+ return dirs[0] ?? null;
1199
+ }
1200
+ async function readLoopsSupervisorRun(runDir, opts = {}) {
1201
+ const ws = opts.ws ?? join2(runDir, "ws");
1202
+ const supRunDir = await findSupervisorRunDirIn(ws);
1203
+ const result = await readMaybe(join2(runDir, "result.json"));
1204
+ const resultObj = parseJson(result);
1205
+ let workers = null;
1206
+ let workersMissingReason = null;
1207
+ const workerCwds = [];
1208
+ if (supRunDir === null) {
1209
+ workersMissingReason = `no supervisor run dir under ${join2(ws, ".loops", "supervisor")}`;
1210
+ } else {
1211
+ const workersDir = join2(supRunDir, "workers");
1212
+ const entries = await readdir2(workersDir).catch(() => null);
1213
+ if (entries === null) {
1214
+ workersMissingReason = `workers/ directory absent under ${supRunDir}`;
1215
+ } else {
1216
+ const labels = [
1217
+ ...new Set(
1218
+ entries.filter((f) => f.endsWith(".ndjson")).map((f) => f.replace(/\.inbox\.ndjson$/, "").replace(/\.ndjson$/, ""))
1219
+ )
1220
+ ].sort();
1221
+ workers = [];
1222
+ for (const label of labels) {
1223
+ const events = await readMaybe(join2(workersDir, `${label}.ndjson`));
1224
+ const inbox = await readMaybe(join2(workersDir, `${label}.inbox.ndjson`));
1225
+ const patch = await readMaybe(join2(workersDir, `${label}.patch`));
1226
+ workers.push({
1227
+ label,
1228
+ events,
1229
+ inbox,
1230
+ patchBytes: patch === null ? null : Buffer.byteLength(patch)
1231
+ });
1232
+ for (const ev of parseJsonl(events)) {
1233
+ if (ev.kind === "started" && typeof ev.cwd === "string") workerCwds.push(ev.cwd);
1234
+ }
1235
+ }
1236
+ }
1237
+ }
1238
+ let harnessWorkerTokens = null;
1239
+ let harnessMissingReason = null;
1240
+ if (opts.opencodeDb === null) {
1241
+ harnessMissingReason = "opencode join disabled";
1242
+ } else if (workerCwds.length === 0) {
1243
+ harnessMissingReason = "no worker clone cwds in workers/*.ndjson (nothing to join)";
1244
+ } else {
1245
+ const db = await openOpencodeDb(opts.opencodeDb ?? DEFAULT_OPENCODE_DB);
1246
+ if (db === null) {
1247
+ harnessMissingReason = `opencode session store unreadable at ${opts.opencodeDb ?? DEFAULT_OPENCODE_DB}`;
1248
+ } else {
1249
+ try {
1250
+ const seen = /* @__PURE__ */ new Set();
1251
+ let sessions = 0;
1252
+ let input = 0;
1253
+ let output = 0;
1254
+ for (const cwd of new Set(workerCwds)) {
1255
+ for (const row of findOpencodeSessionsByDirectory(db, cwd)) {
1256
+ if (seen.has(row.id)) continue;
1257
+ seen.add(row.id);
1258
+ sessions += 1;
1259
+ input += row.tokensInput;
1260
+ output += row.tokensOutput + row.tokensReasoning;
1261
+ }
1262
+ }
1263
+ harnessWorkerTokens = { store: "opencode", sessions, input, output };
1264
+ } finally {
1265
+ db.close();
1266
+ }
1267
+ }
1268
+ }
1269
+ const patchPath = opts.patchPath ?? (typeof resultObj?.patchPath === "string" ? resultObj.patchPath : null);
1270
+ let judge = await readMaybe(join2(runDir, "judge.json"));
1271
+ let judgeSource = judge === null ? null : join2(runDir, "judge.json");
1272
+ if (judge === null && opts.ledgerPath !== void 0) {
1273
+ const row = await findLedgerRow(opts.ledgerPath, runDir);
1274
+ if (row !== null) {
1275
+ judge = JSON.stringify(row);
1276
+ judgeSource = `${opts.ledgerPath} (ledger row)`;
1277
+ }
1278
+ }
1279
+ return {
1280
+ runRef: runDir,
1281
+ instanceId: typeof resultObj?.iid === "string" ? resultObj.iid : instanceIdFromPath(runDir),
1282
+ arm: typeof resultObj?.arm === "string" ? resultObj.arm : basename2(runDir),
1283
+ supRunDir,
1284
+ journal: supRunDir === null ? null : await readMaybe(join2(supRunDir, "journal.jsonl")),
1285
+ brainLog: supRunDir === null ? null : await readMaybe(join2(supRunDir, "brain.jsonl")),
1286
+ state: supRunDir === null ? null : await readMaybe(join2(supRunDir, "state.json")),
1287
+ progress: supRunDir === null ? null : await readMaybe(join2(supRunDir, "progress.ndjson")),
1288
+ workers,
1289
+ workersMissingReason,
1290
+ result,
1291
+ judge,
1292
+ judgeSource,
1293
+ patch: patchPath === null ? null : await readMaybe(patchPath),
1294
+ driverLog: await readMaybe(join2(runDir, "driver.log")),
1295
+ harnessWorkerTokens,
1296
+ harnessMissingReason,
1297
+ // loops prices its own inference, runs a verify per worker, and keeps each
1298
+ // worker's patch — every fact the analyzer can use is expressible here.
1299
+ limits: NO_SOURCE_LIMITS,
1300
+ traceCommand: null
1301
+ };
1302
+ }
1303
+ function loopsSupervisorRunReader(runDir, opts = {}) {
1304
+ return { runRef: runDir, read: () => readLoopsSupervisorRun(runDir, opts) };
1305
+ }
1306
+ async function findLedgerRow(ledgerPath, runDir) {
1307
+ const rows = parseJsonl(await readMaybe(ledgerPath));
1308
+ const exact = rows.find((r) => r.runDir === runDir);
1309
+ if (exact !== void 0) return exact;
1310
+ const iid = instanceIdFromPath(runDir);
1311
+ const arm = basename2(runDir);
1312
+ return rows.find((r) => r.iid === iid && r.arm === arm) ?? null;
1313
+ }
1314
+ function instanceIdFromPath(runDir) {
1315
+ const parts = runDir.split("/").filter(Boolean);
1316
+ const armIdx = parts.length - 1;
1317
+ const iid = parts[armIdx - 1];
1318
+ return parts[armIdx - 2] === "runs" && iid !== void 0 ? iid : null;
1319
+ }
1320
+ async function analyzeSupervisorRun(input, opts = {}) {
1321
+ if (typeof input === "string") {
1322
+ return analyzeSupervisorRunSources(await readLoopsSupervisorRun(input, opts));
1323
+ }
1324
+ if (isReader(input)) return analyzeSupervisorRunSources(await input.read());
1325
+ return analyzeSupervisorRunSources(input);
1326
+ }
1327
+ function isReader(input) {
1328
+ return typeof input.read === "function";
1329
+ }
1330
+ async function writeSupervisorRunReport(runDir, opts = {}) {
1331
+ const sources = await readLoopsSupervisorRun(runDir, opts);
1332
+ const report = analyzeSupervisorRunSources(sources);
1333
+ const md = renderSupervisorRunMarkdown(report);
1334
+ const dest = opts.reportDir ?? runDir;
1335
+ const stem = opts.reportDir === void 0 ? "run-report" : supervisorReportStem(runDir);
1336
+ if (opts.reportDir !== void 0) await mkdir(opts.reportDir, { recursive: true }).catch(() => {
1337
+ });
1338
+ await writeFile(join2(dest, `${stem}.json`), JSON.stringify(report, null, 1)).catch(() => {
1339
+ });
1340
+ await writeFile(join2(dest, `${stem}.md`), md).catch(() => {
1341
+ });
1342
+ const headline = renderSupervisorRunHeadline(report);
1343
+ if (opts.appendHeadlineTo !== void 0) {
1344
+ await appendFile(opts.appendHeadlineTo, `${headline}
1345
+ `).catch(() => {
1346
+ });
1347
+ }
1348
+ if (opts.echo !== false) console.log(headline);
1349
+ return report;
1350
+ }
1351
+ function supervisorReportStem(runDir) {
1352
+ const parts = runDir.split("/").filter(Boolean);
1353
+ const arm = parts[parts.length - 1] ?? "cell";
1354
+ const iid = parts[parts.length - 2] ?? "instance";
1355
+ const rep = parts.find((p) => /^rep-\d+$/.test(p));
1356
+ const armRunsIdx = parts.indexOf("arm-runs");
1357
+ const tag2 = armRunsIdx >= 0 ? parts[armRunsIdx + 1] : void 0;
1358
+ return [tag2, rep, iid, arm].filter((s) => s !== void 0 && s !== "runs").join(".").replace(/[^A-Za-z0-9._-]/g, "_");
1359
+ }
1360
+ async function writeSupervisorRunReportSafe(runDir, opts = {}) {
1361
+ try {
1362
+ return await writeSupervisorRunReport(runDir, opts);
1363
+ } catch (err) {
1364
+ console.log(
1365
+ `RUN-REPORT failed for ${runDir}: ${err instanceof Error ? err.message : String(err)}`
1366
+ );
1367
+ return null;
1368
+ }
1369
+ }
1370
+ async function reportSupervisorRound(outDir, opts = {}) {
1371
+ const runDirs = await findSupervisorRunDirs(outDir);
1372
+ const reports = [];
1373
+ for (const runDir of runDirs) {
1374
+ const r = await writeSupervisorRunReportSafe(runDir, { ...opts, echo: opts.echo ?? false });
1375
+ if (r !== null) reports.push(r);
1376
+ }
1377
+ const rollup = rollupSupervisorRuns(reports);
1378
+ const md = renderSupervisorRollupMarkdown(
1379
+ rollup,
1380
+ opts.title ?? `Round rollup \u2014 ${basename2(outDir)}`
1381
+ );
1382
+ const dest = opts.reportDir ?? outDir;
1383
+ if (opts.reportDir !== void 0) await mkdir(opts.reportDir, { recursive: true }).catch(() => {
1384
+ });
1385
+ await writeFile(join2(dest, "run-report-round.json"), JSON.stringify(rollup, null, 1)).catch(
1386
+ () => {
1387
+ }
1388
+ );
1389
+ await writeFile(join2(dest, "run-report-round.md"), md).catch(() => {
1390
+ });
1391
+ if (opts.appendHeadlineTo !== void 0) {
1392
+ await appendFile(opts.appendHeadlineTo, `${md}
1393
+ `).catch(() => {
1394
+ });
1395
+ }
1396
+ if (opts.echo !== false) console.log(md);
1397
+ return rollup;
1398
+ }
1399
+ async function findSupervisorRunDirs(root) {
1400
+ const found = [];
1401
+ const walk = async (dir, depth) => {
1402
+ if (depth > 8) return;
1403
+ const entries = await readdir2(dir, { withFileTypes: true }).catch(() => []);
1404
+ for (const e of entries) {
1405
+ if (!e.isDirectory()) continue;
1406
+ if (e.name === "node_modules" || e.name === ".git") continue;
1407
+ const full = join2(dir, e.name);
1408
+ if (e.name === "runs") {
1409
+ const iids = await readdir2(full, { withFileTypes: true }).catch(() => []);
1410
+ for (const iid of iids) {
1411
+ if (!iid.isDirectory()) continue;
1412
+ const arms = await readdir2(join2(full, iid.name), { withFileTypes: true }).catch(() => []);
1413
+ for (const arm of arms) if (arm.isDirectory()) found.push(join2(full, iid.name, arm.name));
1414
+ }
1415
+ continue;
1416
+ }
1417
+ await walk(full, depth + 1);
1418
+ }
1419
+ };
1420
+ await walk(root, 0);
1421
+ return found.sort();
1422
+ }
1423
+
1424
+ // src/supervisor-run/rollout-nodes.ts
1425
+ var EMPTY_COST = {
1426
+ usd: null,
1427
+ tokens_in: null,
1428
+ tokens_out: null,
1429
+ tokens_reasoning: null,
1430
+ cache_read: null,
1431
+ cache_write: null,
1432
+ wall_s: null
1433
+ };
1434
+ function supervisorRunRolloutLines(src, opts = {}) {
1435
+ const gaps = [];
1436
+ const tree = parseSupervisorTree(src);
1437
+ if (src.journal === null) {
1438
+ gaps.push(
1439
+ `tree: ${src.supRunDir === null ? "no supervisor run dir" : "journal absent"} \u2014 no nodes recoverable`
1440
+ );
1441
+ return { rootId: null, nodes: [], gaps };
1442
+ }
1443
+ const state = parseJson(src.state);
1444
+ const result = parseJson(src.result);
1445
+ const judge = parseJson(src.judge);
1446
+ const stateResult = asRecord(state?.result);
1447
+ const rootId = tree.rootId;
1448
+ const runId = opts.runId ?? rootId ?? src.runRef;
1449
+ const suite = opts.suite ?? "supervisor-run";
1450
+ const instanceId = src.instanceId ?? src.runRef;
1451
+ const split = opts.split ?? "search";
1452
+ const capturedAt = opts.capturedAt ?? (/* @__PURE__ */ new Date()).toISOString();
1453
+ const base = {
1454
+ schema: ROLLOUT_SCHEMA,
1455
+ run_id: runId,
1456
+ experiment_id: opts.experimentId ?? null,
1457
+ candidate_id: opts.candidateId ?? null,
1458
+ generation: opts.generation ?? null,
1459
+ candidate_index: opts.candidateIndex ?? null,
1460
+ task: {
1461
+ suite,
1462
+ instance_id: instanceId,
1463
+ split,
1464
+ seed: opts.seed ?? null,
1465
+ rep: opts.rep ?? 0
1466
+ },
1467
+ messages: [],
1468
+ tool_defs: []
1469
+ };
1470
+ const nodes = [];
1471
+ if (rootId !== null) {
1472
+ const judgeResolved = typeof judge?.resolved === "boolean" ? judge.resolved : null;
1473
+ const judgeScore = typeof judge?.score === "number" ? judge.score : null;
1474
+ const reward = judgeScore ?? (judgeResolved === null ? null : judgeResolved ? 1 : 0);
1475
+ if (reward === null) gaps.push("root.outcome.reward: no judge verdict for this run");
1476
+ const wallMs = tree.startedAt !== null && tree.completedAt !== null && tree.completedAt >= tree.startedAt ? tree.completedAt - tree.startedAt : null;
1477
+ nodes.push({
1478
+ ...base,
1479
+ rollout_id: rootId,
1480
+ parent_rollout_id: null,
1481
+ role: "supervisor",
1482
+ policy: {
1483
+ harness: opts.supervisorHarness ?? null,
1484
+ harness_version: null,
1485
+ model: opts.supervisorModel ?? null,
1486
+ provider: null,
1487
+ profile_commit: null,
1488
+ sampling: null
1489
+ },
1490
+ outcome: {
1491
+ reward,
1492
+ reward_source: src.judgeSource,
1493
+ verdict: judge ?? null,
1494
+ metrics: {
1495
+ arm: src.arm,
1496
+ sup_status: typeof state?.status === "string" ? state.status : null,
1497
+ sup_verdict: typeof state?.verdict === "string" ? state.verdict : null,
1498
+ delivered: typeof stateResult.delivered === "boolean" ? stateResult.delivered : null,
1499
+ verify_pass: typeof result?.verify_pass === "boolean" ? result.verify_pass : null,
1500
+ started_at: tree.startedAt,
1501
+ completed_at: tree.completedAt,
1502
+ workers_spawned: tree.workerSpawns.length,
1503
+ brain_metered_events: tree.brain.meteredCount
1504
+ },
1505
+ is_completed: state?.status === "completed",
1506
+ is_truncated: false,
1507
+ error: null
1508
+ },
1509
+ cost: {
1510
+ ...EMPTY_COST,
1511
+ usd: src.limits.spendUsd !== null ? null : typeof stateResult.spentUsd === "number" ? stateResult.spentUsd : tree.brain.usd,
1512
+ tokens_in: tree.brain.tokensIn,
1513
+ tokens_out: tree.brain.tokensOut,
1514
+ cache_read: tree.brain.hasCache ? tree.brain.cacheRead : null,
1515
+ cache_write: tree.brain.hasCache ? tree.brain.cacheWrite : null,
1516
+ wall_s: wallMs === null ? null : wallMs / 1e3
1517
+ },
1518
+ artifacts: {
1519
+ patch_path: typeof result?.patchPath === "string" ? result.patchPath : null,
1520
+ run_dir: src.supRunDir,
1521
+ transcript_ref: src.rootTranscriptRef !== void 0 ? src.rootTranscriptRef : src.supRunDir === null ? null : `${src.supRunDir}/journal.jsonl`
1522
+ },
1523
+ provenance: {
1524
+ captured_at: capturedAt,
1525
+ capture: "backfill",
1526
+ gap: "supervision journal carries structure and spend, not the brain transcript"
1527
+ }
1528
+ });
1529
+ } else {
1530
+ gaps.push("tree.root: no parentless `spawned` event in the journal");
1531
+ }
1532
+ const closeById = new Map(tree.closes.map((c) => [c.id, c]));
1533
+ const sourceByLabel = new Map((src.workers ?? []).map((w) => [w.label, w]));
1534
+ for (const spawn of tree.workerSpawns) {
1535
+ const close = closeById.get(spawn.id) ?? null;
1536
+ const facts = tree.workerLogs.get(spawn.label) ?? null;
1537
+ const workerSource = sourceByLabel.get(spawn.label) ?? null;
1538
+ const wallMs = facts?.started != null && facts.finishedAt != null ? facts.finishedAt - facts.started : null;
1539
+ const reward = facts?.passed === null || facts === null ? null : facts.passed ? 1 : 0;
1540
+ if (reward === null) {
1541
+ gaps.push(`worker ${spawn.label}: no verify verdict (worker logs absent or unfinished)`);
1542
+ }
1543
+ nodes.push({
1544
+ ...base,
1545
+ rollout_id: spawn.id,
1546
+ parent_rollout_id: spawn.parent,
1547
+ role: "worker",
1548
+ policy: {
1549
+ harness: opts.workerHarness ?? null,
1550
+ harness_version: null,
1551
+ model: opts.workerModel ?? null,
1552
+ provider: null,
1553
+ profile_commit: null,
1554
+ sampling: null
1555
+ },
1556
+ outcome: {
1557
+ reward,
1558
+ reward_source: reward === null ? null : "worker-self-verify",
1559
+ verdict: close === null ? null : { kind: close.kind, status: close.status, verdict: close.verdict },
1560
+ metrics: {
1561
+ label: spawn.label,
1562
+ spawned_at: spawn.at,
1563
+ settled_at: close?.at ?? null,
1564
+ started_at: facts?.started ?? null,
1565
+ finished_at: facts?.finishedAt ?? null,
1566
+ wall_ms: wallMs,
1567
+ patch_bytes: facts?.finishedPatchBytes ?? null,
1568
+ evidence_bytes: facts?.evidenceBytes ?? null,
1569
+ steers_queued: facts?.steersQueued ?? null,
1570
+ steers_delivered: facts?.steersDelivered ?? null,
1571
+ questions: facts?.questions ?? null
1572
+ },
1573
+ is_completed: close?.kind === "settled",
1574
+ is_truncated: close?.kind === "cancelled",
1575
+ error: close?.kind === "cancelled" ? close.verdict ?? "cancelled" : null
1576
+ },
1577
+ // `close.spend` is only a measurement when the close event carried one;
1578
+ // a store that never priced this worker must leave null, not 0.
1579
+ cost: {
1580
+ ...EMPTY_COST,
1581
+ usd: src.limits.spendUsd !== null || !close?.hasSpend ? null : close.spend.usd,
1582
+ tokens_in: workerSource?.tokensIn ?? (close?.hasSpend ? close.spend.tokens.input : null),
1583
+ tokens_out: workerSource?.tokensOut ?? (close?.hasSpend ? close.spend.tokens.output : null),
1584
+ // Mirror the tokens_in/out journal fallback so a loops-shaped store whose
1585
+ // `settled` spend carries cache counters is not reported as null cache
1586
+ // beside real tokens. Gate on `hasCache` — a spend object without cache
1587
+ // counters must stay null, not a fabricated 0.
1588
+ cache_read: workerSource?.cacheRead ?? (close?.hasSpend && close.spend.tokens.hasCache ? close.spend.tokens.cacheRead : null),
1589
+ cache_write: workerSource?.cacheWrite ?? (close?.hasSpend && close.spend.tokens.hasCache ? close.spend.tokens.cacheWrite : null),
1590
+ wall_s: wallMs === null ? null : wallMs / 1e3
1591
+ },
1592
+ // A reader that knows where its worker artifacts live says so; only the
1593
+ // loops layout is derivable from `supRunDir`, so guessing it for another
1594
+ // store would mint rows pointing at paths that never existed.
1595
+ artifacts: {
1596
+ patch_path: workerSource?.patchPath !== void 0 ? workerSource.patchPath : src.supRunDir === null ? null : `${src.supRunDir}/workers/${spawn.label}.patch`,
1597
+ run_dir: src.supRunDir,
1598
+ transcript_ref: workerSource?.transcriptRef !== void 0 ? workerSource.transcriptRef : src.supRunDir === null ? null : `${src.supRunDir}/workers/${spawn.label}.ndjson`
1599
+ },
1600
+ provenance: {
1601
+ captured_at: capturedAt,
1602
+ capture: "backfill",
1603
+ gap: workerSource?.transcriptRef == null ? "worker transcript lives in the harness session store \u2014 hydrate via src/rollout/readers" : "worker transcript recorded by the harness; messages not inlined into this row"
1604
+ }
1605
+ });
1606
+ }
1607
+ const invalid = nodes.filter((n) => !isRolloutLine(n));
1608
+ if (invalid.length > 0) {
1609
+ gaps.push(`${invalid.length} node(s) failed tangle.rollout.v1 validation`);
1610
+ }
1611
+ return { rootId, nodes, gaps };
1612
+ }
1613
+
1614
+ export {
1615
+ unavailable,
1616
+ isUnavailable,
1617
+ showMeasured,
1618
+ NO_SOURCE_LIMITS,
1619
+ SUPERVISOR_RUN_SCHEMA,
1620
+ SUPERVISOR_RUN_ROLLUP_SCHEMA,
1621
+ parseSupervisorTree,
1622
+ analyzeSupervisorRunSources,
1623
+ parsePatch,
1624
+ rollupSupervisorRuns,
1625
+ DEFAULT_SPAWN_TOOLS,
1626
+ DEFAULT_STEER_TOOLS,
1627
+ DEFAULT_CANCEL_TOOLS,
1628
+ readClaudeCodeSupervisorRun,
1629
+ claudeCodeSupervisorRunReader,
1630
+ renderSupervisorRunHeadline,
1631
+ renderSupervisorRunMarkdown,
1632
+ renderSupervisorRollupMarkdown,
1633
+ findSupervisorRunDirIn,
1634
+ readLoopsSupervisorRun,
1635
+ loopsSupervisorRunReader,
1636
+ analyzeSupervisorRun,
1637
+ writeSupervisorRunReport,
1638
+ supervisorReportStem,
1639
+ writeSupervisorRunReportSafe,
1640
+ reportSupervisorRound,
1641
+ findSupervisorRunDirs,
1642
+ supervisorRunRolloutLines
1643
+ };
1644
+ //# sourceMappingURL=chunk-LKKT3IVV.js.map