@hawkeyexl/manni 4.5.0 → 4.6.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/README.md +1 -1
- package/dist/batch-OZOACN4G.js +41 -0
- package/dist/build-Z5VOVADH.js +32 -0
- package/dist/calibrate-TZYT42DR.js +36 -0
- package/dist/capture-SL4MNDDP.js +34 -0
- package/dist/check-5OMA42Y6.js +26 -0
- package/dist/{check-ESNS7ZBC.js → check-OAJWC3WD.js} +7 -5
- package/dist/{check-RQIIIURI.js → check-PKY7KTQE.js} +13 -14
- package/dist/check-WB2QDHJQ.js +880 -0
- package/dist/check-WB2QDHJQ.js.map +1 -0
- package/dist/{chunk-F2OVHJNC.js → chunk-2JFPBZJK.js} +11 -11
- package/dist/{chunk-CP4ZM43V.js → chunk-2KRB3EG7.js} +2 -2
- package/dist/{chunk-QLUMKWQQ.js → chunk-2M6DVNI5.js} +10 -6
- package/dist/{chunk-QLUMKWQQ.js.map → chunk-2M6DVNI5.js.map} +1 -1
- package/dist/{chunk-HQFVJDEN.js → chunk-3NWX34ZI.js} +19 -19
- package/dist/{chunk-TCC737IS.js → chunk-5G53XATW.js} +4 -4
- package/dist/{chunk-B7XUEQF3.js → chunk-6KKZXB2O.js} +7 -5
- package/dist/{chunk-B7XUEQF3.js.map → chunk-6KKZXB2O.js.map} +1 -1
- package/dist/{chunk-SNEHX4VE.js → chunk-733Y2STG.js} +2 -2
- package/dist/{chunk-ELZ2IIOF.js → chunk-73TC4DYH.js} +2 -2
- package/dist/{chunk-QY3DOZMS.js → chunk-7WRP5FFP.js} +4 -4
- package/dist/{chunk-3R5Y3XOL.js → chunk-APKH2T6E.js} +29 -591
- package/dist/chunk-APKH2T6E.js.map +1 -0
- package/dist/{chunk-FLF6AECQ.js → chunk-B3TS4WRM.js} +17 -15
- package/dist/{chunk-FLF6AECQ.js.map → chunk-B3TS4WRM.js.map} +1 -1
- package/dist/chunk-BWCWTHYS.js +146 -0
- package/dist/chunk-BWCWTHYS.js.map +1 -0
- package/dist/{chunk-C6HZ6C5T.js → chunk-C3B2VJNC.js} +2 -2
- package/dist/{chunk-NN4ASVTY.js → chunk-C73RSUIC.js} +4 -4
- package/dist/chunk-CU57HT2E.js +116 -0
- package/dist/chunk-CU57HT2E.js.map +1 -0
- package/dist/chunk-CWXZ2MRT.js +222 -0
- package/dist/chunk-CWXZ2MRT.js.map +1 -0
- package/dist/{chunk-GAZFKZHM.js → chunk-DC5DDJVE.js} +6 -6
- package/dist/{chunk-52PDIZKV.js → chunk-ECVQNGTH.js} +3 -3
- package/dist/chunk-FLAO4ZS3.js +502 -0
- package/dist/chunk-FLAO4ZS3.js.map +1 -0
- package/dist/chunk-FQGLRJGX.js +174 -0
- package/dist/chunk-FQGLRJGX.js.map +1 -0
- package/dist/{chunk-XWFNO5PO.js → chunk-GAJIJKET.js} +3 -3
- package/dist/{chunk-QCBICNKC.js → chunk-GREQKO6M.js} +202 -54
- package/dist/chunk-GREQKO6M.js.map +1 -0
- package/dist/chunk-HX44GB4Z.js +30 -0
- package/dist/chunk-HX44GB4Z.js.map +1 -0
- package/dist/{chunk-7ZUCEMCY.js → chunk-IY7NYNPZ.js} +9 -11
- package/dist/{chunk-7ZUCEMCY.js.map → chunk-IY7NYNPZ.js.map} +1 -1
- package/dist/{chunk-527HHQPS.js → chunk-KCOFEWV5.js} +6 -4
- package/dist/{chunk-527HHQPS.js.map → chunk-KCOFEWV5.js.map} +1 -1
- package/dist/chunk-M2RLHZ4J.js +849 -0
- package/dist/chunk-M2RLHZ4J.js.map +1 -0
- package/dist/{chunk-K3U5F2QT.js → chunk-M6GSS72T.js} +12 -12
- package/dist/{chunk-UK2I3ACY.js → chunk-MHM4QEBV.js} +4 -4
- package/dist/{chunk-EL2BZTDL.js → chunk-MOWMCEOJ.js} +13 -11
- package/dist/{chunk-EL2BZTDL.js.map → chunk-MOWMCEOJ.js.map} +1 -1
- package/dist/{chunk-OIZ3XMKB.js → chunk-MWBJ3KTF.js} +4 -4
- package/dist/chunk-N7JSEDSC.js +233 -0
- package/dist/chunk-N7JSEDSC.js.map +1 -0
- package/dist/{chunk-MZC62HPZ.js → chunk-NBMPHD4I.js} +2 -2
- package/dist/{chunk-YA63PVWI.js → chunk-NHHOPZRA.js} +12 -10
- package/dist/{chunk-YA63PVWI.js.map → chunk-NHHOPZRA.js.map} +1 -1
- package/dist/{chunk-DX32P2DT.js → chunk-NTWW7J76.js} +2 -2
- package/dist/{chunk-6XTQDFGP.js → chunk-PCFG7E6B.js} +3 -3
- package/dist/{chunk-6XTQDFGP.js.map → chunk-PCFG7E6B.js.map} +1 -1
- package/dist/{chunk-SJO2FTPG.js → chunk-PL4UAF4S.js} +73 -1304
- package/dist/chunk-PL4UAF4S.js.map +1 -0
- package/dist/chunk-QCHVR4ZM.js +286 -0
- package/dist/chunk-QCHVR4ZM.js.map +1 -0
- package/dist/{chunk-JQU7FHYH.js → chunk-QW77LCOU.js} +10 -10
- package/dist/{chunk-IA7BMIPE.js → chunk-SM6NOMJS.js} +2 -2
- package/dist/{chunk-DE7JSWXV.js → chunk-SWSTHZBI.js} +8 -6
- package/dist/{chunk-DE7JSWXV.js.map → chunk-SWSTHZBI.js.map} +1 -1
- package/dist/{chunk-NGK3N6F2.js → chunk-T6SYGLWR.js} +3 -3
- package/dist/chunk-TH4JVQPF.js +133 -0
- package/dist/chunk-TH4JVQPF.js.map +1 -0
- package/dist/{chunk-TVIX653Q.js → chunk-TLXE7LYK.js} +4 -4
- package/dist/{chunk-HVFR66GQ.js → chunk-TUAAR3MM.js} +3280 -283
- package/dist/chunk-TUAAR3MM.js.map +1 -0
- package/dist/{chunk-TA42STP7.js → chunk-UAKNZVCD.js} +4 -4
- package/dist/{chunk-C3KI45RF.js → chunk-UJSGS2Y5.js} +4 -4
- package/dist/{chunk-ZX43SRZR.js → chunk-UWQ2UVHE.js} +8 -6
- package/dist/{chunk-ZX43SRZR.js.map → chunk-UWQ2UVHE.js.map} +1 -1
- package/dist/chunk-WQWKYAAK.js +86 -0
- package/dist/chunk-WQWKYAAK.js.map +1 -0
- package/dist/{chunk-LJ2RDH3F.js → chunk-XG6PVB5S.js} +1 -23
- package/dist/chunk-XG6PVB5S.js.map +1 -0
- package/dist/{chunk-Q35YIDBV.js → chunk-XKBEHKWO.js} +29 -264
- package/dist/chunk-XKBEHKWO.js.map +1 -0
- package/dist/chunk-XPEWUK7J.js +532 -0
- package/dist/chunk-XPEWUK7J.js.map +1 -0
- package/dist/{chunk-BOIF2XGG.js → chunk-XXDXIIQ4.js} +2 -2
- package/dist/chunk-YLK2J5YK.js +610 -0
- package/dist/chunk-YLK2J5YK.js.map +1 -0
- package/dist/{chunk-7OQAU4EA.js → chunk-Z4IZGEM3.js} +16 -12
- package/dist/{chunk-7OQAU4EA.js.map → chunk-Z4IZGEM3.js.map} +1 -1
- package/dist/{cli-E3H6ZP4I.js → cli-6EWVMEN6.js} +5 -3
- package/dist/{cli-E3H6ZP4I.js.map → cli-6EWVMEN6.js.map} +1 -1
- package/dist/{cli-MHTYPVOD.js → cli-6H7HVDMQ.js} +113 -20
- package/dist/{cli-MHTYPVOD.js.map → cli-6H7HVDMQ.js.map} +1 -1
- package/dist/{cli-HUIMNJT6.js → cli-EPVZHAZS.js} +19 -20
- package/dist/{cli-HUIMNJT6.js.map → cli-EPVZHAZS.js.map} +1 -1
- package/dist/{cli-6PGVHP7Y.js → cli-LUCSGTVI.js} +17 -19
- package/dist/{cli-6PGVHP7Y.js.map → cli-LUCSGTVI.js.map} +1 -1
- package/dist/{cli-PRZHQ3QB.js → cli-NIG4N3XM.js} +14 -15
- package/dist/{cli-PRZHQ3QB.js.map → cli-NIG4N3XM.js.map} +1 -1
- package/dist/{cli-TYGOHJOJ.js → cli-PSKNB66T.js} +225 -58
- package/dist/cli-PSKNB66T.js.map +1 -0
- package/dist/{cli-QKIJOXGW.js → cli-UO4NHIB3.js} +17 -17
- package/dist/{cli-AAG3N4NB.js → cli-UQ7A3YA2.js} +17 -18
- package/dist/{cli-AAG3N4NB.js.map → cli-UQ7A3YA2.js.map} +1 -1
- package/dist/{cli-YW6VWBHX.js → cli-YHYDLQAF.js} +10 -10
- package/dist/cli.js +11 -11
- package/dist/config-P4HDNTYD.js +26 -0
- package/dist/conformance-ZFCMKM6M.js +18 -0
- package/dist/docmeta.js +9 -9
- package/dist/github-7ZCH5PSO.js +26 -0
- package/dist/github-POT6724K.js +31 -0
- package/dist/host-6GURH5HE.js +23 -0
- package/dist/index.d.ts +72 -4
- package/dist/index.js +176 -166
- package/dist/index.js.map +1 -1
- package/dist/{json-4DCKDGUV.js → json-KT2K62L5.js} +12 -13
- package/dist/lint-5AQ5C2BV.js +29 -0
- package/dist/list-5JZKX2NK.js +28 -0
- package/dist/list-SWPFXQMU.js +30 -0
- package/dist/{page-QEDB7EVU.js → page-UPSXRP75.js} +11 -12
- package/dist/prepare-6GNEWCNH.js +155 -0
- package/dist/prepare-6GNEWCNH.js.map +1 -0
- package/dist/{pretty-SQX33UM2.js → pretty-Q6FQAOK4.js} +13 -14
- package/dist/{pretty-5DGEBZSV.js → pretty-SQRPFPSQ.js} +12 -13
- package/dist/provider-AMGRDY6V.js +34 -0
- package/dist/release-IECYCLTD.js +36 -0
- package/dist/release-IECYCLTD.js.map +1 -0
- package/dist/{reporters-OHSI3RMV.js → reporters-45MMC2ID.js} +11 -12
- package/dist/{reporters-YELDDYJD.js → reporters-XUV4MM64.js} +7 -5
- package/dist/reporters-XUV4MM64.js.map +1 -0
- package/dist/run-KMWY2IAN.js +32 -0
- package/dist/run-KMWY2IAN.js.map +1 -0
- package/dist/{term-2WMDZ4PY.js → term-PCJUCWNF.js} +16 -17
- package/dist/{term-2WMDZ4PY.js.map → term-PCJUCWNF.js.map} +1 -1
- package/dist/validate-IMNCZNWG.js +17 -0
- package/dist/validate-IMNCZNWG.js.map +1 -0
- package/package.json +2 -2
- package/plugin/manni/.claude-plugin/plugin.json +1 -1
- package/plugin/manni/hooks/hooks.json +47 -0
- package/dist/batch-GOE3UXOD.js +0 -36
- package/dist/build-M7BMEHL2.js +0 -33
- package/dist/calibrate-6RRAILUY.js +0 -31
- package/dist/capture-RJPQ6B4N.js +0 -32
- package/dist/check-BQD64X3N.js +0 -27
- package/dist/chunk-3R5Y3XOL.js.map +0 -1
- package/dist/chunk-3YHAYXBC.js +0 -164
- package/dist/chunk-3YHAYXBC.js.map +0 -1
- package/dist/chunk-HVFR66GQ.js.map +0 -1
- package/dist/chunk-LJ2RDH3F.js.map +0 -1
- package/dist/chunk-NAZRLJAK.js +0 -3090
- package/dist/chunk-NAZRLJAK.js.map +0 -1
- package/dist/chunk-Q35YIDBV.js.map +0 -1
- package/dist/chunk-QCBICNKC.js.map +0 -1
- package/dist/chunk-SJO2FTPG.js.map +0 -1
- package/dist/chunk-TU2WUBVZ.js +0 -2
- package/dist/cli-TYGOHJOJ.js.map +0 -1
- package/dist/github-3WMSTNTK.js +0 -32
- package/dist/github-OXSJQ4PS.js +0 -27
- package/dist/lint-YDHVVZHT.js +0 -30
- package/dist/list-3ZO2NIYO.js +0 -31
- package/dist/list-EJH6VAOW.js +0 -29
- package/dist/run-IP6NZHAG.js +0 -33
- package/dist/validate-G6HH3YXL.js +0 -16
- /package/dist/{batch-GOE3UXOD.js.map → batch-OZOACN4G.js.map} +0 -0
- /package/dist/{build-M7BMEHL2.js.map → build-Z5VOVADH.js.map} +0 -0
- /package/dist/{calibrate-6RRAILUY.js.map → calibrate-TZYT42DR.js.map} +0 -0
- /package/dist/{capture-RJPQ6B4N.js.map → capture-SL4MNDDP.js.map} +0 -0
- /package/dist/{check-BQD64X3N.js.map → check-5OMA42Y6.js.map} +0 -0
- /package/dist/{check-ESNS7ZBC.js.map → check-OAJWC3WD.js.map} +0 -0
- /package/dist/{check-RQIIIURI.js.map → check-PKY7KTQE.js.map} +0 -0
- /package/dist/{chunk-F2OVHJNC.js.map → chunk-2JFPBZJK.js.map} +0 -0
- /package/dist/{chunk-CP4ZM43V.js.map → chunk-2KRB3EG7.js.map} +0 -0
- /package/dist/{chunk-HQFVJDEN.js.map → chunk-3NWX34ZI.js.map} +0 -0
- /package/dist/{chunk-TCC737IS.js.map → chunk-5G53XATW.js.map} +0 -0
- /package/dist/{chunk-SNEHX4VE.js.map → chunk-733Y2STG.js.map} +0 -0
- /package/dist/{chunk-ELZ2IIOF.js.map → chunk-73TC4DYH.js.map} +0 -0
- /package/dist/{chunk-QY3DOZMS.js.map → chunk-7WRP5FFP.js.map} +0 -0
- /package/dist/{chunk-C6HZ6C5T.js.map → chunk-C3B2VJNC.js.map} +0 -0
- /package/dist/{chunk-NN4ASVTY.js.map → chunk-C73RSUIC.js.map} +0 -0
- /package/dist/{chunk-GAZFKZHM.js.map → chunk-DC5DDJVE.js.map} +0 -0
- /package/dist/{chunk-52PDIZKV.js.map → chunk-ECVQNGTH.js.map} +0 -0
- /package/dist/{chunk-XWFNO5PO.js.map → chunk-GAJIJKET.js.map} +0 -0
- /package/dist/{chunk-K3U5F2QT.js.map → chunk-M6GSS72T.js.map} +0 -0
- /package/dist/{chunk-UK2I3ACY.js.map → chunk-MHM4QEBV.js.map} +0 -0
- /package/dist/{chunk-OIZ3XMKB.js.map → chunk-MWBJ3KTF.js.map} +0 -0
- /package/dist/{chunk-MZC62HPZ.js.map → chunk-NBMPHD4I.js.map} +0 -0
- /package/dist/{chunk-DX32P2DT.js.map → chunk-NTWW7J76.js.map} +0 -0
- /package/dist/{chunk-JQU7FHYH.js.map → chunk-QW77LCOU.js.map} +0 -0
- /package/dist/{chunk-IA7BMIPE.js.map → chunk-SM6NOMJS.js.map} +0 -0
- /package/dist/{chunk-NGK3N6F2.js.map → chunk-T6SYGLWR.js.map} +0 -0
- /package/dist/{chunk-TVIX653Q.js.map → chunk-TLXE7LYK.js.map} +0 -0
- /package/dist/{chunk-TA42STP7.js.map → chunk-UAKNZVCD.js.map} +0 -0
- /package/dist/{chunk-C3KI45RF.js.map → chunk-UJSGS2Y5.js.map} +0 -0
- /package/dist/{chunk-BOIF2XGG.js.map → chunk-XXDXIIQ4.js.map} +0 -0
- /package/dist/{cli-QKIJOXGW.js.map → cli-UO4NHIB3.js.map} +0 -0
- /package/dist/{chunk-TU2WUBVZ.js.map → cli-YHYDLQAF.js.map} +0 -0
- /package/dist/{cli-YW6VWBHX.js.map → config-P4HDNTYD.js.map} +0 -0
- /package/dist/{github-3WMSTNTK.js.map → conformance-ZFCMKM6M.js.map} +0 -0
- /package/dist/{github-OXSJQ4PS.js.map → github-7ZCH5PSO.js.map} +0 -0
- /package/dist/{json-4DCKDGUV.js.map → github-POT6724K.js.map} +0 -0
- /package/dist/{lint-YDHVVZHT.js.map → host-6GURH5HE.js.map} +0 -0
- /package/dist/{list-3ZO2NIYO.js.map → json-KT2K62L5.js.map} +0 -0
- /package/dist/{list-EJH6VAOW.js.map → lint-5AQ5C2BV.js.map} +0 -0
- /package/dist/{page-QEDB7EVU.js.map → list-5JZKX2NK.js.map} +0 -0
- /package/dist/{pretty-5DGEBZSV.js.map → list-SWPFXQMU.js.map} +0 -0
- /package/dist/{pretty-SQX33UM2.js.map → page-UPSXRP75.js.map} +0 -0
- /package/dist/{reporters-OHSI3RMV.js.map → pretty-Q6FQAOK4.js.map} +0 -0
- /package/dist/{reporters-YELDDYJD.js.map → pretty-SQRPFPSQ.js.map} +0 -0
- /package/dist/{run-IP6NZHAG.js.map → provider-AMGRDY6V.js.map} +0 -0
- /package/dist/{validate-G6HH3YXL.js.map → reporters-45MMC2ID.js.map} +0 -0
|
@@ -0,0 +1,849 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
windowFor
|
|
4
|
+
} from "./chunk-CWXZ2MRT.js";
|
|
5
|
+
import {
|
|
6
|
+
applyAvailabilityRecord,
|
|
7
|
+
foldBranchAvailability,
|
|
8
|
+
newAvailability,
|
|
9
|
+
newReplay,
|
|
10
|
+
remapAvailability
|
|
11
|
+
} from "./chunk-APKH2T6E.js";
|
|
12
|
+
import {
|
|
13
|
+
makeRedactor
|
|
14
|
+
} from "./chunk-CU57HT2E.js";
|
|
15
|
+
import {
|
|
16
|
+
TracevalsError
|
|
17
|
+
} from "./chunk-UKZNLVJW.js";
|
|
18
|
+
|
|
19
|
+
// src/tracevals/trace/claude.ts
|
|
20
|
+
import { readdir, readFile } from "node:fs/promises";
|
|
21
|
+
import { basename, dirname, join, resolve } from "node:path";
|
|
22
|
+
|
|
23
|
+
// src/tracevals/trace/detect.ts
|
|
24
|
+
var SUPPORTED = "supported formats: Claude Code session files (~/.claude/projects/**.jsonl) and claude -p stream-json";
|
|
25
|
+
function detectFormat(line) {
|
|
26
|
+
let rec;
|
|
27
|
+
try {
|
|
28
|
+
rec = JSON.parse(line);
|
|
29
|
+
} catch {
|
|
30
|
+
throw new TracevalsError(`trace is not JSONL (${SUPPORTED})`);
|
|
31
|
+
}
|
|
32
|
+
if (typeof rec !== "object" || rec === null) {
|
|
33
|
+
throw new TracevalsError(`unrecognized trace format (${SUPPORTED})`);
|
|
34
|
+
}
|
|
35
|
+
if ("parentUuid" in rec || "sessionId" in rec) return "claude-session";
|
|
36
|
+
if ("session_id" in rec) return "claude-stream";
|
|
37
|
+
throw new TracevalsError(`unrecognized trace format (${SUPPORTED})`);
|
|
38
|
+
}
|
|
39
|
+
function detectContentFormat(content) {
|
|
40
|
+
let sawParseable = false;
|
|
41
|
+
for (const line of content.split("\n")) {
|
|
42
|
+
const trimmed = line.trim();
|
|
43
|
+
if (!trimmed) continue;
|
|
44
|
+
let rec;
|
|
45
|
+
try {
|
|
46
|
+
rec = JSON.parse(trimmed);
|
|
47
|
+
} catch {
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
if (typeof rec !== "object" || rec === null) continue;
|
|
51
|
+
sawParseable = true;
|
|
52
|
+
const r = rec;
|
|
53
|
+
if ("parentUuid" in r || "sessionId" in r) return "claude-session";
|
|
54
|
+
if ("session_id" in r) return "claude-stream";
|
|
55
|
+
}
|
|
56
|
+
throw new TracevalsError(
|
|
57
|
+
sawParseable ? `unrecognized trace format (${SUPPORTED})` : `empty or unparseable trace file (${SUPPORTED})`
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// src/tracevals/trace/claude.ts
|
|
62
|
+
async function parseTraceFile(filePath) {
|
|
63
|
+
const absolute = resolve(filePath);
|
|
64
|
+
const content = await readFile(absolute, "utf-8");
|
|
65
|
+
const trace = parseTraceContent(content, absolute);
|
|
66
|
+
await mergeSidecarBranches(trace, absolute);
|
|
67
|
+
return trace;
|
|
68
|
+
}
|
|
69
|
+
function parseTraceContent(content, filePath) {
|
|
70
|
+
const format = detectContentFormat(content);
|
|
71
|
+
const { records, unparseable } = parseLines(content);
|
|
72
|
+
const trace = format === "claude-session" ? parseSession(records, filePath) : parseStream(records, filePath);
|
|
73
|
+
if (unparseable > 0) {
|
|
74
|
+
trace.warnings.push(
|
|
75
|
+
`${unparseable} unparseable JSONL line(s) were skipped`
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
return trace;
|
|
79
|
+
}
|
|
80
|
+
function parseLines(content) {
|
|
81
|
+
const records = [];
|
|
82
|
+
let unparseable = 0;
|
|
83
|
+
for (const line of content.split("\n")) {
|
|
84
|
+
const trimmed = line.trim();
|
|
85
|
+
if (!trimmed) continue;
|
|
86
|
+
try {
|
|
87
|
+
const rec = JSON.parse(trimmed);
|
|
88
|
+
if (typeof rec === "object" && rec !== null) records.push(rec);
|
|
89
|
+
else unparseable += 1;
|
|
90
|
+
} catch {
|
|
91
|
+
unparseable += 1;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return { records, unparseable };
|
|
95
|
+
}
|
|
96
|
+
var SESSION_META_TYPES = /* @__PURE__ */ new Set([
|
|
97
|
+
"queue-operation",
|
|
98
|
+
"attachment",
|
|
99
|
+
"last-prompt",
|
|
100
|
+
"custom-title",
|
|
101
|
+
"summary",
|
|
102
|
+
"mode",
|
|
103
|
+
"pr-link"
|
|
104
|
+
]);
|
|
105
|
+
var COMMAND_NAME_RE = /<command-name>([^<]+)<\/command-name>/;
|
|
106
|
+
var COMMAND_ARGS_RE = /<command-args>([^<]*)<\/command-args>/;
|
|
107
|
+
function newTrace(filePath) {
|
|
108
|
+
return {
|
|
109
|
+
source: "claude-code",
|
|
110
|
+
file: filePath,
|
|
111
|
+
cwd: "",
|
|
112
|
+
events: [],
|
|
113
|
+
toolCalls: [],
|
|
114
|
+
skillInvocations: [],
|
|
115
|
+
agentSpawns: [],
|
|
116
|
+
subagentBranches: [],
|
|
117
|
+
availability: newAvailability(),
|
|
118
|
+
fileAccesses: [],
|
|
119
|
+
userMessages: [],
|
|
120
|
+
assistantTexts: [],
|
|
121
|
+
turnCount: 0,
|
|
122
|
+
warnings: []
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
function pushEvent(trace, event) {
|
|
126
|
+
const index = trace.events.length;
|
|
127
|
+
trace.events.push({ ...event, index });
|
|
128
|
+
return index;
|
|
129
|
+
}
|
|
130
|
+
function plainMessageText(rec) {
|
|
131
|
+
const content = rec.message?.content;
|
|
132
|
+
return typeof content === "string" ? content : void 0;
|
|
133
|
+
}
|
|
134
|
+
function resolveBranch(branches, parentUuid, rootText) {
|
|
135
|
+
if (typeof parentUuid !== "string") return void 0;
|
|
136
|
+
const inherited = branches.resolved.get(parentUuid);
|
|
137
|
+
if (inherited !== void 0) return inherited;
|
|
138
|
+
const pending = branches.spawns.get(parentUuid);
|
|
139
|
+
if (pending === void 0 || pending.length === 0) return void 0;
|
|
140
|
+
const byPrompt = rootText !== void 0 ? pending.find((b) => !b.claimed && b.prompt === rootText) : void 0;
|
|
141
|
+
const branch = byPrompt ?? pending.find((b) => !b.claimed) ?? pending[0];
|
|
142
|
+
if (branch === void 0) return void 0;
|
|
143
|
+
branch.claimed = true;
|
|
144
|
+
return branch.toolUseId;
|
|
145
|
+
}
|
|
146
|
+
function parseSession(records, filePath) {
|
|
147
|
+
const trace = newTrace(filePath);
|
|
148
|
+
const branches = { spawns: /* @__PURE__ */ new Map(), resolved: /* @__PURE__ */ new Map() };
|
|
149
|
+
const availability = newReplay();
|
|
150
|
+
const uuids = /* @__PURE__ */ new Set();
|
|
151
|
+
for (const rec of records) {
|
|
152
|
+
if (typeof rec.uuid === "string") uuids.add(rec.uuid);
|
|
153
|
+
}
|
|
154
|
+
const continuations = [];
|
|
155
|
+
let sawMessage = false;
|
|
156
|
+
let compactions = 0;
|
|
157
|
+
let inputTokens = 0;
|
|
158
|
+
let outputTokens = 0;
|
|
159
|
+
let sawUsage = false;
|
|
160
|
+
for (const rec of records) {
|
|
161
|
+
const type = rec.type;
|
|
162
|
+
const timestamp = rec.timestamp;
|
|
163
|
+
if (timestamp) {
|
|
164
|
+
trace.startedAt ??= timestamp;
|
|
165
|
+
trace.endedAt = timestamp;
|
|
166
|
+
}
|
|
167
|
+
if (rec.sessionId) trace.sessionId ??= rec.sessionId;
|
|
168
|
+
if (rec.subtype === "compact_boundary") compactions += 1;
|
|
169
|
+
if (type !== void 0 && SESSION_META_TYPES.has(type)) {
|
|
170
|
+
if (type === "summary" && !sawMessage && typeof rec.leafUuid === "string" && !uuids.has(rec.leafUuid)) {
|
|
171
|
+
continuations.push(rec.leafUuid);
|
|
172
|
+
}
|
|
173
|
+
const index = pushEvent(trace, { kind: "meta", timestamp, raw: rec });
|
|
174
|
+
if (type === "attachment" && applyAvailabilityRecord(availability, rec.attachment, index)) {
|
|
175
|
+
availability.roster.recorded = true;
|
|
176
|
+
}
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
const sidechain = rec.isSidechain === true;
|
|
180
|
+
if (!sidechain && typeof rec.cwd === "string") trace.cwd = rec.cwd;
|
|
181
|
+
if (!sidechain && typeof rec.gitBranch === "string") {
|
|
182
|
+
trace.gitBranch = rec.gitBranch;
|
|
183
|
+
}
|
|
184
|
+
const context = { sidechain, timestamp, branches };
|
|
185
|
+
if (typeof rec.uuid === "string") context.uuid = rec.uuid;
|
|
186
|
+
if (sidechain) {
|
|
187
|
+
const branchId = resolveBranch(
|
|
188
|
+
branches,
|
|
189
|
+
rec.parentUuid,
|
|
190
|
+
plainMessageText(rec)
|
|
191
|
+
);
|
|
192
|
+
if (branchId !== void 0) {
|
|
193
|
+
context.branchId = branchId;
|
|
194
|
+
if (context.uuid) branches.resolved.set(context.uuid, branchId);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
const message = rec.message;
|
|
198
|
+
if (type === "user" && message) {
|
|
199
|
+
sawMessage = true;
|
|
200
|
+
handleUserMessage(trace, message, context, rec);
|
|
201
|
+
} else if (type === "assistant" && message) {
|
|
202
|
+
sawMessage = true;
|
|
203
|
+
const usage = handleAssistantMessage(trace, message, context, rec);
|
|
204
|
+
if (usage && !sidechain) {
|
|
205
|
+
inputTokens += usage.input;
|
|
206
|
+
outputTokens += usage.output;
|
|
207
|
+
sawUsage = true;
|
|
208
|
+
}
|
|
209
|
+
} else {
|
|
210
|
+
pushEvent(trace, { kind: "system", timestamp, raw: rec });
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
if (sawUsage) trace.usage = { inputTokens, outputTokens };
|
|
214
|
+
for (const leaf of continuations) {
|
|
215
|
+
trace.warnings.push(
|
|
216
|
+
`this trace is a fragment of a longer session: it resumes an earlier one recorded elsewhere (summary leafUuid ${leaf} is not in this file), so evals see only the turns recorded here`
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
if (compactions > 0) {
|
|
220
|
+
trace.warnings.push(
|
|
221
|
+
`the conversation was compacted ${compactions} time(s): the agent stopped seeing the turns before each boundary, so early transcript content is not what it was working from`
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
trace.availability = availability.roster;
|
|
225
|
+
trace.subagentBranches = buildBranches(trace);
|
|
226
|
+
return trace;
|
|
227
|
+
}
|
|
228
|
+
function buildBranches(trace, sidecars = /* @__PURE__ */ new Map()) {
|
|
229
|
+
const ranges = /* @__PURE__ */ new Map();
|
|
230
|
+
for (const event of trace.events) {
|
|
231
|
+
const id = event.branchId;
|
|
232
|
+
if (id === void 0) continue;
|
|
233
|
+
const range = ranges.get(id);
|
|
234
|
+
if (range === void 0) ranges.set(id, { min: event.index, max: event.index });
|
|
235
|
+
else range.max = Math.max(range.max, event.index);
|
|
236
|
+
}
|
|
237
|
+
if (ranges.size === 0) return [];
|
|
238
|
+
const spawnFor = /* @__PURE__ */ new Map();
|
|
239
|
+
for (const spawn of trace.agentSpawns) {
|
|
240
|
+
if (spawn.toolUseId) spawnFor.set(spawn.toolUseId, spawn);
|
|
241
|
+
}
|
|
242
|
+
const parentOf = /* @__PURE__ */ new Map();
|
|
243
|
+
for (const id of ranges.keys()) {
|
|
244
|
+
const spawn = spawnFor.get(id);
|
|
245
|
+
parentOf.set(
|
|
246
|
+
id,
|
|
247
|
+
spawn ? trace.events[spawn.index]?.branchId : void 0
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
const depthOf = (id) => {
|
|
251
|
+
let depth = 1;
|
|
252
|
+
let cursor = parentOf.get(id);
|
|
253
|
+
const seen = /* @__PURE__ */ new Set([id]);
|
|
254
|
+
while (cursor !== void 0 && !seen.has(cursor)) {
|
|
255
|
+
seen.add(cursor);
|
|
256
|
+
depth += 1;
|
|
257
|
+
cursor = parentOf.get(cursor);
|
|
258
|
+
}
|
|
259
|
+
return depth;
|
|
260
|
+
};
|
|
261
|
+
const branches = [];
|
|
262
|
+
for (const [id, range] of ranges) {
|
|
263
|
+
const spawn = spawnFor.get(id);
|
|
264
|
+
const sidecar = sidecars.get(id);
|
|
265
|
+
const branch = {
|
|
266
|
+
branchId: id,
|
|
267
|
+
agentType: sidecar?.agentType ?? spawn?.subagentType ?? id,
|
|
268
|
+
origin: sidecar ? "sidecar" : "inline",
|
|
269
|
+
spawnDepth: sidecar?.spawnDepth ?? depthOf(id),
|
|
270
|
+
spawnIndex: spawn?.index ?? range.min,
|
|
271
|
+
startIndex: range.min,
|
|
272
|
+
endIndex: range.max + 1
|
|
273
|
+
};
|
|
274
|
+
const description = sidecar?.description ?? spawn?.description;
|
|
275
|
+
if (description !== void 0) branch.description = description;
|
|
276
|
+
if (sidecar?.agentId) branch.agentId = sidecar.agentId;
|
|
277
|
+
if (sidecar?.parentAgentId) branch.parentAgentId = sidecar.parentAgentId;
|
|
278
|
+
if (sidecar?.file) branch.file = sidecar.file;
|
|
279
|
+
branches.push(branch);
|
|
280
|
+
}
|
|
281
|
+
branches.sort((a, b) => b.spawnDepth - a.spawnDepth);
|
|
282
|
+
const byId = new Map(branches.map((b) => [b.branchId, b]));
|
|
283
|
+
for (const branch of branches) {
|
|
284
|
+
const parentId = parentOf.get(branch.branchId);
|
|
285
|
+
const parent = parentId === void 0 ? void 0 : byId.get(parentId);
|
|
286
|
+
if (parent === void 0) continue;
|
|
287
|
+
parent.startIndex = Math.min(parent.startIndex, branch.startIndex);
|
|
288
|
+
parent.endIndex = Math.max(parent.endIndex, branch.endIndex);
|
|
289
|
+
}
|
|
290
|
+
branches.sort((a, b) => a.startIndex - b.startIndex);
|
|
291
|
+
return branches;
|
|
292
|
+
}
|
|
293
|
+
function handleUserMessage(trace, message, context, raw) {
|
|
294
|
+
const { sidechain, timestamp, branchId } = context;
|
|
295
|
+
const content = message.content;
|
|
296
|
+
if (typeof content === "string") {
|
|
297
|
+
const index = pushEvent(trace, {
|
|
298
|
+
kind: "user",
|
|
299
|
+
timestamp,
|
|
300
|
+
text: content,
|
|
301
|
+
sidechain,
|
|
302
|
+
branchId,
|
|
303
|
+
raw
|
|
304
|
+
});
|
|
305
|
+
if (sidechain) return;
|
|
306
|
+
const command = COMMAND_NAME_RE.exec(content);
|
|
307
|
+
if (command?.[1]) {
|
|
308
|
+
const invocation = {
|
|
309
|
+
name: command[1].replace(/^\//, ""),
|
|
310
|
+
via: "command-injection",
|
|
311
|
+
index
|
|
312
|
+
};
|
|
313
|
+
const args = COMMAND_ARGS_RE.exec(content)?.[1]?.trim();
|
|
314
|
+
if (args) invocation.args = args;
|
|
315
|
+
trace.skillInvocations.push(invocation);
|
|
316
|
+
}
|
|
317
|
+
trace.userMessages.push(content);
|
|
318
|
+
trace.turnCount += 1;
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
if (Array.isArray(content)) {
|
|
322
|
+
let hasToolResult = false;
|
|
323
|
+
const texts = [];
|
|
324
|
+
for (const block of content) {
|
|
325
|
+
if (block?.type === "tool_result") hasToolResult = true;
|
|
326
|
+
if (block?.type === "text" && typeof block.text === "string") {
|
|
327
|
+
texts.push(block.text);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
if (hasToolResult) {
|
|
331
|
+
pushEvent(trace, {
|
|
332
|
+
kind: "tool_result",
|
|
333
|
+
timestamp,
|
|
334
|
+
sidechain,
|
|
335
|
+
branchId,
|
|
336
|
+
raw
|
|
337
|
+
});
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
const text = texts.join("\n");
|
|
341
|
+
pushEvent(trace, {
|
|
342
|
+
kind: "user",
|
|
343
|
+
timestamp,
|
|
344
|
+
text,
|
|
345
|
+
sidechain,
|
|
346
|
+
branchId,
|
|
347
|
+
raw
|
|
348
|
+
});
|
|
349
|
+
if (!sidechain && text) {
|
|
350
|
+
trace.userMessages.push(text);
|
|
351
|
+
trace.turnCount += 1;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
function handleAssistantMessage(trace, message, context, raw) {
|
|
356
|
+
const { sidechain, timestamp, branchId } = context;
|
|
357
|
+
if (!sidechain && typeof message.model === "string") {
|
|
358
|
+
trace.model ??= message.model;
|
|
359
|
+
}
|
|
360
|
+
const content = message.content;
|
|
361
|
+
if (Array.isArray(content)) {
|
|
362
|
+
for (const block of content) {
|
|
363
|
+
if (block?.type === "text" && typeof block.text === "string") {
|
|
364
|
+
pushEvent(trace, {
|
|
365
|
+
kind: "assistant",
|
|
366
|
+
timestamp,
|
|
367
|
+
text: block.text,
|
|
368
|
+
sidechain,
|
|
369
|
+
branchId,
|
|
370
|
+
raw
|
|
371
|
+
});
|
|
372
|
+
if (!sidechain) trace.assistantTexts.push(block.text);
|
|
373
|
+
} else if (block?.type === "tool_use") {
|
|
374
|
+
extractToolUse(trace, block, context);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
const usage = message.usage;
|
|
379
|
+
if (usage) {
|
|
380
|
+
return {
|
|
381
|
+
input: typeof usage.input_tokens === "number" ? usage.input_tokens : 0,
|
|
382
|
+
output: typeof usage.output_tokens === "number" ? usage.output_tokens : 0
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
return void 0;
|
|
386
|
+
}
|
|
387
|
+
function extractToolUse(trace, block, context) {
|
|
388
|
+
const { sidechain, timestamp, branchId } = context;
|
|
389
|
+
const name = block.name;
|
|
390
|
+
const input = block.input ?? {};
|
|
391
|
+
const toolUseId = typeof block.id === "string" ? block.id : void 0;
|
|
392
|
+
const index = pushEvent(trace, {
|
|
393
|
+
kind: "tool_call",
|
|
394
|
+
timestamp,
|
|
395
|
+
toolName: name,
|
|
396
|
+
sidechain,
|
|
397
|
+
branchId,
|
|
398
|
+
raw: block
|
|
399
|
+
});
|
|
400
|
+
const call = { name, input, index, sidechain };
|
|
401
|
+
if (timestamp) call.timestamp = timestamp;
|
|
402
|
+
if (branchId) call.branchId = branchId;
|
|
403
|
+
trace.toolCalls.push(call);
|
|
404
|
+
switch (name) {
|
|
405
|
+
case "Skill": {
|
|
406
|
+
if (typeof input.skill === "string") {
|
|
407
|
+
const invocation = {
|
|
408
|
+
name: input.skill,
|
|
409
|
+
via: "skill-tool",
|
|
410
|
+
index
|
|
411
|
+
};
|
|
412
|
+
if (toolUseId) invocation.toolUseId = toolUseId;
|
|
413
|
+
if (typeof input.args === "string" && input.args) {
|
|
414
|
+
invocation.args = input.args;
|
|
415
|
+
}
|
|
416
|
+
trace.skillInvocations.push(invocation);
|
|
417
|
+
}
|
|
418
|
+
break;
|
|
419
|
+
}
|
|
420
|
+
case "Agent": {
|
|
421
|
+
if (typeof input.subagent_type === "string") {
|
|
422
|
+
const spawn = {
|
|
423
|
+
subagentType: input.subagent_type,
|
|
424
|
+
index
|
|
425
|
+
};
|
|
426
|
+
if (typeof input.description === "string") {
|
|
427
|
+
spawn.description = input.description;
|
|
428
|
+
}
|
|
429
|
+
if (toolUseId) spawn.toolUseId = toolUseId;
|
|
430
|
+
trace.agentSpawns.push(spawn);
|
|
431
|
+
}
|
|
432
|
+
if (context.branches && context.uuid && toolUseId) {
|
|
433
|
+
const pending = context.branches.spawns.get(context.uuid) ?? [];
|
|
434
|
+
pending.push({
|
|
435
|
+
toolUseId,
|
|
436
|
+
prompt: typeof input.prompt === "string" ? input.prompt : void 0,
|
|
437
|
+
claimed: false
|
|
438
|
+
});
|
|
439
|
+
context.branches.spawns.set(context.uuid, pending);
|
|
440
|
+
}
|
|
441
|
+
break;
|
|
442
|
+
}
|
|
443
|
+
case "Read":
|
|
444
|
+
case "Write":
|
|
445
|
+
case "Edit": {
|
|
446
|
+
if (typeof input.file_path === "string") {
|
|
447
|
+
const op = name === "Read" ? "read" : name === "Write" ? "write" : "edit";
|
|
448
|
+
trace.fileAccesses.push({ path: input.file_path, op, index });
|
|
449
|
+
}
|
|
450
|
+
break;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
function parseStream(records, filePath) {
|
|
455
|
+
const trace = newTrace(filePath);
|
|
456
|
+
const context = { sidechain: false };
|
|
457
|
+
let inputTokens = 0;
|
|
458
|
+
let outputTokens = 0;
|
|
459
|
+
let sawMessageUsage = false;
|
|
460
|
+
for (const rec of records) {
|
|
461
|
+
const type = rec.type;
|
|
462
|
+
if (type === "system" && (rec.subtype === "init" || !rec.subtype)) {
|
|
463
|
+
if (typeof rec.cwd === "string") trace.cwd = rec.cwd;
|
|
464
|
+
if (typeof rec.model === "string") trace.model = rec.model;
|
|
465
|
+
if (typeof rec.session_id === "string") trace.sessionId = rec.session_id;
|
|
466
|
+
pushEvent(trace, { kind: "system", raw: rec });
|
|
467
|
+
continue;
|
|
468
|
+
}
|
|
469
|
+
if (type === "assistant" || type === "user") {
|
|
470
|
+
const message = rec.message ?? rec;
|
|
471
|
+
if (type === "assistant") {
|
|
472
|
+
const usage = handleAssistantMessage(trace, message, context, rec);
|
|
473
|
+
if (usage) {
|
|
474
|
+
inputTokens += usage.input;
|
|
475
|
+
outputTokens += usage.output;
|
|
476
|
+
sawMessageUsage = true;
|
|
477
|
+
}
|
|
478
|
+
} else {
|
|
479
|
+
handleUserMessage(trace, message, context, rec);
|
|
480
|
+
}
|
|
481
|
+
continue;
|
|
482
|
+
}
|
|
483
|
+
if (type === "result") {
|
|
484
|
+
trace.isError = rec.is_error === true;
|
|
485
|
+
if (typeof rec.num_turns === "number") trace.turnCount = rec.num_turns;
|
|
486
|
+
const usage = rec.usage;
|
|
487
|
+
const totalCost = typeof rec.total_cost_usd === "number" ? rec.total_cost_usd : void 0;
|
|
488
|
+
if (usage) {
|
|
489
|
+
trace.usage = {
|
|
490
|
+
inputTokens: typeof usage.input_tokens === "number" ? usage.input_tokens : 0,
|
|
491
|
+
outputTokens: typeof usage.output_tokens === "number" ? usage.output_tokens : 0
|
|
492
|
+
};
|
|
493
|
+
if (totalCost !== void 0) trace.usage.totalCostUsd = totalCost;
|
|
494
|
+
} else if (sawMessageUsage) {
|
|
495
|
+
trace.usage = { inputTokens, outputTokens };
|
|
496
|
+
if (totalCost !== void 0) trace.usage.totalCostUsd = totalCost;
|
|
497
|
+
}
|
|
498
|
+
pushEvent(trace, { kind: "meta", raw: rec });
|
|
499
|
+
continue;
|
|
500
|
+
}
|
|
501
|
+
pushEvent(trace, { kind: "meta", raw: rec });
|
|
502
|
+
}
|
|
503
|
+
if (!trace.usage && sawMessageUsage) {
|
|
504
|
+
trace.usage = { inputTokens, outputTokens };
|
|
505
|
+
}
|
|
506
|
+
return trace;
|
|
507
|
+
}
|
|
508
|
+
var AGENT_META_RE = /^agent-(.+)\.meta\.json$/;
|
|
509
|
+
var AGENT_JSONL_RE = /^agent-(.+)\.jsonl$/;
|
|
510
|
+
function sidecarDirFor(traceFile) {
|
|
511
|
+
const stem = basename(traceFile).replace(/\.jsonl$/i, "");
|
|
512
|
+
return join(dirname(traceFile), stem, "subagents");
|
|
513
|
+
}
|
|
514
|
+
function asSource(id, trace) {
|
|
515
|
+
return {
|
|
516
|
+
id,
|
|
517
|
+
events: trace.events,
|
|
518
|
+
toolCalls: trace.toolCalls,
|
|
519
|
+
fileAccesses: trace.fileAccesses,
|
|
520
|
+
skillInvocations: trace.skillInvocations,
|
|
521
|
+
agentSpawns: trace.agentSpawns
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
function stampBranch(sub, branchId) {
|
|
525
|
+
for (const event of sub.events) {
|
|
526
|
+
event.sidechain = true;
|
|
527
|
+
event.branchId = branchId;
|
|
528
|
+
}
|
|
529
|
+
for (const call of sub.toolCalls) {
|
|
530
|
+
call.sidechain = true;
|
|
531
|
+
call.branchId = branchId;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
async function loadSidecars(dir, trace) {
|
|
535
|
+
let names;
|
|
536
|
+
try {
|
|
537
|
+
names = await readdir(dir);
|
|
538
|
+
} catch (error) {
|
|
539
|
+
const code = error.code;
|
|
540
|
+
if (code !== "ENOENT" && code !== "ENOTDIR") {
|
|
541
|
+
trace.warnings.push(
|
|
542
|
+
`could not read the subagent transcript directory ${dir} (${code ?? "unknown error"}), so subagent turns are missing here`
|
|
543
|
+
);
|
|
544
|
+
}
|
|
545
|
+
return [];
|
|
546
|
+
}
|
|
547
|
+
const metas = /* @__PURE__ */ new Set();
|
|
548
|
+
const transcripts = /* @__PURE__ */ new Set();
|
|
549
|
+
for (const name of names) {
|
|
550
|
+
const meta = AGENT_META_RE.exec(name);
|
|
551
|
+
if (meta?.[1] !== void 0) {
|
|
552
|
+
metas.add(meta[1]);
|
|
553
|
+
continue;
|
|
554
|
+
}
|
|
555
|
+
const jsonl = AGENT_JSONL_RE.exec(name);
|
|
556
|
+
if (jsonl?.[1] !== void 0) transcripts.add(jsonl[1]);
|
|
557
|
+
}
|
|
558
|
+
for (const orphan of [...transcripts].sort()) {
|
|
559
|
+
if (!metas.has(orphan)) {
|
|
560
|
+
trace.warnings.push(
|
|
561
|
+
`subagent transcript agent-${orphan}.jsonl has no agent-${orphan}.meta.json, so it names no Agent spawn and was not merged`
|
|
562
|
+
);
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
const loaded = [];
|
|
566
|
+
for (const agentId of [...metas].sort()) {
|
|
567
|
+
let meta;
|
|
568
|
+
try {
|
|
569
|
+
const raw = await readFile(
|
|
570
|
+
join(dir, `agent-${agentId}.meta.json`),
|
|
571
|
+
"utf-8"
|
|
572
|
+
);
|
|
573
|
+
const parsed = JSON.parse(raw);
|
|
574
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
575
|
+
throw new Error("shape");
|
|
576
|
+
}
|
|
577
|
+
meta = parsed;
|
|
578
|
+
} catch {
|
|
579
|
+
trace.warnings.push(
|
|
580
|
+
`subagent metadata agent-${agentId}.meta.json is unreadable or not an object, so that branch was not merged`
|
|
581
|
+
);
|
|
582
|
+
continue;
|
|
583
|
+
}
|
|
584
|
+
const file = join(dir, `agent-${agentId}.jsonl`);
|
|
585
|
+
let sub;
|
|
586
|
+
try {
|
|
587
|
+
sub = parseTraceContent(await readFile(file, "utf-8"), file);
|
|
588
|
+
} catch {
|
|
589
|
+
trace.warnings.push(
|
|
590
|
+
`subagent transcript agent-${agentId}.jsonl is missing or unreadable, so that branch was not merged`
|
|
591
|
+
);
|
|
592
|
+
continue;
|
|
593
|
+
}
|
|
594
|
+
for (const warning of sub.warnings) {
|
|
595
|
+
trace.warnings.push(`subagent ${meta.agentType ?? agentId}: ${warning}`);
|
|
596
|
+
}
|
|
597
|
+
loaded.push({ agentId, file, meta, sub, source: asSource(agentId, sub) });
|
|
598
|
+
}
|
|
599
|
+
return loaded;
|
|
600
|
+
}
|
|
601
|
+
async function mergeSidecarBranches(trace, traceFile) {
|
|
602
|
+
const loaded = await loadSidecars(sidecarDirFor(traceFile), trace);
|
|
603
|
+
if (loaded.length === 0) return;
|
|
604
|
+
const base = asSource("", trace);
|
|
605
|
+
const sources = /* @__PURE__ */ new Map([["", base]]);
|
|
606
|
+
const spawnOwner = /* @__PURE__ */ new Map();
|
|
607
|
+
const registerSpawns = (source) => {
|
|
608
|
+
for (const spawn of source.agentSpawns) {
|
|
609
|
+
if (spawn.toolUseId !== void 0) {
|
|
610
|
+
spawnOwner.set(spawn.toolUseId, {
|
|
611
|
+
sourceId: source.id,
|
|
612
|
+
local: spawn.index
|
|
613
|
+
});
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
};
|
|
617
|
+
registerSpawns(base);
|
|
618
|
+
const children = /* @__PURE__ */ new Map();
|
|
619
|
+
const attached = [];
|
|
620
|
+
const pending = [...loaded].sort(
|
|
621
|
+
(a, b) => (a.meta.spawnDepth ?? 1) - (b.meta.spawnDepth ?? 1) || (a.agentId < b.agentId ? -1 : a.agentId > b.agentId ? 1 : 0)
|
|
622
|
+
);
|
|
623
|
+
let progress = true;
|
|
624
|
+
while (progress) {
|
|
625
|
+
progress = false;
|
|
626
|
+
for (let i = 0; i < pending.length; i += 1) {
|
|
627
|
+
const sidecar = pending[i];
|
|
628
|
+
const branchId = sidecar.meta.toolUseId;
|
|
629
|
+
const owner = branchId === void 0 ? void 0 : spawnOwner.get(branchId);
|
|
630
|
+
if (branchId === void 0 || owner === void 0) continue;
|
|
631
|
+
pending.splice(i, 1);
|
|
632
|
+
i -= 1;
|
|
633
|
+
progress = true;
|
|
634
|
+
stampBranch(sidecar.sub, branchId);
|
|
635
|
+
sources.set(sidecar.agentId, sidecar.source);
|
|
636
|
+
const key = `${owner.sourceId}\0${owner.local}`;
|
|
637
|
+
children.set(key, [...children.get(key) ?? [], sidecar]);
|
|
638
|
+
registerSpawns(sidecar.source);
|
|
639
|
+
attached.push({ sidecar, branchId });
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
for (const sidecar of pending) {
|
|
643
|
+
trace.warnings.push(
|
|
644
|
+
`subagent transcript agent-${sidecar.agentId}.jsonl names Agent call ${sidecar.meta.toolUseId ?? "(none recorded)"}, which is not in this trace, so that branch was not merged`
|
|
645
|
+
);
|
|
646
|
+
}
|
|
647
|
+
if (attached.length === 0) return;
|
|
648
|
+
const slots = [];
|
|
649
|
+
const emit = (sourceId) => {
|
|
650
|
+
const source = sources.get(sourceId);
|
|
651
|
+
if (source === void 0) return;
|
|
652
|
+
for (let local = 0; local < source.events.length; local += 1) {
|
|
653
|
+
slots.push({ sourceId, local });
|
|
654
|
+
for (const kid of children.get(`${sourceId}\0${local}`) ?? []) {
|
|
655
|
+
emit(kid.agentId);
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
};
|
|
659
|
+
emit("");
|
|
660
|
+
const remap = /* @__PURE__ */ new Map();
|
|
661
|
+
slots.forEach((slot, final) => {
|
|
662
|
+
const row = remap.get(slot.sourceId) ?? [];
|
|
663
|
+
row[slot.local] = final;
|
|
664
|
+
remap.set(slot.sourceId, row);
|
|
665
|
+
});
|
|
666
|
+
const finalIndex = (sourceId, local) => remap.get(sourceId)?.[local] ?? local;
|
|
667
|
+
trace.events = slots.map((slot, final) => {
|
|
668
|
+
const event = sources.get(slot.sourceId)?.events[slot.local];
|
|
669
|
+
return { ...event, index: final };
|
|
670
|
+
});
|
|
671
|
+
const gather = (pick) => [...sources.values()].flatMap(
|
|
672
|
+
(source) => pick(source).map((item) => ({
|
|
673
|
+
...item,
|
|
674
|
+
index: finalIndex(source.id, item.index)
|
|
675
|
+
}))
|
|
676
|
+
).sort((a, b) => a.index - b.index);
|
|
677
|
+
trace.toolCalls = gather((s) => s.toolCalls);
|
|
678
|
+
trace.fileAccesses = gather((s) => s.fileAccesses);
|
|
679
|
+
trace.skillInvocations = gather((s) => s.skillInvocations);
|
|
680
|
+
trace.agentSpawns = gather((s) => s.agentSpawns);
|
|
681
|
+
trace.availability = remapAvailability(
|
|
682
|
+
trace.availability,
|
|
683
|
+
(index) => finalIndex("", index)
|
|
684
|
+
);
|
|
685
|
+
for (const { sidecar, branchId } of attached) {
|
|
686
|
+
const owner = spawnOwner.get(branchId);
|
|
687
|
+
foldBranchAvailability(
|
|
688
|
+
trace.availability,
|
|
689
|
+
sidecar.sub.availability,
|
|
690
|
+
branchId,
|
|
691
|
+
owner === void 0 ? 0 : finalIndex(owner.sourceId, owner.local)
|
|
692
|
+
);
|
|
693
|
+
}
|
|
694
|
+
const described = /* @__PURE__ */ new Map();
|
|
695
|
+
for (const { sidecar, branchId } of attached) {
|
|
696
|
+
const info = {
|
|
697
|
+
agentId: sidecar.agentId,
|
|
698
|
+
file: sidecar.file
|
|
699
|
+
};
|
|
700
|
+
if (sidecar.meta.agentType) info.agentType = sidecar.meta.agentType;
|
|
701
|
+
if (sidecar.meta.description) info.description = sidecar.meta.description;
|
|
702
|
+
if (sidecar.meta.parentAgentId) {
|
|
703
|
+
info.parentAgentId = sidecar.meta.parentAgentId;
|
|
704
|
+
}
|
|
705
|
+
if (typeof sidecar.meta.spawnDepth === "number") {
|
|
706
|
+
info.spawnDepth = sidecar.meta.spawnDepth;
|
|
707
|
+
}
|
|
708
|
+
described.set(branchId, info);
|
|
709
|
+
}
|
|
710
|
+
trace.subagentBranches = buildBranches(trace, described);
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
// src/tracevals/judge/render.ts
|
|
714
|
+
var DEFAULTS = {
|
|
715
|
+
maxBlockChars: 2e3,
|
|
716
|
+
maxTotalChars: 15e4,
|
|
717
|
+
redact: []
|
|
718
|
+
};
|
|
719
|
+
var MIN_TIMELINE_CHARS = 200;
|
|
720
|
+
var HEADER_TRUNCATED = "\n[... header truncated ...]";
|
|
721
|
+
var TIMELINE_SHELL = "\n\n## Timeline\n";
|
|
722
|
+
function renderTrace(trace, options = {}, plan) {
|
|
723
|
+
const { maxBlockChars, maxTotalChars, redact } = { ...DEFAULTS, ...options };
|
|
724
|
+
const scrub = makeRedactor(redact);
|
|
725
|
+
const clip = (text, max) => {
|
|
726
|
+
const safe = scrub(text);
|
|
727
|
+
if (safe.length <= max) return safe;
|
|
728
|
+
return `${safe.slice(0, max)} [... truncated ${safe.length - max} chars ...]`;
|
|
729
|
+
};
|
|
730
|
+
const window = options.window ?? (plan === void 0 ? void 0 : windowFor(trace, plan));
|
|
731
|
+
const scoped = window !== void 0 && window.scope !== "session" ? window : void 0;
|
|
732
|
+
const events = scoped?.events ?? trace.events;
|
|
733
|
+
const fullHeader = scrub(
|
|
734
|
+
[
|
|
735
|
+
"# Session",
|
|
736
|
+
`source: ${trace.source}`,
|
|
737
|
+
trace.model ? `model: ${trace.model}` : void 0,
|
|
738
|
+
`cwd: ${trace.cwd}`,
|
|
739
|
+
trace.gitBranch ? `branch: ${trace.gitBranch}` : void 0,
|
|
740
|
+
scoped ? `scope: ${scoped.label} \u2014 ${events.length} of ${trace.events.length} session events` : void 0,
|
|
741
|
+
// Everything below describes what the judge can actually see. On a
|
|
742
|
+
// scoped render the timeline is the window, so a session-wide count here
|
|
743
|
+
// reads as evidence that the rest was truncated away — the judge would
|
|
744
|
+
// discount a digest that is complete for what it was asked about. The
|
|
745
|
+
// session totals stay, in parentheses, because "3 of 87" is context the
|
|
746
|
+
// window count alone does not carry.
|
|
747
|
+
scoped ? `turns: ${scoped.turnCount} (of ${trace.turnCount} in the session)` : `turns: ${trace.turnCount}`,
|
|
748
|
+
(() => {
|
|
749
|
+
const used = (scoped ?? trace).skillInvocations.map((s) => s.name);
|
|
750
|
+
return used.length > 0 ? `skills used: ${used.join(", ")}` : "skills used: none";
|
|
751
|
+
})(),
|
|
752
|
+
(() => {
|
|
753
|
+
const inWindow = new Set(events.map((e) => e.index));
|
|
754
|
+
const spawns = trace.agentSpawns.filter(
|
|
755
|
+
(a) => !scoped || inWindow.has(a.index)
|
|
756
|
+
);
|
|
757
|
+
return spawns.length > 0 ? `agents spawned: ${spawns.map((a) => a.subagentType).join(", ")}` : void 0;
|
|
758
|
+
})(),
|
|
759
|
+
(() => {
|
|
760
|
+
const inWindow = new Set(events.map((e) => e.index));
|
|
761
|
+
const branches = trace.subagentBranches.filter(
|
|
762
|
+
(b) => !scoped || inWindow.has(b.spawnIndex)
|
|
763
|
+
);
|
|
764
|
+
return branches.length > 0 ? `subagent transcripts: ${branches.map((b) => `${b.agentType} (depth ${b.spawnDepth})`).join(", ")}` : void 0;
|
|
765
|
+
})()
|
|
766
|
+
].filter((line) => line !== void 0).join("\n")
|
|
767
|
+
);
|
|
768
|
+
const headerRoom = Math.max(
|
|
769
|
+
maxTotalChars - TIMELINE_SHELL.length - MIN_TIMELINE_CHARS,
|
|
770
|
+
0
|
|
771
|
+
);
|
|
772
|
+
if (fullHeader.length > headerRoom) options.onTruncated?.();
|
|
773
|
+
const header = fullHeader.length <= headerRoom ? fullHeader : fullHeader.slice(
|
|
774
|
+
0,
|
|
775
|
+
Math.max(headerRoom - HEADER_TRUNCATED.length, 0)
|
|
776
|
+
) + HEADER_TRUNCATED;
|
|
777
|
+
const branchTypes = /* @__PURE__ */ new Map();
|
|
778
|
+
for (const spawn of trace.agentSpawns) {
|
|
779
|
+
if (spawn.toolUseId) branchTypes.set(spawn.toolUseId, spawn.subagentType);
|
|
780
|
+
}
|
|
781
|
+
for (const branch2 of trace.subagentBranches) {
|
|
782
|
+
branchTypes.set(branch2.branchId, branch2.agentType);
|
|
783
|
+
}
|
|
784
|
+
const label = (branchId) => branchTypes.get(branchId) ?? branchId;
|
|
785
|
+
const callAt = new Map(trace.toolCalls.map((call) => [call.index, call]));
|
|
786
|
+
if (scoped?.empty === true) {
|
|
787
|
+
return `${header}${TIMELINE_SHELL}[no turns: ${scrub(scoped.reason ?? "")}]`;
|
|
788
|
+
}
|
|
789
|
+
const lines = [];
|
|
790
|
+
let branch;
|
|
791
|
+
for (const event of events) {
|
|
792
|
+
const tag = event.branchId ? `:${label(event.branchId)}` : event.sidechain ? ":sidechain" : "";
|
|
793
|
+
let line;
|
|
794
|
+
switch (event.kind) {
|
|
795
|
+
case "user":
|
|
796
|
+
if (event.text) line = `[user${tag}] ${clip(event.text, maxBlockChars)}`;
|
|
797
|
+
break;
|
|
798
|
+
case "assistant":
|
|
799
|
+
if (event.text) {
|
|
800
|
+
line = `[assistant${tag}] ${clip(event.text, maxBlockChars)}`;
|
|
801
|
+
}
|
|
802
|
+
break;
|
|
803
|
+
case "tool_call": {
|
|
804
|
+
const call = callAt.get(event.index);
|
|
805
|
+
const input = call ? JSON.stringify(call.input) : "";
|
|
806
|
+
line = `[tool${tag}] ${event.toolName ?? call?.name ?? "?"} ${clip(input, Math.min(maxBlockChars, 500))}`;
|
|
807
|
+
break;
|
|
808
|
+
}
|
|
809
|
+
default:
|
|
810
|
+
break;
|
|
811
|
+
}
|
|
812
|
+
if (line === void 0) continue;
|
|
813
|
+
if (event.branchId !== branch) {
|
|
814
|
+
if (branch !== void 0) lines.push(`[/subagent ${label(branch)}]`);
|
|
815
|
+
branch = event.branchId;
|
|
816
|
+
if (branch !== void 0) {
|
|
817
|
+
lines.push(`[subagent ${label(branch)} (${branch})]`);
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
lines.push(line);
|
|
821
|
+
}
|
|
822
|
+
if (branch !== void 0) lines.push(`[/subagent ${label(branch)}]`);
|
|
823
|
+
let timeline = scrub(lines.join("\n"));
|
|
824
|
+
const budget = Math.max(
|
|
825
|
+
maxTotalChars - header.length - TIMELINE_SHELL.length,
|
|
826
|
+
MIN_TIMELINE_CHARS
|
|
827
|
+
);
|
|
828
|
+
if (timeline.length > budget) {
|
|
829
|
+
options.onTruncated?.();
|
|
830
|
+
const marker = (omitted2) => `
|
|
831
|
+
[... truncated ${omitted2} chars of transcript ...]
|
|
832
|
+
`;
|
|
833
|
+
const room = Math.max(budget - marker(timeline.length).length, 0);
|
|
834
|
+
const headLen = Math.floor(room * 0.6);
|
|
835
|
+
const tailLen = room - headLen;
|
|
836
|
+
const omitted = timeline.length - headLen - tailLen;
|
|
837
|
+
timeline = `${timeline.slice(0, headLen)}${marker(omitted)}${tailLen > 0 ? timeline.slice(-tailLen) : ""}`;
|
|
838
|
+
}
|
|
839
|
+
return `${header}${TIMELINE_SHELL}${timeline}`;
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
export {
|
|
843
|
+
detectFormat,
|
|
844
|
+
detectContentFormat,
|
|
845
|
+
parseTraceFile,
|
|
846
|
+
parseTraceContent,
|
|
847
|
+
renderTrace
|
|
848
|
+
};
|
|
849
|
+
//# sourceMappingURL=chunk-M2RLHZ4J.js.map
|