@tangle-network/traces 0.9.5 → 0.9.6
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-S4TEBT7Q.js → chunk-NP3ZIUR2.js} +153 -69
- package/dist/cli.js +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
|
@@ -920,10 +920,12 @@ 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
926
|
import { basename as basename2, join as join2 } from "path";
|
|
926
927
|
var SERVICE2 = "claude-code";
|
|
928
|
+
var EPOCH = (/* @__PURE__ */ new Date(0)).toISOString();
|
|
927
929
|
function asBlocks(content) {
|
|
928
930
|
return Array.isArray(content) ? content : [];
|
|
929
931
|
}
|
|
@@ -943,10 +945,93 @@ function stringifyToolResult(content) {
|
|
|
943
945
|
function createClaudeStream(startStep) {
|
|
944
946
|
return { spans: [], toolSpanByUseId: /* @__PURE__ */ new Map(), step: startStep, sawUserTurn: false };
|
|
945
947
|
}
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
948
|
+
var ClaudeEventConflictError = class extends Error {
|
|
949
|
+
sourcePath;
|
|
950
|
+
eventId;
|
|
951
|
+
constructor(sourcePath, eventId) {
|
|
952
|
+
super(`${sourcePath}: Claude transcript event ${JSON.stringify(eventId)} has conflicting payloads`);
|
|
953
|
+
this.name = "ClaudeEventConflictError";
|
|
954
|
+
this.sourcePath = sourcePath;
|
|
955
|
+
this.eventId = eventId;
|
|
956
|
+
}
|
|
957
|
+
};
|
|
958
|
+
function projectToolResult(toolUseId, isError, output) {
|
|
959
|
+
return {
|
|
960
|
+
toolUseId,
|
|
961
|
+
isError,
|
|
962
|
+
attributes: toolIoAttributes({ output }),
|
|
963
|
+
message: stringifyToolResult(output).slice(0, 500)
|
|
964
|
+
};
|
|
965
|
+
}
|
|
966
|
+
function projectClaudeEvent(event) {
|
|
967
|
+
const timestamp = event.timestamp ?? EPOCH;
|
|
968
|
+
if (event.type === "assistant" && event.message) {
|
|
969
|
+
const tools = [];
|
|
970
|
+
for (const block of asBlocks(event.message.content)) {
|
|
971
|
+
if (block.type !== "tool_use" || !block.name) continue;
|
|
972
|
+
tools.push({ id: block.id || null, name: block.name, attributes: toolIoAttributes({ input: block.input }) });
|
|
973
|
+
}
|
|
974
|
+
return {
|
|
975
|
+
kind: "assistant",
|
|
976
|
+
timestamp,
|
|
977
|
+
model: event.message.model ?? null,
|
|
978
|
+
inputTokens: event.message.usage?.input_tokens ?? null,
|
|
979
|
+
outputTokens: event.message.usage?.output_tokens ?? null,
|
|
980
|
+
cachedInputTokens: event.message.usage?.cache_read_input_tokens ?? null,
|
|
981
|
+
cacheWriteInputTokens: event.message.usage?.cache_creation_input_tokens ?? null,
|
|
982
|
+
content: textOf2(event.message.content) || null,
|
|
983
|
+
tools
|
|
984
|
+
};
|
|
985
|
+
}
|
|
986
|
+
if (event.type === "user" && event.message) {
|
|
987
|
+
const results = [];
|
|
988
|
+
for (const block of asBlocks(event.message.content)) {
|
|
989
|
+
if (block.type !== "tool_result" || !block.tool_use_id) continue;
|
|
990
|
+
results.push(projectToolResult(block.tool_use_id, block.is_error === true, block.content));
|
|
991
|
+
}
|
|
992
|
+
const prompt = textOf2(event.message.content);
|
|
993
|
+
return {
|
|
994
|
+
kind: "user",
|
|
995
|
+
timestamp,
|
|
996
|
+
prompt: prompt || null,
|
|
997
|
+
...prompt ? { isSidechain: event.isSidechain === true, userType: event.userType ?? null } : {},
|
|
998
|
+
results
|
|
999
|
+
};
|
|
1000
|
+
}
|
|
1001
|
+
if (event.type === "attachment" && event.attachment?.type === "tool_result" && event.attachment.toolUseID) {
|
|
1002
|
+
return {
|
|
1003
|
+
kind: "attachment",
|
|
1004
|
+
timestamp,
|
|
1005
|
+
result: projectToolResult(
|
|
1006
|
+
event.attachment.toolUseID,
|
|
1007
|
+
typeof event.attachment.exitCode === "number" && event.attachment.exitCode !== 0,
|
|
1008
|
+
event.attachment.stderr ?? ""
|
|
1009
|
+
)
|
|
1010
|
+
};
|
|
1011
|
+
}
|
|
1012
|
+
return { kind: "ignored" };
|
|
1013
|
+
}
|
|
1014
|
+
function fingerprintClaudeEvent(event) {
|
|
1015
|
+
return createHash4("sha256").update(JSON.stringify(event)).digest("hex");
|
|
1016
|
+
}
|
|
1017
|
+
function consumeDistinctClaudeEvent(event, ctx, state, seen, sourcePath) {
|
|
1018
|
+
const projection = projectClaudeEvent(event);
|
|
1019
|
+
if (event.uuid) {
|
|
1020
|
+
const fingerprint = fingerprintClaudeEvent(projection);
|
|
1021
|
+
const previous = seen.get(event.uuid);
|
|
1022
|
+
if (previous !== void 0) {
|
|
1023
|
+
if (previous !== fingerprint) {
|
|
1024
|
+
throw new ClaudeEventConflictError(sourcePath, event.uuid);
|
|
1025
|
+
}
|
|
1026
|
+
return false;
|
|
1027
|
+
}
|
|
1028
|
+
seen.set(event.uuid, fingerprint);
|
|
1029
|
+
}
|
|
1030
|
+
consumeClaudeEvent(projection, event.uuid ?? `${ctx.idPrefix}step${state.step}`, ctx, state);
|
|
1031
|
+
return true;
|
|
1032
|
+
}
|
|
1033
|
+
function consumeClaudeEvent(event, uid, ctx, state) {
|
|
1034
|
+
if (event.kind === "assistant") {
|
|
950
1035
|
const llmId = `${ctx.idPrefix}${uid}`;
|
|
951
1036
|
state.spans.push(
|
|
952
1037
|
span({
|
|
@@ -955,47 +1040,45 @@ function consumeClaudeEvent(ev, ctx, state) {
|
|
|
955
1040
|
parentSpanId: ctx.rootParent,
|
|
956
1041
|
name: "llm.turn",
|
|
957
1042
|
kind: "LLM",
|
|
958
|
-
startTime:
|
|
1043
|
+
startTime: event.timestamp,
|
|
959
1044
|
service: SERVICE2,
|
|
960
1045
|
agent: ctx.agent,
|
|
961
|
-
model:
|
|
962
|
-
inputTokens:
|
|
963
|
-
outputTokens:
|
|
964
|
-
cachedInputTokens:
|
|
965
|
-
cacheWriteInputTokens:
|
|
1046
|
+
model: event.model,
|
|
1047
|
+
inputTokens: event.inputTokens,
|
|
1048
|
+
outputTokens: event.outputTokens,
|
|
1049
|
+
cachedInputTokens: event.cachedInputTokens,
|
|
1050
|
+
cacheWriteInputTokens: event.cacheWriteInputTokens,
|
|
966
1051
|
step: state.step,
|
|
967
|
-
content:
|
|
1052
|
+
content: event.content
|
|
968
1053
|
})
|
|
969
1054
|
);
|
|
970
1055
|
state.step += 1;
|
|
971
1056
|
let toolIdx = 0;
|
|
972
|
-
for (const
|
|
973
|
-
if (block.type !== "tool_use" || !block.name) continue;
|
|
1057
|
+
for (const tool of event.tools) {
|
|
974
1058
|
const toolSpan = span({
|
|
975
1059
|
traceId: ctx.traceId,
|
|
976
1060
|
spanId: `${ctx.idPrefix}${uid}:tool:${toolIdx}`,
|
|
977
1061
|
parentSpanId: llmId,
|
|
978
|
-
name: `tool.${
|
|
1062
|
+
name: `tool.${tool.name}`,
|
|
979
1063
|
kind: "TOOL",
|
|
980
|
-
startTime:
|
|
1064
|
+
startTime: event.timestamp,
|
|
981
1065
|
service: SERVICE2,
|
|
982
1066
|
agent: ctx.agent,
|
|
983
|
-
tool:
|
|
1067
|
+
tool: tool.name,
|
|
984
1068
|
step: state.step,
|
|
985
|
-
extra:
|
|
1069
|
+
extra: tool.attributes
|
|
986
1070
|
});
|
|
987
1071
|
state.spans.push(toolSpan);
|
|
988
|
-
if (
|
|
1072
|
+
if (tool.id) state.toolSpanByUseId.set(tool.id, toolSpan);
|
|
989
1073
|
toolIdx += 1;
|
|
990
1074
|
state.step += 1;
|
|
991
1075
|
}
|
|
992
|
-
} else if (
|
|
993
|
-
|
|
994
|
-
if (prompt) {
|
|
1076
|
+
} else if (event.kind === "user") {
|
|
1077
|
+
if (event.prompt) {
|
|
995
1078
|
const actor = claudeActor({
|
|
996
|
-
text: prompt,
|
|
997
|
-
isSidechain:
|
|
998
|
-
userType:
|
|
1079
|
+
text: event.prompt,
|
|
1080
|
+
isSidechain: event.isSidechain,
|
|
1081
|
+
userType: event.userType ?? null,
|
|
999
1082
|
isFirstUserTurn: !state.sawUserTurn
|
|
1000
1083
|
});
|
|
1001
1084
|
state.sawUserTurn = true;
|
|
@@ -1004,41 +1087,49 @@ function consumeClaudeEvent(ev, ctx, state) {
|
|
|
1004
1087
|
traceId: ctx.traceId,
|
|
1005
1088
|
spanId: `${ctx.idPrefix}${uid}:user`,
|
|
1006
1089
|
parentSpanId: ctx.rootParent,
|
|
1007
|
-
startTime:
|
|
1090
|
+
startTime: event.timestamp,
|
|
1008
1091
|
service: SERVICE2,
|
|
1009
1092
|
agent: ctx.agent,
|
|
1010
1093
|
step: state.step,
|
|
1011
|
-
content: prompt,
|
|
1094
|
+
content: event.prompt,
|
|
1012
1095
|
actor
|
|
1013
1096
|
})
|
|
1014
1097
|
);
|
|
1015
1098
|
state.step += 1;
|
|
1016
1099
|
}
|
|
1017
|
-
for (const
|
|
1018
|
-
|
|
1019
|
-
backfillResult(
|
|
1020
|
-
state.toolSpanByUseId.get(block.tool_use_id),
|
|
1021
|
-
ts,
|
|
1022
|
-
block.is_error === true,
|
|
1023
|
-
block.content
|
|
1024
|
-
);
|
|
1100
|
+
for (const result of event.results) {
|
|
1101
|
+
backfillResult(state.toolSpanByUseId.get(result.toolUseId), event.timestamp, result);
|
|
1025
1102
|
}
|
|
1026
|
-
} else if (
|
|
1027
|
-
|
|
1028
|
-
backfillResult(state.toolSpanByUseId.get(ev.attachment.toolUseID), ts, err, ev.attachment.stderr ?? "");
|
|
1103
|
+
} else if (event.kind === "attachment") {
|
|
1104
|
+
backfillResult(state.toolSpanByUseId.get(event.result.toolUseId), event.timestamp, event.result);
|
|
1029
1105
|
}
|
|
1030
1106
|
}
|
|
1031
1107
|
function finishClaudeStream(state) {
|
|
1032
1108
|
return { spans: state.spans, toolSpanByUseId: state.toolSpanByUseId, nextStep: state.step };
|
|
1033
1109
|
}
|
|
1034
|
-
function
|
|
1110
|
+
function setRootTimeBounds(root, spans) {
|
|
1111
|
+
let firstTimestamp;
|
|
1112
|
+
let lastTimestamp;
|
|
1113
|
+
for (const item of spans) {
|
|
1114
|
+
if (item === root) continue;
|
|
1115
|
+
for (const source of [item.start_time, item.end_time]) {
|
|
1116
|
+
if (!source) continue;
|
|
1117
|
+
const value = Date.parse(source);
|
|
1118
|
+
if (!Number.isFinite(value)) continue;
|
|
1119
|
+
if (!firstTimestamp || value < firstTimestamp.value) firstTimestamp = { value, source };
|
|
1120
|
+
if (!lastTimestamp || value > lastTimestamp.value) lastTimestamp = { value, source };
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
if (firstTimestamp) root.start_time = firstTimestamp.source;
|
|
1124
|
+
if (lastTimestamp) root.end_time = lastTimestamp.source;
|
|
1125
|
+
}
|
|
1126
|
+
function backfillResult(s, endTime, result) {
|
|
1035
1127
|
if (!s) return;
|
|
1036
1128
|
s.end_time = endTime;
|
|
1037
|
-
const code = isError ? "ERROR" : "OK";
|
|
1129
|
+
const code = result.isError ? "ERROR" : "OK";
|
|
1038
1130
|
s.status = { code };
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
if (isError && message) s.status.message = message.slice(0, 500);
|
|
1131
|
+
Object.assign(s.attributes, result.attributes);
|
|
1132
|
+
if (result.isError && result.message) s.status.message = result.message;
|
|
1042
1133
|
}
|
|
1043
1134
|
var ClaudeAdapter = class {
|
|
1044
1135
|
harness = "claude-code";
|
|
@@ -1096,17 +1187,10 @@ var ClaudeAdapter = class {
|
|
|
1096
1187
|
};
|
|
1097
1188
|
const state = createClaudeStream(ctx.startStep);
|
|
1098
1189
|
let discoveredTraceId;
|
|
1099
|
-
|
|
1100
|
-
let lastTimestamp;
|
|
1101
|
-
let sawEvent = false;
|
|
1190
|
+
const seen = /* @__PURE__ */ new Map();
|
|
1102
1191
|
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
1192
|
if (!discoveredTraceId && event.sessionId) discoveredTraceId = event.sessionId;
|
|
1109
|
-
|
|
1193
|
+
if (!consumeDistinctClaudeEvent(event, ctx, state, seen, ref.path)) continue;
|
|
1110
1194
|
}
|
|
1111
1195
|
const main = finishClaudeStream(state);
|
|
1112
1196
|
const traceId = discoveredTraceId ?? sourceTraceId;
|
|
@@ -1115,21 +1199,20 @@ var ClaudeAdapter = class {
|
|
|
1115
1199
|
item.trace_id = traceId;
|
|
1116
1200
|
if (item.parent_span_id === sourceRootId) item.parent_span_id = rootId;
|
|
1117
1201
|
}
|
|
1118
|
-
const
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
})
|
|
1130
|
-
];
|
|
1202
|
+
const root = span({
|
|
1203
|
+
traceId,
|
|
1204
|
+
spanId: rootId,
|
|
1205
|
+
parentSpanId: null,
|
|
1206
|
+
name: "session",
|
|
1207
|
+
kind: "AGENT",
|
|
1208
|
+
startTime: (/* @__PURE__ */ new Date(0)).toISOString(),
|
|
1209
|
+
service: SERVICE2,
|
|
1210
|
+
agent: SERVICE2
|
|
1211
|
+
});
|
|
1212
|
+
const spans = [root];
|
|
1131
1213
|
appendAll(spans, main.spans);
|
|
1132
1214
|
await this.foldSubagents(ref, traceId, main, spans, options);
|
|
1215
|
+
setRootTimeBounds(root, spans);
|
|
1133
1216
|
return spans;
|
|
1134
1217
|
}
|
|
1135
1218
|
/** Parse `<session>/subagents/agent-*.jsonl`, parenting each under its Agent call. */
|
|
@@ -1162,11 +1245,12 @@ var ClaudeAdapter = class {
|
|
|
1162
1245
|
rootParent: parent
|
|
1163
1246
|
};
|
|
1164
1247
|
const state = createClaudeStream(ctx.startStep);
|
|
1248
|
+
const seen = /* @__PURE__ */ new Map();
|
|
1165
1249
|
for await (const event of readJsonl(
|
|
1166
1250
|
join2(subDir, file),
|
|
1167
1251
|
sessionJsonlOptions(ref, options)
|
|
1168
1252
|
)) {
|
|
1169
|
-
|
|
1253
|
+
consumeDistinctClaudeEvent(event, ctx, state, seen, join2(subDir, file));
|
|
1170
1254
|
}
|
|
1171
1255
|
const parsed = finishClaudeStream(state);
|
|
1172
1256
|
appendAll(out, parsed.spans);
|
|
@@ -3483,7 +3567,7 @@ async function applyRedactor(spans, redactor) {
|
|
|
3483
3567
|
}
|
|
3484
3568
|
|
|
3485
3569
|
// src/file-export.ts
|
|
3486
|
-
import { createHash as
|
|
3570
|
+
import { createHash as createHash5 } from "crypto";
|
|
3487
3571
|
import { mkdtemp as mkdtemp2, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
|
|
3488
3572
|
import { tmpdir as tmpdir2 } from "os";
|
|
3489
3573
|
import { join as join13 } from "path";
|
|
@@ -3521,7 +3605,7 @@ function stableJson(value) {
|
|
|
3521
3605
|
});
|
|
3522
3606
|
}
|
|
3523
3607
|
function hashId(value, chars = 16) {
|
|
3524
|
-
return
|
|
3608
|
+
return createHash5("sha256").update(stableJson(value)).digest("hex").slice(0, chars);
|
|
3525
3609
|
}
|
|
3526
3610
|
function isoTime(value) {
|
|
3527
3611
|
if (typeof value === "string") {
|
|
@@ -5760,7 +5844,7 @@ async function saveReport(path, report) {
|
|
|
5760
5844
|
}
|
|
5761
5845
|
|
|
5762
5846
|
// src/live.ts
|
|
5763
|
-
import { createHash as
|
|
5847
|
+
import { createHash as createHash6 } from "crypto";
|
|
5764
5848
|
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
5849
|
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
5850
|
var CLAIM_DONE_RE = /\b(done|fixed|complete|completed|implemented|merged|verified|all green|tests pass|ready|works now)\b/i;
|
|
@@ -5780,7 +5864,7 @@ var sleep = (ms, signal) => new Promise((resolve2) => {
|
|
|
5780
5864
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
5781
5865
|
});
|
|
5782
5866
|
function hash(parts) {
|
|
5783
|
-
return
|
|
5867
|
+
return createHash6("sha256").update(parts.join("\0")).digest("hex").slice(0, 16);
|
|
5784
5868
|
}
|
|
5785
5869
|
function asString(value) {
|
|
5786
5870
|
if (typeof value === "string" && value.trim()) return value;
|
package/dist/cli.js
CHANGED
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.6",
|
|
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",
|