@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.
@@ -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-dev-container`). */
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(`--since: expected 30m / 2h / 7d or an ISO date, got "${value}"`);
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: join36 } = await import("path");
1028
- const path = outPath ?? join36(await mkdtemp8(join36(tmpdir8(), "traces-")), "spans.otlp.jsonl");
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/forge.ts
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 { basename as basename3, join as join5 } from "path";
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 textOf3(content) {
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 join6(homedir4(), this.homeDirName, "tmp");
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
- join6(homedir4(), this.homeDirName, "projects.json")
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 readdir6(root);
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 = join6(root, pd, "chats");
2472
+ const chatsDir = join5(root, pd, "chats");
2602
2473
  let files;
2603
2474
  try {
2604
- files = await readdir6(chatsDir);
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 = join6(chatsDir, f);
2482
+ const path = join5(chatsDir, f);
2612
2483
  let st;
2613
2484
  try {
2614
- st = await stat5(path);
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 = textOf3(m.content);
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: textOf3(m.content) || null,
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.isMeta === true) return "injected";
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 readdir8, stat as stat6 } from "fs/promises";
2879
- import { homedir as homedir6 } from "os";
2880
- import { basename as basename5, join as join8, relative as relative3 } from "path";
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 readdir7, realpath } from "fs/promises";
2892
- import { homedir as homedir5 } from "os";
2893
- import { dirname as dirname3, isAbsolute as isAbsolute2, join as join7, relative as relative2, resolve as resolve3 } from "path";
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 basename4, dirname as dirname2, isAbsolute, relative, resolve as resolve2 } from "path";
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) || basename4(resolvedTranscriptDir) !== cleanRunId || basename4(dirname2(resolvedTranscriptDir)) !== "workflows" || basename4(dirname2(dirname2(resolvedTranscriptDir))) !== "subagents") {
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 readdir7(dir, { withFileTypes: true });
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 = join7(dir, entry.name);
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(homedir5(), ".claude", "projects");
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 = join7(dirname3(resolve3(ref.path)), sessionId);
2980
+ const sessionRoot2 = join6(dirname3(resolve3(ref.path)), sessionId);
3107
2981
  return {
3108
2982
  sessionRoot: sessionRoot2,
3109
- transcriptDir: join7(sessionRoot2, "subagents", "workflows", binding.runId)
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 = join7(ref.path.replace(/\.jsonl$/, ""), "subagents");
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 SERVICE5 = "claude-code";
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 textOf4(content) {
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: textOf4(event.message.content) || null,
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 = textOf4(event.message.content);
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 = textOf4(event.attachment.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: SERVICE5,
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: SERVICE5,
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: SERVICE5,
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 actor = claudeActorFromOrigin(event.originKind, () => claudeActor({
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: SERVICE5,
3538
+ service: SERVICE4,
3664
3539
  agent: ctx.agent,
3665
3540
  step: state.step,
3666
- content: actor === "human" ? typedPromptText(event.prompt) : event.prompt,
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 actor = claudeActorFromOrigin(event.originKind, () => "injected");
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: SERVICE5,
3570
+ service: SERVICE4,
3694
3571
  agent: ctx.agent,
3695
3572
  step: state.step,
3696
- content: actor === "human" ? typedPromptText(event.prompt) : event.prompt,
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: SERVICE5,
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: SERVICE5,
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 join8(homedir6(), ".claude", "projects");
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 stat6(path);
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 readdir8(root);
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 = join8(root, dir);
3834
+ const dirPath = join7(root, dir);
3957
3835
  let files;
3958
3836
  try {
3959
- files = await readdir8(dirPath);
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 = join8(dirPath, f);
3844
+ const path = join7(dirPath, f);
3967
3845
  let st;
3968
3846
  try {
3969
- st = await stat6(path);
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: basename5(f, ".jsonl"), path, cwd, mtimeMs: st.mtimeMs });
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: SERVICE5,
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: SERVICE5,
4075
- agent: SERVICE5
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 = join8(ref.path.replace(/\.jsonl$/, ""), "subagents");
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: basename5(file, ".jsonl").replace(/^agent-/, ""),
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
- basename5(file),
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: SERVICE5,
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 readdir9, stat as stat7 } from "fs/promises";
4714
- import { homedir as homedir7 } from "os";
4715
- import { basename as basename6, join as join9 } from "path";
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 SERVICE6 = "codex";
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 textOf5(content) {
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: join36 } = joinItem(windows, entry.startMs, entry.endMs);
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"] = join36;
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: SERVICE6,
5051
- agent: SERVICE6,
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: SERVICE6,
5084
- agent: SERVICE6,
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 readdir9(root);
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 = join9(root, y);
5022
+ const yp = join8(root, y);
5139
5023
  let months;
5140
5024
  try {
5141
- months = await readdir9(yp);
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 = join9(yp, m);
5031
+ const mp = join8(yp, m);
5148
5032
  let days;
5149
5033
  try {
5150
- days = await readdir9(mp);
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 = join9(mp, d);
5040
+ const dp = join8(mp, d);
5157
5041
  let files;
5158
5042
  try {
5159
- files = await readdir9(dp);
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 join9(dp, f);
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 ? join9(process.env.CODEX_HOME, "sessions") : join9(homedir7(), ".codex", "sessions");
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 stat7(path);
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 = basename6(path).replace(/^rollout-[\dT-]+-/, "").replace(/\.jsonl$/, "");
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 (!basename6(path).includes(sessionId)) continue;
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: SERVICE6,
5320
- agent: SERVICE6,
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: SERVICE6,
5386
- agent: SERVICE6,
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: SERVICE6,
5421
- agent: SERVICE6,
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: SERVICE6,
5444
- agent: SERVICE6,
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: SERVICE6,
5531
- agent: SERVICE6,
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: SERVICE6,
5610
- agent: SERVICE6,
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: SERVICE6,
5643
- agent: SERVICE6,
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 = textOf5(l.payload.content);
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: SERVICE6,
5767
- agent: SERVICE6,
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: SERVICE6,
5813
- agent: SERVICE6,
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 = textOf5(l.payload.content);
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: SERVICE6,
5833
- agent: SERVICE6,
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 readdir10, stat as stat8 } from "fs/promises";
5892
- import { homedir as homedir8 } from "os";
5893
- import { join as join10 } from "path";
5894
- var SERVICE7 = "opencode";
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 textOf6(parts) {
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 ?? join10(homedir8(), ".local", "share");
5907
- return join10(base, "opencode", "storage");
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 = join10(this.storage(), "message");
5798
+ const msgRoot = join9(this.storage(), "message");
5911
5799
  let sessionDirs;
5912
5800
  try {
5913
- sessionDirs = await readdir10(msgRoot);
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 = join10(msgRoot, sid);
5808
+ const dir = join9(msgRoot, sid);
5921
5809
  let st;
5922
5810
  try {
5923
- st = await stat8(dir);
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 = join10(ref.path, file);
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 = join10(this.storage(), "part", mid);
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) => join10(directory, file)));
5954
- const parts = await Promise.all(files.map((file) => readJsonFile(join10(directory, file))));
5955
- if (message.role !== "user" || textOf6(parts)) step += 1;
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 = join10(this.storage(), "part");
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(join10(ref.path, f), options));
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: SERVICE7,
5980
- agent: SERVICE7
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 = join10(partRoot, mid);
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(join10(pdir, pf), options));
5886
+ parts.push(await readJsonFile(join9(pdir, pf), options));
5997
5887
  }
5998
- const turnText2 = textOf6(parts);
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: SERVICE7,
6009
- agent: SERVICE7,
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: SERVICE7,
6031
- agent: SERVICE7,
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: SERVICE7,
6063
- agent: SERVICE7,
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 homedir9 } from "os";
6083
- import { open, stat as stat9 } from "fs/promises";
6084
- import { join as join11 } from "path";
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 : join11(ref.path, "wire.jsonl")));
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(homedir9(), { sinceMs: opts.sinceMs, cwd: opts.cwd });
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(homedir9(), { nativeSessionId: sessionId, sinceMs: opts.sinceMs, cwd: opts.cwd });
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 stat9(settings);
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 readdir11, stat as stat10 } from "fs/promises";
6501
- import { homedir as homedir10 } from "os";
6502
- import { basename as basename7, join as join12 } from "path";
6503
- var SERVICE8 = "qwen";
6504
- function textOf7(parts) {
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 join12(homedir10(), ".qwen", "projects");
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 readdir11(root);
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 = join12(root, pd, "chats");
6416
+ const chatsDir = join11(root, pd, "chats");
6526
6417
  let files;
6527
6418
  try {
6528
- files = await readdir11(chatsDir);
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 = join12(chatsDir, f);
6427
+ const path = join11(chatsDir, f);
6537
6428
  let st;
6538
6429
  try {
6539
- st = await stat10(path);
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: basename7(f, ".jsonl"), path, cwd: null, mtimeMs: st.mtimeMs });
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: SERVICE8,
6580
- agent: SERVICE8,
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(textOf7(r.message?.parts)) || null,
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: SERVICE8,
6601
- agent: SERVICE8,
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(textOf7(r.message?.parts));
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: SERVICE8,
6622
- agent: SERVICE8,
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: SERVICE8,
6659
- agent: SERVICE8
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 stat11 } from "fs/promises";
6701
- import { dirname as dirname4, isAbsolute as isAbsolute3, join as join13, normalize, parse as parsePath } from "path";
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 stat11(`${cwd}/.git`);
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 stat11(path);
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(join13(cur, ".git"))) return cur;
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 = join13(current, nextPart);
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 = join13(current, segment);
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 basename16 = (() => {
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] = basename16;
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 join14 } from "path";
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 ?? join14(await mkdtemp(join14(tmpdir2(), "traces-evidence-")), "policy-evidence.jsonl");
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 join15 } from "path";
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 = join15(cwd, artifactDir, "skill-runs.jsonl");
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 basename8(word) {
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(basename8(word))) start += 1;
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 = basename8(words[0] ?? "");
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 = basename8(words[0] ?? "");
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 basename9(word) {
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 (basename9(words[0] ?? "") !== "git" || words.length < 2) continue;
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(basename9(words[0])) || words[1] !== "pr") continue;
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 = "3";
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, spanId) => {
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.span_id);
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.span_id);
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.span_id);
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.span_id);
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
- turns: kept.map((span2) => ({ actor: "human", at: span2.start_time, text: turnText(span2), spanId: span2.span_id })),
9189
- excluded: [...excluded].sort(([left], [right]) => left.localeCompare(right)).map(([reason, spanIds]) => ({ reason, turns: spanIds.length, spanIds }))
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].sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0).map(([traceId, traceSpans]) => sessionFactsForTrace(traceId, sessionByTrace.get(traceId) ?? null, traceSpans, options));
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 promptSpans = spans.filter((span2) => span2.name === "user.prompt");
9217
- const { turns: humanTurns, excluded: excludedTurns } = humanTurnsOf(spans);
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 digest = createHash7("sha256");
9336
- const ordered2 = [...spans].sort((a, b) => a.trace_id.localeCompare(b.trace_id) || a.span_id.localeCompare(b.span_id));
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
- for (const span2 of ordered2) {
9339
- digest.update(canonicalJson(span2)).update("\n");
9340
- for (const time of [span2.start_time, span2.end_time]) {
9341
- if (Number.isFinite(Date.parse(time)) && (evidenceThrough === null || Date.parse(time) > Date.parse(evidenceThrough))) evidenceThrough = time;
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
- schemaVersion: 1,
9346
- kind: "traces.session_facts_report",
9347
- measurementVersion: SESSION_FACTS_MEASUREMENT_VERSION,
9348
- sourceDigest: "sha256:" + digest.digest("hex"),
9349
- evidenceThrough,
9350
- contentIncluded: options.includeContent !== false,
9351
- generatedAt: options.generatedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
9352
- harness: options.harness ?? null,
9353
- spanCount: spans.length,
9354
- sessions: computeSessionFacts(spans, options)
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 join19, relative as relative5, resolve as resolve6, sep as sep2 } from "path";
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 readdir14, readFile as readFile7, stat as stat13, writeFile as writeFile5 } from "fs/promises";
9642
- import { dirname as dirname7, join as join18, relative as relative4, resolve as resolve5, sep } from "path";
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 join16, resolve as resolve4 } from "path";
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 readdir12, readFile as readFile5 } from "fs/promises";
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 readdir12("/proc", { withFileTypes: true });
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 stat19 = await readFile5(`/proc/${entry.name}/stat`, "utf8");
10264
- const commandEnd = stat19.lastIndexOf(")");
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 = stat19.slice(commandEnd + 1).trim().split(/\s+/);
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(join16(tmpdir3(), "traces-improvement-"));
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: join16(directory, "result.json"),
11946
- evidence: join16(directory, "evidence.jsonl"),
11947
- report: join16(directory, "report.md"),
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(join16(tmpdir3(), "traces-improvement-"));
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 ?? join16(directory, "traces.otlp.jsonl")
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 readdir13, readFile as readFile6, stat as stat12, writeFile as writeFile4 } from "fs/promises";
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 join17 } from "path";
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 stat12(path);
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(join17(current, ".git")) || await dirExists(join17(current, ".evolve")) || await fileExists(join17(current, "AGENTS.md")) || await fileExists(join17(current, "CLAUDE.md"))) {
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 readdir13(dir, { withFileTypes: true });
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 = join17(dir, entry.name);
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(join17(root, name), "instruction-doc");
12177
+ const summary = await summarizeFile(join16(root, name), "instruction-doc");
12140
12178
  if (summary) files.push(summary);
12141
12179
  }
12142
- const evolve = join17(root, ".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(join17(evolve, name), "evolve-jsonl");
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(join17(evolve, name), "evolve-json");
12186
+ const summary = await summarizeFile(join16(evolve, name), "evolve-json");
12149
12187
  if (summary) files.push(summary);
12150
12188
  }
12151
- files.push(...await walkMarkdown(join17(evolve, "reflections"), "reflection"));
12152
- files.push(...await walkMarkdown(join17(evolve, "handoffs"), "handoff"));
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(join17(evolve, name), "handoff");
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(join17(evolve, "progress.md"), "other");
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 readdir13(evolve, { withFileTypes: true });
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 ?? join17(await mkdtemp3(join17(tmpdir4(), "traces-index-")), "session-index.json");
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 stat13(path);
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 readdir14(dir, { withFileTypes: true })) {
12758
- const path = join18(dir, entry.name);
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 = join18(ctx.evolveDir, name);
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 = join18(ctx.ledgerDir, name);
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 = join18(ctx.evolveDir, name);
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, join18(ctx.ledgerDir, name));
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 readdir14(ctx.evolveDir, { withFileTypes: true })).filter((entry) => entry.isFile() && /^handoff-.*\.md$/.test(entry.name)).map((entry) => entry.name).sort();
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 = join18(ctx.evolveDir, latest);
12817
- await cp(source, join18(ctx.ledgerDir, latest));
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 = join18(ctx.evolveDir, "reflections");
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 readdir14(reflectionsDir, { withFileTypes: true })).filter((entry) => entry.isFile() && entry.name.endsWith(".md")).map((entry) => entry.name).sort();
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(join18(ctx.ledgerDir, "reflections"), { recursive: true });
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 = join18(reflectionsDir, name);
12849
- await cp(source, join18(ctx.ledgerDir, "reflections", name));
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 stat13(ref.path);
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 readdir14(outDir)).length > 0) {
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(join18(outDir, "session"), { recursive: true });
12881
- const subagentsSource = join18(ref.path.replace(/\.jsonl$/, ""), "subagents");
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, join18(outDir, "session", "subagents"), { recursive: true });
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 = join18(outDir, path);
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(join18(outDir, "derived"), { recursive: true });
12902
- const otlpPath = join18(outDir, "derived", "trace.otlp.jsonl");
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(join18(outDir, "derived", "report.md"), investigation.report, "utf8");
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(join18(outDir, "derived", "evidence.jsonl"), serializePolicyEvidence([evidence3]), "utf8");
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(join18(outDir, "derived", "session-index.json"), serializeSessionIndex(index), "utf8");
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 = join18(contextRoot, ".evolve");
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 = join18(outDir, "ledger");
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(join18(outDir, "repo"), { recursive: true });
12961
- await writeFile5(join18(outDir, "repo", "git-log.txt"), log.stdout ? `${log.stdout}
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 = join18(root, path);
13001
- const info = await stat13(filePath);
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 = join18(outDir, "manifest.json");
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 = join18(resolve5(bundleDir), "manifest.json");
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 readdir14(dir, { withFileTypes: true })) {
13058
- const path = join18(dir, entry.name);
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(join18(directory, file.path)).catch((error) => {
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(join18(directory, file.path));
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 = join18(manifest.projection.sourceDirectory, "manifest.json");
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 = join19(current, part);
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(join19(root, "manifest.json"))).isSymbolicLink()) throw new Error("bundle manifest must not be a symlink");
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(join19(root, TRACE_FILE));
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 basename11, dirname as dirname10, join as join22, resolve as resolve8 } from "path";
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 stat14,
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 basename10, dirname as dirname9, isAbsolute as isAbsolute5, join as join20, relative as relative6, resolve as resolve7, sep as sep3 } from "path";
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 = join20(
13664
+ const temporary = join19(
13627
13665
  dirname9(path),
13628
- `.${basename10(path)}.${claimId}.tmp`
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 = join20(outputDirectory, file);
13718
- await link(join20(stagingDirectory, file), destination);
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 stat14(canonical);
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 stat14(canonical);
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 = join20(
13826
+ const temporary = join19(
13789
13827
  dirname9(path),
13790
- `.${basename10(path)}.${randomUUID()}.tmp`
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 join21 } from "path";
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 = join21(trajectoryDirectory, row.trajId);
13861
+ const caseDirectory = join20(trajectoryDirectory, row.trajId);
13824
13862
  const stepsPath = await containedFile(
13825
13863
  trajectoryDirectory,
13826
- join21(caseDirectory, "steps.json"),
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(join21(stagingDirectory, outputFile), serialized, signal);
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 = join21(caseDirectory, "task.md");
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 = basename11(outputDirectory);
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
- join22(dirname10(outputDirectory), `.${outputBase}.tmp-`)
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: basename11(rowsPath),
14265
+ rowsFile: basename10(rowsPath),
14228
14266
  rowsSha256,
14229
14267
  rowsBytes: rowsBytes.byteLength,
14230
- trajectoryDirectory: basename11(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
- join22(stagingDirectory, receiptFile),
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: join22(outputDirectory, receiptFile),
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 readdir15, writeFile as writeFile7 } from "fs/promises";
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 join23, resolve as resolve9 } from "path";
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(join23(tmpdir5(), "traces-hodoscope-input-"));
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 readdir15(directory)).length > 0) {
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 = join23(directory, "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(join23(samples, file), `${JSON.stringify(trajectory, null, 2)}
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(join23(outputRoot, "traces-hodoscope-"));
14760
- const input = join23(directory, "input");
14761
- const analysisFile = join23(directory, "analysis.hodoscope.json");
14762
- const samplesFile = join23(directory, "samples.json");
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 join24 } from "path";
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: stat19 } = await import("fs/promises");
15437
+ const { copyFile: copyFile2, mkdtemp: mkdtemp8, stat: stat18 } = await import("fs/promises");
15400
15438
  const { tmpdir: tmpdir8 } = await import("os");
15401
- const { join: join36 } = await import("path");
15402
- const cwd = await mkdtemp8(join36(tmpdir8(), "traces-prime-"));
15403
- await copyFile2(otlpPath, join36(cwd, TRAJECTORY_FILE_NAME));
15404
- const artifactBytes = (await stat19(otlpPath)).size;
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 tokens3 = 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;
15433
- return { calls, tokens: tokens3, estimated: a.estimated || b.estimated };
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 = join24(cwd, FINDINGS_FILE_NAME);
15679
+ const path = join23(cwd, FINDINGS_FILE_NAME);
15642
15680
  let raw;
15643
- const { readFile: readFile21, stat: stat19 } = await import("fs/promises");
15681
+ const { readFile: readFile21, stat: stat18 } = await import("fs/promises");
15644
15682
  try {
15645
- const size = (await stat19(path)).size;
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 join25 } from "path";
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 ?? join25(await mkdtemp6(join25(tmpdir6(), "traces-export-")), "spans.openinference.jsonl");
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 basename12, join as join26 } from "path";
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: tokens3 }) => tokens3 >= 5e5).sort((a, b) => b.tokens - a.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: tokens3 }) => `${sessionLabel(row)}: ${tokens3.toLocaleString()} reported summed tokens (${row.metrics.inputTokens.toLocaleString()} in, ${row.metrics.outputTokens.toLocaleString()} out)`),
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) => basename12(file.path) === "skill-runs.jsonl" && (file.jsonl?.rows ?? 0) > 0).map((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 ?? join26(await mkdtemp7(join26(tmpdir7(), "traces-inspect-")), "inspection-report.md");
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 stat15 } from "fs/promises";
17459
- import { basename as basename13, join as join27 } from "path";
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(join27(runDir, COORDINATION_LOG_FILE)));
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 : basename13(runDir),
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: join27(runDir, SPAWN_JOURNAL_FILE),
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 stat15(join27(runDir, SPAWN_JOURNAL_FILE)).then((s) => s.isFile()).catch(() => false);
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(join27(runDir, SPAWN_JOURNAL_FILE)));
17711
- const coordination = readCoordinationLog(await readMaybe(join27(runDir, COORDINATION_LOG_FILE)));
17712
- const resultText = await readMaybe(join27(runDir, RESULT_FILE));
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 tokens2(roll) {
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 readdir16 } from "fs/promises";
17893
- import { join as join28 } from "path";
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 ${tokens2(totals.driver)} \xB7 ${usd(totals.driver)}`);
18109
- lines.push(` leaf inference ${tokens2(totals.leaves)} \xB7 ${usd(totals.leaves)}`);
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 ${tokens2(node.driver)} \xB7 ${usd(node.driver)} \xB7 ${node.toolCalls} tool call(s)`
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 ${tokens2(node.children)} \xB7 ${usd(node.children)}`
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 readdir16(dir, { withFileTypes: true }).catch(() => []);
18204
+ const entries = await readdir15(dir, { withFileTypes: true }).catch(() => []);
18167
18205
  for (const entry of entries) {
18168
- const child = join28(dir, entry.name);
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 readdir16(child, { withFileTypes: true }).catch(() => []);
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(join28(child, inner.name));
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 readdir17 } from "fs/promises";
18184
- import { join as join29 } from "path";
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 ${tokens2(node.driver)} \xB7 ${usd(node.driver)}` + budgetUse(node, node.driver)
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 ${tokens2(node.children)} \xB7 ${usd(node.children)}`
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 ${tokens2(node.settled)} \xB7 ${usd(node.settled)}` + budgetUse(node, node.settled)
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 ${tokens2(totals.driver)} \xB7 ${usd(totals.driver)}`);
18251
- lines.push(` worker total ${tokens2(totals.workers)} \xB7 ${usd(totals.workers)}`);
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 readdir17(dir, { withFileTypes: true }).catch(() => null);
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 = join29(dir, entry.name);
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 stat16 } from "fs/promises";
18385
+ import { stat as stat15 } from "fs/promises";
18348
18386
  async function resolveRunWatchTarget(target) {
18349
- const info = await stat16(target).catch(() => null);
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 join30, resolve as resolve10 } from "path";
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: join30(directory, "answers.json"),
19230
- report: join30(directory, "report.md"),
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 readdir18, readFile as readFile16, writeFile as writeFile11 } from "fs/promises";
19294
- import { dirname as dirname11, join as join31, resolve as resolve11 } from "path";
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(join31(opts.viewDir, path), "utf8"));
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(join31(opts.sourceDir, sourcePath), "utf8"));
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 readdir18(outDir)).length > 0) {
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(join31(sourceDir, path));
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
- `${join31(sourceDir, path)} is not listed in the bundle manifest \u2014 the bundle changed after assembly and cannot be projected; re-run \`traces bundle\``
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
- `${join31(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\``
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 = join31(outDir, candidate.path);
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(join31(sourceDir, "manifest.json"));
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 stat17 } from "fs/promises";
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 stat17(path)).isFile()) {
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 homedir11 } from "os";
19736
- import { dirname as dirname12, join as join32 } from "path";
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 ?? join32(homedir11(), ".local", "state");
19740
- return join32(base, "tangle-traces", "uploaded.json");
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 basename14, join as join33 } from "path";
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(join33(cwd, ".git", "HEAD"), "utf8");
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]: basename14(item.ref.path),
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 readdir19, readFile as readFile19 } from "fs/promises";
19972
- import { basename as basename15, extname, join as join34, relative as relative7 } from "path";
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) || basename15(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 stat19;
20056
+ let stat18;
20019
20057
  try {
20020
- stat19 = await lstat4(target);
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 (stat19.isFile()) return [target];
20026
- if (!stat19.isDirectory()) {
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 readdir19(dir, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name))) {
20033
- const path = join34(dir, entry.name);
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 readdir20, readFile as readFile20, rename as rename3, stat as stat18, writeFile as writeFile13 } from "fs/promises";
20086
- import { homedir as homedir12 } from "os";
20087
- import { dirname as dirname13, join as join35 } from "path";
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 ?? join35(homedir12(), ".codex");
20148
+ const codexHome = process.env.CODEX_HOME ?? join34(homedir11(), ".codex");
20111
20149
  return {
20112
- claudeProjects: join35(homedir12(), STORES["claude-code.projects-jsonl"].root),
20113
- codexSessions: [join35(codexHome, ...STORES["codex.rollout-jsonl"].root.split("/").slice(1)), join35(codexHome, "archived_sessions")],
20114
- kimiSessions: join35(homedir12(), STORES["kimi.session-dir"].root)
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 ?? join35(homedir12(), ".local", "state");
20119
- return join35(state, "traces", "skill-usage.json");
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 stat18(path);
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 readdir20(root, { recursive: true, withFileTypes: true });
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(join35(entry.parentPath, entry.name));
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 ?? join35(homedir12(), ".codex");
20520
+ const codexHome = process.env.CODEX_HOME ?? join34(homedir11(), ".codex");
20483
20521
  return {
20484
- claudeHome: join35(homedir12(), ".claude"),
20485
- codexSkillDirs: [join35(codexHome, "skills"), join35(codexHome, "skills", ".system"), join35(homedir12(), ".agents", "skills")]
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 readdir20(directory);
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 = join35(directory, entry);
20536
+ const path = join34(directory, entry);
20499
20537
  try {
20500
- await stat18(join35(path, "SKILL.md"));
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(join35(roots.claudeHome, "skills"))) {
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(join35(roots.claudeHome, "settings.json"));
20558
+ const settings = await readJsonObject(join34(roots.claudeHome, "settings.json"));
20521
20559
  const enabled = settings?.enabledPlugins ?? {};
20522
- const plugins = await readJsonObject(join35(roots.claudeHome, "plugins", "installed_plugins.json"));
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(join35(install.installPath, "skills"), `${plugin}:`)) {
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
- tokens2 as tokens,
20913
+ tokens,
20873
20914
  STATUS_MARK,
20874
20915
  connectors,
20875
20916
  forEachTreeNode,