@tangle-network/traces 0.9.5 → 0.9.7

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.
@@ -920,10 +920,19 @@ function classifyReaction(text) {
920
920
  var CORRECTIVE_REACTIONS = ["correction", "frustration", "jargon", "structure"];
921
921
 
922
922
  // src/adapters/claude.ts
923
+ import { createHash as createHash4 } from "crypto";
923
924
  import { readdir as readdir3, stat as stat2 } from "fs/promises";
924
925
  import { homedir as homedir2 } from "os";
925
- import { basename as basename2, join as join2 } from "path";
926
+ import { basename as basename2, join as join2, relative } from "path";
927
+ import {
928
+ applyLlmSpanOtlpAttributes as applyLlmSpanOtlpAttributes2,
929
+ LLM_CACHED_TOKENS,
930
+ LLM_CACHE_WRITE_TOKENS,
931
+ LLM_INPUT_TOKENS,
932
+ LLM_OUTPUT_TOKENS
933
+ } from "@tangle-network/agent-eval/trace-attributes";
926
934
  var SERVICE2 = "claude-code";
935
+ var EPOCH = (/* @__PURE__ */ new Date(0)).toISOString();
927
936
  function asBlocks(content) {
928
937
  return Array.isArray(content) ? content : [];
929
938
  }
@@ -941,61 +950,181 @@ function stringifyToolResult(content) {
941
950
  return "";
942
951
  }
943
952
  function createClaudeStream(startStep) {
944
- return { spans: [], toolSpanByUseId: /* @__PURE__ */ new Map(), step: startStep, sawUserTurn: false };
945
- }
946
- function consumeClaudeEvent(ev, ctx, state) {
947
- const ts = ev.timestamp ?? (/* @__PURE__ */ new Date(0)).toISOString();
948
- const uid = ev.uuid ?? `${ctx.idPrefix}step${state.step}`;
949
- if (ev.type === "assistant" && ev.message) {
950
- const llmId = `${ctx.idPrefix}${uid}`;
951
- state.spans.push(
952
- span({
953
+ return {
954
+ spans: [],
955
+ toolSpanByUseId: /* @__PURE__ */ new Map(),
956
+ llmSpanByMessageId: /* @__PURE__ */ new Map(),
957
+ llmContentByMessageId: /* @__PURE__ */ new Map(),
958
+ toolCountByMessageId: /* @__PURE__ */ new Map(),
959
+ step: startStep,
960
+ sawUserTurn: false
961
+ };
962
+ }
963
+ var ClaudeEventConflictError = class extends Error {
964
+ sourcePath;
965
+ eventId;
966
+ constructor(sourcePath, eventId) {
967
+ super(`${sourcePath}: Claude transcript event ${JSON.stringify(eventId)} has conflicting payloads`);
968
+ this.name = "ClaudeEventConflictError";
969
+ this.sourcePath = sourcePath;
970
+ this.eventId = eventId;
971
+ }
972
+ };
973
+ function projectToolResult(toolUseId, isError, output) {
974
+ return {
975
+ toolUseId,
976
+ isError,
977
+ attributes: toolIoAttributes({ output }),
978
+ message: stringifyToolResult(output).slice(0, 500)
979
+ };
980
+ }
981
+ function projectClaudeEvent(event) {
982
+ const timestamp = event.timestamp ?? EPOCH;
983
+ if (event.type === "assistant" && event.message) {
984
+ const tools = [];
985
+ for (const block of asBlocks(event.message.content)) {
986
+ if (block.type !== "tool_use" || !block.name) continue;
987
+ tools.push({ id: block.id || null, name: block.name, attributes: toolIoAttributes({ input: block.input }) });
988
+ }
989
+ return {
990
+ kind: "assistant",
991
+ timestamp,
992
+ messageId: event.message.id ?? null,
993
+ model: event.message.model ?? null,
994
+ inputTokens: event.message.usage?.input_tokens ?? null,
995
+ outputTokens: event.message.usage?.output_tokens ?? null,
996
+ cachedInputTokens: event.message.usage?.cache_read_input_tokens ?? null,
997
+ cacheWriteInputTokens: event.message.usage?.cache_creation_input_tokens ?? null,
998
+ content: textOf2(event.message.content) || null,
999
+ tools
1000
+ };
1001
+ }
1002
+ if (event.type === "user" && event.message) {
1003
+ const results = [];
1004
+ for (const block of asBlocks(event.message.content)) {
1005
+ if (block.type !== "tool_result" || !block.tool_use_id) continue;
1006
+ results.push(projectToolResult(block.tool_use_id, block.is_error === true, block.content));
1007
+ }
1008
+ const prompt = textOf2(event.message.content);
1009
+ return {
1010
+ kind: "user",
1011
+ timestamp,
1012
+ prompt: prompt || null,
1013
+ ...prompt ? { isSidechain: event.isSidechain === true, userType: event.userType ?? null } : {},
1014
+ results
1015
+ };
1016
+ }
1017
+ if (event.type === "attachment" && event.attachment?.type === "tool_result" && event.attachment.toolUseID) {
1018
+ return {
1019
+ kind: "attachment",
1020
+ timestamp,
1021
+ result: projectToolResult(
1022
+ event.attachment.toolUseID,
1023
+ typeof event.attachment.exitCode === "number" && event.attachment.exitCode !== 0,
1024
+ event.attachment.stderr ?? ""
1025
+ )
1026
+ };
1027
+ }
1028
+ return { kind: "ignored" };
1029
+ }
1030
+ function fingerprintClaudeEvent(event) {
1031
+ return createHash4("sha256").update(JSON.stringify(event)).digest("hex");
1032
+ }
1033
+ function consumeDistinctClaudeEvent(event, ctx, state, seen, sourcePath) {
1034
+ const projection = projectClaudeEvent(event);
1035
+ if (event.uuid) {
1036
+ const fingerprint = fingerprintClaudeEvent(projection);
1037
+ const previous = seen.get(event.uuid);
1038
+ if (previous !== void 0) {
1039
+ if (previous !== fingerprint) {
1040
+ throw new ClaudeEventConflictError(sourcePath, event.uuid);
1041
+ }
1042
+ return false;
1043
+ }
1044
+ seen.set(event.uuid, fingerprint);
1045
+ }
1046
+ consumeClaudeEvent(projection, event.uuid ?? `step${state.step}`, ctx, state);
1047
+ return true;
1048
+ }
1049
+ function consumeClaudeEvent(event, uid, ctx, state) {
1050
+ if (event.kind === "assistant") {
1051
+ const messageId = event.messageId ?? uid;
1052
+ let llmSpan = state.llmSpanByMessageId.get(messageId);
1053
+ if (!llmSpan) {
1054
+ llmSpan = span({
953
1055
  traceId: ctx.traceId,
954
- spanId: llmId,
1056
+ spanId: `${ctx.idPrefix}${uid}`,
955
1057
  parentSpanId: ctx.rootParent,
956
1058
  name: "llm.turn",
957
1059
  kind: "LLM",
958
- startTime: ts,
1060
+ startTime: event.timestamp,
959
1061
  service: SERVICE2,
960
1062
  agent: ctx.agent,
961
- model: ev.message.model ?? null,
962
- inputTokens: ev.message.usage?.input_tokens ?? null,
963
- outputTokens: ev.message.usage?.output_tokens ?? null,
964
- cachedInputTokens: ev.message.usage?.cache_read_input_tokens ?? null,
965
- cacheWriteInputTokens: ev.message.usage?.cache_creation_input_tokens ?? null,
966
- step: state.step,
967
- content: textOf2(ev.message.content) || null
968
- })
969
- );
970
- state.step += 1;
971
- let toolIdx = 0;
972
- for (const block of asBlocks(ev.message.content)) {
973
- if (block.type !== "tool_use" || !block.name) continue;
1063
+ model: event.model,
1064
+ inputTokens: event.inputTokens,
1065
+ outputTokens: event.outputTokens,
1066
+ cachedInputTokens: event.cachedInputTokens,
1067
+ cacheWriteInputTokens: event.cacheWriteInputTokens,
1068
+ step: state.step
1069
+ });
1070
+ state.spans.push(llmSpan);
1071
+ state.llmSpanByMessageId.set(messageId, llmSpan);
1072
+ state.step += 1;
1073
+ } else {
1074
+ includeSpanTimestamp(llmSpan, event.timestamp);
1075
+ }
1076
+ applyLlmSpanOtlpAttributes2(llmSpan.attributes, {
1077
+ model: event.model ?? void 0,
1078
+ inputTokens: maxCapturedUsage(
1079
+ llmSpan.attributes[LLM_INPUT_TOKENS],
1080
+ event.inputTokens
1081
+ ),
1082
+ outputTokens: maxCapturedUsage(
1083
+ llmSpan.attributes[LLM_OUTPUT_TOKENS],
1084
+ event.outputTokens
1085
+ ),
1086
+ cachedTokens: maxCapturedUsage(
1087
+ llmSpan.attributes[LLM_CACHED_TOKENS],
1088
+ event.cachedInputTokens
1089
+ ),
1090
+ cacheWriteTokens: maxCapturedUsage(
1091
+ llmSpan.attributes[LLM_CACHE_WRITE_TOKENS],
1092
+ event.cacheWriteInputTokens
1093
+ )
1094
+ });
1095
+ mergeMessageContent(llmSpan, messageId, event.content, state);
1096
+ for (const tool of event.tools) {
1097
+ const existingTool = tool.id ? state.toolSpanByUseId.get(tool.id) : void 0;
1098
+ if (existingTool) {
1099
+ Object.assign(existingTool.attributes, tool.attributes);
1100
+ includeSpanTimestamp(existingTool, event.timestamp);
1101
+ continue;
1102
+ }
1103
+ const toolIdx = state.toolCountByMessageId.get(messageId) ?? 0;
974
1104
  const toolSpan = span({
975
1105
  traceId: ctx.traceId,
976
- spanId: `${ctx.idPrefix}${uid}:tool:${toolIdx}`,
977
- parentSpanId: llmId,
978
- name: `tool.${block.name}`,
1106
+ spanId: `${llmSpan.span_id}:tool:${toolIdx}`,
1107
+ parentSpanId: llmSpan.span_id,
1108
+ name: `tool.${tool.name}`,
979
1109
  kind: "TOOL",
980
- startTime: ts,
1110
+ startTime: event.timestamp,
981
1111
  service: SERVICE2,
982
1112
  agent: ctx.agent,
983
- tool: block.name,
1113
+ tool: tool.name,
984
1114
  step: state.step,
985
- extra: toolIoAttributes({ input: block.input })
1115
+ extra: tool.attributes
986
1116
  });
987
1117
  state.spans.push(toolSpan);
988
- if (block.id) state.toolSpanByUseId.set(block.id, toolSpan);
989
- toolIdx += 1;
1118
+ if (tool.id) state.toolSpanByUseId.set(tool.id, toolSpan);
1119
+ state.toolCountByMessageId.set(messageId, toolIdx + 1);
990
1120
  state.step += 1;
991
1121
  }
992
- } else if (ev.type === "user" && ev.message) {
993
- const prompt = textOf2(ev.message.content);
994
- if (prompt) {
1122
+ } else if (event.kind === "user") {
1123
+ if (event.prompt) {
995
1124
  const actor = claudeActor({
996
- text: prompt,
997
- isSidechain: ev.isSidechain,
998
- userType: ev.userType ?? null,
1125
+ text: event.prompt,
1126
+ isSidechain: event.isSidechain,
1127
+ userType: event.userType ?? null,
999
1128
  isFirstUserTurn: !state.sawUserTurn
1000
1129
  });
1001
1130
  state.sawUserTurn = true;
@@ -1004,41 +1133,95 @@ function consumeClaudeEvent(ev, ctx, state) {
1004
1133
  traceId: ctx.traceId,
1005
1134
  spanId: `${ctx.idPrefix}${uid}:user`,
1006
1135
  parentSpanId: ctx.rootParent,
1007
- startTime: ts,
1136
+ startTime: event.timestamp,
1008
1137
  service: SERVICE2,
1009
1138
  agent: ctx.agent,
1010
1139
  step: state.step,
1011
- content: prompt,
1140
+ content: event.prompt,
1012
1141
  actor
1013
1142
  })
1014
1143
  );
1015
1144
  state.step += 1;
1016
1145
  }
1017
- for (const block of asBlocks(ev.message.content)) {
1018
- if (block.type !== "tool_result" || !block.tool_use_id) continue;
1019
- backfillResult(
1020
- state.toolSpanByUseId.get(block.tool_use_id),
1021
- ts,
1022
- block.is_error === true,
1023
- block.content
1024
- );
1146
+ for (const result of event.results) {
1147
+ backfillResult(state.toolSpanByUseId.get(result.toolUseId), event.timestamp, result);
1025
1148
  }
1026
- } else if (ev.type === "attachment" && ev.attachment?.type === "tool_result" && ev.attachment.toolUseID) {
1027
- const err = typeof ev.attachment.exitCode === "number" && ev.attachment.exitCode !== 0;
1028
- backfillResult(state.toolSpanByUseId.get(ev.attachment.toolUseID), ts, err, ev.attachment.stderr ?? "");
1149
+ } else if (event.kind === "attachment") {
1150
+ backfillResult(state.toolSpanByUseId.get(event.result.toolUseId), event.timestamp, event.result);
1029
1151
  }
1030
1152
  }
1153
+ function maxCapturedUsage(current, next) {
1154
+ const captured = typeof current === "number" && Number.isFinite(current) ? current : void 0;
1155
+ if (next == null) return captured;
1156
+ return captured === void 0 ? next : Math.max(captured, next);
1157
+ }
1158
+ function includeSpanTimestamp(target, timestamp) {
1159
+ if (!timestamp) return;
1160
+ const time = Date.parse(timestamp);
1161
+ if (!Number.isFinite(time)) return;
1162
+ const start = Date.parse(target.start_time);
1163
+ const end = Date.parse(target.end_time);
1164
+ if (!Number.isFinite(start) || time < start) target.start_time = timestamp;
1165
+ if (!Number.isFinite(end) || time > end) target.end_time = timestamp;
1166
+ }
1167
+ function mergeMessageContent(llmSpan, messageId, content, state) {
1168
+ if (!content) return;
1169
+ const parts = state.llmContentByMessageId.get(messageId) ?? /* @__PURE__ */ new Set();
1170
+ if (parts.has(content)) return;
1171
+ parts.add(content);
1172
+ state.llmContentByMessageId.set(messageId, parts);
1173
+ llmSpan.attributes.content = capText([...parts].join("\n"));
1174
+ }
1031
1175
  function finishClaudeStream(state) {
1032
1176
  return { spans: state.spans, toolSpanByUseId: state.toolSpanByUseId, nextStep: state.step };
1033
1177
  }
1034
- function backfillResult(s, endTime, isError, output) {
1178
+ function setRootTimeBounds(root, spans) {
1179
+ let firstTimestamp;
1180
+ let lastTimestamp;
1181
+ for (const item of spans) {
1182
+ if (item === root) continue;
1183
+ for (const source of [item.start_time, item.end_time]) {
1184
+ if (!source) continue;
1185
+ const value = Date.parse(source);
1186
+ if (!Number.isFinite(value)) continue;
1187
+ if (!firstTimestamp || value < firstTimestamp.value) firstTimestamp = { value, source };
1188
+ if (!lastTimestamp || value > lastTimestamp.value) lastTimestamp = { value, source };
1189
+ }
1190
+ }
1191
+ if (firstTimestamp) root.start_time = firstTimestamp.source;
1192
+ if (lastTimestamp) root.end_time = lastTimestamp.source;
1193
+ }
1194
+ function backfillResult(s, endTime, result) {
1035
1195
  if (!s) return;
1036
1196
  s.end_time = endTime;
1037
- const code = isError ? "ERROR" : "OK";
1197
+ const code = result.isError ? "ERROR" : "OK";
1038
1198
  s.status = { code };
1039
- recordToolOutput(s, output);
1040
- const message = stringifyToolResult(output);
1041
- if (isError && message) s.status.message = message.slice(0, 500);
1199
+ Object.assign(s.attributes, result.attributes);
1200
+ if (result.isError && result.message) s.status.message = result.message;
1201
+ }
1202
+ async function listSubagentFiles(root) {
1203
+ const pending = [root];
1204
+ const files = [];
1205
+ while (pending.length > 0) {
1206
+ const dir = pending.pop();
1207
+ if (!dir) continue;
1208
+ let entries;
1209
+ try {
1210
+ entries = await readdir3(dir, { withFileTypes: true });
1211
+ } catch (error) {
1212
+ if (isMissingPathError(error)) continue;
1213
+ throw error;
1214
+ }
1215
+ for (const entry of entries) {
1216
+ const path = join2(dir, entry.name);
1217
+ if (entry.isDirectory()) {
1218
+ pending.push(path);
1219
+ } else if (entry.isFile() && /^agent-.*\.jsonl$/.test(entry.name)) {
1220
+ files.push(path);
1221
+ }
1222
+ }
1223
+ }
1224
+ return files.sort();
1042
1225
  }
1043
1226
  var ClaudeAdapter = class {
1044
1227
  harness = "claude-code";
@@ -1096,17 +1279,10 @@ var ClaudeAdapter = class {
1096
1279
  };
1097
1280
  const state = createClaudeStream(ctx.startStep);
1098
1281
  let discoveredTraceId;
1099
- let firstTimestamp;
1100
- let lastTimestamp;
1101
- let sawEvent = false;
1282
+ const seen = /* @__PURE__ */ new Map();
1102
1283
  for await (const event of readJsonl(ref.path, sessionJsonlOptions(ref, options))) {
1103
- if (!sawEvent) {
1104
- firstTimestamp = event.timestamp;
1105
- sawEvent = true;
1106
- }
1107
- lastTimestamp = event.timestamp;
1108
1284
  if (!discoveredTraceId && event.sessionId) discoveredTraceId = event.sessionId;
1109
- consumeClaudeEvent(event, ctx, state);
1285
+ if (!consumeDistinctClaudeEvent(event, ctx, state, seen, ref.path)) continue;
1110
1286
  }
1111
1287
  const main = finishClaudeStream(state);
1112
1288
  const traceId = discoveredTraceId ?? sourceTraceId;
@@ -1115,63 +1291,75 @@ var ClaudeAdapter = class {
1115
1291
  item.trace_id = traceId;
1116
1292
  if (item.parent_span_id === sourceRootId) item.parent_span_id = rootId;
1117
1293
  }
1118
- const spans = [
1119
- span({
1120
- traceId,
1121
- spanId: rootId,
1122
- parentSpanId: null,
1123
- name: "session",
1124
- kind: "AGENT",
1125
- startTime: firstTimestamp ?? (/* @__PURE__ */ new Date(0)).toISOString(),
1126
- endTime: lastTimestamp,
1127
- service: SERVICE2,
1128
- agent: SERVICE2
1129
- })
1130
- ];
1294
+ const root = span({
1295
+ traceId,
1296
+ spanId: rootId,
1297
+ parentSpanId: null,
1298
+ name: "session",
1299
+ kind: "AGENT",
1300
+ startTime: (/* @__PURE__ */ new Date(0)).toISOString(),
1301
+ service: SERVICE2,
1302
+ agent: SERVICE2
1303
+ });
1304
+ const spans = [root];
1131
1305
  appendAll(spans, main.spans);
1132
1306
  await this.foldSubagents(ref, traceId, main, spans, options);
1307
+ setRootTimeBounds(root, spans);
1133
1308
  return spans;
1134
1309
  }
1135
- /** Parse `<session>/subagents/agent-*.jsonl`, parenting each under its Agent call. */
1310
+ /** Parse nested `subagents/.../agent-*.jsonl`, parenting each under its Agent call. */
1136
1311
  async foldSubagents(ref, traceId, main, out, options) {
1137
1312
  const subDir = join2(ref.path.replace(/\.jsonl$/, ""), "subagents");
1138
- let files;
1139
- try {
1140
- files = await readdir3(subDir);
1141
- } catch (error) {
1142
- if (isMissingPathError(error)) return;
1143
- throw error;
1144
- }
1313
+ const files = await listSubagentFiles(subDir);
1145
1314
  let step = main.nextStep;
1315
+ const parsedAgents = [];
1146
1316
  for (const file of files) {
1147
- if (!file.endsWith(".jsonl")) continue;
1148
- const hash2 = basename2(file, ".jsonl");
1149
- const metaPath = join2(subDir, `${hash2}.meta.json`);
1317
+ const sourceKey = relative(subDir, file).replace(/\.jsonl$/, "").split(/[\\/]/).map((segment) => encodeURIComponent(segment)).join(":");
1318
+ const metaPath = file.replace(/\.jsonl$/, ".meta.json");
1150
1319
  let meta = {};
1151
1320
  try {
1152
1321
  meta = await readJsonFile(metaPath);
1153
1322
  } catch (error) {
1154
1323
  if (!isMissingJsonSource(error)) throw error;
1155
1324
  }
1156
- const parent = meta.toolUseId && main.toolSpanByUseId.get(meta.toolUseId)?.span_id || `root:${traceId}`;
1157
1325
  const ctx = {
1158
1326
  traceId,
1159
1327
  agent: meta.agentType ? `subagent:${meta.agentType}` : "subagent",
1160
1328
  startStep: step,
1161
- idPrefix: `${hash2}:`,
1162
- rootParent: parent
1329
+ idPrefix: `${sourceKey}:`,
1330
+ rootParent: `root:${traceId}`
1163
1331
  };
1164
1332
  const state = createClaudeStream(ctx.startStep);
1333
+ const seen = /* @__PURE__ */ new Map();
1165
1334
  for await (const event of readJsonl(
1166
- join2(subDir, file),
1335
+ file,
1167
1336
  sessionJsonlOptions(ref, options)
1168
1337
  )) {
1169
- consumeClaudeEvent(event, ctx, state);
1338
+ consumeDistinctClaudeEvent(event, ctx, state, seen, file);
1170
1339
  }
1171
1340
  const parsed = finishClaudeStream(state);
1172
- appendAll(out, parsed.spans);
1341
+ parsedAgents.push({
1342
+ agentId: basename2(file, ".jsonl").replace(/^agent-/, ""),
1343
+ meta,
1344
+ parsed
1345
+ });
1173
1346
  step = parsed.nextStep;
1174
1347
  }
1348
+ const byAgentId = /* @__PURE__ */ new Map();
1349
+ for (const agent of parsedAgents) {
1350
+ if (byAgentId.has(agent.agentId)) {
1351
+ throw new Error(`Duplicate Claude subagent id: ${agent.agentId}`);
1352
+ }
1353
+ byAgentId.set(agent.agentId, agent.parsed);
1354
+ }
1355
+ for (const agent of parsedAgents) {
1356
+ const parentTools = agent.meta.parentAgentId ? byAgentId.get(agent.meta.parentAgentId)?.toolSpanByUseId : main.toolSpanByUseId;
1357
+ const parent = agent.meta.toolUseId && parentTools?.get(agent.meta.toolUseId)?.span_id || `root:${traceId}`;
1358
+ for (const item of agent.parsed.spans) {
1359
+ if (item.parent_span_id === `root:${traceId}`) item.parent_span_id = parent;
1360
+ }
1361
+ appendAll(out, agent.parsed.spans);
1362
+ }
1175
1363
  }
1176
1364
  };
1177
1365
 
@@ -3483,22 +3671,22 @@ async function applyRedactor(spans, redactor) {
3483
3671
  }
3484
3672
 
3485
3673
  // src/file-export.ts
3486
- import { createHash as createHash4 } from "crypto";
3674
+ import { createHash as createHash5 } from "crypto";
3487
3675
  import { mkdtemp as mkdtemp2, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
3488
3676
  import { tmpdir as tmpdir2 } from "os";
3489
3677
  import { join as join13 } from "path";
3490
3678
  import {
3491
3679
  firstNumberAttr,
3492
3680
  LLM_CACHED_TOKEN_ATTR_KEYS,
3493
- LLM_CACHED_TOKENS,
3681
+ LLM_CACHED_TOKENS as LLM_CACHED_TOKENS2,
3494
3682
  LLM_CACHE_WRITE_TOKEN_ATTR_KEYS,
3495
- LLM_CACHE_WRITE_TOKENS,
3683
+ LLM_CACHE_WRITE_TOKENS as LLM_CACHE_WRITE_TOKENS2,
3496
3684
  LLM_COST_ATTR_KEYS,
3497
3685
  LLM_COST_USD,
3498
3686
  LLM_INPUT_TOKEN_ATTR_KEYS,
3499
- LLM_INPUT_TOKENS,
3687
+ LLM_INPUT_TOKENS as LLM_INPUT_TOKENS2,
3500
3688
  LLM_OUTPUT_TOKEN_ATTR_KEYS,
3501
- LLM_OUTPUT_TOKENS,
3689
+ LLM_OUTPUT_TOKENS as LLM_OUTPUT_TOKENS2,
3502
3690
  LLM_REASONING_TOKEN_ATTR_KEYS,
3503
3691
  LLM_REASONING_TOKENS
3504
3692
  } from "@tangle-network/agent-eval/trace-attributes";
@@ -3521,7 +3709,7 @@ function stableJson(value) {
3521
3709
  });
3522
3710
  }
3523
3711
  function hashId(value, chars = 16) {
3524
- return createHash4("sha256").update(stableJson(value)).digest("hex").slice(0, chars);
3712
+ return createHash5("sha256").update(stableJson(value)).digest("hex").slice(0, chars);
3525
3713
  }
3526
3714
  function isoTime(value) {
3527
3715
  if (typeof value === "string") {
@@ -3887,11 +4075,11 @@ var COMPUTED_SPAN_ATTRIBUTES = /* @__PURE__ */ new Set([
3887
4075
  "tool.name",
3888
4076
  "llm.input_tokens",
3889
4077
  "llm.output_tokens",
3890
- LLM_INPUT_TOKENS,
3891
- LLM_OUTPUT_TOKENS,
4078
+ LLM_INPUT_TOKENS2,
4079
+ LLM_OUTPUT_TOKENS2,
3892
4080
  LLM_REASONING_TOKENS,
3893
- LLM_CACHED_TOKENS,
3894
- LLM_CACHE_WRITE_TOKENS,
4081
+ LLM_CACHED_TOKENS2,
4082
+ LLM_CACHE_WRITE_TOKENS2,
3895
4083
  LLM_COST_USD,
3896
4084
  "step",
3897
4085
  "content"
@@ -5760,7 +5948,7 @@ async function saveReport(path, report) {
5760
5948
  }
5761
5949
 
5762
5950
  // src/live.ts
5763
- import { createHash as createHash5 } from "crypto";
5951
+ import { createHash as createHash6 } from "crypto";
5764
5952
  var VERIFY_RE = /\b(test|tests|vitest|jest|pytest|go test|cargo test|tsc|typecheck|lint|biome|eslint|build|check:invariants|preflight)\b/i;
5765
5953
  var CHANGE_RE = /\b(apply_patch|edit|write|create|delete|rename|replace|update|git apply|sed -i|perl -pi|tee\s|cat\s+>)/i;
5766
5954
  var CLAIM_DONE_RE = /\b(done|fixed|complete|completed|implemented|merged|verified|all green|tests pass|ready|works now)\b/i;
@@ -5780,7 +5968,7 @@ var sleep = (ms, signal) => new Promise((resolve2) => {
5780
5968
  signal?.addEventListener("abort", onAbort, { once: true });
5781
5969
  });
5782
5970
  function hash(parts) {
5783
- return createHash5("sha256").update(parts.join("\0")).digest("hex").slice(0, 16);
5971
+ return createHash6("sha256").update(parts.join("\0")).digest("hex").slice(0, 16);
5784
5972
  }
5785
5973
  function asString(value) {
5786
5974
  if (typeof value === "string" && value.trim()) return value;
package/dist/cli.js CHANGED
@@ -40,7 +40,7 @@ import {
40
40
  writePolicyEvidenceFile,
41
41
  writeSessionIndexFile,
42
42
  writeTraceEvidenceExportFile
43
- } from "./chunk-S4TEBT7Q.js";
43
+ } from "./chunk-NHXPTFKC.js";
44
44
 
45
45
  // src/cli.ts
46
46
  import { readFileSync } from "fs";
package/dist/index.d.ts CHANGED
@@ -399,7 +399,7 @@ declare function stampSpanWorkdirRepoAttrs(spans: readonly OtlpSpan[]): Promise<
399
399
  * (token trajectory) and `message.content[]` tool_use blocks; tool results
400
400
  * arrive as `tool_result` blocks in the following user message or as a
401
401
  * `tool_result` attachment. Subagent runs live in a sibling
402
- * `<session>/subagents/agent-*.jsonl` with a `.meta.json` carrying the
402
+ * nested `<session>/subagents/.../agent-*.jsonl` files with a `.meta.json` carrying the
403
403
  * spawning `toolUseId`, so we parent each subagent under its `Agent` call.
404
404
  *
405
405
  * Shared by the claudish / openclaw / nanoclaw forks via aliases — they
@@ -412,7 +412,7 @@ declare class ClaudeAdapter implements HarnessTraceAdapter {
412
412
  private root;
413
413
  locate(opts?: LocateOptions): Promise<SessionRef[]>;
414
414
  parse(ref: SessionRef, options?: ParseOptions): Promise<OtlpSpan[]>;
415
- /** Parse `<session>/subagents/agent-*.jsonl`, parenting each under its Agent call. */
415
+ /** Parse nested `subagents/.../agent-*.jsonl`, parenting each under its Agent call. */
416
416
  private foldSubagents;
417
417
  }
418
418
 
package/dist/index.js CHANGED
@@ -117,7 +117,7 @@ import {
117
117
  writeSessionIndexFile,
118
118
  writeTraceEvidenceExportFile,
119
119
  writeTraceImprovementArtifacts
120
- } from "./chunk-S4TEBT7Q.js";
120
+ } from "./chunk-NHXPTFKC.js";
121
121
 
122
122
  // src/collect.ts
123
123
  async function collectSessions(opts = {}) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/traces",
3
- "version": "0.9.5",
3
+ "version": "0.9.7",
4
4
  "description": "Point it at your coding-agent session traces (Claude Code, Codex, OpenCode, Gemini, Pi, …) and get failure-mode + efficiency findings. CLI + SDK over the @tangle-network/agent-eval analyst suite — observe live sessions, run your own analysts, redact, and upload to the Tangle Intelligence Platform.",
5
5
  "type": "module",
6
6
  "license": "MIT",