@tangle-network/traces 0.9.6 → 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.
- package/dist/{chunk-NP3ZIUR2.js → chunk-NHXPTFKC.js} +145 -41
- package/dist/cli.js +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/package.json +1 -1
|
@@ -923,7 +923,14 @@ var CORRECTIVE_REACTIONS = ["correction", "frustration", "jargon", "structure"];
|
|
|
923
923
|
import { createHash as createHash4 } from "crypto";
|
|
924
924
|
import { readdir as readdir3, stat as stat2 } from "fs/promises";
|
|
925
925
|
import { homedir as homedir2 } from "os";
|
|
926
|
-
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";
|
|
927
934
|
var SERVICE2 = "claude-code";
|
|
928
935
|
var EPOCH = (/* @__PURE__ */ new Date(0)).toISOString();
|
|
929
936
|
function asBlocks(content) {
|
|
@@ -943,7 +950,15 @@ function stringifyToolResult(content) {
|
|
|
943
950
|
return "";
|
|
944
951
|
}
|
|
945
952
|
function createClaudeStream(startStep) {
|
|
946
|
-
return {
|
|
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
|
+
};
|
|
947
962
|
}
|
|
948
963
|
var ClaudeEventConflictError = class extends Error {
|
|
949
964
|
sourcePath;
|
|
@@ -974,6 +989,7 @@ function projectClaudeEvent(event) {
|
|
|
974
989
|
return {
|
|
975
990
|
kind: "assistant",
|
|
976
991
|
timestamp,
|
|
992
|
+
messageId: event.message.id ?? null,
|
|
977
993
|
model: event.message.model ?? null,
|
|
978
994
|
inputTokens: event.message.usage?.input_tokens ?? null,
|
|
979
995
|
outputTokens: event.message.usage?.output_tokens ?? null,
|
|
@@ -1027,16 +1043,17 @@ function consumeDistinctClaudeEvent(event, ctx, state, seen, sourcePath) {
|
|
|
1027
1043
|
}
|
|
1028
1044
|
seen.set(event.uuid, fingerprint);
|
|
1029
1045
|
}
|
|
1030
|
-
consumeClaudeEvent(projection, event.uuid ??
|
|
1046
|
+
consumeClaudeEvent(projection, event.uuid ?? `step${state.step}`, ctx, state);
|
|
1031
1047
|
return true;
|
|
1032
1048
|
}
|
|
1033
1049
|
function consumeClaudeEvent(event, uid, ctx, state) {
|
|
1034
1050
|
if (event.kind === "assistant") {
|
|
1035
|
-
const
|
|
1036
|
-
state.
|
|
1037
|
-
|
|
1051
|
+
const messageId = event.messageId ?? uid;
|
|
1052
|
+
let llmSpan = state.llmSpanByMessageId.get(messageId);
|
|
1053
|
+
if (!llmSpan) {
|
|
1054
|
+
llmSpan = span({
|
|
1038
1055
|
traceId: ctx.traceId,
|
|
1039
|
-
spanId:
|
|
1056
|
+
spanId: `${ctx.idPrefix}${uid}`,
|
|
1040
1057
|
parentSpanId: ctx.rootParent,
|
|
1041
1058
|
name: "llm.turn",
|
|
1042
1059
|
kind: "LLM",
|
|
@@ -1048,17 +1065,46 @@ function consumeClaudeEvent(event, uid, ctx, state) {
|
|
|
1048
1065
|
outputTokens: event.outputTokens,
|
|
1049
1066
|
cachedInputTokens: event.cachedInputTokens,
|
|
1050
1067
|
cacheWriteInputTokens: event.cacheWriteInputTokens,
|
|
1051
|
-
step: state.step
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
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);
|
|
1057
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;
|
|
1058
1104
|
const toolSpan = span({
|
|
1059
1105
|
traceId: ctx.traceId,
|
|
1060
|
-
spanId: `${
|
|
1061
|
-
parentSpanId:
|
|
1106
|
+
spanId: `${llmSpan.span_id}:tool:${toolIdx}`,
|
|
1107
|
+
parentSpanId: llmSpan.span_id,
|
|
1062
1108
|
name: `tool.${tool.name}`,
|
|
1063
1109
|
kind: "TOOL",
|
|
1064
1110
|
startTime: event.timestamp,
|
|
@@ -1070,7 +1116,7 @@ function consumeClaudeEvent(event, uid, ctx, state) {
|
|
|
1070
1116
|
});
|
|
1071
1117
|
state.spans.push(toolSpan);
|
|
1072
1118
|
if (tool.id) state.toolSpanByUseId.set(tool.id, toolSpan);
|
|
1073
|
-
toolIdx
|
|
1119
|
+
state.toolCountByMessageId.set(messageId, toolIdx + 1);
|
|
1074
1120
|
state.step += 1;
|
|
1075
1121
|
}
|
|
1076
1122
|
} else if (event.kind === "user") {
|
|
@@ -1104,6 +1150,28 @@ function consumeClaudeEvent(event, uid, ctx, state) {
|
|
|
1104
1150
|
backfillResult(state.toolSpanByUseId.get(event.result.toolUseId), event.timestamp, event.result);
|
|
1105
1151
|
}
|
|
1106
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
|
+
}
|
|
1107
1175
|
function finishClaudeStream(state) {
|
|
1108
1176
|
return { spans: state.spans, toolSpanByUseId: state.toolSpanByUseId, nextStep: state.step };
|
|
1109
1177
|
}
|
|
@@ -1131,6 +1199,30 @@ function backfillResult(s, endTime, result) {
|
|
|
1131
1199
|
Object.assign(s.attributes, result.attributes);
|
|
1132
1200
|
if (result.isError && result.message) s.status.message = result.message;
|
|
1133
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();
|
|
1225
|
+
}
|
|
1134
1226
|
var ClaudeAdapter = class {
|
|
1135
1227
|
harness = "claude-code";
|
|
1136
1228
|
aliases = ["claude", "claudish", "openclaw", "nanoclaw"];
|
|
@@ -1215,47 +1307,59 @@ var ClaudeAdapter = class {
|
|
|
1215
1307
|
setRootTimeBounds(root, spans);
|
|
1216
1308
|
return spans;
|
|
1217
1309
|
}
|
|
1218
|
-
/** Parse
|
|
1310
|
+
/** Parse nested `subagents/.../agent-*.jsonl`, parenting each under its Agent call. */
|
|
1219
1311
|
async foldSubagents(ref, traceId, main, out, options) {
|
|
1220
1312
|
const subDir = join2(ref.path.replace(/\.jsonl$/, ""), "subagents");
|
|
1221
|
-
|
|
1222
|
-
try {
|
|
1223
|
-
files = await readdir3(subDir);
|
|
1224
|
-
} catch (error) {
|
|
1225
|
-
if (isMissingPathError(error)) return;
|
|
1226
|
-
throw error;
|
|
1227
|
-
}
|
|
1313
|
+
const files = await listSubagentFiles(subDir);
|
|
1228
1314
|
let step = main.nextStep;
|
|
1315
|
+
const parsedAgents = [];
|
|
1229
1316
|
for (const file of files) {
|
|
1230
|
-
|
|
1231
|
-
const
|
|
1232
|
-
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");
|
|
1233
1319
|
let meta = {};
|
|
1234
1320
|
try {
|
|
1235
1321
|
meta = await readJsonFile(metaPath);
|
|
1236
1322
|
} catch (error) {
|
|
1237
1323
|
if (!isMissingJsonSource(error)) throw error;
|
|
1238
1324
|
}
|
|
1239
|
-
const parent = meta.toolUseId && main.toolSpanByUseId.get(meta.toolUseId)?.span_id || `root:${traceId}`;
|
|
1240
1325
|
const ctx = {
|
|
1241
1326
|
traceId,
|
|
1242
1327
|
agent: meta.agentType ? `subagent:${meta.agentType}` : "subagent",
|
|
1243
1328
|
startStep: step,
|
|
1244
|
-
idPrefix: `${
|
|
1245
|
-
rootParent:
|
|
1329
|
+
idPrefix: `${sourceKey}:`,
|
|
1330
|
+
rootParent: `root:${traceId}`
|
|
1246
1331
|
};
|
|
1247
1332
|
const state = createClaudeStream(ctx.startStep);
|
|
1248
1333
|
const seen = /* @__PURE__ */ new Map();
|
|
1249
1334
|
for await (const event of readJsonl(
|
|
1250
|
-
|
|
1335
|
+
file,
|
|
1251
1336
|
sessionJsonlOptions(ref, options)
|
|
1252
1337
|
)) {
|
|
1253
|
-
consumeDistinctClaudeEvent(event, ctx, state, seen,
|
|
1338
|
+
consumeDistinctClaudeEvent(event, ctx, state, seen, file);
|
|
1254
1339
|
}
|
|
1255
1340
|
const parsed = finishClaudeStream(state);
|
|
1256
|
-
|
|
1341
|
+
parsedAgents.push({
|
|
1342
|
+
agentId: basename2(file, ".jsonl").replace(/^agent-/, ""),
|
|
1343
|
+
meta,
|
|
1344
|
+
parsed
|
|
1345
|
+
});
|
|
1257
1346
|
step = parsed.nextStep;
|
|
1258
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
|
+
}
|
|
1259
1363
|
}
|
|
1260
1364
|
};
|
|
1261
1365
|
|
|
@@ -3574,15 +3678,15 @@ import { join as join13 } from "path";
|
|
|
3574
3678
|
import {
|
|
3575
3679
|
firstNumberAttr,
|
|
3576
3680
|
LLM_CACHED_TOKEN_ATTR_KEYS,
|
|
3577
|
-
LLM_CACHED_TOKENS,
|
|
3681
|
+
LLM_CACHED_TOKENS as LLM_CACHED_TOKENS2,
|
|
3578
3682
|
LLM_CACHE_WRITE_TOKEN_ATTR_KEYS,
|
|
3579
|
-
LLM_CACHE_WRITE_TOKENS,
|
|
3683
|
+
LLM_CACHE_WRITE_TOKENS as LLM_CACHE_WRITE_TOKENS2,
|
|
3580
3684
|
LLM_COST_ATTR_KEYS,
|
|
3581
3685
|
LLM_COST_USD,
|
|
3582
3686
|
LLM_INPUT_TOKEN_ATTR_KEYS,
|
|
3583
|
-
LLM_INPUT_TOKENS,
|
|
3687
|
+
LLM_INPUT_TOKENS as LLM_INPUT_TOKENS2,
|
|
3584
3688
|
LLM_OUTPUT_TOKEN_ATTR_KEYS,
|
|
3585
|
-
LLM_OUTPUT_TOKENS,
|
|
3689
|
+
LLM_OUTPUT_TOKENS as LLM_OUTPUT_TOKENS2,
|
|
3586
3690
|
LLM_REASONING_TOKEN_ATTR_KEYS,
|
|
3587
3691
|
LLM_REASONING_TOKENS
|
|
3588
3692
|
} from "@tangle-network/agent-eval/trace-attributes";
|
|
@@ -3971,11 +4075,11 @@ var COMPUTED_SPAN_ATTRIBUTES = /* @__PURE__ */ new Set([
|
|
|
3971
4075
|
"tool.name",
|
|
3972
4076
|
"llm.input_tokens",
|
|
3973
4077
|
"llm.output_tokens",
|
|
3974
|
-
|
|
3975
|
-
|
|
4078
|
+
LLM_INPUT_TOKENS2,
|
|
4079
|
+
LLM_OUTPUT_TOKENS2,
|
|
3976
4080
|
LLM_REASONING_TOKENS,
|
|
3977
|
-
|
|
3978
|
-
|
|
4081
|
+
LLM_CACHED_TOKENS2,
|
|
4082
|
+
LLM_CACHE_WRITE_TOKENS2,
|
|
3979
4083
|
LLM_COST_USD,
|
|
3980
4084
|
"step",
|
|
3981
4085
|
"content"
|
package/dist/cli.js
CHANGED
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
|
|
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
|
|
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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tangle-network/traces",
|
|
3
|
-
"version": "0.9.
|
|
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",
|