@tangle-network/traces 0.16.3 → 0.17.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.
@@ -9,13 +9,13 @@ function sourceFileId(path) {
9
9
  function pointerPart(value) {
10
10
  return value.replaceAll("~", "~0").replaceAll("/", "~1");
11
11
  }
12
- function locateSourceObjects(value, path, bytes, offset = 0) {
12
+ function locateSourceObjects(value, path, bytes2, offset = 0) {
13
13
  if (!value || typeof value !== "object") return;
14
14
  const record2 = {
15
15
  sourceId: sourceFileId(path),
16
16
  recordOffset: offset,
17
- recordBytes: bytes.length,
18
- recordSha256: createHash("sha256").update(bytes).digest("hex")
17
+ recordBytes: bytes2.length,
18
+ recordSha256: createHash("sha256").update(bytes2).digest("hex")
19
19
  };
20
20
  const pending = [{ value, pointer: "#" }];
21
21
  while (pending.length > 0) {
@@ -317,35 +317,35 @@ function serialize(value) {
317
317
  return { value: canonicalJson(value), mimeType: "application/json" };
318
318
  }
319
319
  function utf8Prefix(value, maxBytes) {
320
- let bytes = 0;
320
+ let bytes2 = 0;
321
321
  let end = 0;
322
322
  for (const char of value) {
323
323
  const charBytes = Buffer.byteLength(char);
324
- if (bytes + charBytes > maxBytes) break;
325
- bytes += charBytes;
324
+ if (bytes2 + charBytes > maxBytes) break;
325
+ bytes2 += charBytes;
326
326
  end += char.length;
327
327
  }
328
328
  return Buffer.from(value.slice(0, end), "utf8").toString("utf8");
329
329
  }
330
- function truncatedValue(value, bytes) {
330
+ function truncatedValue(value, bytes2) {
331
331
  const longestMarker = `
332
- [truncated ${bytes} bytes]`;
332
+ [truncated ${bytes2} bytes]`;
333
333
  const prefix = utf8Prefix(value, TOOL_IO_VALUE_MAX_BYTES - Buffer.byteLength(longestMarker));
334
- const omittedBytes = bytes - Buffer.byteLength(prefix);
334
+ const omittedBytes = bytes2 - Buffer.byteLength(prefix);
335
335
  return `${prefix}
336
336
  [truncated ${omittedBytes} bytes]`;
337
337
  }
338
338
  function valueAttributes(side, raw) {
339
339
  const serialized = serialize(raw);
340
340
  if (!serialized) return {};
341
- const bytes = Buffer.byteLength(serialized.value);
341
+ const bytes2 = Buffer.byteLength(serialized.value);
342
342
  const sha2562 = createHash2("sha256").update(serialized.value).digest("hex");
343
- const truncated = bytes > TOOL_IO_VALUE_MAX_BYTES;
344
- const value = truncated ? truncatedValue(serialized.value, bytes) : serialized.value;
343
+ const truncated = bytes2 > TOOL_IO_VALUE_MAX_BYTES;
344
+ const value = truncated ? truncatedValue(serialized.value, bytes2) : serialized.value;
345
345
  return {
346
346
  [`${side}.value`]: value,
347
347
  [`${side}.mime_type`]: serialized.mimeType,
348
- [`traces.${side}.bytes`]: bytes,
348
+ [`traces.${side}.bytes`]: bytes2,
349
349
  [`traces.${side}.sha256`]: sha2562,
350
350
  [`traces.${side}.truncated`]: truncated
351
351
  };
@@ -1011,16 +1011,36 @@ function toOpenInferenceSpan(s) {
1011
1011
  }
1012
1012
  function serializeSpans(spans) {
1013
1013
  if (spans.length === 0) return "";
1014
- const validated = validateOtlpSpans(spans, "serialized spans");
1015
- return `${validated.map((s) => JSON.stringify(toOpenInferenceSpan(s))).join("\n")}
1014
+ let out = "";
1015
+ for (const line of iterSerializedSpans(spans)) out += line;
1016
+ return out;
1017
+ }
1018
+ function* iterSerializedSpans(spans) {
1019
+ for (const s of validateOtlpSpans(spans, "serialized spans")) {
1020
+ yield `${JSON.stringify(toOpenInferenceSpan(s))}
1016
1021
  `;
1022
+ }
1017
1023
  }
1024
+ var OTLP_WRITE_CHUNK_CHARS = 4 * 1024 * 1024;
1018
1025
  async function writeOtlpFile(spans, outPath) {
1019
- const { mkdtemp: mkdtemp8, writeFile: writeFile13 } = await import("fs/promises");
1026
+ const { mkdtemp: mkdtemp8, open: open2 } = await import("fs/promises");
1020
1027
  const { tmpdir: tmpdir8 } = await import("os");
1021
1028
  const { join: join35 } = await import("path");
1022
1029
  const path = outPath ?? join35(await mkdtemp8(join35(tmpdir8(), "traces-")), "spans.otlp.jsonl");
1023
- await writeFile13(path, serializeSpans(spans), "utf8");
1030
+ const handle = await open2(path, "w");
1031
+ try {
1032
+ let chunk = "";
1033
+ for (const line of iterSerializedSpans(spans)) {
1034
+ chunk += line;
1035
+ if (chunk.length >= OTLP_WRITE_CHUNK_CHARS) {
1036
+ await handle.write(chunk);
1037
+ chunk = "";
1038
+ }
1039
+ }
1040
+ if (chunk.length > 0) await handle.write(chunk);
1041
+ } finally {
1042
+ await handle.close();
1043
+ }
1024
1044
  return path;
1025
1045
  }
1026
1046
 
@@ -1367,18 +1387,18 @@ function isMissingJsonSource(error) {
1367
1387
  }
1368
1388
  async function readJsonFile(path, options = {}) {
1369
1389
  let raw;
1370
- let bytes;
1390
+ let bytes2;
1371
1391
  try {
1372
1392
  if (options.captureSources) {
1373
- bytes = await readFile3(path);
1374
- raw = bytes.toString("utf8");
1393
+ bytes2 = await readFile3(path);
1394
+ raw = bytes2.toString("utf8");
1375
1395
  } else raw = await readFile3(path, "utf8");
1376
1396
  } catch (error) {
1377
1397
  throw new JsonSourceError(path, "read", error);
1378
1398
  }
1379
1399
  try {
1380
1400
  const value = JSON.parse(raw);
1381
- if (bytes) locateSourceObjects(value, path, bytes);
1401
+ if (bytes2) locateSourceObjects(value, path, bytes2);
1382
1402
  return value;
1383
1403
  } catch (error) {
1384
1404
  throw new JsonSourceError(path, "parse", pathSafeParseCause(error));
@@ -1585,9 +1605,9 @@ function parseLine(rawLine, path, lineNumber, byteOffset, options) {
1585
1605
  const jsonBytes = rawLine.at(-1) === 13 ? rawLine.subarray(0, -1) : rawLine;
1586
1606
  if (jsonBytes.length === 0) return void 0;
1587
1607
  if (!isUtf8(jsonBytes)) return handleCorruption(rawLine, path, lineNumber, byteOffset, options);
1588
- const json2 = jsonBytes.toString("utf8");
1589
- if (json2.trim().length === 0) return void 0;
1590
1608
  try {
1609
+ const json2 = jsonBytes.toString("utf8");
1610
+ if (json2.trim().length === 0) return void 0;
1591
1611
  const value = JSON.parse(json2);
1592
1612
  if (options.captureSources) locateSourceObjects(value, path, rawLine, byteOffset);
1593
1613
  return value;
@@ -1615,12 +1635,12 @@ async function* readJsonl(path, options = { mode: "strict" }) {
1615
1635
  try {
1616
1636
  for await (const chunk of input) {
1617
1637
  options.signal?.throwIfAborted();
1618
- const bytes = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
1638
+ const bytes2 = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
1619
1639
  let start = 0;
1620
- let newline = bytes.indexOf(10);
1640
+ let newline = bytes2.indexOf(10);
1621
1641
  while (newline !== -1) {
1622
1642
  options.signal?.throwIfAborted();
1623
- fragments.push(bytes.subarray(start, newline));
1643
+ fragments.push(bytes2.subarray(start, newline));
1624
1644
  const rawLine = fragments.length === 1 ? fragments[0] : Buffer.concat(fragments);
1625
1645
  fragments = [];
1626
1646
  lineNumber += 1;
@@ -1628,9 +1648,9 @@ async function* readJsonl(path, options = { mode: "strict" }) {
1628
1648
  if (parsed !== void 0) yield parsed;
1629
1649
  byteOffset += rawLine.length + 1;
1630
1650
  start = newline + 1;
1631
- newline = bytes.indexOf(10, start);
1651
+ newline = bytes2.indexOf(10, start);
1632
1652
  }
1633
- if (start < bytes.length) fragments.push(bytes.subarray(start));
1653
+ if (start < bytes2.length) fragments.push(bytes2.subarray(start));
1634
1654
  }
1635
1655
  if (fragments.length > 0) {
1636
1656
  options.signal?.throwIfAborted();
@@ -1814,7 +1834,7 @@ var CORRECTIVE_REACTIONS = ["correction", "frustration", "jargon", "structure"];
1814
1834
  import { createHash as createHash6 } from "crypto";
1815
1835
  import { readdir as readdir5, stat as stat3 } from "fs/promises";
1816
1836
  import { homedir as homedir3 } from "os";
1817
- import { basename as basename5, join as join5, relative as relative3 } from "path";
1837
+ import { basename as basename4, join as join5, relative as relative3 } from "path";
1818
1838
  import {
1819
1839
  applyLlmSpanOtlpAttributes as applyLlmSpanOtlpAttributes2,
1820
1840
  LLM_CACHED_TOKENS,
@@ -1827,7 +1847,7 @@ import { ATTR as ATTR3, deriveHexId as deriveHexId2 } from "@tangle-network/agen
1827
1847
  // src/adapters/claude-subagent-sources.ts
1828
1848
  import { readdir as readdir4, realpath } from "fs/promises";
1829
1849
  import { homedir as homedir2 } from "os";
1830
- import { basename as basename4, dirname as dirname3, isAbsolute as isAbsolute2, join as join4, relative as relative2, resolve as resolve3 } from "path";
1850
+ import { dirname as dirname3, isAbsolute as isAbsolute2, join as join4, relative as relative2, resolve as resolve3 } from "path";
1831
1851
 
1832
1852
  // src/adapters/claude-workflow.ts
1833
1853
  import { basename as basename3, dirname as dirname2, isAbsolute, relative, resolve as resolve2 } from "path";
@@ -1928,7 +1948,7 @@ function workflowRunIdForSubagent(subDir, file) {
1928
1948
  }
1929
1949
  return parts[1];
1930
1950
  }
1931
- function bindWorkflowSubagent(file, runId, transcriptDir, bindings) {
1951
+ function bindWorkflowSubagent(file, runId, transcriptDir, bindings, sourceTranscriptDir = transcriptDir) {
1932
1952
  const runBindings = bindings.get(runId);
1933
1953
  if (!runBindings || runBindings.length === 0) {
1934
1954
  throw new ClaudeTaskScopeError(
@@ -1942,7 +1962,7 @@ function bindWorkflowSubagent(file, runId, transcriptDir, bindings) {
1942
1962
  );
1943
1963
  }
1944
1964
  const localBindings = runBindings.filter(
1945
- (binding) => resolve2(binding.transcriptDir) === expectedDir
1965
+ (binding) => resolve2(binding.transcriptDir) === resolve2(sourceTranscriptDir)
1946
1966
  );
1947
1967
  if (localBindings.length === 0) {
1948
1968
  throw new ClaudeTaskScopeError(
@@ -1995,7 +2015,7 @@ function selectWorkflowBinding(file, bindings, childStartedAt, explicitToolUseId
1995
2015
  }
1996
2016
 
1997
2017
  // src/adapters/claude-subagent-sources.ts
1998
- async function listSubagentFiles(root, signal) {
2018
+ async function listSubagentFiles(root, signal, copiedWorkflow = false) {
1999
2019
  const pending = [root];
2000
2020
  const files = [];
2001
2021
  while (pending.length > 0) {
@@ -2012,6 +2032,11 @@ async function listSubagentFiles(root, signal) {
2012
2032
  signal?.throwIfAborted();
2013
2033
  for (const entry of entries) {
2014
2034
  const path = join4(dir, entry.name);
2035
+ if (copiedWorkflow && entry.isSymbolicLink()) {
2036
+ throw new ClaudeTaskScopeError(
2037
+ `Claude Workflow subagent does not match copied source: ${path}`
2038
+ );
2039
+ }
2015
2040
  if (entry.isDirectory()) {
2016
2041
  pending.push(path);
2017
2042
  } else if (entry.isFile() && /^agent-.*\.jsonl$/.test(entry.name)) {
@@ -2029,23 +2054,47 @@ function workflowStorageRoot(ref) {
2029
2054
  const nativeRoot = resolve3(homedir2(), ".claude", "projects");
2030
2055
  return pathIsWithin(nativeRoot, ref.path) ? nativeRoot : dirname3(resolve3(ref.path));
2031
2056
  }
2057
+ function relocatedWorkflowDirectory(ref, binding) {
2058
+ const source = binding.transcriptDir;
2059
+ if (resolve3(source) !== source || source.includes("\\")) return void 0;
2060
+ const parts = source.split("/").slice(-7);
2061
+ const sessionId = parts[3];
2062
+ 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;
2063
+ const sessionRoot2 = join4(dirname3(resolve3(ref.path)), sessionId);
2064
+ return {
2065
+ sessionRoot: sessionRoot2,
2066
+ transcriptDir: join4(sessionRoot2, "subagents", "workflows", binding.runId)
2067
+ };
2068
+ }
2032
2069
  async function workflowTranscriptDirectories(ref, bindings) {
2033
2070
  const allowedRoot = workflowStorageRoot(ref);
2034
2071
  const allowedRealRoot = await realpath(allowedRoot);
2035
2072
  const directories = /* @__PURE__ */ new Map();
2036
2073
  for (const runBindings of bindings.values()) {
2037
2074
  for (const binding of runBindings) {
2038
- const directory = resolve3(binding.transcriptDir);
2075
+ let directory = resolve3(binding.transcriptDir);
2076
+ let relocation;
2039
2077
  if (!pathIsWithin(allowedRoot, directory)) {
2040
- throw new ClaudeTaskScopeError(
2041
- `Claude Workflow transcript directory escapes ${allowedRoot}: ${directory}`
2042
- );
2078
+ relocation = relocatedWorkflowDirectory(ref, binding);
2079
+ if (!relocation || !pathIsWithin(allowedRoot, relocation.transcriptDir)) {
2080
+ throw new ClaudeTaskScopeError(
2081
+ `Claude Workflow transcript directory escapes ${allowedRoot}: ${directory}`
2082
+ );
2083
+ }
2084
+ directory = relocation.transcriptDir;
2043
2085
  }
2044
2086
  let realDirectory;
2045
2087
  try {
2046
2088
  realDirectory = await realpath(directory);
2047
2089
  } catch (error) {
2048
- if (isMissingPathError(error)) continue;
2090
+ if (isMissingPathError(error)) {
2091
+ if (relocation) {
2092
+ throw new ClaudeTaskScopeError(
2093
+ `Cannot relocate Claude Workflow run ${binding.runId}: copied transcript directory is missing: ${directory}`
2094
+ );
2095
+ }
2096
+ continue;
2097
+ }
2049
2098
  throw error;
2050
2099
  }
2051
2100
  if (!pathIsWithin(allowedRealRoot, realDirectory)) {
@@ -2053,10 +2102,38 @@ async function workflowTranscriptDirectories(ref, bindings) {
2053
2102
  `Claude Workflow transcript directory escapes ${allowedRoot}: ${directory}`
2054
2103
  );
2055
2104
  }
2056
- directories.set(realDirectory, directory);
2105
+ if (relocation) {
2106
+ const copiedRoot = dirname3(resolve3(ref.path));
2107
+ const copiedRealRoot = await realpath(copiedRoot);
2108
+ const sessionRealRoot = await realpath(relocation.sessionRoot);
2109
+ const expectedSessionRealRoot = resolve3(
2110
+ copiedRealRoot,
2111
+ relative2(copiedRoot, relocation.sessionRoot)
2112
+ );
2113
+ const expectedRealDirectory = resolve3(copiedRealRoot, relative2(copiedRoot, directory));
2114
+ if (sessionRealRoot !== expectedSessionRealRoot || realDirectory !== expectedRealDirectory) {
2115
+ throw new ClaudeTaskScopeError(
2116
+ `Claude Workflow transcript directory does not match copied session subtree ${relocation.sessionRoot}: ${directory}`
2117
+ );
2118
+ }
2119
+ }
2120
+ const previous = directories.get(realDirectory);
2121
+ if (previous && (previous.runId !== binding.runId || resolve3(previous.sourceTranscriptDir ?? previous.transcriptDir) !== resolve3(binding.transcriptDir))) {
2122
+ throw new ClaudeTaskScopeError(
2123
+ `Claude Workflow transcript directory ${directory} has conflicting source bindings`
2124
+ );
2125
+ }
2126
+ directories.set(realDirectory, {
2127
+ runId: binding.runId,
2128
+ transcriptDir: directory,
2129
+ sourceTranscriptDir: binding.transcriptDir,
2130
+ ...relocation ? { copiedWorkflow: true } : {}
2131
+ });
2057
2132
  }
2058
2133
  }
2059
- return [...directories.values()].sort();
2134
+ return [...directories.values()].sort(
2135
+ (left, right) => left.transcriptDir.localeCompare(right.transcriptDir)
2136
+ );
2060
2137
  }
2061
2138
  async function collectClaudeSubagentSources(ref, bindings, signal) {
2062
2139
  const subDir = join4(ref.path.replace(/\.jsonl$/, ""), "subagents");
@@ -2073,10 +2150,11 @@ async function collectClaudeSubagentSources(ref, bindings, signal) {
2073
2150
  });
2074
2151
  }
2075
2152
  }
2076
- for (const transcriptDir of await workflowTranscriptDirectories(ref, bindings)) {
2153
+ for (const location of await workflowTranscriptDirectories(ref, bindings)) {
2077
2154
  signal?.throwIfAborted();
2078
- const runId = basename4(transcriptDir);
2079
- for (const file of await listSubagentFiles(transcriptDir, signal)) {
2155
+ const { runId, transcriptDir } = location;
2156
+ const files2 = await listSubagentFiles(transcriptDir, signal, location.copiedWorkflow);
2157
+ for (const file of files2) {
2080
2158
  const path = resolve3(file);
2081
2159
  filesByPath.set(path, file);
2082
2160
  const previous = workflowByPath.get(path);
@@ -2085,7 +2163,7 @@ async function collectClaudeSubagentSources(ref, bindings, signal) {
2085
2163
  `Claude Workflow subagent ${file} belongs to multiple transcript directories`
2086
2164
  );
2087
2165
  }
2088
- workflowByPath.set(path, { runId, transcriptDir });
2166
+ workflowByPath.set(path, location);
2089
2167
  }
2090
2168
  }
2091
2169
  const files = [...filesByPath.values()].sort();
@@ -2171,6 +2249,7 @@ function createClaudeStream(startStep) {
2171
2249
  step: startStep,
2172
2250
  sawUserTurn: false,
2173
2251
  promptSpans: [],
2252
+ promptSpanByUid: /* @__PURE__ */ new Map(),
2174
2253
  sawRecordedOrigin: false,
2175
2254
  firstRecordAt: null,
2176
2255
  lastRecordAt: null
@@ -2203,6 +2282,9 @@ function projectToolResult(toolUseId, isError, output, structuredWorkflowRun, ou
2203
2282
  }
2204
2283
  function projectClaudeEvent(event) {
2205
2284
  const timestamp = event.timestamp ?? EPOCH;
2285
+ if (event.type === "system" && event.subtype === "compact_boundary") {
2286
+ return { kind: "compaction", timestamp: event.timestamp ?? "", metadata: event.compactMetadata ?? {}, parentToolUseId: event.parent_tool_use_id };
2287
+ }
2206
2288
  if (event.type === "assistant" && event.message) {
2207
2289
  const tools = [];
2208
2290
  for (const block of asBlocks(event.message.content)) {
@@ -2344,7 +2426,7 @@ function indexWorkflowProjection(projection, index) {
2344
2426
  function fingerprintClaudeEvent(event) {
2345
2427
  return createHash6("sha256").update(JSON.stringify(
2346
2428
  event,
2347
- (key, value) => key === "contentSource" || key.startsWith(SOURCE_ATTRIBUTE_PREFIX) ? void 0 : value
2429
+ (key, value) => key === "contentSource" || key === "originKind" || key.startsWith(SOURCE_ATTRIBUTE_PREFIX) ? void 0 : value
2348
2430
  )).digest("hex");
2349
2431
  }
2350
2432
  function startsClaudeTask(projection) {
@@ -2358,21 +2440,43 @@ function startsClaudeTask(projection) {
2358
2440
  function distinctClaudeEvent(event, seen, sourcePath, fallbackUid) {
2359
2441
  const projection = projectClaudeEvent(event);
2360
2442
  if (event.uuid) {
2443
+ const uid = event.uuid;
2361
2444
  const fingerprint = fingerprintClaudeEvent(projection);
2362
- const previous = seen.get(event.uuid);
2363
- if (previous !== void 0) {
2364
- if (previous !== fingerprint) {
2365
- throw new ClaudeEventConflictError(sourcePath, event.uuid);
2445
+ const originKind = (projection.kind === "user" || projection.kind === "queued-prompt" ? projection.originKind : null) ?? null;
2446
+ const previous = seen.get(uid);
2447
+ if (previous) {
2448
+ if (previous.fingerprint !== fingerprint) {
2449
+ throw new ClaudeEventConflictError(sourcePath, uid);
2450
+ }
2451
+ if (previous.originKind === null && originKind !== null) return { originEnrichment: originKind, uid };
2452
+ if (previous.originKind !== null && originKind !== null && previous.originKind !== originKind) {
2453
+ throw new ClaudeEventConflictError(sourcePath, uid);
2366
2454
  }
2367
2455
  return void 0;
2368
2456
  }
2369
- seen.set(event.uuid, fingerprint);
2457
+ seen.set(uid, { fingerprint, originKind });
2370
2458
  }
2371
2459
  return { projection, uid: event.uuid ?? fallbackUid };
2372
2460
  }
2461
+ function applyOriginEnrichment(uid, originKind, state) {
2462
+ const prompt = state.promptSpanByUid.get(uid);
2463
+ if (!prompt) return;
2464
+ if (prompt.attributes[ORIGIN_RECORDED_ATTR] === true) return;
2465
+ state.sawRecordedOrigin = true;
2466
+ prompt.attributes[ORIGIN_RECORDED_ATTR] = true;
2467
+ prompt.attributes[ORIGIN_KIND_ATTR] = originKind;
2468
+ prompt.attributes[ACTOR_ATTR] = claudeActorFromOrigin(
2469
+ originKind,
2470
+ () => prompt.attributes[ACTOR_ATTR]
2471
+ );
2472
+ }
2373
2473
  function consumeDistinctClaudeEvent(event, ctx, state, seen, sourcePath) {
2374
2474
  const accepted = distinctClaudeEvent(event, seen, sourcePath, `step${state.step}`);
2375
2475
  if (!accepted) return false;
2476
+ if ("originEnrichment" in accepted) {
2477
+ applyOriginEnrichment(accepted.uid, accepted.originEnrichment, state);
2478
+ return false;
2479
+ }
2376
2480
  consumeClaudeEvent(accepted.projection, accepted.uid, ctx, state);
2377
2481
  return true;
2378
2482
  }
@@ -2382,6 +2486,34 @@ function inStreamParent(parentToolUseId, state) {
2382
2486
  return parentTool ? { span: parentTool, confidence: "explicit" } : void 0;
2383
2487
  }
2384
2488
  function consumeClaudeEvent(event, uid, ctx, state) {
2489
+ if (event.kind === "compaction") {
2490
+ const extra = {};
2491
+ const numericFields = {
2492
+ preTokens: "traces.compaction.tokens_before",
2493
+ postTokens: "traces.compaction.tokens_after",
2494
+ durationMs: "traces.compaction.latency_ms",
2495
+ cumulativeDroppedTokens: "traces.compaction.cumulative_dropped_tokens"
2496
+ };
2497
+ for (const [field, key] of Object.entries(numericFields)) {
2498
+ const value = event.metadata[field];
2499
+ if (typeof value === "number" && Number.isFinite(value) && value >= 0) extra[key] = value;
2500
+ }
2501
+ if (typeof event.metadata.trigger === "string") extra["traces.compaction.trigger"] = event.metadata.trigger;
2502
+ const parent = inStreamParent(event.parentToolUseId, state);
2503
+ state.spans.push(span({
2504
+ traceId: ctx.traceId,
2505
+ spanId: ctx.idPrefix + uid + ":compacted",
2506
+ parentSpanId: parent ? parent.span.span_id : ctx.rootParent,
2507
+ name: "session.compacted",
2508
+ kind: "CHAIN",
2509
+ startTime: event.timestamp,
2510
+ service: SERVICE2,
2511
+ agent: ctx.agent,
2512
+ step: state.step++,
2513
+ extra
2514
+ }));
2515
+ return;
2516
+ }
2385
2517
  if (event.kind === "assistant") {
2386
2518
  const messageId = event.messageId ?? uid;
2387
2519
  let llmSpan = state.llmSpanByMessageId.get(messageId);
@@ -2495,6 +2627,7 @@ function consumeClaudeEvent(event, uid, ctx, state) {
2495
2627
  if (userParent) prompt.attributes[ATTR3.parentConfidence] = userParent.confidence;
2496
2628
  state.spans.push(prompt);
2497
2629
  state.promptSpans.push(prompt);
2630
+ state.promptSpanByUid.set(uid, prompt);
2498
2631
  state.step += 1;
2499
2632
  }
2500
2633
  for (const result of event.results) {
@@ -2524,6 +2657,7 @@ function consumeClaudeEvent(event, uid, ctx, state) {
2524
2657
  }
2525
2658
  state.spans.push(prompt);
2526
2659
  state.promptSpans.push(prompt);
2660
+ state.promptSpanByUid.set(uid, prompt);
2527
2661
  state.step += 1;
2528
2662
  } else if (event.kind === "file-change") {
2529
2663
  state.spans.push(span({
@@ -2685,7 +2819,7 @@ async function readClaudeWorkflowBindings(ref, options) {
2685
2819
  options.signal?.throwIfAborted();
2686
2820
  const accepted = distinctClaudeEvent(event, seen, ref.path, `source${row}`);
2687
2821
  row += 1;
2688
- if (accepted) indexWorkflowProjection(accepted.projection, index);
2822
+ if (accepted && "projection" in accepted) indexWorkflowProjection(accepted.projection, index);
2689
2823
  }
2690
2824
  return bindWorkflowRuns(index);
2691
2825
  }
@@ -2794,7 +2928,7 @@ var ClaudeAdapter = class {
2794
2928
  if (opts.sinceMs && st.mtimeMs < opts.sinceMs) continue;
2795
2929
  const cwd = `/${dir.replace(/^-/, "").replace(/-/g, "/")}`;
2796
2930
  if (opts.cwd && !cwd.startsWith(opts.cwd)) continue;
2797
- refs.push({ harness: this.harness, sessionId: basename5(f, ".jsonl"), path, cwd, mtimeMs: st.mtimeMs });
2931
+ refs.push({ harness: this.harness, sessionId: basename4(f, ".jsonl"), path, cwd, mtimeMs: st.mtimeMs });
2798
2932
  }
2799
2933
  }
2800
2934
  return refs.sort((a, b) => b.mtimeMs - a.mtimeMs);
@@ -2837,7 +2971,10 @@ var ClaudeAdapter = class {
2837
2971
  runResult = { failed: event.is_error, subtype: event.subtype ?? "unknown" };
2838
2972
  }
2839
2973
  const accepted = distinctClaudeEvent(event, seen, ref.path, `step${state.step}`);
2840
- if (!accepted) continue;
2974
+ if (!accepted || "originEnrichment" in accepted) {
2975
+ if (accepted) applyOriginEnrichment(accepted.uid, accepted.originEnrichment, state);
2976
+ continue;
2977
+ }
2841
2978
  indexWorkflowProjection(accepted.projection, workflowIndex);
2842
2979
  const startsTask = startsClaudeTask(accepted.projection);
2843
2980
  if (startsTask && taskScope === "latest") {
@@ -2945,7 +3082,7 @@ var ClaudeAdapter = class {
2945
3082
  const agent = {
2946
3083
  file,
2947
3084
  sourceKey,
2948
- agentId: basename5(file, ".jsonl").replace(/^agent-/, ""),
3085
+ agentId: basename4(file, ".jsonl").replace(/^agent-/, ""),
2949
3086
  meta,
2950
3087
  ...meta.toolUseId ? { parentToolUseId: meta.toolUseId } : {}
2951
3088
  };
@@ -2954,11 +3091,12 @@ var ClaudeAdapter = class {
2954
3091
  file,
2955
3092
  workflowRunId,
2956
3093
  workflowLocation.transcriptDir,
2957
- workflowBindings
3094
+ workflowBindings,
3095
+ workflowLocation.sourceTranscriptDir
2958
3096
  );
2959
3097
  const parsed = await parseClaudeSubagent(ref, traceId, agent, options);
2960
3098
  const { binding, confidence } = selectWorkflowBinding(
2961
- basename5(file),
3099
+ basename4(file),
2962
3100
  runBindings,
2963
3101
  firstSubagentTimestamp(agent, parsed),
2964
3102
  meta.toolUseId
@@ -3527,7 +3665,7 @@ async function resolveCodexParentTask(ref, childSessionId, options = {}) {
3527
3665
  // src/adapters/codex.ts
3528
3666
  import { readdir as readdir6, stat as stat4 } from "fs/promises";
3529
3667
  import { homedir as homedir4 } from "os";
3530
- import { basename as basename6, join as join6 } from "path";
3668
+ import { basename as basename5, join as join6 } from "path";
3531
3669
  import { deriveHexId as deriveHexId3 } from "@tangle-network/agent-trace-contract";
3532
3670
  var SERVICE3 = "codex";
3533
3671
  var SESSION_HEAD_LINES = 40;
@@ -3999,7 +4137,7 @@ var CodexAdapter = class {
3999
4137
  }
4000
4138
  if (opts.sinceMs && st.mtimeMs < opts.sinceMs) return void 0;
4001
4139
  let cwd = null;
4002
- let id = basename6(path).replace(/^rollout-[\dT-]+-/, "").replace(/\.jsonl$/, "");
4140
+ let id = basename5(path).replace(/^rollout-[\dT-]+-/, "").replace(/\.jsonl$/, "");
4003
4141
  const ref = { harness: this.harness, sessionId: id, path, cwd, mtimeMs: st.mtimeMs };
4004
4142
  const head = await takeJsonl(path, SESSION_HEAD_LINES, sessionJsonlOptions(ref));
4005
4143
  for (const parsed of head) {
@@ -4027,7 +4165,7 @@ var CodexAdapter = class {
4027
4165
  async locateBySessionId(sessionId, opts = {}) {
4028
4166
  const refs = [];
4029
4167
  for await (const path of walkRollouts(this.root())) {
4030
- if (!basename6(path).includes(sessionId)) continue;
4168
+ if (!basename5(path).includes(sessionId)) continue;
4031
4169
  const ref = await this.refFromPath(path, opts);
4032
4170
  if (ref?.sessionId === sessionId) refs.push(ref);
4033
4171
  }
@@ -4242,12 +4380,12 @@ var CodexAdapter = class {
4242
4380
  spans.push(turnSpan);
4243
4381
  step += 1;
4244
4382
  };
4245
- const recordInheritedContext = (l, ts) => {
4383
+ const recordInheritedContext = (l, ts, inScope = false) => {
4246
4384
  if (l.type === "compacted") {
4247
4385
  const payload = l.payload;
4248
4386
  if (!payload) return;
4249
4387
  const summary = capText(typeof payload.message === "string" ? payload.message : "");
4250
- if (summary && claimInheritedSpan()) {
4388
+ if (claimInheritedSpan()) {
4251
4389
  spans.push(span({
4252
4390
  traceId,
4253
4391
  spanId: `inherited:${step}:compacted`,
@@ -4262,7 +4400,7 @@ var CodexAdapter = class {
4262
4400
  contentSource: textSources(payload, "message"),
4263
4401
  extra: {
4264
4402
  [INHERITED_SPAN_ATTR]: true,
4265
- [INHERITED_SOURCE_ATTR]: "compacted",
4403
+ [INHERITED_SOURCE_ATTR]: inScope ? "compacted" : "pre-task-prefix",
4266
4404
  ...typeof payload.window_number === "number" ? { "traces.codex.compaction_window_number": payload.window_number } : {},
4267
4405
  ...payload.window_id ? { "traces.codex.compaction_window_id": payload.window_id } : {},
4268
4406
  ...payload.previous_window_id ? { "traces.codex.compaction_previous_window_id": payload.previous_window_id } : {}
@@ -4436,7 +4574,7 @@ var CodexAdapter = class {
4436
4574
  step += 1;
4437
4575
  }
4438
4576
  } else if (l.type === "compacted") {
4439
- recordInheritedContext(l, ts);
4577
+ recordInheritedContext(l, ts, true);
4440
4578
  } else if (l.type === "response_item" && (l.payload?.type === "function_call" || l.payload?.type === "custom_tool_call")) {
4441
4579
  const outerName = l.payload.name ?? "tool";
4442
4580
  const callId = l.payload.call_id ?? `${step}`;
@@ -5276,7 +5414,7 @@ var CopilotAdapter = class {
5276
5414
  // src/adapters/factory.ts
5277
5415
  import { readdir as readdir8, stat as stat6 } from "fs/promises";
5278
5416
  import { homedir as homedir6 } from "os";
5279
- import { basename as basename7, join as join8 } from "path";
5417
+ import { basename as basename6, join as join8 } from "path";
5280
5418
  var SERVICE6 = "factory";
5281
5419
  function textOf4(content) {
5282
5420
  return capText(
@@ -5321,7 +5459,7 @@ var FactoryAdapter = class {
5321
5459
  throw error;
5322
5460
  }
5323
5461
  if (opts.sinceMs && st.mtimeMs < opts.sinceMs) continue;
5324
- refs.push({ harness: this.harness, sessionId: basename7(f, ".jsonl"), path, cwd, mtimeMs: st.mtimeMs });
5462
+ refs.push({ harness: this.harness, sessionId: basename6(f, ".jsonl"), path, cwd, mtimeMs: st.mtimeMs });
5325
5463
  }
5326
5464
  }
5327
5465
  return refs.sort((a, b) => b.mtimeMs - a.mtimeMs);
@@ -5460,7 +5598,7 @@ var FactoryAdapter = class {
5460
5598
  // src/adapters/forge.ts
5461
5599
  import { readdir as readdir9, stat as stat7 } from "fs/promises";
5462
5600
  import { homedir as homedir7 } from "os";
5463
- import { basename as basename8, join as join9 } from "path";
5601
+ import { basename as basename7, join as join9 } from "path";
5464
5602
  var SERVICE7 = "forge";
5465
5603
  function tokens(t) {
5466
5604
  if (!t) return null;
@@ -5498,7 +5636,7 @@ var ForgeAdapter = class {
5498
5636
  throw error;
5499
5637
  }
5500
5638
  if (opts.sinceMs && st.mtimeMs < opts.sinceMs) continue;
5501
- refs.push({ harness: this.harness, sessionId: basename8(f, ".json"), path, cwd: dir, mtimeMs: st.mtimeMs });
5639
+ refs.push({ harness: this.harness, sessionId: basename7(f, ".json"), path, cwd: dir, mtimeMs: st.mtimeMs });
5502
5640
  }
5503
5641
  }
5504
5642
  return refs.sort((a, b) => b.mtimeMs - a.mtimeMs);
@@ -5960,7 +6098,7 @@ var OpencodeAdapter = class {
5960
6098
  // src/adapters/pi.ts
5961
6099
  import { readdir as readdir12, stat as stat10 } from "fs/promises";
5962
6100
  import { homedir as homedir10 } from "os";
5963
- import { basename as basename9, join as join12, resolve as resolve4 } from "path";
6101
+ import { basename as basename8, join as join12, resolve as resolve4 } from "path";
5964
6102
  var SERVICE9 = "pi";
5965
6103
  function terminalStatus(message) {
5966
6104
  const stopReason = message?.stopReason ?? "missing";
@@ -6042,7 +6180,7 @@ var PiAdapter = class {
6042
6180
  }
6043
6181
  const refs = [];
6044
6182
  for (const path of paths) {
6045
- const f = basename9(path);
6183
+ const f = basename8(path);
6046
6184
  let st;
6047
6185
  try {
6048
6186
  st = await stat10(path);
@@ -6051,7 +6189,7 @@ var PiAdapter = class {
6051
6189
  throw error;
6052
6190
  }
6053
6191
  if (opts.sinceMs && st.mtimeMs < opts.sinceMs) continue;
6054
- let id = basename9(f, ".jsonl").replace(/^[\dTZ.-]+_/, "");
6192
+ let id = basename8(f, ".jsonl").replace(/^[\dTZ.-]+_/, "");
6055
6193
  let cwd = null;
6056
6194
  const ref = {
6057
6195
  harness: this.harness,
@@ -6099,6 +6237,27 @@ var PiAdapter = class {
6099
6237
  if (!sessionLine && l.type === "session") {
6100
6238
  sessionLine = { id: l.id, timestamp: l.timestamp };
6101
6239
  }
6240
+ if (l.type === "compaction") {
6241
+ spans.push(span({
6242
+ traceId: sourceTraceId,
6243
+ spanId: "compacted:" + (l.id ?? step),
6244
+ parentSpanId: sourceRootId,
6245
+ name: "session.compacted",
6246
+ kind: "CHAIN",
6247
+ service: SERVICE9,
6248
+ agent: SERVICE9,
6249
+ startTime: l.timestamp ?? "",
6250
+ step: step++,
6251
+ content: typeof l.summary === "string" ? capText(l.summary) : void 0,
6252
+ contentSource: sourceOf(l, "summary"),
6253
+ extra: {
6254
+ ...typeof l.tokensBefore === "number" && Number.isFinite(l.tokensBefore) && l.tokensBefore >= 0 ? { "traces.compaction.tokens_before": l.tokensBefore } : {},
6255
+ ...typeof l.firstKeptEntryId === "string" ? { "traces.pi.first_kept_entry_id": l.firstKeptEntryId } : {},
6256
+ ...l.systemMessage !== void 0 ? { "gen_ai.system_instructions": JSON.stringify(l.systemMessage) } : {}
6257
+ }
6258
+ }));
6259
+ continue;
6260
+ }
6102
6261
  if (l.type !== "message" || !l.message) continue;
6103
6262
  const ts = l.timestamp ?? (/* @__PURE__ */ new Date(0)).toISOString();
6104
6263
  const mid = l.id ?? `m${step}`;
@@ -6263,7 +6422,7 @@ var PiAdapter = class {
6263
6422
  // src/adapters/qwen.ts
6264
6423
  import { readdir as readdir13, stat as stat11 } from "fs/promises";
6265
6424
  import { homedir as homedir11 } from "os";
6266
- import { basename as basename10, join as join13 } from "path";
6425
+ import { basename as basename9, join as join13 } from "path";
6267
6426
  var SERVICE10 = "qwen";
6268
6427
  function textOf9(parts) {
6269
6428
  if (!parts) return "";
@@ -6306,7 +6465,7 @@ var QwenAdapter = class {
6306
6465
  throw error;
6307
6466
  }
6308
6467
  if (opts.sinceMs && st.mtimeMs < opts.sinceMs) continue;
6309
- refs.push({ harness: this.harness, sessionId: basename10(f, ".jsonl"), path, cwd: null, mtimeMs: st.mtimeMs });
6468
+ refs.push({ harness: this.harness, sessionId: basename9(f, ".jsonl"), path, cwd: null, mtimeMs: st.mtimeMs });
6310
6469
  }
6311
6470
  }
6312
6471
  return refs.sort((a, b) => b.mtimeMs - a.mtimeMs);
@@ -6719,7 +6878,7 @@ function extractAbsolutePaths(spans) {
6719
6878
  async function resolveRepoAttrs(cwd) {
6720
6879
  if (!cwd) return {};
6721
6880
  const attrs = { [ATTR.CWD]: cwd };
6722
- const basename19 = (() => {
6881
+ const basename18 = (() => {
6723
6882
  const trimmed = cwd.replace(/\/+$/g, "");
6724
6883
  const idx = trimmed.lastIndexOf("/");
6725
6884
  const name = idx === -1 ? trimmed : trimmed.slice(idx + 1);
@@ -6741,7 +6900,7 @@ async function resolveRepoAttrs(cwd) {
6741
6900
  if (commit) attrs[ATTR.GIT_COMMIT] = commit;
6742
6901
  } catch {
6743
6902
  }
6744
- attrs[ATTR.SUBJECT_KEY] = basename19;
6903
+ attrs[ATTR.SUBJECT_KEY] = basename18;
6745
6904
  return attrs;
6746
6905
  }
6747
6906
  async function repoCandidateForPath(path) {
@@ -7336,7 +7495,7 @@ async function readLoopRuns(cwds, sessionIds) {
7336
7495
  }
7337
7496
  return { linkedCounts, unlinkedCounts, filesRead };
7338
7497
  }
7339
- async function analyzeAdoption(spans, opts = {}) {
7498
+ function computeAdoption(spans) {
7340
7499
  const { sessionByTrace, conflicts } = indexSessionIdsByTrace(spans);
7341
7500
  const identifiedSessions = new Set(sessionByTrace.values());
7342
7501
  const unassignedTraceIds = new Set(
@@ -7393,11 +7552,9 @@ async function analyzeAdoption(spans, opts = {}) {
7393
7552
  subagentSpawns[type] = (subagentSpawns[type] ?? 0) + 1;
7394
7553
  }
7395
7554
  }
7396
- const {
7397
- linkedCounts: loopDispatchedRuns,
7398
- unlinkedCounts: unlinkedLoopDispatchedRuns,
7399
- filesRead
7400
- } = await readLoopRuns(opts.cwds ?? [], identifiedSessions);
7555
+ const loopDispatchedRuns = {};
7556
+ const unlinkedLoopDispatchedRuns = {};
7557
+ const filesRead = 0;
7401
7558
  const totalSkillInvocations = Object.values(skillInvocations).reduce((a, b) => a + b, 0);
7402
7559
  const totalSkillDocumentReads = Object.values(skillDocumentReads).reduce((a, b) => a + b, 0);
7403
7560
  const totalSubagentSpawns = Object.values(subagentSpawns).reduce((a, b) => a + b, 0);
@@ -7441,6 +7598,19 @@ async function analyzeAdoption(spans, opts = {}) {
7441
7598
  totalUnlinkedLoopDispatchedRuns
7442
7599
  };
7443
7600
  }
7601
+ async function analyzeAdoption(spans, opts = {}) {
7602
+ const report = computeAdoption(spans);
7603
+ const { sessionByTrace } = indexSessionIdsByTrace(spans);
7604
+ const history = await readLoopRuns(opts.cwds ?? [], new Set(sessionByTrace.values()));
7605
+ return {
7606
+ ...report,
7607
+ loopDispatchedRuns: history.linkedCounts,
7608
+ totalLoopDispatchedRuns: Object.values(history.linkedCounts).reduce((sum, count2) => sum + count2, 0),
7609
+ unlinkedLoopDispatchedRuns: history.unlinkedCounts,
7610
+ totalUnlinkedLoopDispatchedRuns: Object.values(history.unlinkedCounts).reduce((sum, count2) => sum + count2, 0),
7611
+ skillRunFilesRead: history.filesRead
7612
+ };
7613
+ }
7444
7614
 
7445
7615
  // src/agentic-routing.ts
7446
7616
  import {
@@ -7933,7 +8103,7 @@ var COMMAND_PREFIXES = /* @__PURE__ */ new Set([
7933
8103
  var ASSIGNMENT = /^[A-Za-z_][A-Za-z0-9_]*=/;
7934
8104
  var MAX_SHELL_DEPTH = 4;
7935
8105
  var MAX_WORDS = 2e4;
7936
- function basename11(word) {
8106
+ function basename10(word) {
7937
8107
  const cut = word.lastIndexOf("/");
7938
8108
  return cut === -1 ? word : word.slice(cut + 1);
7939
8109
  }
@@ -8032,7 +8202,7 @@ var ShellScanner = class {
8032
8202
  let start = 0;
8033
8203
  while (start < words.length) {
8034
8204
  const word = words[start];
8035
- if (ASSIGNMENT.test(word) || COMMAND_PREFIXES.has(basename11(word))) start += 1;
8205
+ if (ASSIGNMENT.test(word) || COMMAND_PREFIXES.has(basename10(word))) start += 1;
8036
8206
  else break;
8037
8207
  }
8038
8208
  return start === 0 || start >= words.length ? words : words.slice(start);
@@ -8173,7 +8343,7 @@ function shellCommands(script, depth = 0) {
8173
8343
  const out = [];
8174
8344
  for (const words of commands) {
8175
8345
  out.push(words);
8176
- const name = basename11(words[0] ?? "");
8346
+ const name = basename10(words[0] ?? "");
8177
8347
  if (!SHELLS.has(name) || depth >= MAX_SHELL_DEPTH) continue;
8178
8348
  for (let index = 1; index < words.length; index += 1) {
8179
8349
  const word = words[index];
@@ -8201,7 +8371,7 @@ function commandTextFromInput(input) {
8201
8371
  if (!Array.isArray(value) || value.length === 0) return void 0;
8202
8372
  const words = value.filter((entry) => typeof entry === "string");
8203
8373
  if (words.length !== value.length) return void 0;
8204
- const head = basename11(words[0] ?? "");
8374
+ const head = basename10(words[0] ?? "");
8205
8375
  if (SHELLS.has(head)) {
8206
8376
  const flag = words.findIndex((word, position) => position > 0 && /^-[a-z]*c$/.test(word));
8207
8377
  if (flag !== -1 && words[flag + 1] !== void 0) return words[flag + 1];
@@ -8263,7 +8433,7 @@ function stringAttribute2(span2, key) {
8263
8433
  const value = span2.attributes[key];
8264
8434
  return typeof value === "string" ? value : null;
8265
8435
  }
8266
- function basename12(word) {
8436
+ function basename11(word) {
8267
8437
  const cut = word.lastIndexOf("/");
8268
8438
  return cut === -1 ? word : word.slice(cut + 1);
8269
8439
  }
@@ -8346,7 +8516,7 @@ function numbersInLine(line) {
8346
8516
  function inferHeadBranch(commands, upTo, output) {
8347
8517
  for (let index = upTo - 1; index >= 0; index -= 1) {
8348
8518
  const words = commands[index];
8349
- if (basename12(words[0] ?? "") !== "git" || words.length < 2) continue;
8519
+ if (basename11(words[0] ?? "") !== "git" || words.length < 2) continue;
8350
8520
  const sub = words[1];
8351
8521
  const rest = words.slice(2);
8352
8522
  if (sub === "push") {
@@ -8399,13 +8569,13 @@ function mergeEvent(record2, recordIndex, words) {
8399
8569
  const target = positional[0] ?? null;
8400
8570
  const numberInCommand = target === null ? null : /^#?(\d+)$/.exec(target)?.[1] ?? numbersInLine(target)[0] ?? null;
8401
8571
  const numberInOutput = numberInCommand === null ? MERGED_NUMBER.exec(record2.output)?.[1] ?? null : null;
8402
- const number = numberInCommand ?? numberInOutput;
8403
- if (!reachedGh(record2, "merge", number !== null)) return null;
8572
+ const number2 = numberInCommand ?? numberInOutput;
8573
+ if (!reachedGh(record2, "merge", number2 !== null)) return null;
8404
8574
  const evidence3 = numberInCommand !== null ? "the pull request the command names" : numberInOutput !== null ? `the merge line this command printed, in span ${record2.spanId}` : target !== null ? "the branch the command names; no output that joins to it stated a number" : "none: the command named neither a pull request nor a branch";
8405
8575
  return {
8406
8576
  action: "merge",
8407
- number,
8408
- headBranch: number === null ? target : null,
8577
+ number: number2,
8578
+ headBranch: number2 === null ? target : null,
8409
8579
  command: words.join(" "),
8410
8580
  evidence: evidence3,
8411
8581
  spanIds: [record2.spanId],
@@ -8421,7 +8591,7 @@ function eventsFromRecord(record2, recordIndex) {
8421
8591
  let createIndex = 0;
8422
8592
  for (let index = 0; index < commands.length; index += 1) {
8423
8593
  const words = commands[index];
8424
- if (words.length < 3 || !GH_NAMES.has(basename12(words[0])) || words[1] !== "pr") continue;
8594
+ if (words.length < 3 || !GH_NAMES.has(basename11(words[0])) || words[1] !== "pr") continue;
8425
8595
  const action = words[2];
8426
8596
  if (action === "create") {
8427
8597
  const printedNumber = printed[createIndex] ?? null;
@@ -8444,7 +8614,7 @@ function numberFromLaterOutput(records, after, headBranch) {
8444
8614
  ${record2.output}`.split("\n")) {
8445
8615
  const line = raw.replace(ANSI, "");
8446
8616
  if (!line.includes(headBranch)) continue;
8447
- for (const number of numbersInLine(line)) found.add(number);
8617
+ for (const number2 of numbersInLine(line)) found.add(number2);
8448
8618
  }
8449
8619
  if (found.size === 1) return { number: [...found][0], spanId: record2.spanId };
8450
8620
  }
@@ -8533,9 +8703,144 @@ function readPullRequests(spans) {
8533
8703
  };
8534
8704
  }
8535
8705
 
8706
+ // src/session-facts.ts
8707
+ import { createHash as createHash7 } from "crypto";
8708
+
8709
+ // src/session-measurements.ts
8710
+ var number = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : null;
8711
+ function measure(spans, read, reason) {
8712
+ let total = 0;
8713
+ let measured2 = 0;
8714
+ for (const span2 of spans) {
8715
+ const value = read(span2);
8716
+ if (value !== null) {
8717
+ total += value;
8718
+ measured2 += 1;
8719
+ }
8720
+ }
8721
+ return {
8722
+ total: measured2 > 0 || spans.length === 0 ? total : null,
8723
+ measured: measured2,
8724
+ eligible: spans.length,
8725
+ unavailable: measured2 === spans.length ? null : reason
8726
+ };
8727
+ }
8728
+ function bytes(span2, side) {
8729
+ const recorded = number(span2.attributes[`traces.${side}.bytes`]);
8730
+ if (recorded !== null) return recorded;
8731
+ const text2 = span2.attributes[`${side}.value`];
8732
+ if (typeof text2 !== "string" || span2.attributes[`traces.${side}.truncated`] === true) return null;
8733
+ return Buffer.byteLength(text2);
8734
+ }
8735
+ function toolMeasurements(tools) {
8736
+ const groups = /* @__PURE__ */ new Map();
8737
+ for (const tool of tools) {
8738
+ const name = typeof tool.attributes["tool.name"] === "string" ? tool.attributes["tool.name"] : tool.name;
8739
+ const group = groups.get(name);
8740
+ if (group) group.push(tool);
8741
+ else groups.set(name, [tool]);
8742
+ }
8743
+ return [...groups].sort(([a], [b]) => a.localeCompare(b)).map(([name, spans]) => ({
8744
+ name,
8745
+ calls: spans.length,
8746
+ outcomes: {
8747
+ ok: spans.filter((span2) => span2.status.code === "OK").length,
8748
+ error: spans.filter((span2) => span2.status.code === "ERROR").length,
8749
+ unset: spans.filter((span2) => span2.status.code === "UNSET").length
8750
+ },
8751
+ inputBytes: measure(spans, (span2) => bytes(span2, "input"), "Input byte count missing or truncated without a size receipt."),
8752
+ outputBytes: measure(spans, (span2) => bytes(span2, "output"), "Output byte count missing or truncated without a size receipt."),
8753
+ callToResultMs: measure(spans, (span2) => {
8754
+ if (span2.status.code === "UNSET") return null;
8755
+ const elapsed2 = Date.parse(span2.end_time) - Date.parse(span2.start_time);
8756
+ return Number.isFinite(elapsed2) && elapsed2 > 0 ? elapsed2 : null;
8757
+ }, "A completed span with distinct recorded endpoints is required; equal endpoints may be an adapter default."),
8758
+ spanIds: spans.map((span2) => span2.span_id)
8759
+ }));
8760
+ }
8761
+ function compactions(spans) {
8762
+ const events = spans.filter((span2) => span2.name === "session.compacted" && span2.attributes[INHERITED_SOURCE_ATTR] !== "pre-task-prefix").sort((a, b) => Date.parse(a.start_time) - Date.parse(b.start_time));
8763
+ if (events.length === 0) return {
8764
+ value: null,
8765
+ spanIds: [],
8766
+ unavailable: "No compaction boundary was captured. This does not establish that no compaction occurred."
8767
+ };
8768
+ return {
8769
+ value: events.map((span2, i) => {
8770
+ const previous = i > 0 ? events[i - 1] : void 0;
8771
+ const interval = previous ? Date.parse(span2.start_time) - Date.parse(previous.start_time) : NaN;
8772
+ return {
8773
+ spanId: span2.span_id,
8774
+ at: span2.start_time,
8775
+ sincePreviousMs: Number.isFinite(interval) && interval >= 0 ? interval : null,
8776
+ latencyMs: number(span2.attributes["traces.compaction.latency_ms"]),
8777
+ tokensBefore: number(span2.attributes["traces.compaction.tokens_before"]),
8778
+ tokensAfter: number(span2.attributes["traces.compaction.tokens_after"])
8779
+ };
8780
+ }),
8781
+ spanIds: events.map((span2) => span2.span_id),
8782
+ unavailable: null,
8783
+ partial: "Observed boundaries only. Latency and context sizes are null unless the source explicitly records them."
8784
+ };
8785
+ }
8786
+ function contextFootprints(spans) {
8787
+ const out = [];
8788
+ for (const span2 of spans) {
8789
+ const fields = ["gen_ai.tool.definitions", "gen_ai.system_instructions"];
8790
+ if (span2.name === "message.system" || span2.name === "message.developer" || span2.name === "user.prompt" && span2.attributes["tangle.actor"] === "injected") fields.push("content");
8791
+ for (const field of fields) {
8792
+ const value = span2.attributes[field];
8793
+ if (value === void 0 || value === null) continue;
8794
+ const text2 = typeof value === "string" ? value : canonicalJson(value);
8795
+ let entries = Array.isArray(value) ? value.length : null;
8796
+ if (typeof value === "string") {
8797
+ try {
8798
+ const parsed = JSON.parse(value);
8799
+ if (Array.isArray(parsed)) entries = parsed.length;
8800
+ } catch {
8801
+ }
8802
+ }
8803
+ out.push({ spanId: span2.span_id, field, bytes: Buffer.byteLength(text2), lines: text2.split("\n").length, entries, scope: "captured-attribute" });
8804
+ }
8805
+ }
8806
+ return out;
8807
+ }
8808
+ function computeSessionMeasurements(spans, tools, sessionId, harness) {
8809
+ const spawns = tools.filter((span2) => span2.attributes["traces.agent.spawn"] === true || span2.attributes["traces.codex.agent_operation"] === "spawn_agent").sort((a, b) => Date.parse(a.start_time) - Date.parse(b.start_time));
8810
+ return {
8811
+ observedSpawns: spawns.length,
8812
+ spawnIntervalsMs: spawns.map((span2, i) => {
8813
+ const previous = spawns[i - 1];
8814
+ const interval = previous ? Date.parse(span2.start_time) - Date.parse(previous.start_time) : NaN;
8815
+ return { spanId: span2.span_id, intervalMs: Number.isFinite(interval) && interval >= 0 ? interval : null };
8816
+ }),
8817
+ execution: spans.length > 0 ? summarizeSpanExecution(spans) : null,
8818
+ adoption: computeAdoption(spans),
8819
+ relationship: describeSessionRelationship({
8820
+ sessionId: sessionId ?? spans[0]?.trace_id ?? "",
8821
+ harness: harness ?? "",
8822
+ path: "",
8823
+ cwd: null,
8824
+ mtimeMs: 0
8825
+ }, spans),
8826
+ tools: toolMeasurements(tools),
8827
+ compactions: compactions(spans),
8828
+ contextFootprints: contextFootprints(spans),
8829
+ coverage: {
8830
+ profile: "Native spans do not establish the authored or effective AgentProfile revision; join the Runtime profile receipt by recorded identity.",
8831
+ materializedResources: "Captured attributes measure only the visible representation. Missing tools, skills, MCP definitions, and resources are unknown.",
8832
+ contextTokens: "No tokenizer estimates are substituted for the actual request. Per-component context token counts require producer telemetry.",
8833
+ toolTokens: "Tool byte counts are not model input tokens; token attribution to tool content is not captured.",
8834
+ compaction: "Only recorded boundaries are counted. Zero-duration adapter defaults are not measured compaction latency.",
8835
+ semanticAssessments: "Success at the task, useful skill application, waste, reward hacking, safety, and dishonesty require separately attributed evaluation."
8836
+ }
8837
+ };
8838
+ }
8839
+
8536
8840
  // src/session-facts.ts
8537
8841
  import { OPENINFERENCE_SPAN_KIND as OPENINFERENCE_SPAN_KIND4, TOOL_NAME as TOOL_NAME5 } from "@tangle-network/agent-eval/trace-attributes";
8538
8842
  import { ingestSpans, rankFirstFailure } from "@tangle-network/agent-eval/diagnosis";
8843
+ var SESSION_FACTS_MEASUREMENT_VERSION = "3";
8539
8844
  var SESSION_TOKEN_TOTAL_ATTR = "traces.session.total_tokens";
8540
8845
  var SPAN_SYNTHESIZED_ATTR = "traces.codex.span_synthesized";
8541
8846
  var CORRUPTION_COUNT_ATTR = "traces.session.corruption_count";
@@ -8810,15 +9115,17 @@ function humanTurnsOf(spans) {
8810
9115
  excluded: [...excluded].sort(([left], [right]) => left.localeCompare(right)).map(([reason, spanIds]) => ({ reason, turns: spanIds.length, spanIds }))
8811
9116
  };
8812
9117
  }
8813
- function computeSessionFacts(spans) {
9118
+ function computeSessionFacts(spans, options = {}) {
8814
9119
  const { sessionByTrace } = indexSessionIdsByTrace(spans);
8815
9120
  const byTrace = /* @__PURE__ */ new Map();
8816
9121
  for (const span2 of spans) {
8817
- byTrace.set(span2.trace_id, [...byTrace.get(span2.trace_id) ?? [], span2]);
9122
+ const group = byTrace.get(span2.trace_id);
9123
+ if (group) group.push(span2);
9124
+ else byTrace.set(span2.trace_id, [span2]);
8818
9125
  }
8819
- return [...byTrace].sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0).map(([traceId, traceSpans]) => sessionFactsForTrace(traceId, sessionByTrace.get(traceId) ?? null, traceSpans));
9126
+ return [...byTrace].sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0).map(([traceId, traceSpans]) => sessionFactsForTrace(traceId, sessionByTrace.get(traceId) ?? null, traceSpans, options));
8820
9127
  }
8821
- function sessionFactsForTrace(traceId, sessionId, unordered) {
9128
+ function sessionFactsForTrace(traceId, sessionId, unordered, options) {
8822
9129
  const spans = [...unordered].sort(byTraceOrder);
8823
9130
  const harness = spans.map((span2) => stringAttr2(span2, "service.name")).find((name) => name !== null) ?? null;
8824
9131
  const ownSpans = spans.filter((span2) => !isSubagentSpan(span2.attributes));
@@ -8857,7 +9164,7 @@ function sessionFactsForTrace(traceId, sessionId, unordered) {
8857
9164
  (span2) => span2.parent_span_id !== null && span2.attributes[CORRUPTION_RECEIPT_VERSION_ATTR] === void 0
8858
9165
  ).length;
8859
9166
  const corruptionSpan = spans.find((span2) => typeof span2.attributes[CORRUPTION_COUNT_ATTR] === "number");
8860
- return {
9167
+ const facts = {
8861
9168
  schemaVersion: 1,
8862
9169
  kind: "traces.session_facts",
8863
9170
  traceId,
@@ -8874,6 +9181,7 @@ function sessionFactsForTrace(traceId, sessionId, unordered) {
8874
9181
  spanIds: [],
8875
9182
  unavailable: `no span carries ${CORRUPTION_COUNT_ATTR}; this trace was not read through the session integrity path, so whether any record was skipped is unknown`
8876
9183
  },
9184
+ measurements: computeSessionMeasurements(ownSpans, invoked, sessionId, harness),
8877
9185
  toolCalls: {
8878
9186
  value: invoked.length,
8879
9187
  spanIds: invoked.map((span2) => span2.span_id),
@@ -8926,15 +9234,50 @@ function sessionFactsForTrace(traceId, sessionId, unordered) {
8926
9234
  unavailable: `no span carries ${SESSION_TOKEN_TOTAL_ATTR}; the per-turn llm.turn deltas in this trace are a different quantity and summing them would not be the harness total`
8927
9235
  }
8928
9236
  };
9237
+ if (options.includeContent !== false) return facts;
9238
+ const withheld = () => ({
9239
+ value: null,
9240
+ spanIds: [],
9241
+ unavailable: "Content was not requested."
9242
+ });
9243
+ return {
9244
+ ...facts,
9245
+ humanTurns: withheld(),
9246
+ finalMessages: withheld(),
9247
+ changedFiles: withheld(),
9248
+ pullRequests: withheld(),
9249
+ subagents: {
9250
+ ...facts.subagents,
9251
+ value: facts.subagents.value?.map((entry) => ({
9252
+ ...entry,
9253
+ taskName: null,
9254
+ taskNameUnavailable: "Assignment text was not requested."
9255
+ })) ?? null
9256
+ },
9257
+ firstFailure: facts.firstFailure.status === "found" ? { ...facts.firstFailure, message: null, classification: facts.firstFailure.classification ? { ...facts.firstFailure.classification, reason: "Content was not requested." } : null } : facts.firstFailure
9258
+ };
8929
9259
  }
8930
9260
  function buildSessionFactsReport(spans, options = {}) {
9261
+ const digest = createHash7("sha256");
9262
+ const ordered2 = [...spans].sort((a, b) => a.trace_id.localeCompare(b.trace_id) || a.span_id.localeCompare(b.span_id));
9263
+ let evidenceThrough = null;
9264
+ for (const span2 of ordered2) {
9265
+ digest.update(canonicalJson(span2)).update("\n");
9266
+ for (const time of [span2.start_time, span2.end_time]) {
9267
+ if (Number.isFinite(Date.parse(time)) && (evidenceThrough === null || Date.parse(time) > Date.parse(evidenceThrough))) evidenceThrough = time;
9268
+ }
9269
+ }
8931
9270
  return {
8932
9271
  schemaVersion: 1,
8933
9272
  kind: "traces.session_facts_report",
9273
+ measurementVersion: SESSION_FACTS_MEASUREMENT_VERSION,
9274
+ sourceDigest: "sha256:" + digest.digest("hex"),
9275
+ evidenceThrough,
9276
+ contentIncluded: options.includeContent !== false,
8934
9277
  generatedAt: options.generatedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
8935
9278
  harness: options.harness ?? null,
8936
9279
  spanCount: spans.length,
8937
- sessions: computeSessionFacts(spans)
9280
+ sessions: computeSessionFacts(spans, options)
8938
9281
  };
8939
9282
  }
8940
9283
  function factLine(label2, fact, rendered) {
@@ -8983,6 +9326,8 @@ function renderSessionFacts(report) {
8983
9326
  lines.push(factLine("changed files", facts.changedFiles, String(facts.changedFiles.value?.length ?? 0)));
8984
9327
  lines.push(factLine("first record", facts.firstRecordAt, String(facts.firstRecordAt.value)));
8985
9328
  lines.push(factLine("last record", facts.lastRecordAt, String(facts.lastRecordAt.value)));
9329
+ lines.push(factLine("compactions", facts.measurements.compactions, String(facts.measurements.compactions.value?.length ?? 0)));
9330
+ lines.push(" skills: " + facts.measurements.adoption.totalSkillInvocations + " recorded invocation(s), " + facts.measurements.adoption.totalSkillDocumentReads + " document read(s); telemetry " + facts.measurements.adoption.skillTelemetryStatus);
8986
9331
  lines.push(factLine("token total", facts.tokenTotal, String(facts.tokenTotal.value)));
8987
9332
  lines.push(` first failure: ${renderFirstFailure(facts.firstFailure)}`);
8988
9333
  if ((facts.unreadRecords.value ?? 0) > 0) {
@@ -9028,6 +9373,9 @@ ${JSON.stringify({ sessions, ...dropped2.length ? { omitted_fields: dropped2 } :
9028
9373
  ["tool_call_span_ids", (facts) => {
9029
9374
  facts.tool_calls.span_ids = [];
9030
9375
  }],
9376
+ ["measurements", (facts) => {
9377
+ delete facts.measurements;
9378
+ }],
9031
9379
  ["tool_calls_by_name", (facts) => {
9032
9380
  delete facts.tool_calls_by_name;
9033
9381
  }],
@@ -9152,7 +9500,8 @@ function compactFacts(facts) {
9152
9500
  first_record_at: factJson(facts.firstRecordAt),
9153
9501
  last_record_at: factJson(facts.lastRecordAt),
9154
9502
  token_total: factJson(facts.tokenTotal),
9155
- first_failure: compactFirstFailure(facts.firstFailure)
9503
+ first_failure: compactFirstFailure(facts.firstFailure),
9504
+ measurements: facts.measurements
9156
9505
  };
9157
9506
  }
9158
9507
  function compactFirstFailure(failure2) {
@@ -9181,7 +9530,7 @@ function sessionFactsContext(spans, options = {}) {
9181
9530
  if (report.sessions.length === 0) return void 0;
9182
9531
  return renderSessionFactsContext(report, options);
9183
9532
  }
9184
- var SESSION_FACTS_VERSION_SUFFIX = "session-facts.2";
9533
+ var SESSION_FACTS_VERSION_SUFFIX = "session-facts." + SESSION_FACTS_MEASUREMENT_VERSION;
9185
9534
  function withSessionFactsContext(definitions, spans, options = {}) {
9186
9535
  const sheet = sessionFactsContext(spans, options);
9187
9536
  if (sheet === void 0) return [...definitions];
@@ -9208,13 +9557,13 @@ import { OtlpFileTraceStore } from "@tangle-network/agent-eval/traces";
9208
9557
 
9209
9558
  // src/bundle-source.ts
9210
9559
  import { isUtf8 as isUtf82 } from "buffer";
9211
- import { createHash as createHash9 } from "crypto";
9560
+ import { createHash as createHash10 } from "crypto";
9212
9561
  import { constants } from "fs";
9213
9562
  import { lstat as lstat2, open, realpath as realpath3 } from "fs/promises";
9214
9563
  import { dirname as dirname8, isAbsolute as isAbsolute4, join as join20, relative as relative5, resolve as resolve7, sep as sep2 } from "path";
9215
9564
 
9216
9565
  // src/bundle.ts
9217
- import { createHash as createHash8 } from "crypto";
9566
+ import { createHash as createHash9 } from "crypto";
9218
9567
  import { copyFile, cp, lstat, mkdir as mkdir2, readdir as readdir16, readFile as readFile7, stat as stat14, writeFile as writeFile5 } from "fs/promises";
9219
9568
  import { dirname as dirname7, join as join19, relative as relative4, resolve as resolve6, sep } from "path";
9220
9569
 
@@ -10443,7 +10792,7 @@ function measured(value, render) {
10443
10792
  }
10444
10793
  function distributionRow(label2, distribution, unit = "") {
10445
10794
  if (distribution.n === 0) return `| ${label2} | 0 | not captured | not captured | not captured | not captured | not captured |`;
10446
- const value = (number) => measured(number, (captured) => `${decimal(captured)}${unit}`);
10795
+ const value = (number2) => measured(number2, (captured) => `${decimal(captured)}${unit}`);
10447
10796
  return `| ${label2} | ${count(distribution.n)} | ${value(distribution.min)} | ${value(distribution.mean)} | ${value(distribution.p50)} | ${value(distribution.p95)} | ${value(distribution.max)} |`;
10448
10797
  }
10449
10798
  function tokenRows(usage) {
@@ -11794,7 +12143,7 @@ async function writeSessionIndexFile(index, outPath) {
11794
12143
  }
11795
12144
 
11796
12145
  // src/session-selection.ts
11797
- import { createHash as createHash7 } from "crypto";
12146
+ import { createHash as createHash8 } from "crypto";
11798
12147
  import { createReadStream as createReadStream2 } from "fs";
11799
12148
 
11800
12149
  // src/session-workflow.ts
@@ -12120,7 +12469,7 @@ async function collectSessionWorkflow(adapter, seeds, options = {}) {
12120
12469
  // src/session-selection.ts
12121
12470
  async function fileSha256(path, signal) {
12122
12471
  signal?.throwIfAborted();
12123
- const hash2 = createHash7("sha256");
12472
+ const hash2 = createHash8("sha256");
12124
12473
  try {
12125
12474
  for await (const chunk of createReadStream2(path, { signal })) {
12126
12475
  signal?.throwIfAborted();
@@ -12148,7 +12497,7 @@ async function sourceSnapshot(adapter, ref, signal) {
12148
12497
  signal?.throwIfAborted();
12149
12498
  files.push({ path, sha256: await fileSha256(path, signal) });
12150
12499
  }
12151
- const sha2562 = files.length === 1 ? files[0].sha256 : createHash7("sha256").update(JSON.stringify(files)).digest("hex");
12500
+ const sha2562 = files.length === 1 ? files[0].sha256 : createHash8("sha256").update(JSON.stringify(files)).digest("hex");
12152
12501
  return { sha256: sha2562, files };
12153
12502
  }
12154
12503
  function summarizeWorkflows(workflows) {
@@ -12274,8 +12623,8 @@ var KNOWN_LIMITS = [
12274
12623
  "progress.md is copied whole; no session-dated section extraction is performed",
12275
12624
  "open pull-request state is not captured: the bundler makes no network calls"
12276
12625
  ];
12277
- function sha256Hex(bytes) {
12278
- return createHash8("sha256").update(bytes).digest("hex");
12626
+ function sha256Hex(bytes2) {
12627
+ return createHash9("sha256").update(bytes2).digest("hex");
12279
12628
  }
12280
12629
  function bundlePath(...parts) {
12281
12630
  return parts.join("/");
@@ -12682,13 +13031,13 @@ async function verifySessionBundle(bundleDir) {
12682
13031
  let source;
12683
13032
  if (manifest.projection) {
12684
13033
  const sourceManifest = join19(manifest.projection.sourceDirectory, "manifest.json");
12685
- const bytes = await readFile7(sourceManifest).catch((error) => {
13034
+ const bytes2 = await readFile7(sourceManifest).catch((error) => {
12686
13035
  if (isMissingPathError(error)) return null;
12687
13036
  throw error;
12688
13037
  });
12689
13038
  source = {
12690
13039
  directory: manifest.projection.sourceDirectory,
12691
- status: bytes === null ? "unavailable" : sha256Hex(bytes) === manifest.projection.sourceManifestSha256 ? "match" : "mismatch"
13040
+ status: bytes2 === null ? "unavailable" : sha256Hex(bytes2) === manifest.projection.sourceManifestSha256 ? "match" : "mismatch"
12692
13041
  };
12693
13042
  }
12694
13043
  return {
@@ -12732,8 +13081,8 @@ async function openBundleFile(root, file) {
12732
13081
  return handle;
12733
13082
  }
12734
13083
  async function fileDigest(handle, signal) {
12735
- const hash2 = createHash9("sha256");
12736
- for await (const bytes of handle.createReadStream({ start: 0, autoClose: false, signal })) hash2.update(bytes);
13084
+ const hash2 = createHash10("sha256");
13085
+ for await (const bytes2 of handle.createReadStream({ start: 0, autoClose: false, signal })) hash2.update(bytes2);
12737
13086
  return hash2.digest("hex");
12738
13087
  }
12739
13088
  async function validateFile(root, file, signal) {
@@ -12833,7 +13182,7 @@ async function createBundleSourceReader(directory, spans, options = {}) {
12833
13182
  handle = await openBundleFile(root, file);
12834
13183
  const before = await handle.stat();
12835
13184
  if (await fileDigest(handle, signal) !== file.sha256) return unavailable("retained source digest mismatch");
12836
- const hash2 = createHash9("sha256");
13185
+ const hash2 = createHash10("sha256");
12837
13186
  const record2 = Buffer.alloc(ref.recordBytes);
12838
13187
  let position = 0;
12839
13188
  for await (const raw of handle.createReadStream({
@@ -12842,10 +13191,10 @@ async function createBundleSourceReader(directory, spans, options = {}) {
12842
13191
  autoClose: false,
12843
13192
  signal
12844
13193
  })) {
12845
- const bytes2 = Buffer.isBuffer(raw) ? raw : Buffer.from(raw);
12846
- hash2.update(bytes2);
12847
- bytes2.copy(record2, position);
12848
- position += bytes2.length;
13194
+ const bytes3 = Buffer.isBuffer(raw) ? raw : Buffer.from(raw);
13195
+ hash2.update(bytes3);
13196
+ bytes3.copy(record2, position);
13197
+ position += bytes3.length;
12849
13198
  }
12850
13199
  const after = await handle.stat();
12851
13200
  if (position !== ref.recordBytes || hash2.digest("hex") !== ref.recordSha256 || before.size !== after.size || before.mtimeMs !== after.mtimeMs || before.ctimeMs !== after.ctimeMs) {
@@ -12868,22 +13217,22 @@ async function createBundleSourceReader(directory, spans, options = {}) {
12868
13217
  if (input.offset > field.length) return unavailable("source offset is outside the selected field");
12869
13218
  const window = field.subarray(input.offset, Math.min(field.length, input.offset + input.limit));
12870
13219
  if (window.length > 0 && (window[0] & 192) === 128) return unavailable("offset splits a UTF-8 character");
12871
- let bytes = window;
13220
+ let bytes2 = window;
12872
13221
  for (let omitted = 0; omitted <= 3; omitted += 1) {
12873
- bytes = window.subarray(0, Math.max(0, window.length - omitted));
12874
- if (isUtf82(bytes)) break;
13222
+ bytes2 = window.subarray(0, Math.max(0, window.length - omitted));
13223
+ if (isUtf82(bytes2)) break;
12875
13224
  }
12876
- if (!isUtf82(bytes) || bytes.length === 0 && input.offset < field.length) {
13225
+ if (!isUtf82(bytes2) || bytes2.length === 0 && input.offset < field.length) {
12877
13226
  return unavailable("window cannot contain a complete UTF-8 character");
12878
13227
  }
12879
- const next = input.offset + bytes.length;
13228
+ const next = input.offset + bytes2.length;
12880
13229
  return {
12881
13230
  status: "available",
12882
13231
  trace_id: input.trace_id,
12883
13232
  span_id: input.span_id,
12884
13233
  attribute: input.attribute,
12885
13234
  source_index: sourceIndex,
12886
- text: bytes.toString("utf8"),
13235
+ text: bytes2.toString("utf8"),
12887
13236
  offset: input.offset,
12888
13237
  total_bytes: field.length,
12889
13238
  next_offset: next < field.length ? next : null,
@@ -13016,7 +13365,7 @@ async function analyzeSpans(spans, opts = {}) {
13016
13365
  }
13017
13366
 
13018
13367
  // src/chat-trajectory.ts
13019
- import { createHash as createHash10 } from "crypto";
13368
+ import { createHash as createHash11 } from "crypto";
13020
13369
  function isChatTrajectoryMessage(value) {
13021
13370
  return isObject2(value) && typeof value.role === "string" && typeof value.content === "string";
13022
13371
  }
@@ -13101,7 +13450,7 @@ function trajectoryId(trajectory) {
13101
13450
  const value = trajectory.trajectory_id ?? trajectory.traj_id ?? trajectory.instance_id;
13102
13451
  if (typeof value === "string" && value.trim()) return value;
13103
13452
  if (typeof value === "number" && Number.isSafeInteger(value)) return String(value);
13104
- return `trajectory-${createHash10("sha256").update(JSON.stringify(trajectory.messages)).digest("hex").slice(0, 24)}`;
13453
+ return `trajectory-${createHash11("sha256").update(JSON.stringify(trajectory.messages)).digest("hex").slice(0, 24)}`;
13105
13454
  }
13106
13455
  function messageKind(role) {
13107
13456
  const normalized = role.trim().toLowerCase();
@@ -13158,8 +13507,8 @@ function parseTime2(value) {
13158
13507
  }
13159
13508
  function finiteNumber(value, key) {
13160
13509
  if (!isObject2(value)) return void 0;
13161
- const number = value[key];
13162
- return typeof number === "number" && Number.isFinite(number) ? number : void 0;
13510
+ const number2 = value[key];
13511
+ return typeof number2 === "number" && Number.isFinite(number2) ? number2 : void 0;
13163
13512
  }
13164
13513
  function stringValue2(value) {
13165
13514
  return typeof value === "string" && value.trim() ? value : void 0;
@@ -13178,10 +13527,10 @@ import {
13178
13527
  readFile as readFile10,
13179
13528
  rm as rm2
13180
13529
  } from "fs/promises";
13181
- import { basename as basename14, dirname as dirname10, join as join23, resolve as resolve9 } from "path";
13530
+ import { basename as basename13, dirname as dirname10, join as join23, resolve as resolve9 } from "path";
13182
13531
 
13183
13532
  // src/codetracebench-files.ts
13184
- import { createHash as createHash11, randomUUID } from "crypto";
13533
+ import { createHash as createHash12, randomUUID } from "crypto";
13185
13534
  import {
13186
13535
  link,
13187
13536
  lstat as lstat3,
@@ -13196,13 +13545,13 @@ import {
13196
13545
  writeFile as writeFile6
13197
13546
  } from "fs/promises";
13198
13547
  import { hostname } from "os";
13199
- import { basename as basename13, dirname as dirname9, isAbsolute as isAbsolute5, join as join21, relative as relative6, resolve as resolve8, sep as sep3 } from "path";
13548
+ import { basename as basename12, dirname as dirname9, isAbsolute as isAbsolute5, join as join21, relative as relative6, resolve as resolve8, sep as sep3 } from "path";
13200
13549
  async function claimImportLock(input) {
13201
13550
  const claimId = randomUUID();
13202
13551
  const path = `${input.outputDirectory}.lock`;
13203
13552
  const temporary = join21(
13204
13553
  dirname9(path),
13205
- `.${basename13(path)}.${claimId}.tmp`
13554
+ `.${basename12(path)}.${claimId}.tmp`
13206
13555
  );
13207
13556
  const metadata = {
13208
13557
  kind: "traces.codetracebench-import-lock",
@@ -13364,7 +13713,7 @@ async function atomicWriteFile(path, content, signal) {
13364
13713
  signal.throwIfAborted();
13365
13714
  const temporary = join21(
13366
13715
  dirname9(path),
13367
- `.${basename13(path)}.${randomUUID()}.tmp`
13716
+ `.${basename12(path)}.${randomUUID()}.tmp`
13368
13717
  );
13369
13718
  try {
13370
13719
  await writeFile6(temporary, content, {
@@ -13379,7 +13728,7 @@ async function atomicWriteFile(path, content, signal) {
13379
13728
  }
13380
13729
  }
13381
13730
  function sha256(value) {
13382
- return createHash11("sha256").update(value).digest("hex");
13731
+ return createHash12("sha256").update(value).digest("hex");
13383
13732
  }
13384
13733
  function isObject3(value) {
13385
13734
  return value !== null && typeof value === "object" && !Array.isArray(value);
@@ -13616,10 +13965,10 @@ async function readOptionalTask(trajectoryDirectory, caseDirectory, row, signal)
13616
13965
  content: `Task: ${row.taskName}`
13617
13966
  };
13618
13967
  }
13619
- const bytes = await readFile9(path, { signal });
13620
- const content = bytes.toString("utf8").trim();
13968
+ const bytes2 = await readFile9(path, { signal });
13969
+ const content = bytes2.toString("utf8").trim();
13621
13970
  if (!content) throw new Error(`${row.trajId}/task.md is empty`);
13622
- return { source: "task.md", content, path, bytes };
13971
+ return { source: "task.md", content, path, bytes: bytes2 };
13623
13972
  }
13624
13973
  function assertNoLabelLeak(spans, row) {
13625
13974
  for (const item of spans) {
@@ -13697,7 +14046,7 @@ async function importCodeTraceBench(options) {
13697
14046
  const rowsPath = resolve9(options.rowsPath);
13698
14047
  const trajectoryPath = resolve9(options.trajectoryDir);
13699
14048
  const outputDirectory = resolve9(options.outDir);
13700
- const outputBase = basename14(outputDirectory);
14049
+ const outputBase = basename13(outputDirectory);
13701
14050
  if (!outputBase || outputDirectory === dirname10(outputDirectory)) {
13702
14051
  throw new Error("CodeTraceBench output directory must not be a filesystem root");
13703
14052
  }
@@ -13801,10 +14150,10 @@ async function runCodeTraceBenchImport(input) {
13801
14150
  generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
13802
14151
  input: {
13803
14152
  revision,
13804
- rowsFile: basename14(rowsPath),
14153
+ rowsFile: basename13(rowsPath),
13805
14154
  rowsSha256,
13806
14155
  rowsBytes: rowsBytes.byteLength,
13807
- trajectoryDirectory: basename14(trajectoryDirectory),
14156
+ trajectoryDirectory: basename13(trajectoryDirectory),
13808
14157
  sha256: inputSha256
13809
14158
  },
13810
14159
  settings: {
@@ -14269,7 +14618,7 @@ async function cmdVerifyFindings(argv) {
14269
14618
  }
14270
14619
 
14271
14620
  // src/hodoscope.ts
14272
- import { createHash as createHash12 } from "crypto";
14621
+ import { createHash as createHash13 } from "crypto";
14273
14622
  import { mkdir as mkdir5, mkdtemp as mkdtemp5, readFile as readFile11, readdir as readdir17, writeFile as writeFile7 } from "fs/promises";
14274
14623
  import { endianness, tmpdir as tmpdir5 } from "os";
14275
14624
  import { join as join24, resolve as resolve10 } from "path";
@@ -14308,7 +14657,7 @@ async function writeHodoscopeInput(otlpPath, options = {}) {
14308
14657
  throw new Error(`Hodoscope trace '${traceId}' contains no assistant actions`);
14309
14658
  }
14310
14659
  actionCount += actions;
14311
- const file = `${createHash12("sha256").update(traceId).digest("hex").slice(0, 24)}.json`;
14660
+ const file = `${createHash13("sha256").update(traceId).digest("hex").slice(0, 24)}.json`;
14312
14661
  await writeFile7(join24(samples, file), `${JSON.stringify(trajectory, null, 2)}
14313
14662
  `, "utf8");
14314
14663
  }
@@ -14820,7 +15169,7 @@ function validateEmbedding(encoded, dimensions, path) {
14820
15169
  `Hodoscope ${path} encodes ${encoded.length} characters; expected ${expectedLength} for ${dimensions} float32 values`
14821
15170
  );
14822
15171
  }
14823
- const bytes = new Uint8Array(dimensions * 4);
15172
+ const bytes2 = new Uint8Array(dimensions * 4);
14824
15173
  for (let group = 0; group < dimensions; group += 1) {
14825
15174
  let value = 0;
14826
15175
  const encodedOffset = group * 5;
@@ -14836,12 +15185,12 @@ function validateEmbedding(encoded, dimensions, path) {
14836
15185
  throw new Error(`Hodoscope ${path} contains an overflowing base85 block at index ${group}`);
14837
15186
  }
14838
15187
  const byteOffset = group * 4;
14839
- bytes[byteOffset] = Math.floor(value / 16777216) & 255;
14840
- bytes[byteOffset + 1] = Math.floor(value / 65536) & 255;
14841
- bytes[byteOffset + 2] = Math.floor(value / 256) & 255;
14842
- bytes[byteOffset + 3] = value & 255;
15188
+ bytes2[byteOffset] = Math.floor(value / 16777216) & 255;
15189
+ bytes2[byteOffset + 1] = Math.floor(value / 65536) & 255;
15190
+ bytes2[byteOffset + 2] = Math.floor(value / 256) & 255;
15191
+ bytes2[byteOffset + 3] = value & 255;
14843
15192
  }
14844
- const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
15193
+ const view = new DataView(bytes2.buffer, bytes2.byteOffset, bytes2.byteLength);
14845
15194
  const littleEndian = endianness() === "LE";
14846
15195
  for (let index = 0; index < dimensions; index += 1) {
14847
15196
  if (!Number.isFinite(view.getFloat32(index * 4, littleEndian))) {
@@ -14956,7 +15305,7 @@ function assertPositiveInteger(name, value) {
14956
15305
  }
14957
15306
 
14958
15307
  // src/analyst-engine-prime.ts
14959
- import { createHash as createHash13 } from "crypto";
15308
+ import { createHash as createHash14 } from "crypto";
14960
15309
  import { request as httpRequest } from "http";
14961
15310
  import { request as httpsRequest } from "https";
14962
15311
  var DEFAULT_BRIDGE_URL = "http://localhost:4181";
@@ -14969,6 +15318,17 @@ var MAX_FINDINGS = 10;
14969
15318
  var MAX_SPAN_IDS_PER_FINDING = 8;
14970
15319
  var SEVERITIES = /* @__PURE__ */ new Set(["critical", "high", "medium", "low", "info"]);
14971
15320
  var ANALYST_ID = "prime";
15321
+ var TRAJECTORY_FILE_NAME = "trajectory.otlp.jsonl";
15322
+ async function fileDelivery(otlpPath, perAttributeCharCap, signal) {
15323
+ signal?.throwIfAborted();
15324
+ const { copyFile: copyFile2, mkdtemp: mkdtemp8, stat: stat19 } = await import("fs/promises");
15325
+ const { tmpdir: tmpdir8 } = await import("os");
15326
+ const { join: join35 } = await import("path");
15327
+ const cwd = await mkdtemp8(join35(tmpdir8(), "traces-prime-"));
15328
+ await copyFile2(otlpPath, join35(cwd, TRAJECTORY_FILE_NAME));
15329
+ const artifactBytes = (await stat19(otlpPath)).size;
15330
+ return { mode: "file-cwd", perAttributeCharCap, renderedChars: artifactBytes, cwd, fileName: TRAJECTORY_FILE_NAME };
15331
+ }
14972
15332
  function positiveIntegerEnv(name) {
14973
15333
  const raw = process.env[name];
14974
15334
  if (raw === void 0 || raw === "") return void 0;
@@ -15019,7 +15379,7 @@ function capRowAttributes(row, cap) {
15019
15379
  }
15020
15380
  return { ...row, attributes: capped };
15021
15381
  }
15022
- async function projectArtifact(otlpPath, maxInlineChars, perAttributeCharCap, signal) {
15382
+ async function projectArtifact(otlpPath, maxInlineChars, perAttributeCharCap, deliveryMode, signal) {
15023
15383
  const rows = [];
15024
15384
  const spanTraces = /* @__PURE__ */ new Map();
15025
15385
  let index = 0;
@@ -15043,9 +15403,18 @@ async function projectArtifact(otlpPath, maxInlineChars, perAttributeCharCap, si
15043
15403
  }
15044
15404
  if (rows.length === 0) throw new Error(`${otlpPath} contains no spans`);
15045
15405
  const traceCount = new Set(rows.map((row) => row.trace_id)).size;
15406
+ if (deliveryMode === "file") {
15407
+ return {
15408
+ rows,
15409
+ rendered: null,
15410
+ delivery: await fileDelivery(otlpPath, perAttributeCharCap, signal),
15411
+ traceCount,
15412
+ spanTraces
15413
+ };
15414
+ }
15046
15415
  let projected = rows;
15047
15416
  let rendered = JSON.stringify(projected);
15048
- const delivery = {
15417
+ let delivery = {
15049
15418
  mode: "inline-json",
15050
15419
  perAttributeCharCap: null,
15051
15420
  renderedChars: rendered.length
@@ -15053,18 +15422,30 @@ async function projectArtifact(otlpPath, maxInlineChars, perAttributeCharCap, si
15053
15422
  if (rendered.length > maxInlineChars) {
15054
15423
  projected = rows.map((row) => capRowAttributes(row, perAttributeCharCap));
15055
15424
  rendered = JSON.stringify(projected);
15056
- delivery.perAttributeCharCap = perAttributeCharCap;
15057
- delivery.renderedChars = rendered.length;
15425
+ delivery = {
15426
+ mode: "inline-json",
15427
+ perAttributeCharCap,
15428
+ renderedChars: rendered.length
15429
+ };
15058
15430
  }
15059
15431
  if (rendered.length > maxInlineChars) {
15060
- throw new Error(
15061
- `trajectory renders to ${rendered.length} chars even at per-attribute cap ${perAttributeCharCap} (budget ${maxInlineChars}); inline delivery impossible`
15062
- );
15432
+ if (deliveryMode === "inline") {
15433
+ throw new Error(
15434
+ `trajectory renders to ${rendered.length} chars even at per-attribute cap ${perAttributeCharCap} (budget ${maxInlineChars}); inline delivery impossible`
15435
+ );
15436
+ }
15437
+ return {
15438
+ rows,
15439
+ rendered: null,
15440
+ delivery: await fileDelivery(otlpPath, perAttributeCharCap, signal),
15441
+ traceCount,
15442
+ spanTraces
15443
+ };
15063
15444
  }
15064
15445
  return { rows: projected, rendered, delivery, traceCount, spanTraces };
15065
15446
  }
15066
15447
  var OUTPUT_CONTRACT_LINES = [
15067
- "OUTPUT CONTRACT (you have no trace tools and no REPL):",
15448
+ "OUTPUT CONTRACT (inline delivery \u2014 the whole trajectory is in this prompt):",
15068
15449
  "You are a one-shot analyst. Every fact you need is in the TRAJECTORY JSON below.",
15069
15450
  "Do not run shell commands, do not read or write files, do not use any tools.",
15070
15451
  "Reply with EXACTLY one fenced ```json code block and no other fenced block. The JSON object has exactly two fields:",
@@ -15080,7 +15461,35 @@ var OUTPUT_CONTRACT_LINES = [
15080
15461
  `Report at most ${MAX_FINDINGS} findings; a finding cites 1..${MAX_SPAN_IDS_PER_FINDING} span_ids, each copied VERBATIM from a span in the trajectory below.`,
15081
15462
  '"findings" is [] only for a clean trajectory.'
15082
15463
  ];
15464
+ var FILE_DELIVERY_CONTRACT_LINES = [
15465
+ "OUTPUT CONTRACT (file delivery \u2014 the trajectory is a FILE in your working directory):",
15466
+ `You are a recursive language model with a REPL. The trajectory is ${TRAJECTORY_FILE_NAME} in your`,
15467
+ "working directory: OpenInference JSONL, one span object per line. Load it yourself and inspect it with code",
15468
+ '(e.g. rows = [json.loads(line) for line in open("trajectory.otlp.jsonl")]), paging rather than printing it whole;',
15469
+ "fan out with rlm() for sub-questions when that pays for itself. Stay inside your working directory.",
15470
+ "Reply with EXACTLY one fenced ```json code block and no other fenced block. The JSON object has exactly two fields:",
15471
+ ' "answer": string \u2014 ONE short sentence (max 300 chars) summarizing your verdict.',
15472
+ ' "findings": array (possibly empty) of finding rows, each exactly:',
15473
+ ' {"span_ids": [string, ...],',
15474
+ ' "severity": "critical"|"high"|"medium"|"low"|"info",',
15475
+ ' "area": string (short kebab-case topic, max 40 chars),',
15476
+ ' "claim": string (ONE short sentence, max 200 chars),',
15477
+ ' "action": string (optional, ONE short imperative sentence, max 200 chars),',
15478
+ ' "confidence": number 0..1}',
15479
+ "Do NOT include a rationale field. Keep every string SHORT \u2014 long strings get corrupted in transport and void your work.",
15480
+ `Report at most ${MAX_FINDINGS} findings; a finding cites 1..${MAX_SPAN_IDS_PER_FINDING} span_ids, each copied VERBATIM from a span in ${TRAJECTORY_FILE_NAME}.`,
15481
+ '"findings" is [] only for a clean trajectory.'
15482
+ ];
15083
15483
  function buildPrompt(question, projection) {
15484
+ if (projection.delivery.mode === "file-cwd") {
15485
+ return [
15486
+ `QUESTION: ${question}`,
15487
+ "",
15488
+ ...FILE_DELIVERY_CONTRACT_LINES,
15489
+ "",
15490
+ `TRAJECTORY (${projection.traceCount} trace(s); ${projection.rows.length} spans): the file ${projection.delivery.fileName} in your working directory (${projection.delivery.renderedChars} bytes). It is NOT inlined in this prompt.`
15491
+ ].join("\n");
15492
+ }
15084
15493
  return [
15085
15494
  `QUESTION: ${question}`,
15086
15495
  "",
@@ -15180,7 +15589,7 @@ function toAnalystFinding(row, index, spanTraces, model, producedAt) {
15180
15589
  const traces = spanTraces.get(spanId);
15181
15590
  return spanEvidenceUri([...traces][0], spanId);
15182
15591
  });
15183
- const findingId = `prime-${createHash13("sha256").update(`${index}
15592
+ const findingId = `prime-${createHash14("sha256").update(`${index}
15184
15593
  ${row.claim}
15185
15594
  ${uris.join("\n")}`).digest("hex").slice(0, 12)}`;
15186
15595
  return {
@@ -15227,7 +15636,7 @@ var httpJsonTransport = ({ url, body, signal }) => {
15227
15636
  };
15228
15637
  var PrimeBridgeError = class extends Error {
15229
15638
  };
15230
- async function callBridge(transport, url, model, content, timeoutMs, callerSignal, turn) {
15639
+ async function callBridge(transport, url, model, content, timeoutMs, callerSignal, turn, cwd) {
15231
15640
  const controller = new AbortController();
15232
15641
  const timer = setTimeout(
15233
15642
  () => controller.abort(new Error(`${turn}bridge call exceeded ${timeoutMs}ms`)),
@@ -15240,7 +15649,7 @@ async function callBridge(transport, url, model, content, timeoutMs, callerSigna
15240
15649
  try {
15241
15650
  response = await transport({
15242
15651
  url,
15243
- body: { model, messages: [{ role: "user", content }] },
15652
+ body: { model, messages: [{ role: "user", content }], ...cwd ? { cwd } : {} },
15244
15653
  signal: controller.signal
15245
15654
  });
15246
15655
  } catch (error) {
@@ -15287,6 +15696,10 @@ function primeAnalyzer(opts = {}) {
15287
15696
  throw new RangeError("perAttributeCharCap must be a positive safe integer");
15288
15697
  }
15289
15698
  const repairEnabled = opts.repair ?? true;
15699
+ const deliveryMode = opts.delivery ?? "auto";
15700
+ if (deliveryMode !== "auto" && deliveryMode !== "file" && deliveryMode !== "inline") {
15701
+ throw new RangeError(`delivery must be 'auto', 'file', or 'inline', got '${String(deliveryMode)}'`);
15702
+ }
15290
15703
  const transport = opts.transport ?? httpJsonTransport;
15291
15704
  const url = `${bridgeUrl}/v1/chat/completions`;
15292
15705
  return {
@@ -15294,12 +15707,19 @@ function primeAnalyzer(opts = {}) {
15294
15707
  async analyze(otlpPath, analyzeOpts = {}) {
15295
15708
  let projection;
15296
15709
  try {
15297
- projection = await projectArtifact(otlpPath, maxInlineChars, perAttributeCharCap, analyzeOpts.signal);
15710
+ projection = await projectArtifact(
15711
+ otlpPath,
15712
+ maxInlineChars,
15713
+ perAttributeCharCap,
15714
+ deliveryMode,
15715
+ analyzeOpts.signal
15716
+ );
15298
15717
  } catch (error) {
15299
15718
  return failure("", error instanceof Error ? error.message : String(error));
15300
15719
  }
15301
15720
  const question = analyzeOpts.prompt ?? opts.defaultPrompt ?? DEFAULT_QUESTION;
15302
- const deliveryLine = `delivery: ${projection.delivery.mode} (${projection.delivery.renderedChars} chars, per-attribute cap ${projection.delivery.perAttributeCharCap ?? "none"})`;
15721
+ const deliveryLine = projection.delivery.mode === "file-cwd" ? `delivery: file-cwd (${projection.rows.length} spans; ${projection.delivery.fileName} in cwd ${projection.delivery.cwd}, ${projection.delivery.renderedChars} bytes; per-attribute cap not applied)` : `delivery: ${projection.delivery.mode} (${projection.delivery.renderedChars} chars, per-attribute cap ${projection.delivery.perAttributeCharCap ?? "none"})`;
15722
+ const deliveryCwd = projection.delivery.mode === "file-cwd" ? projection.delivery.cwd : void 0;
15303
15723
  let reply;
15304
15724
  try {
15305
15725
  reply = await callBridge(
@@ -15309,7 +15729,8 @@ function primeAnalyzer(opts = {}) {
15309
15729
  buildPrompt(question, projection),
15310
15730
  timeoutMs,
15311
15731
  analyzeOpts.signal,
15312
- ""
15732
+ "",
15733
+ deliveryCwd
15313
15734
  );
15314
15735
  } catch (error) {
15315
15736
  if (!(error instanceof PrimeBridgeError)) throw error;
@@ -15466,7 +15887,7 @@ async function applyRedactor(spans, redactor) {
15466
15887
  }
15467
15888
 
15468
15889
  // src/file-export.ts
15469
- import { createHash as createHash14 } from "crypto";
15890
+ import { createHash as createHash15 } from "crypto";
15470
15891
  import { mkdtemp as mkdtemp6, readFile as readFile12, writeFile as writeFile8 } from "fs/promises";
15471
15892
  import { tmpdir as tmpdir6 } from "os";
15472
15893
  import { join as join25 } from "path";
@@ -15504,7 +15925,7 @@ function stableJson(value) {
15504
15925
  });
15505
15926
  }
15506
15927
  function hashId(value, chars = 16) {
15507
- return createHash14("sha256").update(stableJson(value)).digest("hex").slice(0, chars);
15928
+ return createHash15("sha256").update(stableJson(value)).digest("hex").slice(0, chars);
15508
15929
  }
15509
15930
  function isoTime2(value) {
15510
15931
  if (typeof value === "string") {
@@ -16148,7 +16569,7 @@ async function writeTraceEvidenceExportFile(inputPath, outPath, opts = {}) {
16148
16569
  // src/inspect.ts
16149
16570
  import { mkdtemp as mkdtemp7, readFile as readFile13, writeFile as writeFile9 } from "fs/promises";
16150
16571
  import { tmpdir as tmpdir7 } from "os";
16151
- import { basename as basename15, join as join26 } from "path";
16572
+ import { basename as basename14, join as join26 } from "path";
16152
16573
  var severityRank2 = {
16153
16574
  high: 0,
16154
16575
  medium: 1,
@@ -16331,7 +16752,7 @@ function addContextFindings(index, findings) {
16331
16752
  refs: invalidJsonlFiles.slice(0, 5).map((file) => file.path)
16332
16753
  });
16333
16754
  }
16334
- const skillRunFiles = files.filter((file) => basename15(file.path) === "skill-runs.jsonl" && (file.jsonl?.rows ?? 0) > 0).map((file) => {
16755
+ const skillRunFiles = files.filter((file) => basename14(file.path) === "skill-runs.jsonl" && (file.jsonl?.rows ?? 0) > 0).map((file) => {
16335
16756
  const rows = file.jsonl?.rows ?? 0;
16336
16757
  const keys = file.jsonl?.keys ?? {};
16337
16758
  const linkKeyCounts = {
@@ -16443,7 +16864,7 @@ async function writeInspectionReportFile(report, outPath) {
16443
16864
  }
16444
16865
 
16445
16866
  // src/live.ts
16446
- import { createHash as createHash15 } from "crypto";
16867
+ import { createHash as createHash16 } from "crypto";
16447
16868
  var VERIFY_RE = /\b(test|tests|vitest|jest|pytest|go test|cargo test|tsc|typecheck|lint|biome|eslint|build|check:invariants|preflight)\b/i;
16448
16869
  var CHANGE_RE = /\b(apply_patch|edit|write|create|delete|rename|replace|update|git apply|sed -i|perl -pi|tee\s|cat\s+>)/i;
16449
16870
  var CLAIM_DONE_RE = /\b(done|fixed|complete|completed|implemented|merged|verified|all green|tests pass|ready|works now)\b/i;
@@ -16463,7 +16884,7 @@ var sleep = (ms2, signal) => new Promise((resolve13) => {
16463
16884
  signal?.addEventListener("abort", onAbort, { once: true });
16464
16885
  });
16465
16886
  function hash(parts) {
16466
- return createHash15("sha256").update(parts.join("\0")).digest("hex").slice(0, 16);
16887
+ return createHash16("sha256").update(parts.join("\0")).digest("hex").slice(0, 16);
16467
16888
  }
16468
16889
  function asString(value) {
16469
16890
  if (typeof value === "string" && value.trim()) return value;
@@ -16871,7 +17292,7 @@ async function streamSessions(opts) {
16871
17292
 
16872
17293
  // src/supervisor-run-context.ts
16873
17294
  import { readFile as readFile14, stat as stat16 } from "fs/promises";
16874
- import { basename as basename16, join as join27 } from "path";
17295
+ import { basename as basename15, join as join27 } from "path";
16875
17296
  import { materializeTreeView } from "@tangle-network/agent-runtime/kernel";
16876
17297
  import {
16877
17298
  readRuntimeSupervisorRun
@@ -17037,7 +17458,7 @@ async function readFileRunContext(runDir, opts = {}) {
17037
17458
  return {
17038
17459
  ...sources,
17039
17460
  instanceId: opts.instanceId !== void 0 ? opts.instanceId : sources.instanceId,
17040
- arm: opts.arm !== void 0 ? opts.arm : basename16(runDir),
17461
+ arm: opts.arm !== void 0 ? opts.arm : basename15(runDir),
17041
17462
  workers: sources.workers !== null && coordination.present ? workerSources(sources.workers, coordination) : sources.workers,
17042
17463
  ...sources.journal !== null ? {
17043
17464
  rootTranscriptRef: join27(runDir, SPAWN_JOURNAL_FILE),
@@ -18689,7 +19110,7 @@ async function watchSessions(opts) {
18689
19110
  }
18690
19111
 
18691
19112
  // src/bundle-view.ts
18692
- import { createHash as createHash16 } from "crypto";
19113
+ import { createHash as createHash17 } from "crypto";
18693
19114
  import { mkdir as mkdir7, readdir as readdir20, readFile as readFile16, writeFile as writeFile11 } from "fs/promises";
18694
19115
  import { dirname as dirname11, join as join31, resolve as resolve12 } from "path";
18695
19116
  var EVIDENCE_ONLY_ALLOW_LIST = [
@@ -18882,8 +19303,8 @@ async function projectSessionBundle(opts) {
18882
19303
  const candidates = [];
18883
19304
  for (const path of await listSessionBundleFiles(sourceDir)) {
18884
19305
  if (path === "manifest.json") continue;
18885
- const bytes = await readFile16(join31(sourceDir, path));
18886
- const sha2562 = createHash16("sha256").update(bytes).digest("hex");
19306
+ const bytes2 = await readFile16(join31(sourceDir, path));
19307
+ const sha2562 = createHash17("sha256").update(bytes2).digest("hex");
18887
19308
  const recorded = sourceHashes.get(path);
18888
19309
  if (!recorded) {
18889
19310
  throw new Error(
@@ -18897,10 +19318,10 @@ async function projectSessionBundle(opts) {
18897
19318
  }
18898
19319
  const verdict = classifyEvidenceOnlyPath(path);
18899
19320
  if (!verdict.carried) {
18900
- excluded.push({ path, bytes: bytes.length, sha256: sha2562, rule: verdict.rule, reason: verdict.reason });
19321
+ excluded.push({ path, bytes: bytes2.length, sha256: sha2562, rule: verdict.rule, reason: verdict.reason });
18901
19322
  continue;
18902
19323
  }
18903
- candidates.push({ path, bytes, sha256: sha2562, carries: verdict.carries, owned: verdict.owned });
19324
+ candidates.push({ path, bytes: bytes2, sha256: sha2562, carries: verdict.carries, owned: verdict.owned });
18904
19325
  }
18905
19326
  const report = await findSessionTextLeaks({
18906
19327
  viewDir: sourceDir,
@@ -18959,7 +19380,7 @@ async function projectSessionBundle(opts) {
18959
19380
  knownLimits: [...source.knownLimits, ...EVIDENCE_ONLY_LIMITS],
18960
19381
  projection: {
18961
19382
  sourceDirectory: sourceDir,
18962
- sourceManifestSha256: createHash16("sha256").update(sourceManifestBytes).digest("hex"),
19383
+ sourceManifestSha256: createHash17("sha256").update(sourceManifestBytes).digest("hex"),
18963
19384
  leakCheck
18964
19385
  }
18965
19386
  };
@@ -19169,7 +19590,7 @@ function alreadyUploaded(state, harness, sessionId, hash2) {
19169
19590
  import { readFile as readFile18 } from "fs/promises";
19170
19591
  import { createRequire as createRequire2 } from "module";
19171
19592
  import { hostname as hostname2 } from "os";
19172
- import { basename as basename17, join as join33 } from "path";
19593
+ import { basename as basename16, join as join33 } from "path";
19173
19594
  import { hostedClientFromEnv } from "@tangle-network/agent-eval/hosted";
19174
19595
  import { assessShareSafety as assessShareSafety2, REDACTION_VERSION, redact as redact2, shareAllowed } from "@tangle-network/agent-eval/traces";
19175
19596
  var require2 = createRequire2(import.meta.url);
@@ -19203,7 +19624,7 @@ async function gitBranch(cwd) {
19203
19624
  async function sessionMeta(item) {
19204
19625
  const meta = {
19205
19626
  [ATTR.HARNESS]: item.ref.harness,
19206
- [ATTR.SESSION_FILE]: basename17(item.ref.path),
19627
+ [ATTR.SESSION_FILE]: basename16(item.ref.path),
19207
19628
  [ATTR.HOST]: hostname2(),
19208
19629
  [ATTR.UPLOADER]: `tangle-traces@${TRACES_VERSION}`,
19209
19630
  [ATTR.REDACTION_VERSION]: REDACTION_VERSION,
@@ -19369,7 +19790,7 @@ async function executeUpload(plan, opts = {}) {
19369
19790
 
19370
19791
  // src/verify-safe.ts
19371
19792
  import { lstat as lstat4, readdir as readdir21, readFile as readFile19 } from "fs/promises";
19372
- import { basename as basename18, extname, join as join34, relative as relative7 } from "path";
19793
+ import { basename as basename17, extname, join as join34, relative as relative7 } from "path";
19373
19794
  import {
19374
19795
  assessShareSafety as assessShareSafety3,
19375
19796
  combineVerdicts as combineVerdicts2
@@ -19382,15 +19803,15 @@ async function verifySafe(target, options = {}) {
19382
19803
  const unreadable = [];
19383
19804
  const files = await listFiles(target, unreadable);
19384
19805
  for (const file of files) {
19385
- const label2 = relative7(target, file) || basename18(file);
19386
- let bytes;
19806
+ const label2 = relative7(target, file) || basename17(file);
19807
+ let bytes2;
19387
19808
  try {
19388
- bytes = await readFile19(file);
19809
+ bytes2 = await readFile19(file);
19389
19810
  } catch (error) {
19390
19811
  unreadable.push(`${label2}: ${error instanceof Error ? error.message : String(error)}`);
19391
19812
  continue;
19392
19813
  }
19393
- const text2 = decodeUtf8(bytes);
19814
+ const text2 = decodeUtf8(bytes2);
19394
19815
  if (text2 === void 0) {
19395
19816
  unreadable.push(`${label2}: not UTF-8 text`);
19396
19817
  continue;
@@ -19439,10 +19860,10 @@ async function listFiles(target, unreadable) {
19439
19860
  await walk(target);
19440
19861
  return out;
19441
19862
  }
19442
- function decodeUtf8(bytes) {
19443
- if (bytes.includes(0)) return void 0;
19863
+ function decodeUtf8(bytes2) {
19864
+ if (bytes2.includes(0)) return void 0;
19444
19865
  try {
19445
- return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
19866
+ return new TextDecoder("utf-8", { fatal: true }).decode(bytes2);
19446
19867
  } catch {
19447
19868
  return void 0;
19448
19869
  }
@@ -19513,6 +19934,7 @@ export {
19513
19934
  EXPORT_RULE,
19514
19935
  toOpenInferenceSpan,
19515
19936
  serializeSpans,
19937
+ iterSerializedSpans,
19516
19938
  writeOtlpFile,
19517
19939
  summarizeOtlpExecution,
19518
19940
  summarizeSpanExecution,
@@ -19574,6 +19996,7 @@ export {
19574
19996
  serializePolicyEvidence,
19575
19997
  writePolicyEvidenceFile,
19576
19998
  countSkillRunsJsonl,
19999
+ computeAdoption,
19577
20000
  analyzeAdoption,
19578
20001
  planTraceAgenticRoute,
19579
20002
  traceAgenticKinds,
@@ -19585,6 +20008,7 @@ export {
19585
20008
  shellCommands,
19586
20009
  commandTextFromInput,
19587
20010
  readPullRequests,
20011
+ SESSION_FACTS_MEASUREMENT_VERSION,
19588
20012
  SESSION_TOKEN_TOTAL_ATTR,
19589
20013
  SPAN_SYNTHESIZED_ATTR,
19590
20014
  FACT_TEXT_CAP,