@tangle-network/traces 0.21.1 → 0.23.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 +21 -7
- package/dist/{chunk-LB6AD6SX.js → chunk-COSWFESO.js} +615 -574
- package/dist/cli.js +69 -11
- package/dist/index.d.ts +100 -31
- package/dist/index.js +9 -3
- package/docs/trace-analysts.md +3 -2
- package/package.json +7 -7
|
@@ -110,7 +110,7 @@ var ATTR = {
|
|
|
110
110
|
* (`deriveSubjectKey` reads `tangle.subject.key` first). Resolves to the git
|
|
111
111
|
* remote (host/owner/repo) when readable, else the cwd path basename. */
|
|
112
112
|
SUBJECT_KEY: "tangle.subject.key",
|
|
113
|
-
/** Normalized git remote (e.g. `github.com/tangle-network/agent-
|
|
113
|
+
/** Normalized git remote (e.g. `github.com/tangle-network/agent-container`). */
|
|
114
114
|
GIT_REPOSITORY: "git.repository",
|
|
115
115
|
/** Current branch at conversion time. */
|
|
116
116
|
GIT_BRANCH_NAME: "git.branch",
|
|
@@ -151,6 +151,7 @@ var ATTR = {
|
|
|
151
151
|
* in-container path even when no such path exists on this host. */
|
|
152
152
|
ENVIRONMENT_CWD: "tangle.environment.cwd"
|
|
153
153
|
};
|
|
154
|
+
var SESSION_CWD_ATTR = "traces.session.cwd";
|
|
154
155
|
var INGEST_SOURCE_CLI = "cli";
|
|
155
156
|
function environmentAttributes(environment) {
|
|
156
157
|
if (!environment) return {};
|
|
@@ -254,6 +255,9 @@ function parseIsoToEpochMs(ts) {
|
|
|
254
255
|
return n;
|
|
255
256
|
}
|
|
256
257
|
function parseSince(value) {
|
|
258
|
+
return parseTimeBound(value, "--since");
|
|
259
|
+
}
|
|
260
|
+
function parseTimeBound(value, flag) {
|
|
257
261
|
const m = value.match(/^(\d+)\s*([mhd])$/i);
|
|
258
262
|
if (m) {
|
|
259
263
|
const unit = m[2].toLowerCase();
|
|
@@ -261,7 +265,7 @@ function parseSince(value) {
|
|
|
261
265
|
return Date.now() - Number(m[1]) * ms2;
|
|
262
266
|
}
|
|
263
267
|
const t = Date.parse(value);
|
|
264
|
-
if (Number.isNaN(t)) throw new Error(
|
|
268
|
+
if (Number.isNaN(t)) throw new Error(`${flag}: expected 30m / 2h / 7d or an ISO date, got "${value}"`);
|
|
265
269
|
return t;
|
|
266
270
|
}
|
|
267
271
|
|
|
@@ -1024,8 +1028,8 @@ var OTLP_WRITE_CHUNK_CHARS = 4 * 1024 * 1024;
|
|
|
1024
1028
|
async function writeOtlpFile(spans, outPath) {
|
|
1025
1029
|
const { mkdtemp: mkdtemp8, open: open4 } = await import("fs/promises");
|
|
1026
1030
|
const { tmpdir: tmpdir8 } = await import("os");
|
|
1027
|
-
const { join:
|
|
1028
|
-
const path = outPath ??
|
|
1031
|
+
const { join: join35 } = await import("path");
|
|
1032
|
+
const path = outPath ?? join35(await mkdtemp8(join35(tmpdir8(), "traces-")), "spans.otlp.jsonl");
|
|
1029
1033
|
const handle = await open4(path, "w");
|
|
1030
1034
|
try {
|
|
1031
1035
|
let chunk = "";
|
|
@@ -2406,143 +2410,10 @@ var CopilotAdapter = class {
|
|
|
2406
2410
|
}
|
|
2407
2411
|
};
|
|
2408
2412
|
|
|
2409
|
-
// src/adapters/
|
|
2413
|
+
// src/adapters/gemini.ts
|
|
2410
2414
|
import { readdir as readdir5, stat as stat4 } from "fs/promises";
|
|
2411
2415
|
import { homedir as homedir3 } from "os";
|
|
2412
|
-
import {
|
|
2413
|
-
var SERVICE4 = "forge";
|
|
2414
|
-
function tokens(t) {
|
|
2415
|
-
if (!t) return null;
|
|
2416
|
-
return t.actual ?? t.approx ?? null;
|
|
2417
|
-
}
|
|
2418
|
-
function textOf2(content) {
|
|
2419
|
-
return capText(content ?? "");
|
|
2420
|
-
}
|
|
2421
|
-
var ForgeAdapter = class {
|
|
2422
|
-
harness = "forge";
|
|
2423
|
-
aliases = ["forgecode"];
|
|
2424
|
-
/** Directories scanned for `*-dump.json` exports. */
|
|
2425
|
-
dumpDirs() {
|
|
2426
|
-
return [process.cwd(), join5(homedir3(), ".forge")];
|
|
2427
|
-
}
|
|
2428
|
-
async locate(opts = {}) {
|
|
2429
|
-
const refs = [];
|
|
2430
|
-
const dirs = opts.cwd ? [opts.cwd] : this.dumpDirs();
|
|
2431
|
-
for (const dir of dirs) {
|
|
2432
|
-
let files;
|
|
2433
|
-
try {
|
|
2434
|
-
files = await readdir5(dir);
|
|
2435
|
-
} catch (error) {
|
|
2436
|
-
if (isMissingPathError(error)) continue;
|
|
2437
|
-
throw error;
|
|
2438
|
-
}
|
|
2439
|
-
for (const f of files) {
|
|
2440
|
-
if (!f.endsWith("-dump.json")) continue;
|
|
2441
|
-
const path = join5(dir, f);
|
|
2442
|
-
let st;
|
|
2443
|
-
try {
|
|
2444
|
-
st = await stat4(path);
|
|
2445
|
-
} catch (error) {
|
|
2446
|
-
if (isMissingPathError(error)) continue;
|
|
2447
|
-
throw error;
|
|
2448
|
-
}
|
|
2449
|
-
if (opts.sinceMs && st.mtimeMs < opts.sinceMs) continue;
|
|
2450
|
-
refs.push({ harness: this.harness, sessionId: basename3(f, ".json"), path, cwd: dir, mtimeMs: st.mtimeMs });
|
|
2451
|
-
}
|
|
2452
|
-
}
|
|
2453
|
-
return refs.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
2454
|
-
}
|
|
2455
|
-
async parse(ref, options = {}) {
|
|
2456
|
-
const dump = await readJsonFile(ref.path, options);
|
|
2457
|
-
const ctx = dump.context ?? dump;
|
|
2458
|
-
const traceId = ctx.conversation_id ?? ref.sessionId;
|
|
2459
|
-
const rootId = `root:${traceId}`;
|
|
2460
|
-
const messages = ctx.messages ?? [];
|
|
2461
|
-
const spans = [
|
|
2462
|
-
span({ traceId, spanId: rootId, parentSpanId: null, name: "session", kind: "AGENT", startTime: (/* @__PURE__ */ new Date(0)).toISOString(), service: SERVICE4, agent: SERVICE4 })
|
|
2463
|
-
];
|
|
2464
|
-
if (ctx.conversation_id) spans[0].attributes["tangle.sessionId"] = ctx.conversation_id;
|
|
2465
|
-
const toolByCallId = /* @__PURE__ */ new Map();
|
|
2466
|
-
let step = 0;
|
|
2467
|
-
let lastLlm = rootId;
|
|
2468
|
-
for (const raw of messages) {
|
|
2469
|
-
const entry = raw.message ?? raw;
|
|
2470
|
-
const ts = (/* @__PURE__ */ new Date(0)).toISOString();
|
|
2471
|
-
if (entry.text && entry.text.role === "user") {
|
|
2472
|
-
const prompt = textOf2(entry.text.content);
|
|
2473
|
-
if (prompt) {
|
|
2474
|
-
spans.push(
|
|
2475
|
-
userPromptSpan({
|
|
2476
|
-
traceId,
|
|
2477
|
-
spanId: `user:${step}`,
|
|
2478
|
-
parentSpanId: rootId,
|
|
2479
|
-
startTime: ts,
|
|
2480
|
-
content: prompt,
|
|
2481
|
-
contentSource: textSources(entry.text, "content"),
|
|
2482
|
-
service: SERVICE4,
|
|
2483
|
-
agent: SERVICE4,
|
|
2484
|
-
step
|
|
2485
|
-
})
|
|
2486
|
-
);
|
|
2487
|
-
step += 1;
|
|
2488
|
-
}
|
|
2489
|
-
} else if (entry.text) {
|
|
2490
|
-
const llmId = `llm:${step}`;
|
|
2491
|
-
spans.push(
|
|
2492
|
-
span({
|
|
2493
|
-
traceId,
|
|
2494
|
-
spanId: llmId,
|
|
2495
|
-
parentSpanId: rootId,
|
|
2496
|
-
name: `message.${entry.text.role ?? "unknown"}`,
|
|
2497
|
-
kind: "LLM",
|
|
2498
|
-
startTime: ts,
|
|
2499
|
-
service: SERVICE4,
|
|
2500
|
-
agent: SERVICE4,
|
|
2501
|
-
model: entry.text.model ?? null,
|
|
2502
|
-
inputTokens: tokens(raw.usage?.prompt_tokens),
|
|
2503
|
-
outputTokens: tokens(raw.usage?.completion_tokens),
|
|
2504
|
-
step,
|
|
2505
|
-
content: textOf2(entry.text.content) || null,
|
|
2506
|
-
contentSource: textSources(entry.text, "content")
|
|
2507
|
-
})
|
|
2508
|
-
);
|
|
2509
|
-
lastLlm = llmId;
|
|
2510
|
-
step += 1;
|
|
2511
|
-
for (const tc of entry.text.tool_calls ?? []) {
|
|
2512
|
-
const id = tc.call_id ?? `${step}`;
|
|
2513
|
-
const t = span({
|
|
2514
|
-
traceId,
|
|
2515
|
-
spanId: `tool:${id}`,
|
|
2516
|
-
parentSpanId: llmId,
|
|
2517
|
-
name: `tool.${tc.name ?? "tool"}`,
|
|
2518
|
-
kind: "TOOL",
|
|
2519
|
-
startTime: ts,
|
|
2520
|
-
service: SERVICE4,
|
|
2521
|
-
agent: SERVICE4,
|
|
2522
|
-
tool: tc.name ?? "tool",
|
|
2523
|
-
step,
|
|
2524
|
-
extra: toolIoAttributes({ input: tc.arguments, inputSource: sourceOf(tc, "arguments") })
|
|
2525
|
-
});
|
|
2526
|
-
spans.push(t);
|
|
2527
|
-
toolByCallId.set(id, t);
|
|
2528
|
-
step += 1;
|
|
2529
|
-
}
|
|
2530
|
-
} else if (entry.tool) {
|
|
2531
|
-
const t = toolByCallId.get(entry.tool.call_id ?? "");
|
|
2532
|
-
if (t) {
|
|
2533
|
-
t.status = entry.tool.output?.is_error ? { code: "ERROR", message: "tool reported error" } : { code: "OK" };
|
|
2534
|
-
recordToolOutput(t, entry.tool.output?.values ?? entry.tool.output, entry.tool.output?.values != null ? sourceOf(entry.tool.output, "values") : sourceOf(entry.tool, "output"));
|
|
2535
|
-
}
|
|
2536
|
-
}
|
|
2537
|
-
}
|
|
2538
|
-
return spans;
|
|
2539
|
-
}
|
|
2540
|
-
};
|
|
2541
|
-
|
|
2542
|
-
// src/adapters/gemini.ts
|
|
2543
|
-
import { readdir as readdir6, stat as stat5 } from "fs/promises";
|
|
2544
|
-
import { homedir as homedir4 } from "os";
|
|
2545
|
-
import { join as join6 } from "path";
|
|
2416
|
+
import { join as join5 } from "path";
|
|
2546
2417
|
function iso(ts, fallback) {
|
|
2547
2418
|
if (typeof ts === "number") return new Date(ts).toISOString();
|
|
2548
2419
|
if (typeof ts === "string" && ts.length > 0) return ts;
|
|
@@ -2551,7 +2422,7 @@ function iso(ts, fallback) {
|
|
|
2551
2422
|
function toolStatusError(status2) {
|
|
2552
2423
|
return status2 != null && /error|fail|cancel/i.test(status2);
|
|
2553
2424
|
}
|
|
2554
|
-
function
|
|
2425
|
+
function textOf2(content) {
|
|
2555
2426
|
const s = typeof content === "string" ? content : JSON.stringify(content ?? "");
|
|
2556
2427
|
return capText(s);
|
|
2557
2428
|
}
|
|
@@ -2567,7 +2438,7 @@ var GeminiFamilyAdapter = class {
|
|
|
2567
2438
|
this.homeDirName = cfg.homeDirName;
|
|
2568
2439
|
}
|
|
2569
2440
|
chatsRoot() {
|
|
2570
|
-
return
|
|
2441
|
+
return join5(homedir3(), this.homeDirName, "tmp");
|
|
2571
2442
|
}
|
|
2572
2443
|
/** `<home>/projects.json` maps `{ <absolute-cwd>: <projectName> }`. The session
|
|
2573
2444
|
* dir name (`tmp/<projectHash>/`) is that projectName for registered projects,
|
|
@@ -2577,7 +2448,7 @@ var GeminiFamilyAdapter = class {
|
|
|
2577
2448
|
const nameToPath = /* @__PURE__ */ new Map();
|
|
2578
2449
|
try {
|
|
2579
2450
|
const projects = (await readJsonFile(
|
|
2580
|
-
|
|
2451
|
+
join5(homedir3(), this.homeDirName, "projects.json")
|
|
2581
2452
|
)).projects ?? {};
|
|
2582
2453
|
for (const [path, name] of Object.entries(projects)) nameToPath.set(name, path);
|
|
2583
2454
|
} catch (error) {
|
|
@@ -2589,7 +2460,7 @@ var GeminiFamilyAdapter = class {
|
|
|
2589
2460
|
const root = this.chatsRoot();
|
|
2590
2461
|
let projectDirs;
|
|
2591
2462
|
try {
|
|
2592
|
-
projectDirs = await
|
|
2463
|
+
projectDirs = await readdir5(root);
|
|
2593
2464
|
} catch (error) {
|
|
2594
2465
|
if (isMissingPathError(error)) return [];
|
|
2595
2466
|
throw error;
|
|
@@ -2598,20 +2469,20 @@ var GeminiFamilyAdapter = class {
|
|
|
2598
2469
|
const refs = [];
|
|
2599
2470
|
for (const pd of projectDirs) {
|
|
2600
2471
|
const cwd = projectCwds.get(pd) ?? null;
|
|
2601
|
-
const chatsDir =
|
|
2472
|
+
const chatsDir = join5(root, pd, "chats");
|
|
2602
2473
|
let files;
|
|
2603
2474
|
try {
|
|
2604
|
-
files = await
|
|
2475
|
+
files = await readdir5(chatsDir);
|
|
2605
2476
|
} catch (error) {
|
|
2606
2477
|
if (isMissingPathError(error)) continue;
|
|
2607
2478
|
throw error;
|
|
2608
2479
|
}
|
|
2609
2480
|
for (const f of files) {
|
|
2610
2481
|
if (!f.startsWith("session-") || !f.endsWith(".json")) continue;
|
|
2611
|
-
const path =
|
|
2482
|
+
const path = join5(chatsDir, f);
|
|
2612
2483
|
let st;
|
|
2613
2484
|
try {
|
|
2614
|
-
st = await
|
|
2485
|
+
st = await stat4(path);
|
|
2615
2486
|
} catch (error) {
|
|
2616
2487
|
if (isMissingPathError(error)) continue;
|
|
2617
2488
|
throw error;
|
|
@@ -2649,7 +2520,7 @@ var GeminiFamilyAdapter = class {
|
|
|
2649
2520
|
const ts = iso(m.timestamp, start);
|
|
2650
2521
|
const llmId = `llm:${mid}`;
|
|
2651
2522
|
if (m.type === "user") {
|
|
2652
|
-
const prompt =
|
|
2523
|
+
const prompt = textOf2(m.content);
|
|
2653
2524
|
if (prompt) {
|
|
2654
2525
|
spans.push(
|
|
2655
2526
|
userPromptSpan({
|
|
@@ -2681,7 +2552,7 @@ var GeminiFamilyAdapter = class {
|
|
|
2681
2552
|
inputTokens: m.tokens?.input ?? null,
|
|
2682
2553
|
outputTokens: m.tokens?.output ?? null,
|
|
2683
2554
|
step,
|
|
2684
|
-
content:
|
|
2555
|
+
content: textOf2(m.content) || null,
|
|
2685
2556
|
contentSource: sourceOf(m, "content")
|
|
2686
2557
|
})
|
|
2687
2558
|
);
|
|
@@ -2718,6 +2589,7 @@ var GeminiAdapter = class extends GeminiFamilyAdapter {
|
|
|
2718
2589
|
};
|
|
2719
2590
|
|
|
2720
2591
|
// src/adapters/actor.ts
|
|
2592
|
+
import { claudeRecordIsMachine } from "@tangle-network/harness-sessions";
|
|
2721
2593
|
var CMD_MARKERS = [
|
|
2722
2594
|
"<command-name>",
|
|
2723
2595
|
"<local-command-caveat>",
|
|
@@ -2803,7 +2675,7 @@ function claudePromptStartsTask(args) {
|
|
|
2803
2675
|
}
|
|
2804
2676
|
function claudeActor(args) {
|
|
2805
2677
|
if (args.isSidechain === true) return "subagent-spawn";
|
|
2806
|
-
if (args.
|
|
2678
|
+
if (claudeRecordIsMachine({ ...args, entrypoint: args.entrypoint ?? void 0 })) return "injected";
|
|
2807
2679
|
if (args.userType != null && args.userType !== "external") return "injected";
|
|
2808
2680
|
if (textIsCmdOrInject(args.text)) return "injected";
|
|
2809
2681
|
if (textIsSynthetic(args.text)) return "injected";
|
|
@@ -2848,6 +2720,7 @@ function codexKindsAreHuman(kinds) {
|
|
|
2848
2720
|
return kinds.every((kind) => kind.startsWith("user."));
|
|
2849
2721
|
}
|
|
2850
2722
|
function codexActor(args) {
|
|
2723
|
+
if (args.isFirstUserTurn && args.scriptLaunched) return "injected";
|
|
2851
2724
|
const recorded = codexKindsAreHuman(args.kinds);
|
|
2852
2725
|
if (recorded !== void 0) return recorded ? "human" : "injected";
|
|
2853
2726
|
if ((args.blocks ?? [args.text]).some(isCodexContextBlock)) return "injected";
|
|
@@ -2875,9 +2748,9 @@ var CORRECTIVE_REACTIONS = ["correction", "frustration", "jargon", "structure"];
|
|
|
2875
2748
|
|
|
2876
2749
|
// src/adapters/claude.ts
|
|
2877
2750
|
import { createHash as createHash6 } from "crypto";
|
|
2878
|
-
import { readdir as
|
|
2879
|
-
import { homedir as
|
|
2880
|
-
import { basename as
|
|
2751
|
+
import { readdir as readdir7, stat as stat5 } from "fs/promises";
|
|
2752
|
+
import { homedir as homedir5 } from "os";
|
|
2753
|
+
import { basename as basename4, join as join7, relative as relative3 } from "path";
|
|
2881
2754
|
import {
|
|
2882
2755
|
applyLlmSpanOtlpAttributes as applyLlmSpanOtlpAttributes2,
|
|
2883
2756
|
LLM_CACHED_TOKENS,
|
|
@@ -2886,14 +2759,15 @@ import {
|
|
|
2886
2759
|
LLM_OUTPUT_TOKENS as LLM_OUTPUT_TOKENS2
|
|
2887
2760
|
} from "@tangle-network/agent-eval/trace-attributes";
|
|
2888
2761
|
import { ATTR as ATTR3, deriveHexId as deriveHexId2 } from "@tangle-network/agent-trace-contract";
|
|
2762
|
+
import { claudeRecordIsMachine as claudeRecordIsMachine2, claudeTypedPromptText } from "@tangle-network/harness-sessions";
|
|
2889
2763
|
|
|
2890
2764
|
// src/adapters/claude-subagent-sources.ts
|
|
2891
|
-
import { readdir as
|
|
2892
|
-
import { homedir as
|
|
2893
|
-
import { dirname as dirname3, isAbsolute as isAbsolute2, join as
|
|
2765
|
+
import { readdir as readdir6, realpath } from "fs/promises";
|
|
2766
|
+
import { homedir as homedir4 } from "os";
|
|
2767
|
+
import { dirname as dirname3, isAbsolute as isAbsolute2, join as join6, relative as relative2, resolve as resolve3 } from "path";
|
|
2894
2768
|
|
|
2895
2769
|
// src/adapters/claude-workflow.ts
|
|
2896
|
-
import { basename as
|
|
2770
|
+
import { basename as basename3, dirname as dirname2, isAbsolute, relative, resolve as resolve2 } from "path";
|
|
2897
2771
|
var ClaudeTaskScopeError = class extends Error {
|
|
2898
2772
|
constructor(message) {
|
|
2899
2773
|
super(message);
|
|
@@ -2905,7 +2779,7 @@ function validWorkflowRunReference(runId, transcriptDir) {
|
|
|
2905
2779
|
const cleanRunId = runId.trim();
|
|
2906
2780
|
const cleanTranscriptDir = transcriptDir.trim();
|
|
2907
2781
|
const resolvedTranscriptDir = resolve2(cleanTranscriptDir);
|
|
2908
|
-
if (!cleanRunId || cleanRunId === "." || cleanRunId === ".." || cleanRunId.includes("/") || cleanRunId.includes("\\") || !isAbsolute(cleanTranscriptDir) ||
|
|
2782
|
+
if (!cleanRunId || cleanRunId === "." || cleanRunId === ".." || cleanRunId.includes("/") || cleanRunId.includes("\\") || !isAbsolute(cleanTranscriptDir) || basename3(resolvedTranscriptDir) !== cleanRunId || basename3(dirname2(resolvedTranscriptDir)) !== "workflows" || basename3(dirname2(dirname2(resolvedTranscriptDir))) !== "subagents") {
|
|
2909
2783
|
return void 0;
|
|
2910
2784
|
}
|
|
2911
2785
|
return { runId: cleanRunId, transcriptDir: cleanTranscriptDir };
|
|
@@ -3067,14 +2941,14 @@ async function listSubagentFiles(root, signal, copiedWorkflow = false) {
|
|
|
3067
2941
|
if (!dir) continue;
|
|
3068
2942
|
let entries;
|
|
3069
2943
|
try {
|
|
3070
|
-
entries = await
|
|
2944
|
+
entries = await readdir6(dir, { withFileTypes: true });
|
|
3071
2945
|
} catch (error) {
|
|
3072
2946
|
if (isMissingPathError(error)) continue;
|
|
3073
2947
|
throw error;
|
|
3074
2948
|
}
|
|
3075
2949
|
signal?.throwIfAborted();
|
|
3076
2950
|
for (const entry of entries) {
|
|
3077
|
-
const path =
|
|
2951
|
+
const path = join6(dir, entry.name);
|
|
3078
2952
|
if (copiedWorkflow && entry.isSymbolicLink()) {
|
|
3079
2953
|
throw new ClaudeTaskScopeError(
|
|
3080
2954
|
`Claude Workflow subagent does not match copied source: ${path}`
|
|
@@ -3094,7 +2968,7 @@ function pathIsWithin(root, target) {
|
|
|
3094
2968
|
return path === "" || path !== ".." && !path.startsWith("../") && !path.startsWith("..\\") && !isAbsolute2(path);
|
|
3095
2969
|
}
|
|
3096
2970
|
function workflowStorageRoot(ref) {
|
|
3097
|
-
const nativeRoot = resolve3(
|
|
2971
|
+
const nativeRoot = resolve3(homedir4(), ".claude", "projects");
|
|
3098
2972
|
return pathIsWithin(nativeRoot, ref.path) ? nativeRoot : dirname3(resolve3(ref.path));
|
|
3099
2973
|
}
|
|
3100
2974
|
function relocatedWorkflowDirectory(ref, binding) {
|
|
@@ -3103,10 +2977,10 @@ function relocatedWorkflowDirectory(ref, binding) {
|
|
|
3103
2977
|
const parts = source.split("/").slice(-7);
|
|
3104
2978
|
const sessionId = parts[3];
|
|
3105
2979
|
if (parts[0] !== ".claude" || parts[1] !== "projects" || !parts[2] || !sessionId || !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(sessionId) || parts[4] !== "subagents" || parts[5] !== "workflows" || parts[6] !== binding.runId) return void 0;
|
|
3106
|
-
const sessionRoot2 =
|
|
2980
|
+
const sessionRoot2 = join6(dirname3(resolve3(ref.path)), sessionId);
|
|
3107
2981
|
return {
|
|
3108
2982
|
sessionRoot: sessionRoot2,
|
|
3109
|
-
transcriptDir:
|
|
2983
|
+
transcriptDir: join6(sessionRoot2, "subagents", "workflows", binding.runId)
|
|
3110
2984
|
};
|
|
3111
2985
|
}
|
|
3112
2986
|
async function workflowTranscriptDirectories(ref, bindings) {
|
|
@@ -3179,7 +3053,7 @@ async function workflowTranscriptDirectories(ref, bindings) {
|
|
|
3179
3053
|
);
|
|
3180
3054
|
}
|
|
3181
3055
|
async function collectClaudeSubagentSources(ref, bindings, signal) {
|
|
3182
|
-
const subDir =
|
|
3056
|
+
const subDir = join6(ref.path.replace(/\.jsonl$/, ""), "subagents");
|
|
3183
3057
|
const filesByPath = /* @__PURE__ */ new Map();
|
|
3184
3058
|
const workflowByPath = /* @__PURE__ */ new Map();
|
|
3185
3059
|
for (const file of await listSubagentFiles(subDir, signal)) {
|
|
@@ -3219,7 +3093,7 @@ async function collectClaudeSubagentSources(ref, bindings, signal) {
|
|
|
3219
3093
|
}
|
|
3220
3094
|
|
|
3221
3095
|
// src/adapters/claude.ts
|
|
3222
|
-
var
|
|
3096
|
+
var SERVICE4 = "claude-code";
|
|
3223
3097
|
var AGENT_TOOL_RENAMED_IN = [2, 1, 63];
|
|
3224
3098
|
function modelFacingToolNames(tools, version) {
|
|
3225
3099
|
if (typeof version !== "string" || !atLeast(version, AGENT_TOOL_RENAMED_IN)) return [...tools];
|
|
@@ -3238,10 +3112,9 @@ var RUN_STATUS_ATTR = "run.status";
|
|
|
3238
3112
|
var EPOCH = (/* @__PURE__ */ new Date(0)).toISOString();
|
|
3239
3113
|
var HUMAN_ORIGIN = "human";
|
|
3240
3114
|
var ORIGIN_EVIDENCE_ATTR = "traces.claude.actor_evidence";
|
|
3115
|
+
var MACHINE_RECORD_ATTR = "traces.claude.machine_record";
|
|
3241
3116
|
var ORIGIN_RECORDED_ATTR = "traces.claude.origin_recorded";
|
|
3242
3117
|
var ORIGIN_KIND_ATTR = "traces.claude.origin_kind";
|
|
3243
|
-
var COMMAND_NAME = /<command-name>([\s\S]*?)<\/command-name>/;
|
|
3244
|
-
var COMMAND_ARGS = /<command-args>([\s\S]*?)<\/command-args>/;
|
|
3245
3118
|
var SPAWN_TYPE_KEY = "subagent_type";
|
|
3246
3119
|
var SPAWN_DESCRIPTION_KEY = "description";
|
|
3247
3120
|
var SPAWN_TYPE_ATTR = "traces.claude.subagent_type";
|
|
@@ -3269,7 +3142,7 @@ function normalizeClaudeIds(spans) {
|
|
|
3269
3142
|
function asBlocks(content) {
|
|
3270
3143
|
return Array.isArray(content) ? content : [];
|
|
3271
3144
|
}
|
|
3272
|
-
function
|
|
3145
|
+
function textOf3(content) {
|
|
3273
3146
|
if (typeof content === "string") return capText(content);
|
|
3274
3147
|
return capText(
|
|
3275
3148
|
asBlocks(content).filter((b) => b.type === "text" && typeof b.text === "string").map((b) => b.text).join("\n")
|
|
@@ -3349,7 +3222,7 @@ function projectClaudeEvent(event) {
|
|
|
3349
3222
|
outputTokens: event.message.usage?.output_tokens ?? null,
|
|
3350
3223
|
cachedInputTokens: event.message.usage?.cache_read_input_tokens ?? null,
|
|
3351
3224
|
cacheWriteInputTokens: event.message.usage?.cache_creation_input_tokens ?? null,
|
|
3352
|
-
content:
|
|
3225
|
+
content: textOf3(event.message.content) || null,
|
|
3353
3226
|
contentSource: textSources(event.message, "content"),
|
|
3354
3227
|
parentToolUseId: event.parent_tool_use_id ?? null,
|
|
3355
3228
|
tools
|
|
@@ -3376,7 +3249,7 @@ function projectClaudeEvent(event) {
|
|
|
3376
3249
|
sourceOf(block, "content")
|
|
3377
3250
|
));
|
|
3378
3251
|
}
|
|
3379
|
-
const prompt =
|
|
3252
|
+
const prompt = textOf3(event.message.content);
|
|
3380
3253
|
return {
|
|
3381
3254
|
kind: "user",
|
|
3382
3255
|
timestamp,
|
|
@@ -3386,6 +3259,8 @@ function projectClaudeEvent(event) {
|
|
|
3386
3259
|
...prompt ? {
|
|
3387
3260
|
isSidechain: event.isSidechain === true,
|
|
3388
3261
|
isMeta: event.isMeta === true,
|
|
3262
|
+
isCompactSummary: event.isCompactSummary === true,
|
|
3263
|
+
entrypoint: event.entrypoint ?? null,
|
|
3389
3264
|
userType: event.userType ?? null,
|
|
3390
3265
|
originKind: recordedOrigin(event.origin)
|
|
3391
3266
|
} : {},
|
|
@@ -3406,7 +3281,7 @@ function projectClaudeEvent(event) {
|
|
|
3406
3281
|
};
|
|
3407
3282
|
}
|
|
3408
3283
|
if (event.type === "attachment" && event.attachment?.type === "queued_command") {
|
|
3409
|
-
const prompt =
|
|
3284
|
+
const prompt = textOf3(event.attachment.prompt);
|
|
3410
3285
|
if (prompt) {
|
|
3411
3286
|
return {
|
|
3412
3287
|
kind: "queued-prompt",
|
|
@@ -3435,12 +3310,6 @@ function claudeActorFromOrigin(recorded, fallback) {
|
|
|
3435
3310
|
}
|
|
3436
3311
|
return fallback();
|
|
3437
3312
|
}
|
|
3438
|
-
function typedPromptText(text2) {
|
|
3439
|
-
const name = COMMAND_NAME.exec(text2)?.[1]?.trim();
|
|
3440
|
-
if (!name) return text2;
|
|
3441
|
-
const args = COMMAND_ARGS.exec(text2)?.[1]?.trim();
|
|
3442
|
-
return args ? `${name} ${args}` : name;
|
|
3443
|
-
}
|
|
3444
3313
|
function recordedOrigin(origin) {
|
|
3445
3314
|
const kind = origin?.kind;
|
|
3446
3315
|
return typeof kind === "string" && kind.length > 0 ? kind : null;
|
|
@@ -3512,6 +3381,7 @@ function applyOriginEnrichment(uid, originKind, state) {
|
|
|
3512
3381
|
state.sawRecordedOrigin = true;
|
|
3513
3382
|
prompt.attributes[ORIGIN_RECORDED_ATTR] = true;
|
|
3514
3383
|
prompt.attributes[ORIGIN_KIND_ATTR] = originKind;
|
|
3384
|
+
if (prompt.attributes[MACHINE_RECORD_ATTR] === true) return;
|
|
3515
3385
|
prompt.attributes[ACTOR_ATTR] = claudeActorFromOrigin(
|
|
3516
3386
|
originKind,
|
|
3517
3387
|
() => prompt.attributes[ACTOR_ATTR]
|
|
@@ -3554,7 +3424,7 @@ function consumeClaudeEvent(event, uid, ctx, state) {
|
|
|
3554
3424
|
name: "session.compacted",
|
|
3555
3425
|
kind: "CHAIN",
|
|
3556
3426
|
startTime: event.timestamp,
|
|
3557
|
-
service:
|
|
3427
|
+
service: SERVICE4,
|
|
3558
3428
|
agent: ctx.agent,
|
|
3559
3429
|
step: state.step++,
|
|
3560
3430
|
extra
|
|
@@ -3573,7 +3443,7 @@ function consumeClaudeEvent(event, uid, ctx, state) {
|
|
|
3573
3443
|
name: "llm.turn",
|
|
3574
3444
|
kind: "LLM",
|
|
3575
3445
|
startTime: event.timestamp,
|
|
3576
|
-
service:
|
|
3446
|
+
service: SERVICE4,
|
|
3577
3447
|
agent: ctx.agent,
|
|
3578
3448
|
model: event.model,
|
|
3579
3449
|
inputTokens: event.inputTokens,
|
|
@@ -3626,7 +3496,7 @@ function consumeClaudeEvent(event, uid, ctx, state) {
|
|
|
3626
3496
|
name: `tool.${tool.name}`,
|
|
3627
3497
|
kind: "TOOL",
|
|
3628
3498
|
startTime: event.timestamp,
|
|
3629
|
-
service:
|
|
3499
|
+
service: SERVICE4,
|
|
3630
3500
|
agent: ctx.agent,
|
|
3631
3501
|
tool: tool.name,
|
|
3632
3502
|
step: state.step,
|
|
@@ -3645,10 +3515,15 @@ function consumeClaudeEvent(event, uid, ctx, state) {
|
|
|
3645
3515
|
}
|
|
3646
3516
|
} else if (event.kind === "user") {
|
|
3647
3517
|
if (event.prompt) {
|
|
3648
|
-
const
|
|
3518
|
+
const machine = claudeRecordIsMachine2({
|
|
3519
|
+
text: event.prompt,
|
|
3520
|
+
isMeta: event.isMeta,
|
|
3521
|
+
isCompactSummary: event.isCompactSummary,
|
|
3522
|
+
entrypoint: event.entrypoint ?? void 0
|
|
3523
|
+
});
|
|
3524
|
+
const actor = machine ? "injected" : claudeActorFromOrigin(event.originKind, () => claudeActor({
|
|
3649
3525
|
text: event.prompt ?? "",
|
|
3650
3526
|
isSidechain: event.isSidechain,
|
|
3651
|
-
isMeta: event.isMeta,
|
|
3652
3527
|
userType: event.userType ?? null,
|
|
3653
3528
|
isFirstUserTurn: !state.sawUserTurn
|
|
3654
3529
|
}));
|
|
@@ -3660,13 +3535,14 @@ function consumeClaudeEvent(event, uid, ctx, state) {
|
|
|
3660
3535
|
spanId: `${ctx.idPrefix}${uid}:user`,
|
|
3661
3536
|
parentSpanId: userParent ? userParent.span.span_id : ctx.rootParent,
|
|
3662
3537
|
startTime: event.timestamp,
|
|
3663
|
-
service:
|
|
3538
|
+
service: SERVICE4,
|
|
3664
3539
|
agent: ctx.agent,
|
|
3665
3540
|
step: state.step,
|
|
3666
|
-
content: actor === "human" ?
|
|
3541
|
+
content: actor === "human" ? claudeTypedPromptText(event.prompt) : event.prompt,
|
|
3667
3542
|
contentSource: event.contentSource,
|
|
3668
3543
|
actor
|
|
3669
3544
|
});
|
|
3545
|
+
if (machine) prompt.attributes[MACHINE_RECORD_ATTR] = true;
|
|
3670
3546
|
if (event.originKind) {
|
|
3671
3547
|
prompt.attributes[ORIGIN_RECORDED_ATTR] = true;
|
|
3672
3548
|
prompt.attributes[ORIGIN_KIND_ATTR] = event.originKind;
|
|
@@ -3683,21 +3559,23 @@ function consumeClaudeEvent(event, uid, ctx, state) {
|
|
|
3683
3559
|
} else if (event.kind === "attachment") {
|
|
3684
3560
|
backfillResult(state.toolSpanByUseId.get(event.result.toolUseId), event.timestamp, event.result);
|
|
3685
3561
|
} else if (event.kind === "queued-prompt") {
|
|
3686
|
-
const
|
|
3562
|
+
const machine = claudeRecordIsMachine2({ text: event.prompt });
|
|
3563
|
+
const actor = machine ? "injected" : claudeActorFromOrigin(event.originKind, () => "injected");
|
|
3687
3564
|
if (event.originKind) state.sawRecordedOrigin = true;
|
|
3688
3565
|
const prompt = userPromptSpan({
|
|
3689
3566
|
traceId: ctx.traceId,
|
|
3690
3567
|
spanId: `${ctx.idPrefix}${uid}:queued`,
|
|
3691
3568
|
parentSpanId: ctx.rootParent,
|
|
3692
3569
|
startTime: event.timestamp,
|
|
3693
|
-
service:
|
|
3570
|
+
service: SERVICE4,
|
|
3694
3571
|
agent: ctx.agent,
|
|
3695
3572
|
step: state.step,
|
|
3696
|
-
content: actor === "human" ?
|
|
3573
|
+
content: actor === "human" ? claudeTypedPromptText(event.prompt) : event.prompt,
|
|
3697
3574
|
contentSource: event.contentSource,
|
|
3698
3575
|
actor
|
|
3699
3576
|
});
|
|
3700
3577
|
prompt.attributes["traces.claude.queued"] = true;
|
|
3578
|
+
if (machine) prompt.attributes[MACHINE_RECORD_ATTR] = true;
|
|
3701
3579
|
if (event.originKind) {
|
|
3702
3580
|
prompt.attributes[ORIGIN_RECORDED_ATTR] = true;
|
|
3703
3581
|
prompt.attributes[ORIGIN_KIND_ATTR] = event.originKind;
|
|
@@ -3715,7 +3593,7 @@ function consumeClaudeEvent(event, uid, ctx, state) {
|
|
|
3715
3593
|
kind: "CHAIN",
|
|
3716
3594
|
startTime: event.timestamp,
|
|
3717
3595
|
endTime: event.timestamp,
|
|
3718
|
-
service:
|
|
3596
|
+
service: SERVICE4,
|
|
3719
3597
|
agent: ctx.agent,
|
|
3720
3598
|
step: state.step,
|
|
3721
3599
|
extra: toolIoAttributes({ input: { path: event.path }, inputSource: event.contentSource })
|
|
@@ -3738,7 +3616,7 @@ function mergeAssistantMessage(event, messageId, llmSpan, ctx, state) {
|
|
|
3738
3616
|
kind: "CHAIN",
|
|
3739
3617
|
startTime: event.timestamp,
|
|
3740
3618
|
endTime: event.timestamp,
|
|
3741
|
-
service:
|
|
3619
|
+
service: SERVICE4,
|
|
3742
3620
|
agent: ctx.agent,
|
|
3743
3621
|
step: state.step,
|
|
3744
3622
|
content: typeof llmSpan.attributes.content === "string" ? llmSpan.attributes.content : "",
|
|
@@ -3923,7 +3801,7 @@ var ClaudeAdapter = class {
|
|
|
3923
3801
|
harness = "claude-code";
|
|
3924
3802
|
aliases = ["claude", "claudish", "openclaw", "nanoclaw"];
|
|
3925
3803
|
root() {
|
|
3926
|
-
return
|
|
3804
|
+
return join7(homedir5(), ".claude", "projects");
|
|
3927
3805
|
}
|
|
3928
3806
|
async sourcePaths(ref, options = {}) {
|
|
3929
3807
|
const bindings = await readClaudeWorkflowBindings(ref, options);
|
|
@@ -3933,7 +3811,7 @@ var ClaudeAdapter = class {
|
|
|
3933
3811
|
options.signal?.throwIfAborted();
|
|
3934
3812
|
const path = file.replace(/\.jsonl$/, ".meta.json");
|
|
3935
3813
|
try {
|
|
3936
|
-
const source = await
|
|
3814
|
+
const source = await stat5(path);
|
|
3937
3815
|
if (source.isFile()) metadataFiles.push(path);
|
|
3938
3816
|
} catch (error) {
|
|
3939
3817
|
if (!isMissingPathError(error)) throw error;
|
|
@@ -3946,27 +3824,27 @@ var ClaudeAdapter = class {
|
|
|
3946
3824
|
const root = this.root();
|
|
3947
3825
|
let projectDirs;
|
|
3948
3826
|
try {
|
|
3949
|
-
projectDirs = await
|
|
3827
|
+
projectDirs = await readdir7(root);
|
|
3950
3828
|
} catch (error) {
|
|
3951
3829
|
if (isMissingPathError(error)) return [];
|
|
3952
3830
|
throw error;
|
|
3953
3831
|
}
|
|
3954
3832
|
const refs = [];
|
|
3955
3833
|
for (const dir of projectDirs) {
|
|
3956
|
-
const dirPath =
|
|
3834
|
+
const dirPath = join7(root, dir);
|
|
3957
3835
|
let files;
|
|
3958
3836
|
try {
|
|
3959
|
-
files = await
|
|
3837
|
+
files = await readdir7(dirPath);
|
|
3960
3838
|
} catch (error) {
|
|
3961
3839
|
if (isMissingPathError(error)) continue;
|
|
3962
3840
|
throw error;
|
|
3963
3841
|
}
|
|
3964
3842
|
for (const f of files) {
|
|
3965
3843
|
if (!f.endsWith(".jsonl")) continue;
|
|
3966
|
-
const path =
|
|
3844
|
+
const path = join7(dirPath, f);
|
|
3967
3845
|
let st;
|
|
3968
3846
|
try {
|
|
3969
|
-
st = await
|
|
3847
|
+
st = await stat5(path);
|
|
3970
3848
|
} catch (error) {
|
|
3971
3849
|
if (isMissingPathError(error)) continue;
|
|
3972
3850
|
throw error;
|
|
@@ -3975,7 +3853,7 @@ var ClaudeAdapter = class {
|
|
|
3975
3853
|
if (opts.sinceMs && st.mtimeMs < opts.sinceMs) continue;
|
|
3976
3854
|
const cwd = `/${dir.replace(/^-/, "").replace(/-/g, "/")}`;
|
|
3977
3855
|
if (opts.cwd && !cwd.startsWith(opts.cwd)) continue;
|
|
3978
|
-
refs.push({ harness: this.harness, sessionId:
|
|
3856
|
+
refs.push({ harness: this.harness, sessionId: basename4(f, ".jsonl"), path, cwd, mtimeMs: st.mtimeMs });
|
|
3979
3857
|
}
|
|
3980
3858
|
}
|
|
3981
3859
|
return refs.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
@@ -3990,7 +3868,7 @@ var ClaudeAdapter = class {
|
|
|
3990
3868
|
const sourceRootId = `root:${sourceTraceId}`;
|
|
3991
3869
|
const ctx = {
|
|
3992
3870
|
traceId: sourceTraceId,
|
|
3993
|
-
agent:
|
|
3871
|
+
agent: SERVICE4,
|
|
3994
3872
|
startStep: 0,
|
|
3995
3873
|
idPrefix: "",
|
|
3996
3874
|
rootParent: sourceRootId
|
|
@@ -4004,10 +3882,12 @@ var ClaudeAdapter = class {
|
|
|
4004
3882
|
let active = taskScope === "all";
|
|
4005
3883
|
let offeredTools;
|
|
4006
3884
|
let runResult;
|
|
3885
|
+
let recordedCwd;
|
|
4007
3886
|
for await (const event of readJsonl(ref.path, sessionJsonlOptions(ref, options))) {
|
|
4008
3887
|
options.signal?.throwIfAborted();
|
|
4009
3888
|
const eventSessionId = event.sessionId ?? event.session_id;
|
|
4010
3889
|
if (!discoveredTraceId && eventSessionId) discoveredTraceId = eventSessionId;
|
|
3890
|
+
if (!recordedCwd && typeof event.cwd === "string" && event.cwd.length > 0) recordedCwd = event.cwd;
|
|
4011
3891
|
if (event.type === "system" && event.subtype === "init" && Array.isArray(event.tools)) {
|
|
4012
3892
|
offeredTools = modelFacingToolNames(
|
|
4013
3893
|
event.tools.filter((t) => typeof t === "string"),
|
|
@@ -4071,12 +3951,13 @@ var ClaudeAdapter = class {
|
|
|
4071
3951
|
name: "session",
|
|
4072
3952
|
kind: "AGENT",
|
|
4073
3953
|
startTime: (/* @__PURE__ */ new Date(0)).toISOString(),
|
|
4074
|
-
service:
|
|
4075
|
-
agent:
|
|
3954
|
+
service: SERVICE4,
|
|
3955
|
+
agent: SERVICE4
|
|
4076
3956
|
});
|
|
4077
3957
|
if (offeredTools) {
|
|
4078
3958
|
root.attributes[TOOL_DEFINITIONS_ATTR] = JSON.stringify(offeredTools.map((name) => ({ type: "function", name })));
|
|
4079
3959
|
}
|
|
3960
|
+
if (recordedCwd) root.attributes[SESSION_CWD_ATTR] = recordedCwd;
|
|
4080
3961
|
if (runResult) {
|
|
4081
3962
|
root.attributes[RUN_STATUS_ATTR] = runResult.failed ? "failed" : "completed";
|
|
4082
3963
|
root.status = runResult.failed ? { code: "ERROR", message: `the run ended with ${runResult.subtype}` } : { code: "OK" };
|
|
@@ -4105,7 +3986,7 @@ var ClaudeAdapter = class {
|
|
|
4105
3986
|
}
|
|
4106
3987
|
/** Parse nested `subagents/.../agent-*.jsonl`, parenting each under its Agent call. */
|
|
4107
3988
|
async foldSubagents(ref, traceId, main, workflowBindings, out, options) {
|
|
4108
|
-
const subDir =
|
|
3989
|
+
const subDir = join7(ref.path.replace(/\.jsonl$/, ""), "subagents");
|
|
4109
3990
|
const sources = await collectClaudeSubagentSources(
|
|
4110
3991
|
ref,
|
|
4111
3992
|
workflowBindings,
|
|
@@ -4129,7 +4010,7 @@ var ClaudeAdapter = class {
|
|
|
4129
4010
|
const agent = {
|
|
4130
4011
|
file,
|
|
4131
4012
|
sourceKey,
|
|
4132
|
-
agentId:
|
|
4013
|
+
agentId: basename4(file, ".jsonl").replace(/^agent-/, ""),
|
|
4133
4014
|
meta,
|
|
4134
4015
|
...meta.toolUseId ? { parentToolUseId: meta.toolUseId } : {}
|
|
4135
4016
|
};
|
|
@@ -4143,7 +4024,7 @@ var ClaudeAdapter = class {
|
|
|
4143
4024
|
);
|
|
4144
4025
|
const parsed = await parseClaudeSubagent(ref, traceId, agent, options);
|
|
4145
4026
|
const { binding, confidence } = selectWorkflowBinding(
|
|
4146
|
-
|
|
4027
|
+
basename4(file),
|
|
4147
4028
|
runBindings,
|
|
4148
4029
|
firstSubagentTimestamp(agent, parsed),
|
|
4149
4030
|
meta.toolUseId
|
|
@@ -4261,7 +4142,7 @@ function subagentLifecycleSpan(agent, parsed, traceId, parentSpanId, task, subDi
|
|
|
4261
4142
|
// The transcript records what the child did, not whether the parent
|
|
4262
4143
|
// accepted it; the parent's own tool span carries that status.
|
|
4263
4144
|
status: "UNSET",
|
|
4264
|
-
service:
|
|
4145
|
+
service: SERVICE4,
|
|
4265
4146
|
agent: agent.meta.agentType ? `subagent:${agent.meta.agentType}` : "subagent",
|
|
4266
4147
|
extra: {
|
|
4267
4148
|
...toolIoAttributes({
|
|
@@ -4710,11 +4591,11 @@ async function resolveCodexParentTask(ref, childSessionId, options = {}) {
|
|
|
4710
4591
|
}
|
|
4711
4592
|
|
|
4712
4593
|
// src/adapters/codex.ts
|
|
4713
|
-
import { readdir as
|
|
4714
|
-
import { homedir as
|
|
4715
|
-
import { basename as
|
|
4594
|
+
import { readdir as readdir8, stat as stat6 } from "fs/promises";
|
|
4595
|
+
import { homedir as homedir6 } from "os";
|
|
4596
|
+
import { basename as basename5, join as join8 } from "path";
|
|
4716
4597
|
import { deriveHexId as deriveHexId3 } from "@tangle-network/agent-trace-contract";
|
|
4717
|
-
var
|
|
4598
|
+
var SERVICE5 = "codex";
|
|
4718
4599
|
var SESSION_HEAD_LINES = 40;
|
|
4719
4600
|
var MAX_INHERITED_SPANS = 200;
|
|
4720
4601
|
var CODEX_SOURCE_TRACE_ID = "traces.codex.source_trace_id";
|
|
@@ -4730,6 +4611,9 @@ var CODEX_TOTAL_TOKENS_SOURCE = "codex.token_count.info.total_token_usage";
|
|
|
4730
4611
|
function reportedCount(value) {
|
|
4731
4612
|
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
|
|
4732
4613
|
}
|
|
4614
|
+
function nonEmpty(value) {
|
|
4615
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
4616
|
+
}
|
|
4733
4617
|
function normalizeCodexIds(spans) {
|
|
4734
4618
|
for (const item of spans) {
|
|
4735
4619
|
const sourceTraceId = item.trace_id;
|
|
@@ -4755,7 +4639,7 @@ function tokenUsageSignature(usage) {
|
|
|
4755
4639
|
usage.total_tokens ?? null
|
|
4756
4640
|
]);
|
|
4757
4641
|
}
|
|
4758
|
-
function
|
|
4642
|
+
function textOf4(content) {
|
|
4759
4643
|
return capText(contentToString(content));
|
|
4760
4644
|
}
|
|
4761
4645
|
function numericStatus(value) {
|
|
@@ -5029,9 +4913,9 @@ function itemCountsJson(counts) {
|
|
|
5029
4913
|
}
|
|
5030
4914
|
function placeInnerSpans(pending, windows) {
|
|
5031
4915
|
return pending.map((entry) => {
|
|
5032
|
-
const { parent, join:
|
|
4916
|
+
const { parent, join: join35 } = joinItem(windows, entry.startMs, entry.endMs);
|
|
5033
4917
|
if (parent) entry.span.parent_span_id = parent.span_id;
|
|
5034
|
-
entry.span.attributes["traces.codex.item_join"] =
|
|
4918
|
+
entry.span.attributes["traces.codex.item_join"] = join35;
|
|
5035
4919
|
return entry.span;
|
|
5036
4920
|
});
|
|
5037
4921
|
}
|
|
@@ -5047,8 +4931,8 @@ function commandSpan(context, item, command, recordTime) {
|
|
|
5047
4931
|
startTime: start,
|
|
5048
4932
|
status: status2.code,
|
|
5049
4933
|
statusMessage: status2.message,
|
|
5050
|
-
service:
|
|
5051
|
-
agent:
|
|
4934
|
+
service: SERVICE5,
|
|
4935
|
+
agent: SERVICE5,
|
|
5052
4936
|
extra: {
|
|
5053
4937
|
...toolIoAttributes({
|
|
5054
4938
|
input: { command: command.command, ...command.cwd ? { cwd: command.cwd } : {} },
|
|
@@ -5080,8 +4964,8 @@ function fileChangeSpans(context, item, fileChange, recordTime) {
|
|
|
5080
4964
|
startTime: start,
|
|
5081
4965
|
status: status2.code,
|
|
5082
4966
|
statusMessage: status2.message,
|
|
5083
|
-
service:
|
|
5084
|
-
agent:
|
|
4967
|
+
service: SERVICE5,
|
|
4968
|
+
agent: SERVICE5,
|
|
5085
4969
|
extra: {
|
|
5086
4970
|
...toolIoAttributes({
|
|
5087
4971
|
input: { path: change.path, kind: change.kind, ...change.movePath ? { move_path: change.movePath } : {} },
|
|
@@ -5129,40 +5013,40 @@ function isExpectedBlockingTool(name, input) {
|
|
|
5129
5013
|
async function* walkRollouts(root) {
|
|
5130
5014
|
let years;
|
|
5131
5015
|
try {
|
|
5132
|
-
years = await
|
|
5016
|
+
years = await readdir8(root);
|
|
5133
5017
|
} catch (error) {
|
|
5134
5018
|
if (isMissingPathError(error)) return;
|
|
5135
5019
|
throw error;
|
|
5136
5020
|
}
|
|
5137
5021
|
for (const y of years) {
|
|
5138
|
-
const yp =
|
|
5022
|
+
const yp = join8(root, y);
|
|
5139
5023
|
let months;
|
|
5140
5024
|
try {
|
|
5141
|
-
months = await
|
|
5025
|
+
months = await readdir8(yp);
|
|
5142
5026
|
} catch (error) {
|
|
5143
5027
|
if (isMissingPathError(error)) continue;
|
|
5144
5028
|
throw error;
|
|
5145
5029
|
}
|
|
5146
5030
|
for (const m of months) {
|
|
5147
|
-
const mp =
|
|
5031
|
+
const mp = join8(yp, m);
|
|
5148
5032
|
let days;
|
|
5149
5033
|
try {
|
|
5150
|
-
days = await
|
|
5034
|
+
days = await readdir8(mp);
|
|
5151
5035
|
} catch (error) {
|
|
5152
5036
|
if (isMissingPathError(error)) continue;
|
|
5153
5037
|
throw error;
|
|
5154
5038
|
}
|
|
5155
5039
|
for (const d of days) {
|
|
5156
|
-
const dp =
|
|
5040
|
+
const dp = join8(mp, d);
|
|
5157
5041
|
let files;
|
|
5158
5042
|
try {
|
|
5159
|
-
files = await
|
|
5043
|
+
files = await readdir8(dp);
|
|
5160
5044
|
} catch (error) {
|
|
5161
5045
|
if (isMissingPathError(error)) continue;
|
|
5162
5046
|
throw error;
|
|
5163
5047
|
}
|
|
5164
5048
|
for (const f of files) {
|
|
5165
|
-
if (f.startsWith("rollout-") && f.endsWith(".jsonl")) yield
|
|
5049
|
+
if (f.startsWith("rollout-") && f.endsWith(".jsonl")) yield join8(dp, f);
|
|
5166
5050
|
}
|
|
5167
5051
|
}
|
|
5168
5052
|
}
|
|
@@ -5172,19 +5056,19 @@ var CodexAdapter = class {
|
|
|
5172
5056
|
harness = "codex";
|
|
5173
5057
|
aliases = ["codex-acp"];
|
|
5174
5058
|
root() {
|
|
5175
|
-
return process.env.CODEX_HOME ?
|
|
5059
|
+
return process.env.CODEX_HOME ? join8(process.env.CODEX_HOME, "sessions") : join8(homedir6(), ".codex", "sessions");
|
|
5176
5060
|
}
|
|
5177
5061
|
async refFromPath(path, opts) {
|
|
5178
5062
|
let st;
|
|
5179
5063
|
try {
|
|
5180
|
-
st = await
|
|
5064
|
+
st = await stat6(path);
|
|
5181
5065
|
} catch (error) {
|
|
5182
5066
|
if (isMissingPathError(error)) return void 0;
|
|
5183
5067
|
throw error;
|
|
5184
5068
|
}
|
|
5185
5069
|
if (opts.sinceMs && st.mtimeMs < opts.sinceMs) return void 0;
|
|
5186
5070
|
let cwd = null;
|
|
5187
|
-
let id =
|
|
5071
|
+
let id = basename5(path).replace(/^rollout-[\dT-]+-/, "").replace(/\.jsonl$/, "");
|
|
5188
5072
|
const ref = { harness: this.harness, sessionId: id, path, cwd, mtimeMs: st.mtimeMs };
|
|
5189
5073
|
const head = await takeJsonl(path, SESSION_HEAD_LINES, sessionJsonlOptions(ref));
|
|
5190
5074
|
for (const parsed of head) {
|
|
@@ -5212,7 +5096,7 @@ var CodexAdapter = class {
|
|
|
5212
5096
|
async locateBySessionId(sessionId, opts = {}) {
|
|
5213
5097
|
const refs = [];
|
|
5214
5098
|
for await (const path of walkRollouts(this.root())) {
|
|
5215
|
-
if (!
|
|
5099
|
+
if (!basename5(path).includes(sessionId)) continue;
|
|
5216
5100
|
const ref = await this.refFromPath(path, opts);
|
|
5217
5101
|
if (ref?.sessionId === sessionId) refs.push(ref);
|
|
5218
5102
|
}
|
|
@@ -5307,6 +5191,8 @@ var CodexAdapter = class {
|
|
|
5307
5191
|
}
|
|
5308
5192
|
}
|
|
5309
5193
|
const traceId = meta?.payload?.id ?? ref.sessionId;
|
|
5194
|
+
const recordedCwd = nonEmpty(meta?.payload?.cwd);
|
|
5195
|
+
const scriptLaunched = meta?.payload?.originator === "codex_exec" || meta?.payload?.source === "exec";
|
|
5310
5196
|
const sessionRole = parentSessionId || meta?.payload?.thread_source === "subagent" ? "child" : "operator";
|
|
5311
5197
|
const rootId = `root:${traceId}`;
|
|
5312
5198
|
const root = span({
|
|
@@ -5316,14 +5202,15 @@ var CodexAdapter = class {
|
|
|
5316
5202
|
name: "session",
|
|
5317
5203
|
kind: "AGENT",
|
|
5318
5204
|
startTime: selectedBoundary?.timestamp ?? meta?.timestamp ?? first?.timestamp ?? (/* @__PURE__ */ new Date(0)).toISOString(),
|
|
5319
|
-
service:
|
|
5320
|
-
agent:
|
|
5205
|
+
service: SERVICE5,
|
|
5206
|
+
agent: SERVICE5,
|
|
5321
5207
|
model,
|
|
5322
5208
|
status: "UNSET",
|
|
5323
5209
|
extra: {
|
|
5324
5210
|
// Keep the source session identity searchable after the wire id is normalized.
|
|
5325
5211
|
"tangle.sessionId": traceId,
|
|
5326
5212
|
"traces.session.role": sessionRole,
|
|
5213
|
+
...recordedCwd ? { [SESSION_CWD_ATTR]: recordedCwd } : {},
|
|
5327
5214
|
"traces.codex.task_scope": options.taskScope === "latest" ? "latest" : options.taskScope === "turn" ? "turn" : forkBoundary ? "fork-current" : "all",
|
|
5328
5215
|
...selectedBoundary?.turnId ? { "traces.codex.turn_id": selectedBoundary.turnId } : {},
|
|
5329
5216
|
...parentSessionId ? { "traces.parent_session_id": parentSessionId } : {},
|
|
@@ -5373,7 +5260,7 @@ var CodexAdapter = class {
|
|
|
5373
5260
|
recorded.span.attributes[USER_MESSAGE_EVENT_ATTR] = true;
|
|
5374
5261
|
return;
|
|
5375
5262
|
}
|
|
5376
|
-
const actor = sessionRole === "child" ? "agent" : codexActor({ text: prompt, isFirstUserTurn: !sawUserTurn });
|
|
5263
|
+
const actor = sessionRole === "child" ? "agent" : codexActor({ text: prompt, isFirstUserTurn: !sawUserTurn, scriptLaunched });
|
|
5377
5264
|
sawUserTurn = true;
|
|
5378
5265
|
const turnSpan = userPromptSpan({
|
|
5379
5266
|
traceId,
|
|
@@ -5382,8 +5269,8 @@ var CodexAdapter = class {
|
|
|
5382
5269
|
startTime: ts,
|
|
5383
5270
|
content: prompt,
|
|
5384
5271
|
contentSource,
|
|
5385
|
-
service:
|
|
5386
|
-
agent:
|
|
5272
|
+
service: SERVICE5,
|
|
5273
|
+
agent: SERVICE5,
|
|
5387
5274
|
step,
|
|
5388
5275
|
actor
|
|
5389
5276
|
});
|
|
@@ -5408,7 +5295,7 @@ var CodexAdapter = class {
|
|
|
5408
5295
|
if (inheritedTurnKeys.has(dedupKey)) return;
|
|
5409
5296
|
if (!claimInheritedSpan()) return;
|
|
5410
5297
|
inheritedTurnKeys.add(dedupKey);
|
|
5411
|
-
const actor = codexActor({ text: prompt, blocks, isFirstUserTurn: !sawInheritedTurn, kinds });
|
|
5298
|
+
const actor = codexActor({ text: prompt, blocks, isFirstUserTurn: !sawInheritedTurn, kinds, scriptLaunched });
|
|
5412
5299
|
sawInheritedTurn = true;
|
|
5413
5300
|
const turnSpan = userPromptSpan({
|
|
5414
5301
|
traceId,
|
|
@@ -5417,8 +5304,8 @@ var CodexAdapter = class {
|
|
|
5417
5304
|
startTime: ts,
|
|
5418
5305
|
content: prompt,
|
|
5419
5306
|
contentSource,
|
|
5420
|
-
service:
|
|
5421
|
-
agent:
|
|
5307
|
+
service: SERVICE5,
|
|
5308
|
+
agent: SERVICE5,
|
|
5422
5309
|
step,
|
|
5423
5310
|
actor
|
|
5424
5311
|
});
|
|
@@ -5440,8 +5327,8 @@ var CodexAdapter = class {
|
|
|
5440
5327
|
name: "session.compacted",
|
|
5441
5328
|
kind: "CHAIN",
|
|
5442
5329
|
startTime: ts,
|
|
5443
|
-
service:
|
|
5444
|
-
agent:
|
|
5330
|
+
service: SERVICE5,
|
|
5331
|
+
agent: SERVICE5,
|
|
5445
5332
|
step,
|
|
5446
5333
|
content: summary,
|
|
5447
5334
|
contentSource: textSources(payload, "message"),
|
|
@@ -5527,8 +5414,8 @@ var CodexAdapter = class {
|
|
|
5527
5414
|
name: "subagent.lifecycle",
|
|
5528
5415
|
kind: "AGENT",
|
|
5529
5416
|
startTime: eventTime2,
|
|
5530
|
-
service:
|
|
5531
|
-
agent:
|
|
5417
|
+
service: SERVICE5,
|
|
5418
|
+
agent: SERVICE5,
|
|
5532
5419
|
step,
|
|
5533
5420
|
status: "UNSET",
|
|
5534
5421
|
extra: {
|
|
@@ -5606,8 +5493,8 @@ var CodexAdapter = class {
|
|
|
5606
5493
|
name: "llm.turn",
|
|
5607
5494
|
kind: "LLM",
|
|
5608
5495
|
startTime: ts,
|
|
5609
|
-
service:
|
|
5610
|
-
agent:
|
|
5496
|
+
service: SERVICE5,
|
|
5497
|
+
agent: SERVICE5,
|
|
5611
5498
|
model,
|
|
5612
5499
|
inputTokens: u.input_tokens ?? null,
|
|
5613
5500
|
outputTokens: u.output_tokens ?? null,
|
|
@@ -5639,8 +5526,8 @@ var CodexAdapter = class {
|
|
|
5639
5526
|
kind: "TOOL",
|
|
5640
5527
|
startTime: ts,
|
|
5641
5528
|
status: "UNSET",
|
|
5642
|
-
service:
|
|
5643
|
-
agent:
|
|
5529
|
+
service: SERVICE5,
|
|
5530
|
+
agent: SERVICE5,
|
|
5644
5531
|
tool: name,
|
|
5645
5532
|
step,
|
|
5646
5533
|
extra: {
|
|
@@ -5750,7 +5637,7 @@ var CodexAdapter = class {
|
|
|
5750
5637
|
]);
|
|
5751
5638
|
if (seenAgentMessages.has(messageIdentity)) continue;
|
|
5752
5639
|
seenAgentMessages.add(messageIdentity);
|
|
5753
|
-
const text2 =
|
|
5640
|
+
const text2 = textOf4(l.payload.content);
|
|
5754
5641
|
const author = l.payload.author;
|
|
5755
5642
|
const recipient = l.payload.recipient;
|
|
5756
5643
|
const messageType = /^Message Type:\s*FINAL_ANSWER\b/m.test(text2) ? "final" : /^Message Type:\s*MESSAGE\b/m.test(text2) ? "progress" : "unknown";
|
|
@@ -5763,8 +5650,8 @@ var CodexAdapter = class {
|
|
|
5763
5650
|
name: `message.agent.${messageType}`,
|
|
5764
5651
|
kind: "CHAIN",
|
|
5765
5652
|
startTime: ts,
|
|
5766
|
-
service:
|
|
5767
|
-
agent:
|
|
5653
|
+
service: SERVICE5,
|
|
5654
|
+
agent: SERVICE5,
|
|
5768
5655
|
step,
|
|
5769
5656
|
content: text2,
|
|
5770
5657
|
contentSource: textSources(l.payload, "content"),
|
|
@@ -5799,6 +5686,7 @@ var CodexAdapter = class {
|
|
|
5799
5686
|
text: prompt,
|
|
5800
5687
|
blocks: contentTextBlocks(l.payload.content),
|
|
5801
5688
|
isFirstUserTurn: !sawUserTurn,
|
|
5689
|
+
scriptLaunched,
|
|
5802
5690
|
kinds: l.payload.internal_chat_message_metadata_passthrough?.content_item_kinds
|
|
5803
5691
|
});
|
|
5804
5692
|
sawUserTurn = true;
|
|
@@ -5809,8 +5697,8 @@ var CodexAdapter = class {
|
|
|
5809
5697
|
startTime: ts,
|
|
5810
5698
|
content: prompt,
|
|
5811
5699
|
contentSource: textSources(l.payload, "content"),
|
|
5812
|
-
service:
|
|
5813
|
-
agent:
|
|
5700
|
+
service: SERVICE5,
|
|
5701
|
+
agent: SERVICE5,
|
|
5814
5702
|
step,
|
|
5815
5703
|
actor
|
|
5816
5704
|
});
|
|
@@ -5819,7 +5707,7 @@ var CodexAdapter = class {
|
|
|
5819
5707
|
step += 1;
|
|
5820
5708
|
}
|
|
5821
5709
|
} else if (l.type === "response_item" && l.payload?.type === "message") {
|
|
5822
|
-
const text2 =
|
|
5710
|
+
const text2 = textOf4(l.payload.content);
|
|
5823
5711
|
if (text2) {
|
|
5824
5712
|
spans.push(
|
|
5825
5713
|
span({
|
|
@@ -5829,8 +5717,8 @@ var CodexAdapter = class {
|
|
|
5829
5717
|
name: `message.${l.payload.role ?? "unknown"}`,
|
|
5830
5718
|
kind: "CHAIN",
|
|
5831
5719
|
startTime: ts,
|
|
5832
|
-
service:
|
|
5833
|
-
agent:
|
|
5720
|
+
service: SERVICE5,
|
|
5721
|
+
agent: SERVICE5,
|
|
5834
5722
|
step,
|
|
5835
5723
|
content: text2,
|
|
5836
5724
|
contentSource: textSources(l.payload, "content")
|
|
@@ -5888,14 +5776,14 @@ var CodexAdapter = class {
|
|
|
5888
5776
|
};
|
|
5889
5777
|
|
|
5890
5778
|
// src/adapters/opencode.ts
|
|
5891
|
-
import { readdir as
|
|
5892
|
-
import { homedir as
|
|
5893
|
-
import { join as
|
|
5894
|
-
var
|
|
5779
|
+
import { readdir as readdir9, stat as stat7 } from "fs/promises";
|
|
5780
|
+
import { homedir as homedir7 } from "os";
|
|
5781
|
+
import { join as join9 } from "path";
|
|
5782
|
+
var SERVICE6 = "opencode";
|
|
5895
5783
|
function isoOf(ms2) {
|
|
5896
5784
|
return new Date(ms2 ?? 0).toISOString();
|
|
5897
5785
|
}
|
|
5898
|
-
function
|
|
5786
|
+
function textOf5(parts) {
|
|
5899
5787
|
return capText(
|
|
5900
5788
|
parts.filter((p) => p.type === "text" && typeof p.text === "string").map((p) => p.text).join("\n")
|
|
5901
5789
|
);
|
|
@@ -5903,24 +5791,24 @@ function textOf6(parts) {
|
|
|
5903
5791
|
var OpencodeAdapter = class {
|
|
5904
5792
|
harness = "opencode";
|
|
5905
5793
|
storage() {
|
|
5906
|
-
const base = process.env.XDG_DATA_HOME ??
|
|
5907
|
-
return
|
|
5794
|
+
const base = process.env.XDG_DATA_HOME ?? join9(homedir7(), ".local", "share");
|
|
5795
|
+
return join9(base, "opencode", "storage");
|
|
5908
5796
|
}
|
|
5909
5797
|
async locate(opts = {}) {
|
|
5910
|
-
const msgRoot =
|
|
5798
|
+
const msgRoot = join9(this.storage(), "message");
|
|
5911
5799
|
let sessionDirs;
|
|
5912
5800
|
try {
|
|
5913
|
-
sessionDirs = await
|
|
5801
|
+
sessionDirs = await readdir9(msgRoot);
|
|
5914
5802
|
} catch (error) {
|
|
5915
5803
|
if (isMissingPathError(error)) return [];
|
|
5916
5804
|
throw error;
|
|
5917
5805
|
}
|
|
5918
5806
|
const refs = [];
|
|
5919
5807
|
for (const sid of sessionDirs) {
|
|
5920
|
-
const dir =
|
|
5808
|
+
const dir = join9(msgRoot, sid);
|
|
5921
5809
|
let st;
|
|
5922
5810
|
try {
|
|
5923
|
-
st = await
|
|
5811
|
+
st = await stat7(dir);
|
|
5924
5812
|
} catch (error) {
|
|
5925
5813
|
if (isMissingPathError(error)) continue;
|
|
5926
5814
|
throw error;
|
|
@@ -5936,23 +5824,23 @@ var OpencodeAdapter = class {
|
|
|
5936
5824
|
const paths = [];
|
|
5937
5825
|
let step = 0;
|
|
5938
5826
|
const messages = await Promise.all((await listJsonFiles(ref.path)).map(async (file) => {
|
|
5939
|
-
const path =
|
|
5827
|
+
const path = join9(ref.path, file);
|
|
5940
5828
|
paths.push(path);
|
|
5941
5829
|
return readJsonFile(path);
|
|
5942
5830
|
}));
|
|
5943
5831
|
messages.sort((a, b) => (a.time?.created ?? 0) - (b.time?.created ?? 0));
|
|
5944
5832
|
for (const message of messages) {
|
|
5945
5833
|
const mid = message.id ?? `m${step}`;
|
|
5946
|
-
const directory =
|
|
5834
|
+
const directory = join9(this.storage(), "part", mid);
|
|
5947
5835
|
let files = [];
|
|
5948
5836
|
try {
|
|
5949
5837
|
files = await listJsonFiles(directory);
|
|
5950
5838
|
} catch (error) {
|
|
5951
5839
|
if (!isMissingJsonSource(error)) throw error;
|
|
5952
5840
|
}
|
|
5953
|
-
paths.push(...files.map((file) =>
|
|
5954
|
-
const parts = await Promise.all(files.map((file) => readJsonFile(
|
|
5955
|
-
if (message.role !== "user" ||
|
|
5841
|
+
paths.push(...files.map((file) => join9(directory, file)));
|
|
5842
|
+
const parts = await Promise.all(files.map((file) => readJsonFile(join9(directory, file))));
|
|
5843
|
+
if (message.role !== "user" || textOf5(parts)) step += 1;
|
|
5956
5844
|
step += parts.filter((part) => part.type === "tool" && part.tool).length;
|
|
5957
5845
|
}
|
|
5958
5846
|
return paths.sort();
|
|
@@ -5960,13 +5848,14 @@ var OpencodeAdapter = class {
|
|
|
5960
5848
|
async parse(ref, options = {}) {
|
|
5961
5849
|
const traceId = ref.sessionId;
|
|
5962
5850
|
const rootId = `root:${traceId}`;
|
|
5963
|
-
const partRoot =
|
|
5851
|
+
const partRoot = join9(this.storage(), "part");
|
|
5964
5852
|
const msgFiles = await listJsonFiles(ref.path);
|
|
5965
5853
|
const messages = [];
|
|
5966
5854
|
for (const f of msgFiles) {
|
|
5967
|
-
messages.push(await readJsonFile(
|
|
5855
|
+
messages.push(await readJsonFile(join9(ref.path, f), options));
|
|
5968
5856
|
}
|
|
5969
5857
|
messages.sort((a, b) => (a.time?.created ?? 0) - (b.time?.created ?? 0));
|
|
5858
|
+
const recordedCwd = messages.map((message) => message.path?.cwd).find((cwd) => typeof cwd === "string" && cwd.length > 0);
|
|
5970
5859
|
const spans = [
|
|
5971
5860
|
span({
|
|
5972
5861
|
traceId,
|
|
@@ -5976,15 +5865,16 @@ var OpencodeAdapter = class {
|
|
|
5976
5865
|
kind: "AGENT",
|
|
5977
5866
|
startTime: isoOf(messages[0]?.time?.created),
|
|
5978
5867
|
endTime: isoOf(messages.at(-1)?.time?.completed ?? messages.at(-1)?.time?.created),
|
|
5979
|
-
service:
|
|
5980
|
-
agent:
|
|
5868
|
+
service: SERVICE6,
|
|
5869
|
+
agent: SERVICE6,
|
|
5870
|
+
...recordedCwd ? { extra: { [SESSION_CWD_ATTR]: recordedCwd } } : {}
|
|
5981
5871
|
})
|
|
5982
5872
|
];
|
|
5983
5873
|
let step = 0;
|
|
5984
5874
|
for (const msg of messages) {
|
|
5985
5875
|
const mid = msg.id ?? `m${step}`;
|
|
5986
5876
|
const llmId = `llm:${mid}`;
|
|
5987
|
-
const pdir =
|
|
5877
|
+
const pdir = join9(partRoot, mid);
|
|
5988
5878
|
const parts = [];
|
|
5989
5879
|
let partFiles = [];
|
|
5990
5880
|
try {
|
|
@@ -5993,9 +5883,9 @@ var OpencodeAdapter = class {
|
|
|
5993
5883
|
if (!isMissingJsonSource(error)) throw error;
|
|
5994
5884
|
}
|
|
5995
5885
|
for (const pf of partFiles) {
|
|
5996
|
-
parts.push(await readJsonFile(
|
|
5886
|
+
parts.push(await readJsonFile(join9(pdir, pf), options));
|
|
5997
5887
|
}
|
|
5998
|
-
const turnText2 =
|
|
5888
|
+
const turnText2 = textOf5(parts);
|
|
5999
5889
|
const isUser = msg.role === "user";
|
|
6000
5890
|
if (isUser) {
|
|
6001
5891
|
if (turnText2) {
|
|
@@ -6005,8 +5895,8 @@ var OpencodeAdapter = class {
|
|
|
6005
5895
|
spanId: `${llmId}:user`,
|
|
6006
5896
|
parentSpanId: rootId,
|
|
6007
5897
|
startTime: isoOf(msg.time?.created),
|
|
6008
|
-
service:
|
|
6009
|
-
agent:
|
|
5898
|
+
service: SERVICE6,
|
|
5899
|
+
agent: SERVICE6,
|
|
6010
5900
|
step,
|
|
6011
5901
|
content: turnText2,
|
|
6012
5902
|
contentSource: parts.filter((part) => part.type === "text").flatMap((part) => {
|
|
@@ -6027,8 +5917,8 @@ var OpencodeAdapter = class {
|
|
|
6027
5917
|
kind: "LLM",
|
|
6028
5918
|
startTime: isoOf(msg.time?.created),
|
|
6029
5919
|
endTime: isoOf(msg.time?.completed ?? msg.time?.created),
|
|
6030
|
-
service:
|
|
6031
|
-
agent:
|
|
5920
|
+
service: SERVICE6,
|
|
5921
|
+
agent: SERVICE6,
|
|
6032
5922
|
model: msg.modelID ?? null,
|
|
6033
5923
|
inputTokens: msg.tokens?.input ?? null,
|
|
6034
5924
|
outputTokens: msg.tokens?.output ?? null,
|
|
@@ -6059,8 +5949,8 @@ var OpencodeAdapter = class {
|
|
|
6059
5949
|
endTime: isoOf(msg.time?.completed ?? msg.time?.created),
|
|
6060
5950
|
status: err ? "ERROR" : "OK",
|
|
6061
5951
|
statusMessage: err ? (part.state?.error ?? "").slice(0, 500) : void 0,
|
|
6062
|
-
service:
|
|
6063
|
-
agent:
|
|
5952
|
+
service: SERVICE6,
|
|
5953
|
+
agent: SERVICE6,
|
|
6064
5954
|
tool: part.tool,
|
|
6065
5955
|
step,
|
|
6066
5956
|
extra: toolIoAttributes({
|
|
@@ -6079,9 +5969,9 @@ var OpencodeAdapter = class {
|
|
|
6079
5969
|
};
|
|
6080
5970
|
|
|
6081
5971
|
// src/adapters/harness-sessions.ts
|
|
6082
|
-
import { homedir as
|
|
6083
|
-
import { open, stat as
|
|
6084
|
-
import { join as
|
|
5972
|
+
import { homedir as homedir8 } from "os";
|
|
5973
|
+
import { open, stat as stat8 } from "fs/promises";
|
|
5974
|
+
import { join as join10 } from "path";
|
|
6085
5975
|
import {
|
|
6086
5976
|
claudeCodeRefForFile,
|
|
6087
5977
|
codexRefForFile,
|
|
@@ -6105,7 +5995,7 @@ function nativeRef(harness, ref) {
|
|
|
6105
5995
|
case "pi":
|
|
6106
5996
|
return selected(piRefForFile(ref.path));
|
|
6107
5997
|
case "kimi-code":
|
|
6108
|
-
return selected(kimiRefForFile(ref.path.endsWith("wire.jsonl") ? ref.path :
|
|
5998
|
+
return selected(kimiRefForFile(ref.path.endsWith("wire.jsonl") ? ref.path : join10(ref.path, "wire.jsonl")));
|
|
6109
5999
|
case "factory-droids":
|
|
6110
6000
|
return selected(factoryRefForFile(ref.path));
|
|
6111
6001
|
case "opencode":
|
|
@@ -6316,7 +6206,7 @@ var HarnessSessionsAdapter = class {
|
|
|
6316
6206
|
scopedAdapter;
|
|
6317
6207
|
async locate(opts = {}) {
|
|
6318
6208
|
if (this.scopedAdapter) return this.scopedAdapter.locate(opts);
|
|
6319
|
-
const found = await this.reader.locate(
|
|
6209
|
+
const found = await this.reader.locate(homedir8(), { sinceMs: opts.sinceMs, cwd: opts.cwd });
|
|
6320
6210
|
const refs = found.filter((native) => this.nativeHarness !== "claude-code" || native.parentNativeSessionId === null).map((native) => {
|
|
6321
6211
|
const ref = tracesRef(native, this.harness);
|
|
6322
6212
|
this.refs.set(`${ref.sessionId}\0${ref.path}`, native);
|
|
@@ -6332,7 +6222,7 @@ var HarnessSessionsAdapter = class {
|
|
|
6332
6222
|
if (this.scopedAdapter instanceof ClaudeAdapter) {
|
|
6333
6223
|
return (await this.scopedAdapter.locate(opts)).filter((ref) => ref.sessionId === sessionId);
|
|
6334
6224
|
}
|
|
6335
|
-
const refs = await this.reader.locate(
|
|
6225
|
+
const refs = await this.reader.locate(homedir8(), { nativeSessionId: sessionId, sinceMs: opts.sinceMs, cwd: opts.cwd });
|
|
6336
6226
|
const selected = refs.map((native) => {
|
|
6337
6227
|
const ref = tracesRef(native, this.harness);
|
|
6338
6228
|
this.refs.set(`${ref.sessionId}\0${ref.path}`, native);
|
|
@@ -6350,7 +6240,7 @@ var HarnessSessionsAdapter = class {
|
|
|
6350
6240
|
const settings = ref.path.replace(/\.jsonl$/u, ".settings.json");
|
|
6351
6241
|
if (paths.includes(settings)) return paths;
|
|
6352
6242
|
try {
|
|
6353
|
-
await
|
|
6243
|
+
await stat8(settings);
|
|
6354
6244
|
return [...paths, settings];
|
|
6355
6245
|
} catch (error) {
|
|
6356
6246
|
if (error.code === "ENOENT") return paths;
|
|
@@ -6419,6 +6309,7 @@ var HarnessSessionsAdapter = class {
|
|
|
6419
6309
|
const root = spans[0];
|
|
6420
6310
|
root.attributes["service.name"] = this.harness;
|
|
6421
6311
|
root.attributes["traces.session.role"] = session.parentNativeSessionId ? "child" : "operator";
|
|
6312
|
+
if (session.cwd) root.attributes[SESSION_CWD_ATTR] = session.cwd;
|
|
6422
6313
|
if (session.parentNativeSessionId) root.attributes["traces.parent_session_id"] = session.parentNativeSessionId;
|
|
6423
6314
|
if (session.children.length > 0) {
|
|
6424
6315
|
root.attributes["traces.child_session_ids"] = JSON.stringify(session.children.map((child) => child.nativeSessionId));
|
|
@@ -6497,11 +6388,11 @@ var SHARED_SESSION_ADAPTERS = [
|
|
|
6497
6388
|
];
|
|
6498
6389
|
|
|
6499
6390
|
// src/adapters/qwen.ts
|
|
6500
|
-
import { readdir as
|
|
6501
|
-
import { homedir as
|
|
6502
|
-
import { basename as
|
|
6503
|
-
var
|
|
6504
|
-
function
|
|
6391
|
+
import { readdir as readdir10, stat as stat9 } from "fs/promises";
|
|
6392
|
+
import { homedir as homedir9 } from "os";
|
|
6393
|
+
import { basename as basename6, join as join11 } from "path";
|
|
6394
|
+
var SERVICE7 = "qwen";
|
|
6395
|
+
function textOf6(parts) {
|
|
6505
6396
|
if (!parts) return "";
|
|
6506
6397
|
return parts.filter((p) => typeof p.text === "string").map((p) => p.text).join("");
|
|
6507
6398
|
}
|
|
@@ -6509,23 +6400,23 @@ var QwenAdapter = class {
|
|
|
6509
6400
|
harness = "qwen";
|
|
6510
6401
|
aliases = ["qwen-code"];
|
|
6511
6402
|
root() {
|
|
6512
|
-
return
|
|
6403
|
+
return join11(homedir9(), ".qwen", "projects");
|
|
6513
6404
|
}
|
|
6514
6405
|
async locate(opts = {}) {
|
|
6515
6406
|
const root = this.root();
|
|
6516
6407
|
let projectDirs;
|
|
6517
6408
|
try {
|
|
6518
|
-
projectDirs = await
|
|
6409
|
+
projectDirs = await readdir10(root);
|
|
6519
6410
|
} catch (error) {
|
|
6520
6411
|
if (isMissingPathError(error)) return [];
|
|
6521
6412
|
throw error;
|
|
6522
6413
|
}
|
|
6523
6414
|
const refs = [];
|
|
6524
6415
|
for (const pd of projectDirs) {
|
|
6525
|
-
const chatsDir =
|
|
6416
|
+
const chatsDir = join11(root, pd, "chats");
|
|
6526
6417
|
let files;
|
|
6527
6418
|
try {
|
|
6528
|
-
files = await
|
|
6419
|
+
files = await readdir10(chatsDir);
|
|
6529
6420
|
} catch (error) {
|
|
6530
6421
|
if (isMissingPathError(error)) continue;
|
|
6531
6422
|
throw error;
|
|
@@ -6533,16 +6424,16 @@ var QwenAdapter = class {
|
|
|
6533
6424
|
if (opts.cwd) continue;
|
|
6534
6425
|
for (const f of files) {
|
|
6535
6426
|
if (!f.endsWith(".jsonl")) continue;
|
|
6536
|
-
const path =
|
|
6427
|
+
const path = join11(chatsDir, f);
|
|
6537
6428
|
let st;
|
|
6538
6429
|
try {
|
|
6539
|
-
st = await
|
|
6430
|
+
st = await stat9(path);
|
|
6540
6431
|
} catch (error) {
|
|
6541
6432
|
if (isMissingPathError(error)) continue;
|
|
6542
6433
|
throw error;
|
|
6543
6434
|
}
|
|
6544
6435
|
if (opts.sinceMs && st.mtimeMs < opts.sinceMs) continue;
|
|
6545
|
-
refs.push({ harness: this.harness, sessionId:
|
|
6436
|
+
refs.push({ harness: this.harness, sessionId: basename6(f, ".jsonl"), path, cwd: null, mtimeMs: st.mtimeMs });
|
|
6546
6437
|
}
|
|
6547
6438
|
}
|
|
6548
6439
|
return refs.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
@@ -6576,13 +6467,13 @@ var QwenAdapter = class {
|
|
|
6576
6467
|
name: "llm.turn",
|
|
6577
6468
|
kind: "LLM",
|
|
6578
6469
|
startTime: ts,
|
|
6579
|
-
service:
|
|
6580
|
-
agent:
|
|
6470
|
+
service: SERVICE7,
|
|
6471
|
+
agent: SERVICE7,
|
|
6581
6472
|
model: r.model ?? null,
|
|
6582
6473
|
inputTokens: r.usageMetadata?.promptTokenCount ?? null,
|
|
6583
6474
|
outputTokens: r.usageMetadata?.candidatesTokenCount ?? null,
|
|
6584
6475
|
step,
|
|
6585
|
-
content: capText(
|
|
6476
|
+
content: capText(textOf6(r.message?.parts)) || null,
|
|
6586
6477
|
contentSource: textSources(r.message, "parts")
|
|
6587
6478
|
})
|
|
6588
6479
|
);
|
|
@@ -6597,8 +6488,8 @@ var QwenAdapter = class {
|
|
|
6597
6488
|
name: `tool.${name}`,
|
|
6598
6489
|
kind: "TOOL",
|
|
6599
6490
|
startTime: ts,
|
|
6600
|
-
service:
|
|
6601
|
-
agent:
|
|
6491
|
+
service: SERVICE7,
|
|
6492
|
+
agent: SERVICE7,
|
|
6602
6493
|
tool: name,
|
|
6603
6494
|
step,
|
|
6604
6495
|
extra: toolIoAttributes({ input: p.functionCall.args, inputSource: sourceOf(p.functionCall, "args") })
|
|
@@ -6610,7 +6501,7 @@ var QwenAdapter = class {
|
|
|
6610
6501
|
step += 1;
|
|
6611
6502
|
}
|
|
6612
6503
|
} else if (r.type === "user") {
|
|
6613
|
-
const prompt = capText(
|
|
6504
|
+
const prompt = capText(textOf6(r.message?.parts));
|
|
6614
6505
|
if (prompt) {
|
|
6615
6506
|
spans.push(
|
|
6616
6507
|
userPromptSpan({
|
|
@@ -6618,8 +6509,8 @@ var QwenAdapter = class {
|
|
|
6618
6509
|
spanId: `user:${r.uuid ?? step}`,
|
|
6619
6510
|
parentSpanId: sourceRootId,
|
|
6620
6511
|
startTime: ts,
|
|
6621
|
-
service:
|
|
6622
|
-
agent:
|
|
6512
|
+
service: SERVICE7,
|
|
6513
|
+
agent: SERVICE7,
|
|
6623
6514
|
step,
|
|
6624
6515
|
content: prompt,
|
|
6625
6516
|
contentSource: textSources(r.message, "parts")
|
|
@@ -6655,8 +6546,8 @@ var QwenAdapter = class {
|
|
|
6655
6546
|
kind: "AGENT",
|
|
6656
6547
|
startTime: firstTimestamp ?? (/* @__PURE__ */ new Date(0)).toISOString(),
|
|
6657
6548
|
endTime: lastTimestamp,
|
|
6658
|
-
service:
|
|
6659
|
-
agent:
|
|
6549
|
+
service: SERVICE7,
|
|
6550
|
+
agent: SERVICE7
|
|
6660
6551
|
});
|
|
6661
6552
|
return [root, ...spans];
|
|
6662
6553
|
}
|
|
@@ -6669,8 +6560,7 @@ var ADAPTERS = [
|
|
|
6669
6560
|
new GeminiAdapter(),
|
|
6670
6561
|
new QwenAdapter(),
|
|
6671
6562
|
new AmpAdapter(),
|
|
6672
|
-
new CopilotAdapter()
|
|
6673
|
-
new ForgeAdapter()
|
|
6563
|
+
new CopilotAdapter()
|
|
6674
6564
|
];
|
|
6675
6565
|
var BY_NAME = /* @__PURE__ */ new Map();
|
|
6676
6566
|
for (const a of ADAPTERS) {
|
|
@@ -6697,8 +6587,8 @@ function selectAdapters(opts) {
|
|
|
6697
6587
|
}
|
|
6698
6588
|
|
|
6699
6589
|
// src/repo.ts
|
|
6700
|
-
import { realpath as realpath2, stat as
|
|
6701
|
-
import { dirname as dirname4, isAbsolute as isAbsolute3, join as
|
|
6590
|
+
import { realpath as realpath2, stat as stat10 } from "fs/promises";
|
|
6591
|
+
import { dirname as dirname4, isAbsolute as isAbsolute3, join as join12, normalize, parse as parsePath } from "path";
|
|
6702
6592
|
function normalizeRemote(url) {
|
|
6703
6593
|
const raw = url.trim();
|
|
6704
6594
|
if (!raw) return null;
|
|
@@ -6722,7 +6612,7 @@ function normalizeRemote(url) {
|
|
|
6722
6612
|
return null;
|
|
6723
6613
|
}
|
|
6724
6614
|
async function readGit(cwd) {
|
|
6725
|
-
await
|
|
6615
|
+
await stat10(`${cwd}/.git`);
|
|
6726
6616
|
const { execFile } = await import("child_process");
|
|
6727
6617
|
const { promisify } = await import("util");
|
|
6728
6618
|
const run = promisify(execFile);
|
|
@@ -6815,7 +6705,7 @@ function cwdMatchesSelection(cwd, selections) {
|
|
|
6815
6705
|
}
|
|
6816
6706
|
async function pathStat(path) {
|
|
6817
6707
|
try {
|
|
6818
|
-
return await
|
|
6708
|
+
return await stat10(path);
|
|
6819
6709
|
} catch {
|
|
6820
6710
|
return null;
|
|
6821
6711
|
}
|
|
@@ -6839,7 +6729,7 @@ async function nearestExistingDir(path) {
|
|
|
6839
6729
|
async function findGitRoot(path) {
|
|
6840
6730
|
let cur = await nearestExistingDir(path);
|
|
6841
6731
|
while (cur) {
|
|
6842
|
-
if (await pathStat(
|
|
6732
|
+
if (await pathStat(join12(cur, ".git"))) return cur;
|
|
6843
6733
|
const parent = dirname4(cur);
|
|
6844
6734
|
if (parent === cur) return null;
|
|
6845
6735
|
cur = parent;
|
|
@@ -6857,7 +6747,7 @@ async function repairDashedPath(cwd) {
|
|
|
6857
6747
|
while (index < parts.length) {
|
|
6858
6748
|
const nextPart = parts[index];
|
|
6859
6749
|
if (!nextPart) break;
|
|
6860
|
-
const exact =
|
|
6750
|
+
const exact = join12(current, nextPart);
|
|
6861
6751
|
if (!await directoryExists(exact)) break;
|
|
6862
6752
|
current = exact;
|
|
6863
6753
|
index += 1;
|
|
@@ -6867,7 +6757,7 @@ async function repairDashedPath(cwd) {
|
|
|
6867
6757
|
let consumed = 0;
|
|
6868
6758
|
for (let n = parts.length - index; n >= 1; n -= 1) {
|
|
6869
6759
|
const segment = parts.slice(index, index + n).join("-");
|
|
6870
|
-
const candidate =
|
|
6760
|
+
const candidate = join12(current, segment);
|
|
6871
6761
|
if (await directoryExists(candidate)) {
|
|
6872
6762
|
match = candidate;
|
|
6873
6763
|
consumed = n;
|
|
@@ -6951,7 +6841,7 @@ function extractAbsolutePaths(spans) {
|
|
|
6951
6841
|
async function resolveRepoAttrs(cwd) {
|
|
6952
6842
|
if (!cwd) return {};
|
|
6953
6843
|
const attrs = { [ATTR.CWD]: cwd };
|
|
6954
|
-
const
|
|
6844
|
+
const basename15 = (() => {
|
|
6955
6845
|
const trimmed = cwd.replace(/\/+$/g, "");
|
|
6956
6846
|
const idx = trimmed.lastIndexOf("/");
|
|
6957
6847
|
const name = idx === -1 ? trimmed : trimmed.slice(idx + 1);
|
|
@@ -6973,7 +6863,7 @@ async function resolveRepoAttrs(cwd) {
|
|
|
6973
6863
|
if (commit) attrs[ATTR.GIT_COMMIT] = commit;
|
|
6974
6864
|
} catch {
|
|
6975
6865
|
}
|
|
6976
|
-
attrs[ATTR.SUBJECT_KEY] =
|
|
6866
|
+
attrs[ATTR.SUBJECT_KEY] = basename15;
|
|
6977
6867
|
return attrs;
|
|
6978
6868
|
}
|
|
6979
6869
|
async function repoCandidateForPath(path) {
|
|
@@ -7283,7 +7173,7 @@ function describeSessionRelationship(ref, spans) {
|
|
|
7283
7173
|
// src/evidence.ts
|
|
7284
7174
|
import { mkdtemp, writeFile as writeFile2 } from "fs/promises";
|
|
7285
7175
|
import { tmpdir as tmpdir2 } from "os";
|
|
7286
|
-
import { join as
|
|
7176
|
+
import { join as join13 } from "path";
|
|
7287
7177
|
import {
|
|
7288
7178
|
OPENINFERENCE_SPAN_KIND as OPENINFERENCE_SPAN_KIND3,
|
|
7289
7179
|
TOOL_NAME as TOOL_NAME4
|
|
@@ -7413,14 +7303,14 @@ function serializePolicyEvidence(records) {
|
|
|
7413
7303
|
`;
|
|
7414
7304
|
}
|
|
7415
7305
|
async function writePolicyEvidenceFile(records, outPath) {
|
|
7416
|
-
const path = outPath ??
|
|
7306
|
+
const path = outPath ?? join13(await mkdtemp(join13(tmpdir2(), "traces-evidence-")), "policy-evidence.jsonl");
|
|
7417
7307
|
await writeFile2(path, serializePolicyEvidence(records), "utf8");
|
|
7418
7308
|
return path;
|
|
7419
7309
|
}
|
|
7420
7310
|
|
|
7421
7311
|
// src/adoption.ts
|
|
7422
7312
|
import { readFile as readFile4 } from "fs/promises";
|
|
7423
|
-
import { join as
|
|
7313
|
+
import { join as join14 } from "path";
|
|
7424
7314
|
function toolName2(s) {
|
|
7425
7315
|
const n = s.attributes["tool.name"];
|
|
7426
7316
|
if (typeof n === "string") return n;
|
|
@@ -7540,7 +7430,7 @@ async function readLoopRuns(cwds, sessionIds) {
|
|
|
7540
7430
|
seenCwds.add(cwd);
|
|
7541
7431
|
const firstSourceByRun = /* @__PURE__ */ new Map();
|
|
7542
7432
|
for (const artifactDir of [".agent", ".evolve"]) {
|
|
7543
|
-
const path =
|
|
7433
|
+
const path = join14(cwd, artifactDir, "skill-runs.jsonl");
|
|
7544
7434
|
let raw;
|
|
7545
7435
|
try {
|
|
7546
7436
|
raw = await readFile4(path, "utf8");
|
|
@@ -8177,7 +8067,7 @@ var COMMAND_PREFIXES = /* @__PURE__ */ new Set([
|
|
|
8177
8067
|
var ASSIGNMENT = /^[A-Za-z_][A-Za-z0-9_]*=/;
|
|
8178
8068
|
var MAX_SHELL_DEPTH = 4;
|
|
8179
8069
|
var MAX_WORDS = 2e4;
|
|
8180
|
-
function
|
|
8070
|
+
function basename7(word) {
|
|
8181
8071
|
const cut = word.lastIndexOf("/");
|
|
8182
8072
|
return cut === -1 ? word : word.slice(cut + 1);
|
|
8183
8073
|
}
|
|
@@ -8276,7 +8166,7 @@ var ShellScanner = class {
|
|
|
8276
8166
|
let start = 0;
|
|
8277
8167
|
while (start < words.length) {
|
|
8278
8168
|
const word = words[start];
|
|
8279
|
-
if (ASSIGNMENT.test(word) || COMMAND_PREFIXES.has(
|
|
8169
|
+
if (ASSIGNMENT.test(word) || COMMAND_PREFIXES.has(basename7(word))) start += 1;
|
|
8280
8170
|
else break;
|
|
8281
8171
|
}
|
|
8282
8172
|
return start === 0 || start >= words.length ? words : words.slice(start);
|
|
@@ -8417,7 +8307,7 @@ function shellCommands(script, depth = 0) {
|
|
|
8417
8307
|
const out = [];
|
|
8418
8308
|
for (const words of commands) {
|
|
8419
8309
|
out.push(words);
|
|
8420
|
-
const name =
|
|
8310
|
+
const name = basename7(words[0] ?? "");
|
|
8421
8311
|
if (!SHELLS.has(name) || depth >= MAX_SHELL_DEPTH) continue;
|
|
8422
8312
|
for (let index = 1; index < words.length; index += 1) {
|
|
8423
8313
|
const word = words[index];
|
|
@@ -8445,7 +8335,7 @@ function commandTextFromInput(input) {
|
|
|
8445
8335
|
if (!Array.isArray(value) || value.length === 0) return void 0;
|
|
8446
8336
|
const words = value.filter((entry) => typeof entry === "string");
|
|
8447
8337
|
if (words.length !== value.length) return void 0;
|
|
8448
|
-
const head =
|
|
8338
|
+
const head = basename7(words[0] ?? "");
|
|
8449
8339
|
if (SHELLS.has(head)) {
|
|
8450
8340
|
const flag = words.findIndex((word, position) => position > 0 && /^-[a-z]*c$/.test(word));
|
|
8451
8341
|
if (flag !== -1 && words[flag + 1] !== void 0) return words[flag + 1];
|
|
@@ -8507,7 +8397,7 @@ function stringAttribute2(span2, key) {
|
|
|
8507
8397
|
const value = span2.attributes[key];
|
|
8508
8398
|
return typeof value === "string" ? value : null;
|
|
8509
8399
|
}
|
|
8510
|
-
function
|
|
8400
|
+
function basename8(word) {
|
|
8511
8401
|
const cut = word.lastIndexOf("/");
|
|
8512
8402
|
return cut === -1 ? word : word.slice(cut + 1);
|
|
8513
8403
|
}
|
|
@@ -8590,7 +8480,7 @@ function numbersInLine(line) {
|
|
|
8590
8480
|
function inferHeadBranch(commands, upTo, output) {
|
|
8591
8481
|
for (let index = upTo - 1; index >= 0; index -= 1) {
|
|
8592
8482
|
const words = commands[index];
|
|
8593
|
-
if (
|
|
8483
|
+
if (basename8(words[0] ?? "") !== "git" || words.length < 2) continue;
|
|
8594
8484
|
const sub = words[1];
|
|
8595
8485
|
const rest = words.slice(2);
|
|
8596
8486
|
if (sub === "push") {
|
|
@@ -8665,7 +8555,7 @@ function eventsFromRecord(record2, recordIndex) {
|
|
|
8665
8555
|
let createIndex = 0;
|
|
8666
8556
|
for (let index = 0; index < commands.length; index += 1) {
|
|
8667
8557
|
const words = commands[index];
|
|
8668
|
-
if (words.length < 3 || !GH_NAMES.has(
|
|
8558
|
+
if (words.length < 3 || !GH_NAMES.has(basename8(words[0])) || words[1] !== "pr") continue;
|
|
8669
8559
|
const action = words[2];
|
|
8670
8560
|
if (action === "create") {
|
|
8671
8561
|
const printedNumber = printed[createIndex] ?? null;
|
|
@@ -8914,12 +8804,13 @@ function computeSessionMeasurements(spans, tools, sessionId, harness) {
|
|
|
8914
8804
|
// src/session-facts.ts
|
|
8915
8805
|
import { OPENINFERENCE_SPAN_KIND as OPENINFERENCE_SPAN_KIND4, TOOL_NAME as TOOL_NAME5 } from "@tangle-network/agent-eval/trace-attributes";
|
|
8916
8806
|
import { ingestSpans, rankFirstFailure } from "@tangle-network/agent-eval/diagnosis";
|
|
8917
|
-
var SESSION_FACTS_MEASUREMENT_VERSION = "
|
|
8807
|
+
var SESSION_FACTS_MEASUREMENT_VERSION = "4";
|
|
8918
8808
|
var SESSION_TOKEN_TOTAL_ATTR = "traces.session.total_tokens";
|
|
8919
8809
|
var SPAN_SYNTHESIZED_ATTR = "traces.codex.span_synthesized";
|
|
8920
8810
|
var CORRUPTION_COUNT_ATTR = "traces.session.corruption_count";
|
|
8921
8811
|
var CORRUPTION_RECEIPT_VERSION_ATTR = "traces.session.corruption.receipt_version";
|
|
8922
8812
|
var FACT_TEXT_CAP = 2e3;
|
|
8813
|
+
var ANSWERED_TEXT_CAP = 1500;
|
|
8923
8814
|
var FACT_LIST_CAP = 200;
|
|
8924
8815
|
var CHANGED_FILE_LIST_CAP = 5e3;
|
|
8925
8816
|
function attr(span2, key) {
|
|
@@ -9149,47 +9040,105 @@ function turnText(span2) {
|
|
|
9149
9040
|
function normalizedTurnText(span2) {
|
|
9150
9041
|
return turnText(span2).replace(/\s+/g, " ").trim();
|
|
9151
9042
|
}
|
|
9043
|
+
var CAPTURE_CUT_MARKER = /… \[\+(\d+) chars\]$/;
|
|
9044
|
+
function clipAnswered(raw) {
|
|
9045
|
+
const text2 = raw.trim();
|
|
9046
|
+
const marker = CAPTURE_CUT_MARKER.exec(text2);
|
|
9047
|
+
const body = marker ? text2.slice(0, marker.index) : text2;
|
|
9048
|
+
if (body.length <= ANSWERED_TEXT_CAP) return text2;
|
|
9049
|
+
const lastKept = body.charCodeAt(ANSWERED_TEXT_CAP - 1);
|
|
9050
|
+
const keep = lastKept >= 55296 && lastKept <= 56319 ? ANSWERED_TEXT_CAP - 1 : ANSWERED_TEXT_CAP;
|
|
9051
|
+
const hidden = body.length - keep + (marker ? Number(marker[1]) : 0);
|
|
9052
|
+
return `${body.slice(0, keep)}\u2026 [+${hidden} chars]`;
|
|
9053
|
+
}
|
|
9054
|
+
function isOwnAgentMessage(span2) {
|
|
9055
|
+
return span2.name === "message.assistant" && span2.attributes[INHERITED_SPAN_ATTR] !== true && typeof span2.attributes.content === "string" && span2.attributes.content.trim().length > 0;
|
|
9056
|
+
}
|
|
9152
9057
|
function humanTurnsOf(spans) {
|
|
9153
9058
|
const excluded = /* @__PURE__ */ new Map();
|
|
9154
|
-
const drop = (reason,
|
|
9155
|
-
excluded.set(reason, [...excluded.get(reason) ?? [], spanId]);
|
|
9059
|
+
const drop = (reason, span2) => {
|
|
9060
|
+
excluded.set(reason, [...excluded.get(reason) ?? [], { spanId: span2.span_id, at: span2.start_time }]);
|
|
9156
9061
|
};
|
|
9157
9062
|
const kept = [];
|
|
9063
|
+
const turns = [];
|
|
9158
9064
|
let sinceKept = [];
|
|
9065
|
+
let lastAgentMessage = null;
|
|
9159
9066
|
for (const span2 of spans) {
|
|
9160
9067
|
if (isSubagentSpan(span2.attributes)) {
|
|
9161
|
-
if (span2.name === "user.prompt") drop(SUBAGENT_TURN_REASON, span2
|
|
9068
|
+
if (span2.name === "user.prompt") drop(SUBAGENT_TURN_REASON, span2);
|
|
9162
9069
|
continue;
|
|
9163
9070
|
}
|
|
9164
9071
|
if (span2.name !== "user.prompt") {
|
|
9072
|
+
if (isOwnAgentMessage(span2)) lastAgentMessage = span2;
|
|
9165
9073
|
sinceKept.push(span2);
|
|
9166
9074
|
continue;
|
|
9167
9075
|
}
|
|
9168
9076
|
if (span2.attributes[INHERITED_SPAN_ATTR] === true) {
|
|
9169
9077
|
const source = stringAttr2(span2, INHERITED_SOURCE_ATTR);
|
|
9170
|
-
drop(source === null ? INHERITED_TURN_REASON : `${INHERITED_TURN_REASON} (${source})`, span2
|
|
9078
|
+
drop(source === null ? INHERITED_TURN_REASON : `${INHERITED_TURN_REASON} (${source})`, span2);
|
|
9171
9079
|
continue;
|
|
9172
9080
|
}
|
|
9173
9081
|
const actor = stringAttr2(span2, ACTOR_ATTR) ?? "unclassified";
|
|
9174
9082
|
if (actor !== "human") {
|
|
9175
|
-
drop(TURN_EXCLUSION_REASONS[actor] ?? `the adapter classified this turn as "${actor}", not as a person typing`, span2
|
|
9083
|
+
drop(TURN_EXCLUSION_REASONS[actor] ?? `the adapter classified this turn as "${actor}", not as a person typing`, span2);
|
|
9176
9084
|
continue;
|
|
9177
9085
|
}
|
|
9178
9086
|
const previous = kept[kept.length - 1];
|
|
9179
9087
|
const duplicate = previous !== void 0 && previous.start_time === span2.start_time && normalizedTurnText(previous) === normalizedTurnText(span2) && normalizedTurnText(span2).length > 0 && !sinceKept.some(isAgentActivity);
|
|
9180
9088
|
if (duplicate) {
|
|
9181
|
-
drop(DUPLICATE_TURN_REASON, span2
|
|
9089
|
+
drop(DUPLICATE_TURN_REASON, span2);
|
|
9182
9090
|
continue;
|
|
9183
9091
|
}
|
|
9184
9092
|
kept.push(span2);
|
|
9093
|
+
turns.push({
|
|
9094
|
+
actor: "human",
|
|
9095
|
+
at: span2.start_time,
|
|
9096
|
+
text: turnText(span2),
|
|
9097
|
+
spanId: span2.span_id,
|
|
9098
|
+
answered: lastAgentMessage ? clipAnswered(lastAgentMessage.attributes.content) : null,
|
|
9099
|
+
answeredSpanId: lastAgentMessage?.span_id ?? null
|
|
9100
|
+
});
|
|
9185
9101
|
sinceKept = [];
|
|
9186
9102
|
}
|
|
9103
|
+
return { turns, excluded };
|
|
9104
|
+
}
|
|
9105
|
+
function excludedTurnFacts(excluded, keep) {
|
|
9106
|
+
return [...excluded].map(([reason, entries]) => [reason, entries.filter((entry) => keep(entry.at)).map((entry) => entry.spanId)]).filter(([, spanIds]) => spanIds.length > 0).sort(([left], [right]) => left.localeCompare(right)).map(([reason, spanIds]) => ({ reason, turns: spanIds.length, spanIds }));
|
|
9107
|
+
}
|
|
9108
|
+
function windowBounds(window) {
|
|
9109
|
+
if (!window || window.since === void 0 && window.until === void 0) return null;
|
|
9110
|
+
const bound = (value, name, open4) => {
|
|
9111
|
+
if (value === void 0) return open4;
|
|
9112
|
+
const ms2 = Date.parse(value);
|
|
9113
|
+
if (!Number.isFinite(ms2)) throw new RangeError(`window ${name} is not a readable time: ${value}`);
|
|
9114
|
+
return ms2;
|
|
9115
|
+
};
|
|
9116
|
+
const sinceMs = bound(window.since, "since", Number.NEGATIVE_INFINITY);
|
|
9117
|
+
const untilMs = bound(window.until, "until", Number.POSITIVE_INFINITY);
|
|
9118
|
+
if (!(sinceMs < untilMs)) throw new RangeError(`window since (${window.since}) must be before until (${window.until})`);
|
|
9187
9119
|
return {
|
|
9188
|
-
|
|
9189
|
-
|
|
9120
|
+
since: window.since === void 0 ? null : new Date(sinceMs).toISOString(),
|
|
9121
|
+
until: window.until === void 0 ? null : new Date(untilMs).toISOString(),
|
|
9122
|
+
sinceMs,
|
|
9123
|
+
untilMs
|
|
9190
9124
|
};
|
|
9191
9125
|
}
|
|
9126
|
+
function insideWindow(bounds, time) {
|
|
9127
|
+
if (bounds === null) return true;
|
|
9128
|
+
const ms2 = Date.parse(time);
|
|
9129
|
+
return Number.isFinite(ms2) && ms2 >= bounds.sinceMs && ms2 < bounds.untilMs;
|
|
9130
|
+
}
|
|
9131
|
+
function isRecordSpan(span2) {
|
|
9132
|
+
return span2.parent_span_id !== null && span2.attributes[CORRUPTION_RECEIPT_VERSION_ATTR] === void 0;
|
|
9133
|
+
}
|
|
9192
9134
|
function computeSessionFacts(spans, options = {}) {
|
|
9135
|
+
const bounds = windowBounds(options.window);
|
|
9136
|
+
return groupByTrace(spans).sort((left, right) => compareIds(left.traceId, right.traceId)).map((group) => sessionFactsForTrace(group.traceId, group.sessionId, group.spans, options, bounds));
|
|
9137
|
+
}
|
|
9138
|
+
function compareIds(left, right) {
|
|
9139
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
9140
|
+
}
|
|
9141
|
+
function groupByTrace(spans) {
|
|
9193
9142
|
const { sessionByTrace } = indexSessionIdsByTrace(spans);
|
|
9194
9143
|
const byTrace = /* @__PURE__ */ new Map();
|
|
9195
9144
|
for (const span2 of spans) {
|
|
@@ -9197,9 +9146,13 @@ function computeSessionFacts(spans, options = {}) {
|
|
|
9197
9146
|
if (group) group.push(span2);
|
|
9198
9147
|
else byTrace.set(span2.trace_id, [span2]);
|
|
9199
9148
|
}
|
|
9200
|
-
return [...byTrace].
|
|
9149
|
+
return [...byTrace].map(([traceId, traceSpans]) => ({
|
|
9150
|
+
traceId,
|
|
9151
|
+
sessionId: sessionByTrace.get(traceId) ?? null,
|
|
9152
|
+
spans: traceSpans
|
|
9153
|
+
}));
|
|
9201
9154
|
}
|
|
9202
|
-
function sessionFactsForTrace(traceId, sessionId, unordered, options) {
|
|
9155
|
+
function sessionFactsForTrace(traceId, sessionId, unordered, options, bounds) {
|
|
9203
9156
|
const spans = [...unordered].sort(byTraceOrder);
|
|
9204
9157
|
const harness = spans.map((span2) => stringAttr2(span2, "service.name")).find((name) => name !== null) ?? null;
|
|
9205
9158
|
const ownSpans = spans.filter((span2) => !isSubagentSpan(span2.attributes));
|
|
@@ -9213,8 +9166,11 @@ function sessionFactsForTrace(traceId, sessionId, unordered, options) {
|
|
|
9213
9166
|
for (const span2 of invoked) byName[toolName3(span2)] = (byName[toolName3(span2)] ?? 0) + 1;
|
|
9214
9167
|
const subagents = subagentsOf(ownSpans, spans);
|
|
9215
9168
|
const pullRequests = readPullRequests(ownSpans);
|
|
9216
|
-
const
|
|
9217
|
-
const
|
|
9169
|
+
const inWindow = (at) => insideWindow(bounds, at);
|
|
9170
|
+
const promptSpans = spans.filter((span2) => span2.name === "user.prompt" && inWindow(span2.start_time));
|
|
9171
|
+
const turnFacts = humanTurnsOf(spans);
|
|
9172
|
+
const humanTurns = turnFacts.turns.filter((turn) => inWindow(turn.at));
|
|
9173
|
+
const excludedTurns = excludedTurnFacts(turnFacts.excluded, inWindow);
|
|
9218
9174
|
const actorCounts = /* @__PURE__ */ new Map();
|
|
9219
9175
|
for (const span2 of promptSpans) {
|
|
9220
9176
|
const actor = stringAttr2(span2, ACTOR_ATTR) ?? "unclassified";
|
|
@@ -9234,16 +9190,20 @@ function sessionFactsForTrace(traceId, sessionId, unordered, options) {
|
|
|
9234
9190
|
);
|
|
9235
9191
|
const tokenSpan = spans.find((span2) => typeof span2.attributes[SESSION_TOKEN_TOTAL_ATTR] === "number");
|
|
9236
9192
|
const receipts = spans.filter((span2) => span2.attributes[CORRUPTION_RECEIPT_VERSION_ATTR] !== void 0);
|
|
9237
|
-
const recordSpans = spans.filter(
|
|
9238
|
-
(span2) => span2.parent_span_id !== null && span2.attributes[CORRUPTION_RECEIPT_VERSION_ATTR] === void 0
|
|
9239
|
-
).length;
|
|
9193
|
+
const recordSpans = spans.filter(isRecordSpan).length;
|
|
9240
9194
|
const corruptionSpan = spans.find((span2) => typeof span2.attributes[CORRUPTION_COUNT_ATTR] === "number");
|
|
9195
|
+
const cwdSpan = spans.find((span2) => stringAttr2(span2, SESSION_CWD_ATTR) !== null);
|
|
9241
9196
|
const facts = {
|
|
9242
9197
|
schemaVersion: 1,
|
|
9243
9198
|
kind: "traces.session_facts",
|
|
9244
9199
|
traceId,
|
|
9245
9200
|
sessionId,
|
|
9246
9201
|
harness,
|
|
9202
|
+
cwd: cwdSpan ? { value: stringAttr2(cwdSpan, SESSION_CWD_ATTR), spanIds: [cwdSpan.span_id], unavailable: null } : {
|
|
9203
|
+
value: null,
|
|
9204
|
+
spanIds: [],
|
|
9205
|
+
unavailable: `no span carries ${SESSION_CWD_ATTR}: the session source records no working directory, or the adapter that read it does not read one`
|
|
9206
|
+
},
|
|
9247
9207
|
spanCount: spans.length,
|
|
9248
9208
|
recordSpans,
|
|
9249
9209
|
unreadRecords: corruptionSpan ? {
|
|
@@ -9316,6 +9276,7 @@ function sessionFactsForTrace(traceId, sessionId, unordered, options) {
|
|
|
9316
9276
|
});
|
|
9317
9277
|
return {
|
|
9318
9278
|
...facts,
|
|
9279
|
+
cwd: withheld(),
|
|
9319
9280
|
humanTurns: withheld(),
|
|
9320
9281
|
finalMessages: withheld(),
|
|
9321
9282
|
changedFiles: withheld(),
|
|
@@ -9332,26 +9293,88 @@ function sessionFactsForTrace(traceId, sessionId, unordered, options) {
|
|
|
9332
9293
|
};
|
|
9333
9294
|
}
|
|
9334
9295
|
function buildSessionFactsReport(spans, options = {}) {
|
|
9335
|
-
const
|
|
9336
|
-
|
|
9296
|
+
const builder = createSessionFactsReportBuilder(options);
|
|
9297
|
+
builder.add(spans);
|
|
9298
|
+
return builder.build();
|
|
9299
|
+
}
|
|
9300
|
+
var WINDOW_SESSION_RULE = "a session is selected when at least one of its records starts or ends inside [since, until)";
|
|
9301
|
+
var WINDOW_TURN_RULE = "humanTurns, excludedTurns and turnsByActor keep only user.prompt turns that started inside [since, until); every other fact, and each turn's answered text, covers the whole session";
|
|
9302
|
+
function createSessionFactsReportBuilder(options = {}) {
|
|
9303
|
+
const bounds = windowBounds(options.window);
|
|
9304
|
+
const sessions = [];
|
|
9305
|
+
const digests = [];
|
|
9306
|
+
const outsideWindow = [];
|
|
9307
|
+
const unreadSessions = [];
|
|
9308
|
+
let spanCount = 0;
|
|
9337
9309
|
let evidenceThrough = null;
|
|
9338
|
-
|
|
9339
|
-
digest
|
|
9340
|
-
for (const
|
|
9341
|
-
|
|
9310
|
+
const select = (group) => {
|
|
9311
|
+
const digest = createHash7("sha256");
|
|
9312
|
+
for (const span2 of [...group.spans].sort((a, b) => a.span_id.localeCompare(b.span_id))) {
|
|
9313
|
+
digest.update(canonicalJson(span2)).update("\n");
|
|
9314
|
+
for (const time of [span2.start_time, span2.end_time]) {
|
|
9315
|
+
if (Number.isFinite(Date.parse(time)) && (evidenceThrough === null || Date.parse(time) > Date.parse(evidenceThrough))) evidenceThrough = time;
|
|
9316
|
+
}
|
|
9342
9317
|
}
|
|
9343
|
-
|
|
9318
|
+
digests.push({ traceId: group.traceId, digest: digest.digest("hex") });
|
|
9319
|
+
spanCount += group.spans.length;
|
|
9320
|
+
sessions.push(sessionFactsForTrace(group.traceId, group.sessionId, group.spans, options, bounds));
|
|
9321
|
+
};
|
|
9344
9322
|
return {
|
|
9345
|
-
|
|
9346
|
-
|
|
9347
|
-
|
|
9348
|
-
|
|
9349
|
-
|
|
9350
|
-
|
|
9351
|
-
|
|
9352
|
-
|
|
9353
|
-
|
|
9354
|
-
|
|
9323
|
+
add(spans) {
|
|
9324
|
+
for (const group of groupByTrace(spans)) {
|
|
9325
|
+
if (bounds === null) {
|
|
9326
|
+
select(group);
|
|
9327
|
+
continue;
|
|
9328
|
+
}
|
|
9329
|
+
const records = group.spans.filter(isRecordSpan);
|
|
9330
|
+
if (records.length === 0) {
|
|
9331
|
+
const unread = group.spans.find((span2) => typeof span2.attributes[CORRUPTION_COUNT_ATTR] === "number");
|
|
9332
|
+
unreadSessions.push({
|
|
9333
|
+
sessionId: group.sessionId,
|
|
9334
|
+
traceId: group.traceId,
|
|
9335
|
+
source: null,
|
|
9336
|
+
reason: `no record could be read (${unread ? `${unread.attributes[CORRUPTION_COUNT_ATTR]} unread record(s)` : "no record spans"}), so whether the session holds a record inside the window is unknown`
|
|
9337
|
+
});
|
|
9338
|
+
continue;
|
|
9339
|
+
}
|
|
9340
|
+
if (records.some((span2) => insideWindow(bounds, span2.start_time) || insideWindow(bounds, span2.end_time))) select(group);
|
|
9341
|
+
else outsideWindow.push(group.sessionId ?? group.traceId);
|
|
9342
|
+
}
|
|
9343
|
+
},
|
|
9344
|
+
addUnread(session) {
|
|
9345
|
+
if (bounds === null) {
|
|
9346
|
+
throw new Error("an unread session is reported only by a windowed report; without a window, refuse the selection");
|
|
9347
|
+
}
|
|
9348
|
+
unreadSessions.push(session);
|
|
9349
|
+
},
|
|
9350
|
+
build() {
|
|
9351
|
+
const digest = createHash7("sha256");
|
|
9352
|
+
for (const entry of [...digests].sort((a, b) => a.traceId.localeCompare(b.traceId) || a.digest.localeCompare(b.digest))) {
|
|
9353
|
+
digest.update(`${entry.traceId}
|
|
9354
|
+
${entry.digest}
|
|
9355
|
+
`);
|
|
9356
|
+
}
|
|
9357
|
+
return {
|
|
9358
|
+
schemaVersion: 1,
|
|
9359
|
+
kind: "traces.session_facts_report",
|
|
9360
|
+
measurementVersion: SESSION_FACTS_MEASUREMENT_VERSION,
|
|
9361
|
+
sourceDigest: "sha256:" + digest.digest("hex"),
|
|
9362
|
+
evidenceThrough,
|
|
9363
|
+
contentIncluded: options.includeContent !== false,
|
|
9364
|
+
generatedAt: options.generatedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
9365
|
+
harness: options.harness ?? null,
|
|
9366
|
+
spanCount,
|
|
9367
|
+
selection: bounds === null ? null : {
|
|
9368
|
+
since: bounds.since,
|
|
9369
|
+
until: bounds.until,
|
|
9370
|
+
sessionRule: WINDOW_SESSION_RULE,
|
|
9371
|
+
turnRule: WINDOW_TURN_RULE,
|
|
9372
|
+
outsideWindow: [...outsideWindow].sort(compareIds),
|
|
9373
|
+
unreadSessions: [...unreadSessions]
|
|
9374
|
+
},
|
|
9375
|
+
sessions: [...sessions].sort((left, right) => compareIds(left.traceId, right.traceId))
|
|
9376
|
+
};
|
|
9377
|
+
}
|
|
9355
9378
|
};
|
|
9356
9379
|
}
|
|
9357
9380
|
function factLine(label2, fact, rendered) {
|
|
@@ -9363,8 +9386,18 @@ function renderSessionFacts(report) {
|
|
|
9363
9386
|
const lines = [
|
|
9364
9387
|
`session facts \u2014 ${report.sessions.length} session(s), ${report.spanCount} span(s), deterministic, $0`
|
|
9365
9388
|
];
|
|
9389
|
+
const selection = report.selection;
|
|
9390
|
+
if (selection) {
|
|
9391
|
+
lines.push(
|
|
9392
|
+
`window [${selection.since ?? "open"}, ${selection.until ?? "open"}): turn facts count only turns inside it; ${selection.outsideWindow.length} session(s) read with no record inside it`
|
|
9393
|
+
);
|
|
9394
|
+
for (const unread of selection.unreadSessions) {
|
|
9395
|
+
lines.push(` unread session ${unread.sessionId ?? unread.traceId ?? unread.source ?? "unknown"}: ${unread.reason}`);
|
|
9396
|
+
}
|
|
9397
|
+
}
|
|
9366
9398
|
for (const facts of report.sessions) {
|
|
9367
9399
|
lines.push("", `${facts.sessionId ?? facts.traceId} [${facts.harness ?? "unknown harness"}] ${facts.spanCount} spans`);
|
|
9400
|
+
lines.push(factLine("cwd", facts.cwd, String(facts.cwd.value)));
|
|
9368
9401
|
lines.push(
|
|
9369
9402
|
factLine(
|
|
9370
9403
|
"tool calls",
|
|
@@ -9432,6 +9465,9 @@ function renderSessionFactsContext(report, options = {}) {
|
|
|
9432
9465
|
${JSON.stringify({ sessions, ...dropped2.length ? { omitted_fields: dropped2 } : {} })}`;
|
|
9433
9466
|
const dropped = [];
|
|
9434
9467
|
const sheds = [
|
|
9468
|
+
["human_turn_answered", (facts) => {
|
|
9469
|
+
for (const turn of facts.human_turns) delete turn.answered;
|
|
9470
|
+
}],
|
|
9435
9471
|
["human_turn_text", (facts) => {
|
|
9436
9472
|
for (const turn of facts.human_turns) delete turn.text;
|
|
9437
9473
|
}],
|
|
@@ -9522,6 +9558,7 @@ function compactFacts(facts) {
|
|
|
9522
9558
|
return {
|
|
9523
9559
|
session_id: facts.sessionId,
|
|
9524
9560
|
trace_id: facts.traceId,
|
|
9561
|
+
cwd: factJson(facts.cwd),
|
|
9525
9562
|
spans: facts.spanCount,
|
|
9526
9563
|
tool_calls: {
|
|
9527
9564
|
count: facts.toolCalls.value,
|
|
@@ -9544,7 +9581,8 @@ function compactFacts(facts) {
|
|
|
9544
9581
|
human_turns: (facts.humanTurns.value ?? []).map((turn) => ({
|
|
9545
9582
|
at: turn.at,
|
|
9546
9583
|
span_id: turn.spanId,
|
|
9547
|
-
text: turn.text
|
|
9584
|
+
text: turn.text,
|
|
9585
|
+
answered: turn.answered
|
|
9548
9586
|
})),
|
|
9549
9587
|
excluded_turns: facts.excludedTurns.value?.map((entry) => ({
|
|
9550
9588
|
reason: entry.reason,
|
|
@@ -9634,17 +9672,17 @@ import { isUtf8 as isUtf82 } from "buffer";
|
|
|
9634
9672
|
import { createHash as createHash10 } from "crypto";
|
|
9635
9673
|
import { constants } from "fs";
|
|
9636
9674
|
import { lstat as lstat2, open as open2, realpath as realpath3 } from "fs/promises";
|
|
9637
|
-
import { dirname as dirname8, isAbsolute as isAbsolute4, join as
|
|
9675
|
+
import { dirname as dirname8, isAbsolute as isAbsolute4, join as join18, relative as relative5, resolve as resolve6, sep as sep2 } from "path";
|
|
9638
9676
|
|
|
9639
9677
|
// src/bundle.ts
|
|
9640
9678
|
import { createHash as createHash9 } from "crypto";
|
|
9641
|
-
import { copyFile, cp, lstat, mkdir as mkdir2, readdir as
|
|
9642
|
-
import { dirname as dirname7, join as
|
|
9679
|
+
import { copyFile, cp, lstat, mkdir as mkdir2, readdir as readdir13, readFile as readFile7, stat as stat12, writeFile as writeFile5 } from "fs/promises";
|
|
9680
|
+
import { dirname as dirname7, join as join17, relative as relative4, resolve as resolve5, sep } from "path";
|
|
9643
9681
|
|
|
9644
9682
|
// src/improvement.ts
|
|
9645
9683
|
import { access, mkdir, mkdtemp as mkdtemp2, writeFile as writeFile3 } from "fs/promises";
|
|
9646
9684
|
import { tmpdir as tmpdir3 } from "os";
|
|
9647
|
-
import { dirname as dirname5, join as
|
|
9685
|
+
import { dirname as dirname5, join as join15, resolve as resolve4 } from "path";
|
|
9648
9686
|
import { pathToFileURL } from "url";
|
|
9649
9687
|
import {
|
|
9650
9688
|
AnalystRegistry,
|
|
@@ -10151,7 +10189,7 @@ function formatFindingRejections(reasons) {
|
|
|
10151
10189
|
|
|
10152
10190
|
// src/external.ts
|
|
10153
10191
|
import { spawn } from "child_process";
|
|
10154
|
-
import { readdir as
|
|
10192
|
+
import { readdir as readdir11, readFile as readFile5 } from "fs/promises";
|
|
10155
10193
|
function forceKill(child) {
|
|
10156
10194
|
try {
|
|
10157
10195
|
child.kill("SIGKILL");
|
|
@@ -10256,14 +10294,14 @@ function readProcessTableWithPs() {
|
|
|
10256
10294
|
});
|
|
10257
10295
|
}
|
|
10258
10296
|
async function readLinuxProcProcessTable() {
|
|
10259
|
-
const entries = await
|
|
10297
|
+
const entries = await readdir11("/proc", { withFileTypes: true });
|
|
10260
10298
|
const rows = await Promise.all(
|
|
10261
10299
|
entries.filter((entry) => entry.isDirectory() && /^\d+$/.test(entry.name)).map(async (entry) => {
|
|
10262
10300
|
try {
|
|
10263
|
-
const
|
|
10264
|
-
const commandEnd =
|
|
10301
|
+
const stat18 = await readFile5(`/proc/${entry.name}/stat`, "utf8");
|
|
10302
|
+
const commandEnd = stat18.lastIndexOf(")");
|
|
10265
10303
|
if (commandEnd < 0) return void 0;
|
|
10266
|
-
const fields =
|
|
10304
|
+
const fields = stat18.slice(commandEnd + 1).trim().split(/\s+/);
|
|
10267
10305
|
const pid = Number(entry.name);
|
|
10268
10306
|
const parentPid = Number(fields[1]);
|
|
10269
10307
|
if (!Number.isSafeInteger(pid) || pid < 1 || !Number.isSafeInteger(parentPid) || parentPid < 0) {
|
|
@@ -11938,13 +11976,13 @@ async function runTraceStoreInvestigation(opts) {
|
|
|
11938
11976
|
};
|
|
11939
11977
|
}
|
|
11940
11978
|
async function writeTraceImprovementArtifacts(result, outDir) {
|
|
11941
|
-
const directory = outDir ? resolve4(outDir) : await mkdtemp2(
|
|
11979
|
+
const directory = outDir ? resolve4(outDir) : await mkdtemp2(join15(tmpdir3(), "traces-improvement-"));
|
|
11942
11980
|
await mkdir(directory, { recursive: true });
|
|
11943
11981
|
const paths = {
|
|
11944
11982
|
directory,
|
|
11945
|
-
result:
|
|
11946
|
-
evidence:
|
|
11947
|
-
report:
|
|
11983
|
+
result: join15(directory, "result.json"),
|
|
11984
|
+
evidence: join15(directory, "evidence.jsonl"),
|
|
11985
|
+
report: join15(directory, "report.md"),
|
|
11948
11986
|
traces: result.otlpPath
|
|
11949
11987
|
};
|
|
11950
11988
|
const { report, ...machineResult } = result;
|
|
@@ -11957,11 +11995,11 @@ async function writeTraceImprovementArtifacts(result, outDir) {
|
|
|
11957
11995
|
}
|
|
11958
11996
|
async function runTraceImprovement(opts) {
|
|
11959
11997
|
if (opts.sourceBundle && opts.outDir) await assertOutsideSourceBundle(opts.sourceBundle.path, opts.outDir);
|
|
11960
|
-
const directory = opts.outDir ? resolve4(opts.outDir) : await mkdtemp2(
|
|
11998
|
+
const directory = opts.outDir ? resolve4(opts.outDir) : await mkdtemp2(join15(tmpdir3(), "traces-improvement-"));
|
|
11961
11999
|
await mkdir(directory, { recursive: true });
|
|
11962
12000
|
const investigation = await runTraceInvestigation({
|
|
11963
12001
|
...opts,
|
|
11964
|
-
otlpOutPath: opts.otlpOutPath ??
|
|
12002
|
+
otlpOutPath: opts.otlpOutPath ?? join15(directory, "traces.otlp.jsonl")
|
|
11965
12003
|
});
|
|
11966
12004
|
const result = {
|
|
11967
12005
|
...investigation,
|
|
@@ -11978,9 +12016,9 @@ async function saveReport(path, report) {
|
|
|
11978
12016
|
}
|
|
11979
12017
|
|
|
11980
12018
|
// src/session-index.ts
|
|
11981
|
-
import { mkdtemp as mkdtemp3, readdir as
|
|
12019
|
+
import { mkdtemp as mkdtemp3, readdir as readdir12, readFile as readFile6, stat as stat11, writeFile as writeFile4 } from "fs/promises";
|
|
11982
12020
|
import { tmpdir as tmpdir4 } from "os";
|
|
11983
|
-
import { dirname as dirname6, join as
|
|
12021
|
+
import { dirname as dirname6, join as join16 } from "path";
|
|
11984
12022
|
function indexRow(record2) {
|
|
11985
12023
|
return {
|
|
11986
12024
|
session: record2.session,
|
|
@@ -12035,7 +12073,7 @@ function buildSessionIndex(records, opts = {}) {
|
|
|
12035
12073
|
}
|
|
12036
12074
|
async function pathStat2(path) {
|
|
12037
12075
|
try {
|
|
12038
|
-
return await
|
|
12076
|
+
return await stat11(path);
|
|
12039
12077
|
} catch {
|
|
12040
12078
|
return null;
|
|
12041
12079
|
}
|
|
@@ -12053,7 +12091,7 @@ async function findContextRoot(cwd) {
|
|
|
12053
12091
|
if (!s) return null;
|
|
12054
12092
|
if (!s.isDirectory()) current = dirname6(current);
|
|
12055
12093
|
for (; ; ) {
|
|
12056
|
-
if (await pathStat2(
|
|
12094
|
+
if (await pathStat2(join16(current, ".git")) || await dirExists(join16(current, ".evolve")) || await fileExists(join16(current, "AGENTS.md")) || await fileExists(join16(current, "CLAUDE.md"))) {
|
|
12057
12095
|
return current;
|
|
12058
12096
|
}
|
|
12059
12097
|
const parent = dirname6(current);
|
|
@@ -12115,13 +12153,13 @@ async function walkMarkdown(root, kind, maxFiles = 100) {
|
|
|
12115
12153
|
if (out.length >= maxFiles) return;
|
|
12116
12154
|
let entries;
|
|
12117
12155
|
try {
|
|
12118
|
-
entries = await
|
|
12156
|
+
entries = await readdir12(dir, { withFileTypes: true });
|
|
12119
12157
|
} catch {
|
|
12120
12158
|
return;
|
|
12121
12159
|
}
|
|
12122
12160
|
for (const entry of entries) {
|
|
12123
12161
|
if (out.length >= maxFiles) return;
|
|
12124
|
-
const path =
|
|
12162
|
+
const path = join16(dir, entry.name);
|
|
12125
12163
|
if (entry.isDirectory()) {
|
|
12126
12164
|
await walk(path);
|
|
12127
12165
|
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
@@ -12136,33 +12174,33 @@ async function walkMarkdown(root, kind, maxFiles = 100) {
|
|
|
12136
12174
|
async function collectContextRoot(root) {
|
|
12137
12175
|
const files = [];
|
|
12138
12176
|
for (const name of ["AGENTS.md", "CLAUDE.md", "GEMINI.md"]) {
|
|
12139
|
-
const summary = await summarizeFile(
|
|
12177
|
+
const summary = await summarizeFile(join16(root, name), "instruction-doc");
|
|
12140
12178
|
if (summary) files.push(summary);
|
|
12141
12179
|
}
|
|
12142
|
-
const evolve =
|
|
12180
|
+
const evolve = join16(root, ".evolve");
|
|
12143
12181
|
for (const name of ["skill-runs.jsonl", "governor.jsonl", "experiments.jsonl"]) {
|
|
12144
|
-
const summary = await summarizeFile(
|
|
12182
|
+
const summary = await summarizeFile(join16(evolve, name), "evolve-jsonl");
|
|
12145
12183
|
if (summary) files.push(summary);
|
|
12146
12184
|
}
|
|
12147
12185
|
for (const name of ["scorecard.json", "current.json"]) {
|
|
12148
|
-
const summary = await summarizeFile(
|
|
12186
|
+
const summary = await summarizeFile(join16(evolve, name), "evolve-json");
|
|
12149
12187
|
if (summary) files.push(summary);
|
|
12150
12188
|
}
|
|
12151
|
-
files.push(...await walkMarkdown(
|
|
12152
|
-
files.push(...await walkMarkdown(
|
|
12189
|
+
files.push(...await walkMarkdown(join16(evolve, "reflections"), "reflection"));
|
|
12190
|
+
files.push(...await walkMarkdown(join16(evolve, "handoffs"), "handoff"));
|
|
12153
12191
|
for (const name of await listEvolveRootFiles(evolve)) {
|
|
12154
12192
|
if (/^handoff-.*\.md$/.test(name)) {
|
|
12155
|
-
const summary = await summarizeFile(
|
|
12193
|
+
const summary = await summarizeFile(join16(evolve, name), "handoff");
|
|
12156
12194
|
if (summary) files.push(summary);
|
|
12157
12195
|
}
|
|
12158
12196
|
}
|
|
12159
|
-
const progress2 = await summarizeFile(
|
|
12197
|
+
const progress2 = await summarizeFile(join16(evolve, "progress.md"), "other");
|
|
12160
12198
|
if (progress2) files.push(progress2);
|
|
12161
12199
|
return { root, files: files.sort((a, b) => a.path.localeCompare(b.path)) };
|
|
12162
12200
|
}
|
|
12163
12201
|
async function listEvolveRootFiles(evolve) {
|
|
12164
12202
|
try {
|
|
12165
|
-
const entries = await
|
|
12203
|
+
const entries = await readdir12(evolve, { withFileTypes: true });
|
|
12166
12204
|
return entries.filter((entry) => entry.isFile()).map((entry) => entry.name).sort();
|
|
12167
12205
|
} catch {
|
|
12168
12206
|
return [];
|
|
@@ -12211,7 +12249,7 @@ function serializeSessionIndex(index) {
|
|
|
12211
12249
|
`;
|
|
12212
12250
|
}
|
|
12213
12251
|
async function writeSessionIndexFile(index, outPath) {
|
|
12214
|
-
const path = outPath ??
|
|
12252
|
+
const path = outPath ?? join16(await mkdtemp3(join16(tmpdir4(), "traces-index-")), "session-index.json");
|
|
12215
12253
|
await writeFile4(path, serializeSessionIndex(index), "utf8");
|
|
12216
12254
|
return path;
|
|
12217
12255
|
}
|
|
@@ -12705,7 +12743,7 @@ function bundlePath(...parts) {
|
|
|
12705
12743
|
}
|
|
12706
12744
|
async function statOrNull(path) {
|
|
12707
12745
|
try {
|
|
12708
|
-
return await
|
|
12746
|
+
return await stat12(path);
|
|
12709
12747
|
} catch (error) {
|
|
12710
12748
|
if (isMissingPathError(error)) return null;
|
|
12711
12749
|
if (error.code === "ENOTDIR") return null;
|
|
@@ -12754,8 +12792,8 @@ async function listSessionBundleFiles(root) {
|
|
|
12754
12792
|
const pending = [resolve5(root)];
|
|
12755
12793
|
while (pending.length > 0) {
|
|
12756
12794
|
const dir = pending.pop();
|
|
12757
|
-
for (const entry of await
|
|
12758
|
-
const path =
|
|
12795
|
+
for (const entry of await readdir13(dir, { withFileTypes: true })) {
|
|
12796
|
+
const path = join17(dir, entry.name);
|
|
12759
12797
|
if (entry.isDirectory()) pending.push(path);
|
|
12760
12798
|
else if (entry.isFile()) out.push(relative4(resolve5(root), path).split(sep).join("/"));
|
|
12761
12799
|
}
|
|
@@ -12763,14 +12801,14 @@ async function listSessionBundleFiles(root) {
|
|
|
12763
12801
|
return out.sort();
|
|
12764
12802
|
}
|
|
12765
12803
|
async function sliceLedgerJsonl(ctx, name) {
|
|
12766
|
-
const source =
|
|
12804
|
+
const source = join17(ctx.evolveDir, name);
|
|
12767
12805
|
const s = await statOrNull(source);
|
|
12768
12806
|
if (!s?.isFile()) {
|
|
12769
12807
|
ctx.absent.push({ path: bundlePath("ledger", name), reason: `no ${name} at ${source}` });
|
|
12770
12808
|
return;
|
|
12771
12809
|
}
|
|
12772
12810
|
const text2 = await readFile7(source, "utf8");
|
|
12773
|
-
const target =
|
|
12811
|
+
const target = join17(ctx.ledgerDir, name);
|
|
12774
12812
|
if (ctx.windowStartMs === null || ctx.windowEndMs === null) {
|
|
12775
12813
|
await writeFile5(target, text2, "utf8");
|
|
12776
12814
|
ctx.slices.push({ path: bundlePath("ledger", name), source, rule: "copied-whole" });
|
|
@@ -12789,19 +12827,19 @@ async function sliceLedgerJsonl(ctx, name) {
|
|
|
12789
12827
|
});
|
|
12790
12828
|
}
|
|
12791
12829
|
async function copyLedgerWhole(ctx, name) {
|
|
12792
|
-
const source =
|
|
12830
|
+
const source = join17(ctx.evolveDir, name);
|
|
12793
12831
|
const s = await statOrNull(source);
|
|
12794
12832
|
if (!s?.isFile()) {
|
|
12795
12833
|
ctx.absent.push({ path: bundlePath("ledger", name), reason: `no ${name} at ${source}` });
|
|
12796
12834
|
return;
|
|
12797
12835
|
}
|
|
12798
|
-
await cp(source,
|
|
12836
|
+
await cp(source, join17(ctx.ledgerDir, name));
|
|
12799
12837
|
ctx.slices.push({ path: bundlePath("ledger", name), source, rule: "copied-whole" });
|
|
12800
12838
|
}
|
|
12801
12839
|
async function copyLatestHandoff(ctx) {
|
|
12802
12840
|
let names;
|
|
12803
12841
|
try {
|
|
12804
|
-
names = (await
|
|
12842
|
+
names = (await readdir13(ctx.evolveDir, { withFileTypes: true })).filter((entry) => entry.isFile() && /^handoff-.*\.md$/.test(entry.name)).map((entry) => entry.name).sort();
|
|
12805
12843
|
} catch {
|
|
12806
12844
|
names = [];
|
|
12807
12845
|
}
|
|
@@ -12813,12 +12851,12 @@ async function copyLatestHandoff(ctx) {
|
|
|
12813
12851
|
});
|
|
12814
12852
|
return;
|
|
12815
12853
|
}
|
|
12816
|
-
const source =
|
|
12817
|
-
await cp(source,
|
|
12854
|
+
const source = join17(ctx.evolveDir, latest);
|
|
12855
|
+
await cp(source, join17(ctx.ledgerDir, latest));
|
|
12818
12856
|
ctx.slices.push({ path: bundlePath("ledger", latest), source, rule: "latest-by-name" });
|
|
12819
12857
|
}
|
|
12820
12858
|
async function copyWindowReflections(ctx) {
|
|
12821
|
-
const reflectionsDir =
|
|
12859
|
+
const reflectionsDir = join17(ctx.evolveDir, "reflections");
|
|
12822
12860
|
const s = await statOrNull(reflectionsDir);
|
|
12823
12861
|
if (!s?.isDirectory()) {
|
|
12824
12862
|
ctx.absent.push({
|
|
@@ -12827,7 +12865,7 @@ async function copyWindowReflections(ctx) {
|
|
|
12827
12865
|
});
|
|
12828
12866
|
return;
|
|
12829
12867
|
}
|
|
12830
|
-
const names = (await
|
|
12868
|
+
const names = (await readdir13(reflectionsDir, { withFileTypes: true })).filter((entry) => entry.isFile() && entry.name.endsWith(".md")).map((entry) => entry.name).sort();
|
|
12831
12869
|
const windowed = ctx.windowStartMs === null || ctx.windowEndMs === null ? names : names.filter((name) => {
|
|
12832
12870
|
const date = name.match(/(\d{4}-\d{2}-\d{2})/)?.[1];
|
|
12833
12871
|
if (!date) return false;
|
|
@@ -12842,11 +12880,11 @@ async function copyWindowReflections(ctx) {
|
|
|
12842
12880
|
});
|
|
12843
12881
|
return;
|
|
12844
12882
|
}
|
|
12845
|
-
await mkdir2(
|
|
12883
|
+
await mkdir2(join17(ctx.ledgerDir, "reflections"), { recursive: true });
|
|
12846
12884
|
const rule = ctx.windowStartMs === null ? "copied-whole" : "session-window-dated-files";
|
|
12847
12885
|
for (const name of windowed) {
|
|
12848
|
-
const source =
|
|
12849
|
-
await cp(source,
|
|
12886
|
+
const source = join17(reflectionsDir, name);
|
|
12887
|
+
await cp(source, join17(ctx.ledgerDir, "reflections", name));
|
|
12850
12888
|
ctx.slices.push({ path: bundlePath("ledger", "reflections", name), source, rule });
|
|
12851
12889
|
}
|
|
12852
12890
|
}
|
|
@@ -12854,7 +12892,7 @@ async function assembleSessionBundle(opts) {
|
|
|
12854
12892
|
const { adapter, ref } = opts;
|
|
12855
12893
|
const generatedAt = opts.generatedAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
12856
12894
|
try {
|
|
12857
|
-
await
|
|
12895
|
+
await stat12(ref.path);
|
|
12858
12896
|
} catch (error) {
|
|
12859
12897
|
if (isMissingPathError(error)) {
|
|
12860
12898
|
throw new Error(`session transcript not found at ${ref.path} \u2014 a bundle cannot be assembled without its transcript`);
|
|
@@ -12870,17 +12908,17 @@ async function assembleSessionBundle(opts) {
|
|
|
12870
12908
|
const transcriptSha256 = row.sourceFiles.find((file) => resolve5(file.path) === resolve5(ref.path))?.sha256 ?? row.sourceSha256;
|
|
12871
12909
|
const outDir = resolve5(opts.outDir);
|
|
12872
12910
|
await mkdir2(outDir, { recursive: true });
|
|
12873
|
-
if ((await
|
|
12911
|
+
if ((await readdir13(outDir)).length > 0) {
|
|
12874
12912
|
throw new Error(
|
|
12875
12913
|
`bundle output directory ${outDir} is not empty \u2014 pass a new directory so one bundle holds exactly one session`
|
|
12876
12914
|
);
|
|
12877
12915
|
}
|
|
12878
12916
|
const absent = [];
|
|
12879
12917
|
const slices = [];
|
|
12880
|
-
await mkdir2(
|
|
12881
|
-
const subagentsSource =
|
|
12918
|
+
await mkdir2(join17(outDir, "session"), { recursive: true });
|
|
12919
|
+
const subagentsSource = join17(ref.path.replace(/\.jsonl$/, ""), "subagents");
|
|
12882
12920
|
if ((await statOrNull(subagentsSource))?.isDirectory()) {
|
|
12883
|
-
await cp(subagentsSource,
|
|
12921
|
+
await cp(subagentsSource, join17(outDir, "session", "subagents"), { recursive: true });
|
|
12884
12922
|
} else {
|
|
12885
12923
|
absent.push({ path: bundlePath("session", "subagents"), reason: `no subagents directory at ${subagentsSource}` });
|
|
12886
12924
|
}
|
|
@@ -12890,7 +12928,7 @@ async function assembleSessionBundle(opts) {
|
|
|
12890
12928
|
const sourceId = sourceFileId(file.path);
|
|
12891
12929
|
const subagentRelative = relative4(subagentsSource, file.path);
|
|
12892
12930
|
const path = resolve5(file.path) === resolve5(ref.path) ? bundlePath("session", "transcript.jsonl") : !subagentRelative.startsWith("..") && !subagentRelative.startsWith(sep) ? bundlePath("session", "subagents", subagentRelative) : bundlePath("session", "sources", `${sourceId}.jsonl`);
|
|
12893
|
-
const target =
|
|
12931
|
+
const target = join17(outDir, path);
|
|
12894
12932
|
await mkdir2(dirname7(target), { recursive: true });
|
|
12895
12933
|
await copyFile(file.path, target);
|
|
12896
12934
|
if (await fileSha256(target, opts.signal) !== file.sha256) {
|
|
@@ -12898,8 +12936,8 @@ async function assembleSessionBundle(opts) {
|
|
|
12898
12936
|
}
|
|
12899
12937
|
retainedSourceIds.set(path, sourceId);
|
|
12900
12938
|
}
|
|
12901
|
-
await mkdir2(
|
|
12902
|
-
const otlpPath =
|
|
12939
|
+
await mkdir2(join17(outDir, "derived"), { recursive: true });
|
|
12940
|
+
const otlpPath = join17(outDir, "derived", "trace.otlp.jsonl");
|
|
12903
12941
|
const investigation = await runTraceInvestigation({
|
|
12904
12942
|
spans,
|
|
12905
12943
|
harness: ref.harness,
|
|
@@ -12911,7 +12949,7 @@ async function assembleSessionBundle(opts) {
|
|
|
12911
12949
|
signal: opts.signal,
|
|
12912
12950
|
log: opts.log
|
|
12913
12951
|
});
|
|
12914
|
-
await writeFile5(
|
|
12952
|
+
await writeFile5(join17(outDir, "derived", "report.md"), investigation.report, "utf8");
|
|
12915
12953
|
const evidence3 = await buildPolicyEvidenceRecord(ref, spans, {
|
|
12916
12954
|
generatedAt,
|
|
12917
12955
|
minLoopOccurrences: opts.minLoopOccurrences,
|
|
@@ -12919,13 +12957,13 @@ async function assembleSessionBundle(opts) {
|
|
|
12919
12957
|
otlpPath: bundlePath("derived", "trace.otlp.jsonl"),
|
|
12920
12958
|
sourceSha256: transcriptSha256
|
|
12921
12959
|
});
|
|
12922
|
-
await writeFile5(
|
|
12960
|
+
await writeFile5(join17(outDir, "derived", "evidence.jsonl"), serializePolicyEvidence([evidence3]), "utf8");
|
|
12923
12961
|
const index = await buildSessionIndexFromRows([{ ref, spans }], {
|
|
12924
12962
|
generatedAt,
|
|
12925
12963
|
minLoopOccurrences: opts.minLoopOccurrences,
|
|
12926
12964
|
selection: { command: "bundle", harness: ref.harness, session: ref.sessionId }
|
|
12927
12965
|
});
|
|
12928
|
-
await writeFile5(
|
|
12966
|
+
await writeFile5(join17(outDir, "derived", "session-index.json"), serializeSessionIndex(index), "utf8");
|
|
12929
12967
|
const { firstSpanAt, lastSpanAt } = evidence3.metrics;
|
|
12930
12968
|
const windowStartMs = firstSpanAt !== null ? Date.parse(firstSpanAt) - SESSION_WINDOW_PAD_MS : null;
|
|
12931
12969
|
const windowEndMs = lastSpanAt !== null ? Date.parse(lastSpanAt) + SESSION_WINDOW_PAD_MS : null;
|
|
@@ -12933,11 +12971,11 @@ async function assembleSessionBundle(opts) {
|
|
|
12933
12971
|
if (!contextRoot) {
|
|
12934
12972
|
absent.push({ path: "ledger", reason: `no context root found from session cwd ${ref.cwd ?? "(unknown)"}` });
|
|
12935
12973
|
} else {
|
|
12936
|
-
const evolveDir =
|
|
12974
|
+
const evolveDir = join17(contextRoot, ".evolve");
|
|
12937
12975
|
if (!(await statOrNull(evolveDir))?.isDirectory()) {
|
|
12938
12976
|
absent.push({ path: "ledger", reason: `no .evolve ledger at ${evolveDir}` });
|
|
12939
12977
|
} else {
|
|
12940
|
-
const ledgerDir =
|
|
12978
|
+
const ledgerDir = join17(outDir, "ledger");
|
|
12941
12979
|
await mkdir2(ledgerDir, { recursive: true });
|
|
12942
12980
|
const ctx = { evolveDir, ledgerDir, windowStartMs, windowEndMs, slices, absent };
|
|
12943
12981
|
await sliceLedgerJsonl(ctx, "experiments.jsonl");
|
|
@@ -12957,8 +12995,8 @@ async function assembleSessionBundle(opts) {
|
|
|
12957
12995
|
} else {
|
|
12958
12996
|
const log = await runGitLog(ref.cwd, new Date(windowStartMs).toISOString(), new Date(windowEndMs).toISOString());
|
|
12959
12997
|
if (log.succeeded) {
|
|
12960
|
-
await mkdir2(
|
|
12961
|
-
await writeFile5(
|
|
12998
|
+
await mkdir2(join17(outDir, "repo"), { recursive: true });
|
|
12999
|
+
await writeFile5(join17(outDir, "repo", "git-log.txt"), log.stdout ? `${log.stdout}
|
|
12962
13000
|
` : "", "utf8");
|
|
12963
13001
|
} else {
|
|
12964
13002
|
absent.push({ path: gitLogPath, reason: `git log failed in ${ref.cwd}: ${log.error}` });
|
|
@@ -12997,20 +13035,20 @@ async function assembleSessionBundle(opts) {
|
|
|
12997
13035
|
async function hashSessionBundleFiles(root) {
|
|
12998
13036
|
const files = [];
|
|
12999
13037
|
for (const path of await listSessionBundleFiles(root)) {
|
|
13000
|
-
const filePath =
|
|
13001
|
-
const info = await
|
|
13038
|
+
const filePath = join17(root, path);
|
|
13039
|
+
const info = await stat12(filePath);
|
|
13002
13040
|
files.push({ path, bytes: info.size, sha256: await fileSha256(filePath) });
|
|
13003
13041
|
}
|
|
13004
13042
|
return files;
|
|
13005
13043
|
}
|
|
13006
13044
|
async function writeSessionBundleManifest(outDir, manifest) {
|
|
13007
|
-
const manifestPath =
|
|
13045
|
+
const manifestPath = join17(outDir, "manifest.json");
|
|
13008
13046
|
await writeFile5(manifestPath, `${JSON.stringify(manifest, null, 2)}
|
|
13009
13047
|
`, "utf8");
|
|
13010
13048
|
return manifestPath;
|
|
13011
13049
|
}
|
|
13012
13050
|
async function readSessionBundleManifest(bundleDir) {
|
|
13013
|
-
const path =
|
|
13051
|
+
const path = join17(resolve5(bundleDir), "manifest.json");
|
|
13014
13052
|
let text2;
|
|
13015
13053
|
try {
|
|
13016
13054
|
text2 = await readFile7(path, "utf8");
|
|
@@ -13054,8 +13092,8 @@ async function nonRegularEntries(root) {
|
|
|
13054
13092
|
const pending = [resolve5(root)];
|
|
13055
13093
|
while (pending.length > 0) {
|
|
13056
13094
|
const dir = pending.pop();
|
|
13057
|
-
for (const entry of await
|
|
13058
|
-
const path =
|
|
13095
|
+
for (const entry of await readdir13(dir, { withFileTypes: true })) {
|
|
13096
|
+
const path = join17(dir, entry.name);
|
|
13059
13097
|
if (entry.isDirectory()) pending.push(path);
|
|
13060
13098
|
else if (!entry.isFile()) out.push(relative4(resolve5(root), path).split(sep).join("/"));
|
|
13061
13099
|
}
|
|
@@ -13075,7 +13113,7 @@ async function verifySessionBundle(bundleDir) {
|
|
|
13075
13113
|
continue;
|
|
13076
13114
|
}
|
|
13077
13115
|
listed.add(file.path);
|
|
13078
|
-
const info = await lstat(
|
|
13116
|
+
const info = await lstat(join17(directory, file.path)).catch((error) => {
|
|
13079
13117
|
if (isMissingPathError(error)) return null;
|
|
13080
13118
|
throw error;
|
|
13081
13119
|
});
|
|
@@ -13084,7 +13122,7 @@ async function verifySessionBundle(bundleDir) {
|
|
|
13084
13122
|
continue;
|
|
13085
13123
|
}
|
|
13086
13124
|
if (!info.isFile()) continue;
|
|
13087
|
-
const sha2562 = await fileSha256(
|
|
13125
|
+
const sha2562 = await fileSha256(join17(directory, file.path));
|
|
13088
13126
|
if (info.size !== file.bytes || sha2562 !== file.sha256) {
|
|
13089
13127
|
issues.push({
|
|
13090
13128
|
kind: "mismatch",
|
|
@@ -13104,7 +13142,7 @@ async function verifySessionBundle(bundleDir) {
|
|
|
13104
13142
|
}
|
|
13105
13143
|
let source;
|
|
13106
13144
|
if (manifest.projection) {
|
|
13107
|
-
const sourceManifest =
|
|
13145
|
+
const sourceManifest = join17(manifest.projection.sourceDirectory, "manifest.json");
|
|
13108
13146
|
const bytes2 = await readFile7(sourceManifest).catch((error) => {
|
|
13109
13147
|
if (isMissingPathError(error)) return null;
|
|
13110
13148
|
throw error;
|
|
@@ -13142,7 +13180,7 @@ async function openBundleFile(root, file) {
|
|
|
13142
13180
|
const path = containedPath(root, file.path);
|
|
13143
13181
|
let current = root;
|
|
13144
13182
|
for (const part of relative5(root, path).split(sep2)) {
|
|
13145
|
-
current =
|
|
13183
|
+
current = join18(current, part);
|
|
13146
13184
|
if ((await lstat2(current)).isSymbolicLink()) throw new Error("bundle source symlinks are not permitted");
|
|
13147
13185
|
}
|
|
13148
13186
|
if (await realpath3(path) !== path) throw new Error("bundle source resolved outside its declared path");
|
|
@@ -13181,7 +13219,7 @@ async function createBundleSourceReader(directory, spans, options = {}) {
|
|
|
13181
13219
|
options.signal?.throwIfAborted();
|
|
13182
13220
|
if ((await lstat2(resolve6(directory))).isSymbolicLink()) throw new Error("bundle directory must not be a symlink");
|
|
13183
13221
|
const root = await realpath3(resolve6(directory));
|
|
13184
|
-
if ((await lstat2(
|
|
13222
|
+
if ((await lstat2(join18(root, "manifest.json"))).isSymbolicLink()) throw new Error("bundle manifest must not be a symlink");
|
|
13185
13223
|
const manifest = await readSessionBundleManifest(root);
|
|
13186
13224
|
if (manifest.view !== "full") throw new Error("original source reads require an explicitly authorized full bundle");
|
|
13187
13225
|
if (!Array.isArray(manifest.files)) throw new Error("bundle manifest has no file receipts");
|
|
@@ -13203,7 +13241,7 @@ async function createBundleSourceReader(directory, spans, options = {}) {
|
|
|
13203
13241
|
const traceFile = manifest.files.find((file) => file.path === TRACE_FILE);
|
|
13204
13242
|
if (!traceFile) throw new Error("bundle has no derived trace receipt");
|
|
13205
13243
|
await validateFile(root, traceFile, options.signal);
|
|
13206
|
-
const recorded = await readOtlpInput(
|
|
13244
|
+
const recorded = await readOtlpInput(join18(root, TRACE_FILE));
|
|
13207
13245
|
await validateFile(root, traceFile, options.signal);
|
|
13208
13246
|
const original = new Map(recorded.spans.map((span2) => [identity(span2), span2]));
|
|
13209
13247
|
const allowed = /* @__PURE__ */ new Map();
|
|
@@ -13601,7 +13639,7 @@ import {
|
|
|
13601
13639
|
readFile as readFile10,
|
|
13602
13640
|
rm as rm2
|
|
13603
13641
|
} from "fs/promises";
|
|
13604
|
-
import { basename as
|
|
13642
|
+
import { basename as basename10, dirname as dirname10, join as join21, resolve as resolve8 } from "path";
|
|
13605
13643
|
|
|
13606
13644
|
// src/codetracebench-files.ts
|
|
13607
13645
|
import { createHash as createHash12, randomUUID } from "crypto";
|
|
@@ -13614,18 +13652,18 @@ import {
|
|
|
13614
13652
|
rename as rename2,
|
|
13615
13653
|
rm,
|
|
13616
13654
|
rmdir,
|
|
13617
|
-
stat as
|
|
13655
|
+
stat as stat13,
|
|
13618
13656
|
unlink,
|
|
13619
13657
|
writeFile as writeFile6
|
|
13620
13658
|
} from "fs/promises";
|
|
13621
13659
|
import { hostname } from "os";
|
|
13622
|
-
import { basename as
|
|
13660
|
+
import { basename as basename9, dirname as dirname9, isAbsolute as isAbsolute5, join as join19, relative as relative6, resolve as resolve7, sep as sep3 } from "path";
|
|
13623
13661
|
async function claimImportLock(input) {
|
|
13624
13662
|
const claimId = randomUUID();
|
|
13625
13663
|
const path = `${input.outputDirectory}.lock`;
|
|
13626
|
-
const temporary =
|
|
13664
|
+
const temporary = join19(
|
|
13627
13665
|
dirname9(path),
|
|
13628
|
-
`.${
|
|
13666
|
+
`.${basename9(path)}.${claimId}.tmp`
|
|
13629
13667
|
);
|
|
13630
13668
|
const metadata = {
|
|
13631
13669
|
kind: "traces.codetracebench-import-lock",
|
|
@@ -13714,8 +13752,8 @@ async function publishStagedFiles(stagingDirectory, outputDirectory, files, sign
|
|
|
13714
13752
|
try {
|
|
13715
13753
|
for (const file of files) {
|
|
13716
13754
|
signal.throwIfAborted();
|
|
13717
|
-
const destination =
|
|
13718
|
-
await link(
|
|
13755
|
+
const destination = join19(outputDirectory, file);
|
|
13756
|
+
await link(join19(stagingDirectory, file), destination);
|
|
13719
13757
|
published.push(destination);
|
|
13720
13758
|
}
|
|
13721
13759
|
} catch (error) {
|
|
@@ -13745,7 +13783,7 @@ async function containedDirectory(path) {
|
|
|
13745
13783
|
cause: error
|
|
13746
13784
|
});
|
|
13747
13785
|
}
|
|
13748
|
-
const info = await
|
|
13786
|
+
const info = await stat13(canonical);
|
|
13749
13787
|
if (!info.isDirectory()) {
|
|
13750
13788
|
throw new TypeError(`CodeTraceBench trajectory path is not a directory: ${canonical}`);
|
|
13751
13789
|
}
|
|
@@ -13768,7 +13806,7 @@ async function optionalContainedFile(root, candidate) {
|
|
|
13768
13806
|
if (fromRoot === ".." || fromRoot.startsWith(`..${sep3}`) || isAbsolute5(fromRoot)) {
|
|
13769
13807
|
throw new Error(`CodeTraceBench trajectory file escapes its input directory: ${candidate}`);
|
|
13770
13808
|
}
|
|
13771
|
-
const info = await
|
|
13809
|
+
const info = await stat13(canonical);
|
|
13772
13810
|
if (!info.isFile()) {
|
|
13773
13811
|
throw new TypeError(`CodeTraceBench trajectory path is not a file: ${candidate}`);
|
|
13774
13812
|
}
|
|
@@ -13785,9 +13823,9 @@ async function assertPathMissing(path) {
|
|
|
13785
13823
|
}
|
|
13786
13824
|
async function atomicWriteFile(path, content, signal) {
|
|
13787
13825
|
signal.throwIfAborted();
|
|
13788
|
-
const temporary =
|
|
13826
|
+
const temporary = join19(
|
|
13789
13827
|
dirname9(path),
|
|
13790
|
-
`.${
|
|
13828
|
+
`.${basename9(path)}.${randomUUID()}.tmp`
|
|
13791
13829
|
);
|
|
13792
13830
|
try {
|
|
13793
13831
|
await writeFile6(temporary, content, {
|
|
@@ -13813,17 +13851,17 @@ function isErrno(error, code) {
|
|
|
13813
13851
|
|
|
13814
13852
|
// src/codetracebench-trajectory.ts
|
|
13815
13853
|
import { readFile as readFile9 } from "fs/promises";
|
|
13816
|
-
import { join as
|
|
13854
|
+
import { join as join20 } from "path";
|
|
13817
13855
|
var TRAJECTORY_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,254}$/;
|
|
13818
13856
|
var LABEL_KEY = /\\?["'](?:incorrect_stages|incorrect_step_ids|unuseful_step_ids|annotation_relpath|incorrect_error_stage_count)\\?["']\s*:/i;
|
|
13819
13857
|
var LABEL_ARRAY = /\\?["']labels\\?["']\s*:\s*\[[^\]]*\\?["'](?:incorrect|unuseful)\\?["']/i;
|
|
13820
13858
|
var ANNOTATION_PATH = /(?:agent_failure_analysis|step_annotations(?:_all)?|merged_cleaned_step\d*)[\\/]/i;
|
|
13821
13859
|
async function importTrajectory(input) {
|
|
13822
13860
|
const { index, row, trajectoryDirectory, stagingDirectory, signal } = input;
|
|
13823
|
-
const caseDirectory =
|
|
13861
|
+
const caseDirectory = join20(trajectoryDirectory, row.trajId);
|
|
13824
13862
|
const stepsPath = await containedFile(
|
|
13825
13863
|
trajectoryDirectory,
|
|
13826
|
-
|
|
13864
|
+
join20(caseDirectory, "steps.json"),
|
|
13827
13865
|
`${row.trajId}: steps.json`
|
|
13828
13866
|
);
|
|
13829
13867
|
signal.throwIfAborted();
|
|
@@ -13864,7 +13902,7 @@ async function importTrajectory(input) {
|
|
|
13864
13902
|
const serialized = serializeSpans(spans);
|
|
13865
13903
|
assertNoLabelLeakInText(serialized, row, `${row.trajId}: serialized OTLP`);
|
|
13866
13904
|
const outputFile = `${row.trajId}.otlp.jsonl`;
|
|
13867
|
-
await atomicWriteFile(
|
|
13905
|
+
await atomicWriteFile(join20(stagingDirectory, outputFile), serialized, signal);
|
|
13868
13906
|
return {
|
|
13869
13907
|
index,
|
|
13870
13908
|
traceId: row.trajId,
|
|
@@ -14031,7 +14069,7 @@ function fileRef(value, label2) {
|
|
|
14031
14069
|
};
|
|
14032
14070
|
}
|
|
14033
14071
|
async function readOptionalTask(trajectoryDirectory, caseDirectory, row, signal) {
|
|
14034
|
-
const candidate =
|
|
14072
|
+
const candidate = join20(caseDirectory, "task.md");
|
|
14035
14073
|
const path = await optionalContainedFile(trajectoryDirectory, candidate);
|
|
14036
14074
|
if (!path) {
|
|
14037
14075
|
return {
|
|
@@ -14120,7 +14158,7 @@ async function importCodeTraceBench(options) {
|
|
|
14120
14158
|
const rowsPath = resolve8(options.rowsPath);
|
|
14121
14159
|
const trajectoryPath = resolve8(options.trajectoryDir);
|
|
14122
14160
|
const outputDirectory = resolve8(options.outDir);
|
|
14123
|
-
const outputBase =
|
|
14161
|
+
const outputBase = basename10(outputDirectory);
|
|
14124
14162
|
if (!outputBase || outputDirectory === dirname10(outputDirectory)) {
|
|
14125
14163
|
throw new Error("CodeTraceBench output directory must not be a filesystem root");
|
|
14126
14164
|
}
|
|
@@ -14164,7 +14202,7 @@ async function runCodeTraceBenchImport(input) {
|
|
|
14164
14202
|
const rows = parseRows(rowsBytes.toString("utf8"), rowsPath);
|
|
14165
14203
|
const sourceRows = validateRows(rows, rowsPath);
|
|
14166
14204
|
const stagingDirectory = await mkdtemp4(
|
|
14167
|
-
|
|
14205
|
+
join21(dirname10(outputDirectory), `.${outputBase}.tmp-`)
|
|
14168
14206
|
);
|
|
14169
14207
|
const stop = new AbortController();
|
|
14170
14208
|
const signal = outerSignal ? AbortSignal.any([outerSignal, stop.signal]) : stop.signal;
|
|
@@ -14224,10 +14262,10 @@ async function runCodeTraceBenchImport(input) {
|
|
|
14224
14262
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14225
14263
|
input: {
|
|
14226
14264
|
revision,
|
|
14227
|
-
rowsFile:
|
|
14265
|
+
rowsFile: basename10(rowsPath),
|
|
14228
14266
|
rowsSha256,
|
|
14229
14267
|
rowsBytes: rowsBytes.byteLength,
|
|
14230
|
-
trajectoryDirectory:
|
|
14268
|
+
trajectoryDirectory: basename10(trajectoryDirectory),
|
|
14231
14269
|
sha256: inputSha256
|
|
14232
14270
|
},
|
|
14233
14271
|
settings: {
|
|
@@ -14250,7 +14288,7 @@ async function runCodeTraceBenchImport(input) {
|
|
|
14250
14288
|
};
|
|
14251
14289
|
const receiptFile = "codetracebench-import.json";
|
|
14252
14290
|
await atomicWriteFile(
|
|
14253
|
-
|
|
14291
|
+
join21(stagingDirectory, receiptFile),
|
|
14254
14292
|
`${JSON.stringify(receipt, null, 2)}
|
|
14255
14293
|
`,
|
|
14256
14294
|
signal
|
|
@@ -14265,7 +14303,7 @@ async function runCodeTraceBenchImport(input) {
|
|
|
14265
14303
|
await rm2(stagingDirectory, { recursive: true, force: true });
|
|
14266
14304
|
return {
|
|
14267
14305
|
directory: outputDirectory,
|
|
14268
|
-
receiptPath:
|
|
14306
|
+
receiptPath: join21(outputDirectory, receiptFile),
|
|
14269
14307
|
receipt
|
|
14270
14308
|
};
|
|
14271
14309
|
} catch (error) {
|
|
@@ -14693,20 +14731,20 @@ async function cmdVerifyFindings(argv) {
|
|
|
14693
14731
|
|
|
14694
14732
|
// src/hodoscope.ts
|
|
14695
14733
|
import { createHash as createHash13 } from "crypto";
|
|
14696
|
-
import { mkdir as mkdir5, mkdtemp as mkdtemp5, readFile as readFile11, readdir as
|
|
14734
|
+
import { mkdir as mkdir5, mkdtemp as mkdtemp5, readFile as readFile11, readdir as readdir14, writeFile as writeFile7 } from "fs/promises";
|
|
14697
14735
|
import { endianness, tmpdir as tmpdir5 } from "os";
|
|
14698
|
-
import { join as
|
|
14736
|
+
import { join as join22, resolve as resolve9 } from "path";
|
|
14699
14737
|
var HODOSCOPE_VERSION = "0.2.4";
|
|
14700
14738
|
async function writeHodoscopeInput(otlpPath, options = {}) {
|
|
14701
14739
|
const explicitDirectory = options.directory ? resolve9(options.directory) : void 0;
|
|
14702
|
-
const directory = explicitDirectory ?? await mkdtemp5(
|
|
14740
|
+
const directory = explicitDirectory ?? await mkdtemp5(join22(tmpdir5(), "traces-hodoscope-input-"));
|
|
14703
14741
|
if (explicitDirectory) {
|
|
14704
14742
|
await mkdir5(directory, { recursive: true });
|
|
14705
|
-
if ((await
|
|
14743
|
+
if ((await readdir14(directory)).length > 0) {
|
|
14706
14744
|
throw new Error(`Hodoscope explicit directory must be empty: ${directory}`);
|
|
14707
14745
|
}
|
|
14708
14746
|
}
|
|
14709
|
-
const samples =
|
|
14747
|
+
const samples = join22(directory, "samples");
|
|
14710
14748
|
await mkdir5(samples);
|
|
14711
14749
|
const rows = [];
|
|
14712
14750
|
for await (const value of readJsonl(otlpPath)) rows.push(parseOpenInferenceRow(value));
|
|
@@ -14732,7 +14770,7 @@ async function writeHodoscopeInput(otlpPath, options = {}) {
|
|
|
14732
14770
|
}
|
|
14733
14771
|
actionCount += actions;
|
|
14734
14772
|
const file = `${createHash13("sha256").update(traceId).digest("hex").slice(0, 24)}.json`;
|
|
14735
|
-
await writeFile7(
|
|
14773
|
+
await writeFile7(join22(samples, file), `${JSON.stringify(trajectory, null, 2)}
|
|
14736
14774
|
`, "utf8");
|
|
14737
14775
|
}
|
|
14738
14776
|
return {
|
|
@@ -14756,10 +14794,10 @@ function hodoscopeAnalyzer(options = {}) {
|
|
|
14756
14794
|
async analyze(otlpPath, runOptions2 = {}) {
|
|
14757
14795
|
const outputRoot = options.outputDir ? resolve9(options.outputDir) : tmpdir5();
|
|
14758
14796
|
await mkdir5(outputRoot, { recursive: true });
|
|
14759
|
-
const directory = await mkdtemp5(
|
|
14760
|
-
const input =
|
|
14761
|
-
const analysisFile =
|
|
14762
|
-
const samplesFile =
|
|
14797
|
+
const directory = await mkdtemp5(join22(outputRoot, "traces-hodoscope-"));
|
|
14798
|
+
const input = join22(directory, "input");
|
|
14799
|
+
const analysisFile = join22(directory, "analysis.hodoscope.json");
|
|
14800
|
+
const samplesFile = join22(directory, "samples.json");
|
|
14763
14801
|
try {
|
|
14764
14802
|
const prepared = await writeHodoscopeInput(otlpPath, {
|
|
14765
14803
|
directory: input,
|
|
@@ -15382,7 +15420,7 @@ function assertPositiveInteger(name, value) {
|
|
|
15382
15420
|
import { createHash as createHash14 } from "crypto";
|
|
15383
15421
|
import { request as httpRequest } from "http";
|
|
15384
15422
|
import { request as httpsRequest } from "https";
|
|
15385
|
-
import { join as
|
|
15423
|
+
import { join as join23 } from "path";
|
|
15386
15424
|
var DEFAULT_BRIDGE_URL = "http://localhost:4181";
|
|
15387
15425
|
var DEFAULT_MODEL = "prime/zai/glm-5.2";
|
|
15388
15426
|
var DEFAULT_TIMEOUT_MS = 12e5;
|
|
@@ -15396,12 +15434,12 @@ var ANALYST_ID = "prime";
|
|
|
15396
15434
|
var TRAJECTORY_FILE_NAME = "trajectory.otlp.jsonl";
|
|
15397
15435
|
async function fileDelivery(otlpPath, perAttributeCharCap, signal) {
|
|
15398
15436
|
signal?.throwIfAborted();
|
|
15399
|
-
const { copyFile: copyFile2, mkdtemp: mkdtemp8, stat:
|
|
15437
|
+
const { copyFile: copyFile2, mkdtemp: mkdtemp8, stat: stat18 } = await import("fs/promises");
|
|
15400
15438
|
const { tmpdir: tmpdir8 } = await import("os");
|
|
15401
|
-
const { join:
|
|
15402
|
-
const cwd = await mkdtemp8(
|
|
15403
|
-
await copyFile2(otlpPath,
|
|
15404
|
-
const artifactBytes = (await
|
|
15439
|
+
const { join: join35 } = await import("path");
|
|
15440
|
+
const cwd = await mkdtemp8(join35(tmpdir8(), "traces-prime-"));
|
|
15441
|
+
await copyFile2(otlpPath, join35(cwd, TRAJECTORY_FILE_NAME));
|
|
15442
|
+
const artifactBytes = (await stat18(otlpPath)).size;
|
|
15405
15443
|
return { mode: "file-cwd", perAttributeCharCap, renderedChars: artifactBytes, cwd, fileName: TRAJECTORY_FILE_NAME };
|
|
15406
15444
|
}
|
|
15407
15445
|
function positiveIntegerEnv(name) {
|
|
@@ -15429,8 +15467,8 @@ function normalizeUsage(raw) {
|
|
|
15429
15467
|
}
|
|
15430
15468
|
function mergeUsage(a, b) {
|
|
15431
15469
|
const calls = a.calls !== null && b.calls !== null ? a.calls + b.calls : null;
|
|
15432
|
-
const
|
|
15433
|
-
return { calls, tokens:
|
|
15470
|
+
const tokens2 = a.tokens !== null && b.tokens !== null && a.tokens.input !== null && b.tokens.input !== null && a.tokens.output !== null && b.tokens.output !== null ? { input: a.tokens.input + b.tokens.input, output: a.tokens.output + b.tokens.output } : null;
|
|
15471
|
+
return { calls, tokens: tokens2, estimated: a.estimated || b.estimated };
|
|
15434
15472
|
}
|
|
15435
15473
|
function renderUsage(usage) {
|
|
15436
15474
|
const calls = usage.calls === null ? "uncaptured" : String(usage.calls);
|
|
@@ -15638,11 +15676,11 @@ var FINDINGS_FILE_MAX_BYTES = 256 * 1024;
|
|
|
15638
15676
|
var FINDINGS_FILE_NAME = "findings.json";
|
|
15639
15677
|
async function readFindingsFile2(cwd, signal) {
|
|
15640
15678
|
signal?.throwIfAborted();
|
|
15641
|
-
const path =
|
|
15679
|
+
const path = join23(cwd, FINDINGS_FILE_NAME);
|
|
15642
15680
|
let raw;
|
|
15643
|
-
const { readFile: readFile21, stat:
|
|
15681
|
+
const { readFile: readFile21, stat: stat18 } = await import("fs/promises");
|
|
15644
15682
|
try {
|
|
15645
|
-
const size = (await
|
|
15683
|
+
const size = (await stat18(path)).size;
|
|
15646
15684
|
if (size > FINDINGS_FILE_MAX_BYTES) {
|
|
15647
15685
|
return { parsed: null, receipt: `${FINDINGS_FILE_NAME} is ${size} bytes (cap ${FINDINGS_FILE_MAX_BYTES}); refused` };
|
|
15648
15686
|
}
|
|
@@ -16054,7 +16092,7 @@ async function applyRedactor(spans, redactor) {
|
|
|
16054
16092
|
import { createHash as createHash15 } from "crypto";
|
|
16055
16093
|
import { mkdtemp as mkdtemp6, readFile as readFile12, writeFile as writeFile8 } from "fs/promises";
|
|
16056
16094
|
import { tmpdir as tmpdir6 } from "os";
|
|
16057
|
-
import { join as
|
|
16095
|
+
import { join as join24 } from "path";
|
|
16058
16096
|
import {
|
|
16059
16097
|
firstNumberAttr as firstNumberAttr2,
|
|
16060
16098
|
LLM_CACHED_TOKEN_ATTR_KEYS,
|
|
@@ -16725,7 +16763,7 @@ async function exportTraceEvidenceFile(inputPath, opts = {}) {
|
|
|
16725
16763
|
}
|
|
16726
16764
|
async function writeTraceEvidenceExportFile(inputPath, outPath, opts = {}) {
|
|
16727
16765
|
const result = await exportTraceEvidenceFile(inputPath, opts);
|
|
16728
|
-
const path = outPath ??
|
|
16766
|
+
const path = outPath ?? join24(await mkdtemp6(join24(tmpdir6(), "traces-export-")), "spans.openinference.jsonl");
|
|
16729
16767
|
await writeFile8(path, serializeSpans(result.spans), "utf8");
|
|
16730
16768
|
return { ...result, path };
|
|
16731
16769
|
}
|
|
@@ -16733,7 +16771,7 @@ async function writeTraceEvidenceExportFile(inputPath, outPath, opts = {}) {
|
|
|
16733
16771
|
// src/inspect.ts
|
|
16734
16772
|
import { mkdtemp as mkdtemp7, readFile as readFile13, writeFile as writeFile9 } from "fs/promises";
|
|
16735
16773
|
import { tmpdir as tmpdir7 } from "os";
|
|
16736
|
-
import { basename as
|
|
16774
|
+
import { basename as basename11, join as join25 } from "path";
|
|
16737
16775
|
var severityRank2 = {
|
|
16738
16776
|
high: 0,
|
|
16739
16777
|
medium: 1,
|
|
@@ -16833,7 +16871,7 @@ function addSessionFindings(index, findings) {
|
|
|
16833
16871
|
refs: errorSessions.slice(0, 5).map((row) => row.session.path)
|
|
16834
16872
|
});
|
|
16835
16873
|
}
|
|
16836
|
-
const largeSessions = sessions.map((row) => ({ row, tokens: row.metrics.inputTokens + row.metrics.outputTokens })).filter(({ tokens:
|
|
16874
|
+
const largeSessions = sessions.map((row) => ({ row, tokens: row.metrics.inputTokens + row.metrics.outputTokens })).filter(({ tokens: tokens2 }) => tokens2 >= 5e5).sort((a, b) => b.tokens - a.tokens);
|
|
16837
16875
|
if (largeSessions.length > 0) {
|
|
16838
16876
|
const largest = largeSessions[0]?.tokens ?? 0;
|
|
16839
16877
|
findings.push({
|
|
@@ -16842,7 +16880,7 @@ function addSessionFindings(index, findings) {
|
|
|
16842
16880
|
area: "session",
|
|
16843
16881
|
title: `${largeSessions.length} session(s) reported at least 500k summed tokens`,
|
|
16844
16882
|
impact: largest,
|
|
16845
|
-
evidence: largeSessions.slice(0, 5).map(({ row, tokens:
|
|
16883
|
+
evidence: largeSessions.slice(0, 5).map(({ row, tokens: tokens2 }) => `${sessionLabel(row)}: ${tokens2.toLocaleString()} reported summed tokens (${row.metrics.inputTokens.toLocaleString()} in, ${row.metrics.outputTokens.toLocaleString()} out)`),
|
|
16846
16884
|
next: "Check whether these sessions need shorter handoffs, smaller files, split tasks, or clearer token accounting before optimizing anything else.",
|
|
16847
16885
|
refs: largeSessions.slice(0, 5).map(({ row }) => row.session.path)
|
|
16848
16886
|
});
|
|
@@ -16916,7 +16954,7 @@ function addContextFindings(index, findings) {
|
|
|
16916
16954
|
refs: invalidJsonlFiles.slice(0, 5).map((file) => file.path)
|
|
16917
16955
|
});
|
|
16918
16956
|
}
|
|
16919
|
-
const skillRunFiles = files.filter((file) =>
|
|
16957
|
+
const skillRunFiles = files.filter((file) => basename11(file.path) === "skill-runs.jsonl" && (file.jsonl?.rows ?? 0) > 0).map((file) => {
|
|
16920
16958
|
const rows = file.jsonl?.rows ?? 0;
|
|
16921
16959
|
const keys = file.jsonl?.keys ?? {};
|
|
16922
16960
|
const linkKeyCounts = {
|
|
@@ -17022,7 +17060,7 @@ async function readSessionIndexFile(path) {
|
|
|
17022
17060
|
return parsed;
|
|
17023
17061
|
}
|
|
17024
17062
|
async function writeInspectionReportFile(report, outPath) {
|
|
17025
|
-
const path = outPath ??
|
|
17063
|
+
const path = outPath ?? join25(await mkdtemp7(join25(tmpdir7(), "traces-inspect-")), "inspection-report.md");
|
|
17026
17064
|
await writeFile9(path, renderInspectionReport(report), "utf8");
|
|
17027
17065
|
return path;
|
|
17028
17066
|
}
|
|
@@ -17455,8 +17493,8 @@ async function streamSessions(opts) {
|
|
|
17455
17493
|
}
|
|
17456
17494
|
|
|
17457
17495
|
// src/supervisor-run-context.ts
|
|
17458
|
-
import { readFile as readFile14, stat as
|
|
17459
|
-
import { basename as
|
|
17496
|
+
import { readFile as readFile14, stat as stat14 } from "fs/promises";
|
|
17497
|
+
import { basename as basename12, join as join26 } from "path";
|
|
17460
17498
|
import { materializeTreeView } from "@tangle-network/agent-runtime/kernel";
|
|
17461
17499
|
import {
|
|
17462
17500
|
readRuntimeSupervisorRun
|
|
@@ -17625,14 +17663,14 @@ function parseInstant(v) {
|
|
|
17625
17663
|
}
|
|
17626
17664
|
async function readFileRunContext(runDir, opts = {}) {
|
|
17627
17665
|
const sources = await readRuntimeSupervisorRun(runDir);
|
|
17628
|
-
const coordination = readCoordinationLog(await readMaybe(
|
|
17666
|
+
const coordination = readCoordinationLog(await readMaybe(join26(runDir, COORDINATION_LOG_FILE)));
|
|
17629
17667
|
return {
|
|
17630
17668
|
...sources,
|
|
17631
17669
|
instanceId: opts.instanceId !== void 0 ? opts.instanceId : sources.instanceId,
|
|
17632
|
-
arm: opts.arm !== void 0 ? opts.arm :
|
|
17670
|
+
arm: opts.arm !== void 0 ? opts.arm : basename12(runDir),
|
|
17633
17671
|
workers: sources.workers !== null && coordination.present ? workerSources(sources.workers, coordination) : sources.workers,
|
|
17634
17672
|
...sources.journal !== null ? {
|
|
17635
|
-
rootTranscriptRef:
|
|
17673
|
+
rootTranscriptRef: join26(runDir, SPAWN_JOURNAL_FILE),
|
|
17636
17674
|
traceCommand: `traces watch ${runDir}`
|
|
17637
17675
|
} : {}
|
|
17638
17676
|
};
|
|
@@ -17641,7 +17679,7 @@ function fileRunContextSupervisorRunReader(runDir, opts = {}) {
|
|
|
17641
17679
|
return { runRef: runDir, read: () => readFileRunContext(runDir, opts) };
|
|
17642
17680
|
}
|
|
17643
17681
|
async function isFileRunContextDir(runDir) {
|
|
17644
|
-
return
|
|
17682
|
+
return stat14(join26(runDir, SPAWN_JOURNAL_FILE)).then((s) => s.isFile()).catch(() => false);
|
|
17645
17683
|
}
|
|
17646
17684
|
function parseJsonObject(text2) {
|
|
17647
17685
|
if (text2 === null) return null;
|
|
@@ -17707,9 +17745,9 @@ function mergeRolls(a, b) {
|
|
|
17707
17745
|
};
|
|
17708
17746
|
}
|
|
17709
17747
|
async function readRunContextSnapshot(runDir) {
|
|
17710
|
-
const journal = readRunContextJournal(await readMaybe(
|
|
17711
|
-
const coordination = readCoordinationLog(await readMaybe(
|
|
17712
|
-
const resultText = await readMaybe(
|
|
17748
|
+
const journal = readRunContextJournal(await readMaybe(join26(runDir, SPAWN_JOURNAL_FILE)));
|
|
17749
|
+
const coordination = readCoordinationLog(await readMaybe(join26(runDir, COORDINATION_LOG_FILE)));
|
|
17750
|
+
const resultText = await readMaybe(join26(runDir, RESULT_FILE));
|
|
17713
17751
|
const result = parseResult(resultText);
|
|
17714
17752
|
const view = safeTreeView(journal.events);
|
|
17715
17753
|
const nodes = view === null ? [] : orderNodes(view, journal);
|
|
@@ -17843,7 +17881,7 @@ function usd(roll) {
|
|
|
17843
17881
|
function zeroPricedCost(roll) {
|
|
17844
17882
|
return roll.usdKnownRecords > 0 && roll.usd === 0 && roll.tokensIn + roll.tokensOut > 0;
|
|
17845
17883
|
}
|
|
17846
|
-
function
|
|
17884
|
+
function tokens(roll) {
|
|
17847
17885
|
return `in ${int(roll.tokensIn)} \xB7 out ${int(roll.tokensOut)}`;
|
|
17848
17886
|
}
|
|
17849
17887
|
var STATUS_MARK = {
|
|
@@ -17889,8 +17927,8 @@ function wrap(text2, indent) {
|
|
|
17889
17927
|
}
|
|
17890
17928
|
|
|
17891
17929
|
// src/run-span-tree.ts
|
|
17892
|
-
import { readdir as
|
|
17893
|
-
import { join as
|
|
17930
|
+
import { readdir as readdir15 } from "fs/promises";
|
|
17931
|
+
import { join as join27 } from "path";
|
|
17894
17932
|
import {
|
|
17895
17933
|
firstNumberAttr as firstNumberAttr3,
|
|
17896
17934
|
LLM_COST_ATTR_KEYS as LLM_COST_ATTR_KEYS2,
|
|
@@ -18105,8 +18143,8 @@ function renderSpanRunSnapshot(snapshot) {
|
|
|
18105
18143
|
` ${snapshot.nodes.length} node(s) \xB7 depth ${snapshot.maxDepth} \xB7 ${int(snapshot.spanCount)} span(s) \xB7 ${snapshot.traceIds.length} trace(s) \xB7 elapsed ${ms(snapshot.endMs === null || snapshot.startMs === null ? null : snapshot.endMs - snapshot.startMs)}`
|
|
18106
18144
|
);
|
|
18107
18145
|
const delegating = snapshot.nodes.some((node) => node.role === "driver");
|
|
18108
|
-
lines.push(` driver inference ${
|
|
18109
|
-
lines.push(` leaf inference ${
|
|
18146
|
+
lines.push(` driver inference ${tokens(totals.driver)} \xB7 ${usd(totals.driver)}`);
|
|
18147
|
+
lines.push(` leaf inference ${tokens(totals.leaves)} \xB7 ${usd(totals.leaves)}`);
|
|
18110
18148
|
if (!delegating) {
|
|
18111
18149
|
lines.push(" (no delegation in these spans: every turn belongs to one agent, so the driver half is empty)");
|
|
18112
18150
|
}
|
|
@@ -18129,11 +18167,11 @@ function renderSpanRunSnapshot(snapshot) {
|
|
|
18129
18167
|
].join(" ")
|
|
18130
18168
|
);
|
|
18131
18169
|
lines.push(
|
|
18132
|
-
`${detail}driver ${node.driver.records} turn(s) \xB7 ${
|
|
18170
|
+
`${detail}driver ${node.driver.records} turn(s) \xB7 ${tokens(node.driver)} \xB7 ${usd(node.driver)} \xB7 ${node.toolCalls} tool call(s)`
|
|
18133
18171
|
);
|
|
18134
18172
|
if (node.role === "driver") {
|
|
18135
18173
|
lines.push(
|
|
18136
|
-
`${detail}children ${node.children.records} turn(s) \xB7 ${
|
|
18174
|
+
`${detail}children ${node.children.records} turn(s) \xB7 ${tokens(node.children)} \xB7 ${usd(node.children)}`
|
|
18137
18175
|
);
|
|
18138
18176
|
}
|
|
18139
18177
|
for (const error of node.errors.slice(0, 3)) lines.push(`${detail}error ${error}`);
|
|
@@ -18163,25 +18201,25 @@ function renderSpanRunSnapshot(snapshot) {
|
|
|
18163
18201
|
var SPAN_FILE_PATTERN = /\.(?:otlp|spans|traces|openinference)\.(?:jsonl|ndjson)$|^traces?\.jsonl$|^spans\.jsonl$/i;
|
|
18164
18202
|
async function findSpanSourceFiles(dir) {
|
|
18165
18203
|
const found = [];
|
|
18166
|
-
const entries = await
|
|
18204
|
+
const entries = await readdir15(dir, { withFileTypes: true }).catch(() => []);
|
|
18167
18205
|
for (const entry of entries) {
|
|
18168
|
-
const child =
|
|
18206
|
+
const child = join27(dir, entry.name);
|
|
18169
18207
|
if (entry.isFile()) {
|
|
18170
18208
|
if (SPAN_FILE_PATTERN.test(entry.name)) found.push(child);
|
|
18171
18209
|
continue;
|
|
18172
18210
|
}
|
|
18173
18211
|
if (!entry.isDirectory()) continue;
|
|
18174
|
-
const nested = await
|
|
18212
|
+
const nested = await readdir15(child, { withFileTypes: true }).catch(() => []);
|
|
18175
18213
|
for (const inner of nested) {
|
|
18176
|
-
if (inner.isFile() && SPAN_FILE_PATTERN.test(inner.name)) found.push(
|
|
18214
|
+
if (inner.isFile() && SPAN_FILE_PATTERN.test(inner.name)) found.push(join27(child, inner.name));
|
|
18177
18215
|
}
|
|
18178
18216
|
}
|
|
18179
18217
|
return found.sort();
|
|
18180
18218
|
}
|
|
18181
18219
|
|
|
18182
18220
|
// src/supervisor-run-watch.ts
|
|
18183
|
-
import { readdir as
|
|
18184
|
-
import { join as
|
|
18221
|
+
import { readdir as readdir16 } from "fs/promises";
|
|
18222
|
+
import { join as join28 } from "path";
|
|
18185
18223
|
function budget(node) {
|
|
18186
18224
|
const parts = [`${int(node.budget.maxIterations)}it`, `${int(node.budget.maxTokens)}tok`];
|
|
18187
18225
|
if (node.budget.maxUsd !== void 0) parts.push(`$${node.budget.maxUsd.toFixed(2)}`);
|
|
@@ -18213,17 +18251,17 @@ function renderNode(node, lastAtDepth, lines) {
|
|
|
18213
18251
|
lines.push(head.join(" "));
|
|
18214
18252
|
if (node.driver.records > 0 || node.role === "supervisor") {
|
|
18215
18253
|
lines.push(
|
|
18216
|
-
`${detail}driver ${node.driver.records} turn(s) \xB7 ${
|
|
18254
|
+
`${detail}driver ${node.driver.records} turn(s) \xB7 ${tokens(node.driver)} \xB7 ${usd(node.driver)}` + budgetUse(node, node.driver)
|
|
18217
18255
|
);
|
|
18218
18256
|
}
|
|
18219
18257
|
if (node.children.records > 0 || node.role === "supervisor") {
|
|
18220
18258
|
lines.push(
|
|
18221
|
-
`${detail}children ${node.children.records} settled \xB7 ${
|
|
18259
|
+
`${detail}children ${node.children.records} settled \xB7 ${tokens(node.children)} \xB7 ${usd(node.children)}`
|
|
18222
18260
|
);
|
|
18223
18261
|
}
|
|
18224
18262
|
if (node.settled.records > 0) {
|
|
18225
18263
|
lines.push(
|
|
18226
|
-
`${detail}settled ${node.settled.iterations}it \xB7 ${
|
|
18264
|
+
`${detail}settled ${node.settled.iterations}it \xB7 ${tokens(node.settled)} \xB7 ${usd(node.settled)}` + budgetUse(node, node.settled)
|
|
18227
18265
|
);
|
|
18228
18266
|
}
|
|
18229
18267
|
if (node.cancelReason !== null) lines.push(`${detail}cancelled: ${node.cancelReason}`);
|
|
@@ -18247,8 +18285,8 @@ function renderRunContextSnapshot(snapshot) {
|
|
|
18247
18285
|
lines.push(
|
|
18248
18286
|
` ${snapshot.nodes.length} node(s) \xB7 depth ${snapshot.maxDepth} \xB7 elapsed ${ms(elapsed(snapshot))}${snapshot.terminal ? "" : " \xB7 live"}`
|
|
18249
18287
|
);
|
|
18250
|
-
lines.push(` driver total ${
|
|
18251
|
-
lines.push(` worker total ${
|
|
18288
|
+
lines.push(` driver total ${tokens(totals.driver)} \xB7 ${usd(totals.driver)}`);
|
|
18289
|
+
lines.push(` worker total ${tokens(totals.workers)} \xB7 ${usd(totals.workers)}`);
|
|
18252
18290
|
lines.push("");
|
|
18253
18291
|
if (snapshot.nodes.length === 0) lines.push(" (no node spawned yet)");
|
|
18254
18292
|
forEachTreeNode(
|
|
@@ -18323,12 +18361,12 @@ function elapsed(snapshot) {
|
|
|
18323
18361
|
}
|
|
18324
18362
|
async function resolveRunContextDir(dir) {
|
|
18325
18363
|
if (await isFileRunContextDir(dir)) return dir;
|
|
18326
|
-
const entries = await
|
|
18364
|
+
const entries = await readdir16(dir, { withFileTypes: true }).catch(() => null);
|
|
18327
18365
|
if (entries === null) throw new Error(`cannot read ${dir}`);
|
|
18328
18366
|
const candidates = [];
|
|
18329
18367
|
for (const entry of entries) {
|
|
18330
18368
|
if (!entry.isDirectory()) continue;
|
|
18331
|
-
const child =
|
|
18369
|
+
const child = join28(dir, entry.name);
|
|
18332
18370
|
if (await isFileRunContextDir(child)) candidates.push(child);
|
|
18333
18371
|
}
|
|
18334
18372
|
if (candidates.length === 1) return candidates[0];
|
|
@@ -18344,9 +18382,9 @@ async function resolveRunContextDir(dir) {
|
|
|
18344
18382
|
}
|
|
18345
18383
|
|
|
18346
18384
|
// src/run-watch.ts
|
|
18347
|
-
import { stat as
|
|
18385
|
+
import { stat as stat15 } from "fs/promises";
|
|
18348
18386
|
async function resolveRunWatchTarget(target) {
|
|
18349
|
-
const info = await
|
|
18387
|
+
const info = await stat15(target).catch(() => null);
|
|
18350
18388
|
if (info === null) throw new Error(`cannot read ${target}`);
|
|
18351
18389
|
if (info.isFile()) return { runDir: null, spanFiles: [target], requested: target };
|
|
18352
18390
|
const spanFiles = await findSpanSourceFiles(target);
|
|
@@ -18670,7 +18708,7 @@ function parseJsonAnswer(text2) {
|
|
|
18670
18708
|
|
|
18671
18709
|
// src/ask.ts
|
|
18672
18710
|
import { mkdir as mkdir6, readFile as readFile15, writeFile as writeFile10 } from "fs/promises";
|
|
18673
|
-
import { join as
|
|
18711
|
+
import { join as join29, resolve as resolve10 } from "path";
|
|
18674
18712
|
import { performance } from "perf_hooks";
|
|
18675
18713
|
import { CostCeilingReachedError, CostLedger } from "@tangle-network/agent-eval";
|
|
18676
18714
|
import {
|
|
@@ -19226,8 +19264,8 @@ async function writeTraceQuestionsArtifacts(result, outDir) {
|
|
|
19226
19264
|
await mkdir6(directory, { recursive: true });
|
|
19227
19265
|
const paths = {
|
|
19228
19266
|
directory,
|
|
19229
|
-
result:
|
|
19230
|
-
report:
|
|
19267
|
+
result: join29(directory, "answers.json"),
|
|
19268
|
+
report: join29(directory, "report.md"),
|
|
19231
19269
|
traces: result.otlpPath
|
|
19232
19270
|
};
|
|
19233
19271
|
const { report, ...machine } = result;
|
|
@@ -19290,8 +19328,8 @@ async function watchSessions(opts) {
|
|
|
19290
19328
|
|
|
19291
19329
|
// src/bundle-view.ts
|
|
19292
19330
|
import { createHash as createHash17 } from "crypto";
|
|
19293
|
-
import { mkdir as mkdir7, readdir as
|
|
19294
|
-
import { dirname as dirname11, join as
|
|
19331
|
+
import { mkdir as mkdir7, readdir as readdir17, readFile as readFile16, writeFile as writeFile11 } from "fs/promises";
|
|
19332
|
+
import { dirname as dirname11, join as join30, resolve as resolve11 } from "path";
|
|
19295
19333
|
var EVIDENCE_ONLY_ALLOW_LIST = [
|
|
19296
19334
|
{
|
|
19297
19335
|
path: "derived/session-index.json",
|
|
@@ -19415,7 +19453,7 @@ async function findSessionTextLeaks(opts) {
|
|
|
19415
19453
|
const index = /* @__PURE__ */ new Map();
|
|
19416
19454
|
const carried = [];
|
|
19417
19455
|
for (const path of opts.carriedPaths) {
|
|
19418
|
-
const signatures = sessionTextSignatures(await readFile16(
|
|
19456
|
+
const signatures = sessionTextSignatures(await readFile16(join30(opts.viewDir, path), "utf8"));
|
|
19419
19457
|
carried.push({ path, signatures: signatures.size });
|
|
19420
19458
|
for (const signature of signatures) if (!index.has(signature)) index.set(signature, path);
|
|
19421
19459
|
}
|
|
@@ -19423,7 +19461,7 @@ async function findSessionTextLeaks(opts) {
|
|
|
19423
19461
|
const comparedSources = [];
|
|
19424
19462
|
let sourceSignatures = 0;
|
|
19425
19463
|
for (const sourcePath of opts.excludedPaths) {
|
|
19426
|
-
const signatures = sessionTextSignatures(await readFile16(
|
|
19464
|
+
const signatures = sessionTextSignatures(await readFile16(join30(opts.sourceDir, sourcePath), "utf8"));
|
|
19427
19465
|
comparedSources.push(sourcePath);
|
|
19428
19466
|
sourceSignatures += signatures.size;
|
|
19429
19467
|
for (const signature of signatures) {
|
|
@@ -19470,7 +19508,7 @@ async function projectSessionBundle(opts) {
|
|
|
19470
19508
|
}
|
|
19471
19509
|
const outDir = resolve11(opts.outDir);
|
|
19472
19510
|
await mkdir7(outDir, { recursive: true });
|
|
19473
|
-
if ((await
|
|
19511
|
+
if ((await readdir17(outDir)).length > 0) {
|
|
19474
19512
|
throw new Error(
|
|
19475
19513
|
`view output directory ${outDir} is not empty \u2014 pass a new directory so one directory holds exactly one view`
|
|
19476
19514
|
);
|
|
@@ -19482,17 +19520,17 @@ async function projectSessionBundle(opts) {
|
|
|
19482
19520
|
const candidates = [];
|
|
19483
19521
|
for (const path of await listSessionBundleFiles(sourceDir)) {
|
|
19484
19522
|
if (path === "manifest.json") continue;
|
|
19485
|
-
const bytes2 = await readFile16(
|
|
19523
|
+
const bytes2 = await readFile16(join30(sourceDir, path));
|
|
19486
19524
|
const sha2562 = createHash17("sha256").update(bytes2).digest("hex");
|
|
19487
19525
|
const recorded = sourceHashes.get(path);
|
|
19488
19526
|
if (!recorded) {
|
|
19489
19527
|
throw new Error(
|
|
19490
|
-
`${
|
|
19528
|
+
`${join30(sourceDir, path)} is not listed in the bundle manifest \u2014 the bundle changed after assembly and cannot be projected; re-run \`traces bundle\``
|
|
19491
19529
|
);
|
|
19492
19530
|
}
|
|
19493
19531
|
if (recorded.sha256 !== sha2562) {
|
|
19494
19532
|
throw new Error(
|
|
19495
|
-
`${
|
|
19533
|
+
`${join30(sourceDir, path)} does not match its manifest hash (${recorded.sha256} recorded, ${sha2562} on disk) \u2014 the bundle changed after assembly and cannot be projected; re-run \`traces bundle\``
|
|
19496
19534
|
);
|
|
19497
19535
|
}
|
|
19498
19536
|
const verdict = classifyEvidenceOnlyPath(path);
|
|
@@ -19528,7 +19566,7 @@ async function projectSessionBundle(opts) {
|
|
|
19528
19566
|
});
|
|
19529
19567
|
continue;
|
|
19530
19568
|
}
|
|
19531
|
-
const target =
|
|
19569
|
+
const target = join30(outDir, candidate.path);
|
|
19532
19570
|
await mkdir7(dirname11(target), { recursive: true });
|
|
19533
19571
|
await writeFile11(target, candidate.bytes);
|
|
19534
19572
|
carriedPaths.push(candidate.path);
|
|
@@ -19545,7 +19583,7 @@ async function projectSessionBundle(opts) {
|
|
|
19545
19583
|
})),
|
|
19546
19584
|
matches: 0
|
|
19547
19585
|
};
|
|
19548
|
-
const sourceManifestBytes = await readFile16(
|
|
19586
|
+
const sourceManifestBytes = await readFile16(join30(sourceDir, "manifest.json"));
|
|
19549
19587
|
const manifest = {
|
|
19550
19588
|
schemaVersion: 2,
|
|
19551
19589
|
kind: "traces.session_bundle",
|
|
@@ -19568,14 +19606,14 @@ async function projectSessionBundle(opts) {
|
|
|
19568
19606
|
}
|
|
19569
19607
|
|
|
19570
19608
|
// src/diff.ts
|
|
19571
|
-
import { stat as
|
|
19609
|
+
import { stat as stat16 } from "fs/promises";
|
|
19572
19610
|
import { ATTR as ATTR5 } from "@tangle-network/agent-trace-contract";
|
|
19573
19611
|
import { ingestSpans as ingestSpans2 } from "@tangle-network/agent-eval/diagnosis";
|
|
19574
19612
|
import { diffSteps, diffStepsFromSpans } from "@tangle-network/agent-eval/pipelines";
|
|
19575
19613
|
async function readSpans(path) {
|
|
19576
19614
|
const otlp = await readOtlpInput(path);
|
|
19577
19615
|
if (otlp.spans.length > 0) return [...otlp.spans];
|
|
19578
|
-
if (!(await
|
|
19616
|
+
if (!(await stat16(path)).isFile()) {
|
|
19579
19617
|
throw new Error(`${path}: no OTLP spans found (${otlp.issues.length} unusable row(s))`);
|
|
19580
19618
|
}
|
|
19581
19619
|
return (await exportTraceEvidenceFile(path)).spans;
|
|
@@ -19732,12 +19770,12 @@ function renderRunDiff(report) {
|
|
|
19732
19770
|
|
|
19733
19771
|
// src/upload-state.ts
|
|
19734
19772
|
import { mkdir as mkdir8, readFile as readFile17, writeFile as writeFile12 } from "fs/promises";
|
|
19735
|
-
import { homedir as
|
|
19736
|
-
import { dirname as dirname12, join as
|
|
19773
|
+
import { homedir as homedir10 } from "os";
|
|
19774
|
+
import { dirname as dirname12, join as join31 } from "path";
|
|
19737
19775
|
import { contentHash } from "@tangle-network/agent-eval";
|
|
19738
19776
|
function stateFile() {
|
|
19739
|
-
const base = process.env.XDG_STATE_HOME ??
|
|
19740
|
-
return
|
|
19777
|
+
const base = process.env.XDG_STATE_HOME ?? join31(homedir10(), ".local", "state");
|
|
19778
|
+
return join31(base, "tangle-traces", "uploaded.json");
|
|
19741
19779
|
}
|
|
19742
19780
|
function uploadKey(harness, sessionId) {
|
|
19743
19781
|
return `${harness}:${sessionId}`;
|
|
@@ -19769,7 +19807,7 @@ function alreadyUploaded(state, harness, sessionId, hash2) {
|
|
|
19769
19807
|
import { readFile as readFile18 } from "fs/promises";
|
|
19770
19808
|
import { createRequire as createRequire2 } from "module";
|
|
19771
19809
|
import { hostname as hostname2 } from "os";
|
|
19772
|
-
import { basename as
|
|
19810
|
+
import { basename as basename13, join as join32 } from "path";
|
|
19773
19811
|
import { hostedClientFromEnv } from "@tangle-network/agent-eval/hosted";
|
|
19774
19812
|
import { assessShareSafety as assessShareSafety2, REDACTION_VERSION, redact as redact2, shareAllowed } from "@tangle-network/agent-eval/traces";
|
|
19775
19813
|
var require2 = createRequire2(import.meta.url);
|
|
@@ -19793,7 +19831,7 @@ async function planUpload(opts = {}) {
|
|
|
19793
19831
|
async function gitBranch(cwd) {
|
|
19794
19832
|
if (!cwd) return void 0;
|
|
19795
19833
|
try {
|
|
19796
|
-
const head = await readFile18(
|
|
19834
|
+
const head = await readFile18(join32(cwd, ".git", "HEAD"), "utf8");
|
|
19797
19835
|
const m = head.match(/^ref:\s*refs\/heads\/(.+)$/m);
|
|
19798
19836
|
return m?.[1]?.trim();
|
|
19799
19837
|
} catch {
|
|
@@ -19803,7 +19841,7 @@ async function gitBranch(cwd) {
|
|
|
19803
19841
|
async function sessionMeta(item) {
|
|
19804
19842
|
const meta = {
|
|
19805
19843
|
[ATTR.HARNESS]: item.ref.harness,
|
|
19806
|
-
[ATTR.SESSION_FILE]:
|
|
19844
|
+
[ATTR.SESSION_FILE]: basename13(item.ref.path),
|
|
19807
19845
|
[ATTR.HOST]: hostname2(),
|
|
19808
19846
|
[ATTR.UPLOADER]: `tangle-traces@${TRACES_VERSION}`,
|
|
19809
19847
|
[ATTR.REDACTION_VERSION]: REDACTION_VERSION,
|
|
@@ -19968,8 +20006,8 @@ async function executeUpload(plan, opts = {}) {
|
|
|
19968
20006
|
}
|
|
19969
20007
|
|
|
19970
20008
|
// src/verify-safe.ts
|
|
19971
|
-
import { lstat as lstat4, readdir as
|
|
19972
|
-
import { basename as
|
|
20009
|
+
import { lstat as lstat4, readdir as readdir18, readFile as readFile19 } from "fs/promises";
|
|
20010
|
+
import { basename as basename14, extname, join as join33, relative as relative7 } from "path";
|
|
19973
20011
|
import {
|
|
19974
20012
|
assessShareSafety as assessShareSafety3,
|
|
19975
20013
|
combineVerdicts as combineVerdicts2
|
|
@@ -19982,7 +20020,7 @@ async function verifySafe(target, options = {}) {
|
|
|
19982
20020
|
const unreadable = [];
|
|
19983
20021
|
const files = await listFiles(target, unreadable);
|
|
19984
20022
|
for (const file of files) {
|
|
19985
|
-
const label2 = relative7(target, file) ||
|
|
20023
|
+
const label2 = relative7(target, file) || basename14(file);
|
|
19986
20024
|
let bytes2;
|
|
19987
20025
|
try {
|
|
19988
20026
|
bytes2 = await readFile19(file);
|
|
@@ -20015,22 +20053,22 @@ async function verifySafe(target, options = {}) {
|
|
|
20015
20053
|
return { ...combineVerdicts2(profile, verdicts, unreadable), target, files: files.length };
|
|
20016
20054
|
}
|
|
20017
20055
|
async function listFiles(target, unreadable) {
|
|
20018
|
-
let
|
|
20056
|
+
let stat18;
|
|
20019
20057
|
try {
|
|
20020
|
-
|
|
20058
|
+
stat18 = await lstat4(target);
|
|
20021
20059
|
} catch (error) {
|
|
20022
20060
|
unreadable.push(`${target}: ${error instanceof Error ? error.message : String(error)}`);
|
|
20023
20061
|
return [];
|
|
20024
20062
|
}
|
|
20025
|
-
if (
|
|
20026
|
-
if (!
|
|
20063
|
+
if (stat18.isFile()) return [target];
|
|
20064
|
+
if (!stat18.isDirectory()) {
|
|
20027
20065
|
unreadable.push(`${target}: not a regular file or directory`);
|
|
20028
20066
|
return [];
|
|
20029
20067
|
}
|
|
20030
20068
|
const out = [];
|
|
20031
20069
|
const walk = async (dir) => {
|
|
20032
|
-
for (const entry of (await
|
|
20033
|
-
const path =
|
|
20070
|
+
for (const entry of (await readdir18(dir, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
20071
|
+
const path = join33(dir, entry.name);
|
|
20034
20072
|
if (entry.isDirectory()) await walk(path);
|
|
20035
20073
|
else if (entry.isFile()) out.push(path);
|
|
20036
20074
|
else unreadable.push(`${relative7(target, path)}: not a regular file`);
|
|
@@ -20082,9 +20120,9 @@ function formatVerdict(result) {
|
|
|
20082
20120
|
}
|
|
20083
20121
|
|
|
20084
20122
|
// src/skill-usage.ts
|
|
20085
|
-
import { mkdir as mkdir9, open as open3, readdir as
|
|
20086
|
-
import { homedir as
|
|
20087
|
-
import { dirname as dirname13, join as
|
|
20123
|
+
import { mkdir as mkdir9, open as open3, readdir as readdir19, readFile as readFile20, rename as rename3, stat as stat17, writeFile as writeFile13 } from "fs/promises";
|
|
20124
|
+
import { homedir as homedir11 } from "os";
|
|
20125
|
+
import { dirname as dirname13, join as join34 } from "path";
|
|
20088
20126
|
import { STORES } from "@tangle-network/harness-sessions/catalog";
|
|
20089
20127
|
var CHUNK_BYTES = 8 * 1024 * 1024;
|
|
20090
20128
|
var NEWLINE = 10;
|
|
@@ -20107,16 +20145,16 @@ var SKILL_READ_VERB = /(?:^|[\s;&|(`'"])(?:cat|sed|head|tail|less|more|awk|nl|ba
|
|
|
20107
20145
|
var SKILL_DOCUMENT = /(?:^|[\s'"=:(`])((?:[^\s'"`;|&()]*\/)?([A-Za-z0-9][A-Za-z0-9._-]*))\/SKILL\.md\b/g;
|
|
20108
20146
|
var SESSION_UUID = /([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\.jsonl$/;
|
|
20109
20147
|
function defaultSkillUsageSources() {
|
|
20110
|
-
const codexHome = process.env.CODEX_HOME ??
|
|
20148
|
+
const codexHome = process.env.CODEX_HOME ?? join34(homedir11(), ".codex");
|
|
20111
20149
|
return {
|
|
20112
|
-
claudeProjects:
|
|
20113
|
-
codexSessions: [
|
|
20114
|
-
kimiSessions:
|
|
20150
|
+
claudeProjects: join34(homedir11(), STORES["claude-code.projects-jsonl"].root),
|
|
20151
|
+
codexSessions: [join34(codexHome, ...STORES["codex.rollout-jsonl"].root.split("/").slice(1)), join34(codexHome, "archived_sessions")],
|
|
20152
|
+
kimiSessions: join34(homedir11(), STORES["kimi.session-dir"].root)
|
|
20115
20153
|
};
|
|
20116
20154
|
}
|
|
20117
20155
|
function defaultSkillUsageStorePath() {
|
|
20118
|
-
const state = process.env.XDG_STATE_HOME ??
|
|
20119
|
-
return
|
|
20156
|
+
const state = process.env.XDG_STATE_HOME ?? join34(homedir11(), ".local", "state");
|
|
20157
|
+
return join34(state, "traces", "skill-usage.json");
|
|
20120
20158
|
}
|
|
20121
20159
|
function eventKey(e) {
|
|
20122
20160
|
return `${e.harness}\0${e.id}`;
|
|
@@ -20200,7 +20238,7 @@ var SkillUsageStore = class _SkillUsageStore {
|
|
|
20200
20238
|
async refreshFile(harness, path) {
|
|
20201
20239
|
let info;
|
|
20202
20240
|
try {
|
|
20203
|
-
info = await
|
|
20241
|
+
info = await stat17(path);
|
|
20204
20242
|
} catch (error) {
|
|
20205
20243
|
if (error.code === "ENOENT") return null;
|
|
20206
20244
|
throw error;
|
|
@@ -20467,37 +20505,37 @@ async function jsonlFiles(roots, name) {
|
|
|
20467
20505
|
for (const root of roots) {
|
|
20468
20506
|
let entries;
|
|
20469
20507
|
try {
|
|
20470
|
-
entries = await
|
|
20508
|
+
entries = await readdir19(root, { recursive: true, withFileTypes: true });
|
|
20471
20509
|
} catch (error) {
|
|
20472
20510
|
if (error.code === "ENOENT") continue;
|
|
20473
20511
|
throw error;
|
|
20474
20512
|
}
|
|
20475
20513
|
for (const entry of entries) {
|
|
20476
|
-
if (entry.isFile() && (name ? entry.name === name : entry.name.endsWith(".jsonl"))) files.push(
|
|
20514
|
+
if (entry.isFile() && (name ? entry.name === name : entry.name.endsWith(".jsonl"))) files.push(join34(entry.parentPath, entry.name));
|
|
20477
20515
|
}
|
|
20478
20516
|
}
|
|
20479
20517
|
return files.sort();
|
|
20480
20518
|
}
|
|
20481
20519
|
function defaultSkillCatalogRoots() {
|
|
20482
|
-
const codexHome = process.env.CODEX_HOME ??
|
|
20520
|
+
const codexHome = process.env.CODEX_HOME ?? join34(homedir11(), ".codex");
|
|
20483
20521
|
return {
|
|
20484
|
-
claudeHome:
|
|
20485
|
-
codexSkillDirs: [
|
|
20522
|
+
claudeHome: join34(homedir11(), ".claude"),
|
|
20523
|
+
codexSkillDirs: [join34(codexHome, "skills"), join34(codexHome, "skills", ".system"), join34(homedir11(), ".agents", "skills")]
|
|
20486
20524
|
};
|
|
20487
20525
|
}
|
|
20488
20526
|
async function skillFolders(directory, prefix = "") {
|
|
20489
20527
|
let entries;
|
|
20490
20528
|
try {
|
|
20491
|
-
entries = await
|
|
20529
|
+
entries = await readdir19(directory);
|
|
20492
20530
|
} catch {
|
|
20493
20531
|
return [];
|
|
20494
20532
|
}
|
|
20495
20533
|
const found = [];
|
|
20496
20534
|
for (const entry of entries.sort()) {
|
|
20497
20535
|
if (entry.startsWith(".")) continue;
|
|
20498
|
-
const path =
|
|
20536
|
+
const path = join34(directory, entry);
|
|
20499
20537
|
try {
|
|
20500
|
-
await
|
|
20538
|
+
await stat17(join34(path, "SKILL.md"));
|
|
20501
20539
|
found.push({ name: `${prefix}${entry}`, path });
|
|
20502
20540
|
} catch {
|
|
20503
20541
|
}
|
|
@@ -20514,19 +20552,19 @@ async function readJsonObject(path) {
|
|
|
20514
20552
|
}
|
|
20515
20553
|
async function readInstalledSkills(roots = defaultSkillCatalogRoots()) {
|
|
20516
20554
|
const installed = [];
|
|
20517
|
-
for (const skill of await skillFolders(
|
|
20555
|
+
for (const skill of await skillFolders(join34(roots.claudeHome, "skills"))) {
|
|
20518
20556
|
installed.push({ ...skill, harness: "claude-code" });
|
|
20519
20557
|
}
|
|
20520
|
-
const settings = await readJsonObject(
|
|
20558
|
+
const settings = await readJsonObject(join34(roots.claudeHome, "settings.json"));
|
|
20521
20559
|
const enabled = settings?.enabledPlugins ?? {};
|
|
20522
|
-
const plugins = await readJsonObject(
|
|
20560
|
+
const plugins = await readJsonObject(join34(roots.claudeHome, "plugins", "installed_plugins.json"));
|
|
20523
20561
|
const pluginEntries = plugins?.plugins ?? {};
|
|
20524
20562
|
for (const [key, value] of Object.entries(pluginEntries)) {
|
|
20525
20563
|
if (enabled[key] !== true) continue;
|
|
20526
20564
|
const plugin = key.split("@", 1)[0];
|
|
20527
20565
|
for (const install of Array.isArray(value) ? value : [value]) {
|
|
20528
20566
|
if (typeof install?.installPath !== "string") continue;
|
|
20529
|
-
for (const skill of await skillFolders(
|
|
20567
|
+
for (const skill of await skillFolders(join34(install.installPath, "skills"), `${plugin}:`)) {
|
|
20530
20568
|
installed.push({ ...skill, harness: "claude-code" });
|
|
20531
20569
|
}
|
|
20532
20570
|
}
|
|
@@ -20653,6 +20691,7 @@ export {
|
|
|
20653
20691
|
isSynthesizedSpan,
|
|
20654
20692
|
isInheritedSpan,
|
|
20655
20693
|
ATTR,
|
|
20694
|
+
SESSION_CWD_ATTR,
|
|
20656
20695
|
INGEST_SOURCE_CLI,
|
|
20657
20696
|
environmentAttributes,
|
|
20658
20697
|
stampEnvironmentAttrs,
|
|
@@ -20664,6 +20703,7 @@ export {
|
|
|
20664
20703
|
appendAll,
|
|
20665
20704
|
parseIsoToEpochMs,
|
|
20666
20705
|
parseSince,
|
|
20706
|
+
parseTimeBound,
|
|
20667
20707
|
resolveOtlpInputFiles,
|
|
20668
20708
|
inferSpanKind,
|
|
20669
20709
|
otlpRowToSpan,
|
|
@@ -20699,7 +20739,6 @@ export {
|
|
|
20699
20739
|
CodexExecStreamError,
|
|
20700
20740
|
CodexExecAdapter,
|
|
20701
20741
|
CopilotAdapter,
|
|
20702
|
-
ForgeAdapter,
|
|
20703
20742
|
GeminiAdapter,
|
|
20704
20743
|
textIsSynthetic,
|
|
20705
20744
|
looksLikeAgentPrompt,
|
|
@@ -20753,10 +20792,12 @@ export {
|
|
|
20753
20792
|
SESSION_TOKEN_TOTAL_ATTR,
|
|
20754
20793
|
SPAN_SYNTHESIZED_ATTR,
|
|
20755
20794
|
FACT_TEXT_CAP,
|
|
20795
|
+
ANSWERED_TEXT_CAP,
|
|
20756
20796
|
FACT_LIST_CAP,
|
|
20757
20797
|
CHANGED_FILE_LIST_CAP,
|
|
20758
20798
|
computeSessionFacts,
|
|
20759
20799
|
buildSessionFactsReport,
|
|
20800
|
+
createSessionFactsReportBuilder,
|
|
20760
20801
|
renderSessionFacts,
|
|
20761
20802
|
PREPARED_CONTEXT_BYTE_CEILING,
|
|
20762
20803
|
renderSessionFactsContext,
|
|
@@ -20869,7 +20910,7 @@ export {
|
|
|
20869
20910
|
ms,
|
|
20870
20911
|
usd,
|
|
20871
20912
|
zeroPricedCost,
|
|
20872
|
-
|
|
20913
|
+
tokens,
|
|
20873
20914
|
STATUS_MARK,
|
|
20874
20915
|
connectors,
|
|
20875
20916
|
forEachTreeNode,
|