@tangle-network/traces 0.9.6 → 0.9.8
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/README.md +5 -0
- package/dist/{chunk-NP3ZIUR2.js → chunk-ELWOJS65.js} +708 -158
- package/dist/cli.js +20 -6
- package/dist/index.d.ts +28 -3
- package/dist/index.js +5 -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
|
|
|
@@ -1335,8 +1439,8 @@ function explicitOutputError(value) {
|
|
|
1335
1439
|
const outputStart = text.indexOf("\nOutput:\n");
|
|
1336
1440
|
const header = outputStart >= 0 ? text.slice(0, outputStart) : text;
|
|
1337
1441
|
if (/^Chunk ID:/i.test(header)) {
|
|
1338
|
-
const
|
|
1339
|
-
if (
|
|
1442
|
+
const exitCode2 = header.match(/^Process exited with code\s+(-?\d+)\b/im)?.[1];
|
|
1443
|
+
if (exitCode2 !== void 0) return Number(exitCode2) !== 0;
|
|
1340
1444
|
}
|
|
1341
1445
|
if (/^Script completed\s*\nWall time:/i.test(header)) return false;
|
|
1342
1446
|
if (/^Script failed\s*\nWall time:/i.test(header)) return true;
|
|
@@ -1612,7 +1716,7 @@ var CodexAdapter = class {
|
|
|
1612
1716
|
} else if (l.type === "event_msg" && l.payload?.type === "sub_agent_activity") {
|
|
1613
1717
|
const threadId = l.payload.agent_thread_id;
|
|
1614
1718
|
const occurredAtMs = l.payload.occurred_at_ms;
|
|
1615
|
-
const
|
|
1719
|
+
const eventTime2 = typeof occurredAtMs === "number" && Number.isFinite(occurredAtMs) ? new Date(occurredAtMs).toISOString() : ts;
|
|
1616
1720
|
if (l.payload.kind === "started" && threadId && !subagentByThreadId.has(threadId)) {
|
|
1617
1721
|
const agentPath = l.payload.agent_path ?? "subagent";
|
|
1618
1722
|
const subagentType = agentPath.split("/").filter(Boolean).at(-1) ?? "subagent";
|
|
@@ -1622,7 +1726,7 @@ var CodexAdapter = class {
|
|
|
1622
1726
|
parentSpanId: lastLlm,
|
|
1623
1727
|
name: "tool.Agent",
|
|
1624
1728
|
kind: "TOOL",
|
|
1625
|
-
startTime:
|
|
1729
|
+
startTime: eventTime2,
|
|
1626
1730
|
service: SERVICE3,
|
|
1627
1731
|
agent: SERVICE3,
|
|
1628
1732
|
tool: "Agent",
|
|
@@ -1645,13 +1749,13 @@ var CodexAdapter = class {
|
|
|
1645
1749
|
} else if (l.payload.kind === "completed" && threadId) {
|
|
1646
1750
|
const toolSpan = subagentByThreadId.get(threadId);
|
|
1647
1751
|
if (toolSpan) {
|
|
1648
|
-
toolSpan.end_time =
|
|
1752
|
+
toolSpan.end_time = eventTime2;
|
|
1649
1753
|
toolSpan.status = { code: "OK" };
|
|
1650
1754
|
}
|
|
1651
1755
|
} else if (["interrupted", "failed", "timed_out"].includes(l.payload.kind ?? "") && threadId) {
|
|
1652
1756
|
const toolSpan = subagentByThreadId.get(threadId);
|
|
1653
1757
|
if (toolSpan) {
|
|
1654
|
-
toolSpan.end_time =
|
|
1758
|
+
toolSpan.end_time = eventTime2;
|
|
1655
1759
|
toolSpan.status = { code: "ERROR", message: `subagent ${l.payload.kind}` };
|
|
1656
1760
|
}
|
|
1657
1761
|
}
|
|
@@ -1701,11 +1805,449 @@ var CodexAdapter = class {
|
|
|
1701
1805
|
}
|
|
1702
1806
|
};
|
|
1703
1807
|
|
|
1808
|
+
// src/adapters/codex-exec.ts
|
|
1809
|
+
var SERVICE4 = "codex-exec";
|
|
1810
|
+
var SUPPORTED_EVENT_TYPES = /* @__PURE__ */ new Set([
|
|
1811
|
+
"thread.started",
|
|
1812
|
+
"turn.started",
|
|
1813
|
+
"item.started",
|
|
1814
|
+
"item.completed",
|
|
1815
|
+
"turn.completed",
|
|
1816
|
+
"turn.failed",
|
|
1817
|
+
"error"
|
|
1818
|
+
]);
|
|
1819
|
+
var CodexExecStreamError = class extends Error {
|
|
1820
|
+
sourcePath;
|
|
1821
|
+
constructor(sourcePath, message) {
|
|
1822
|
+
super(`Invalid Codex exec event stream at ${sourcePath}: ${message}`);
|
|
1823
|
+
this.name = "CodexExecStreamError";
|
|
1824
|
+
this.sourcePath = sourcePath;
|
|
1825
|
+
}
|
|
1826
|
+
};
|
|
1827
|
+
function objectValue(value) {
|
|
1828
|
+
return value != null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
1829
|
+
}
|
|
1830
|
+
function stringValue(value) {
|
|
1831
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
1832
|
+
}
|
|
1833
|
+
function isoTime(value) {
|
|
1834
|
+
if (typeof value === "string" && value.trim()) {
|
|
1835
|
+
const parsed = Date.parse(value);
|
|
1836
|
+
if (Number.isFinite(parsed)) return new Date(parsed).toISOString();
|
|
1837
|
+
}
|
|
1838
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
1839
|
+
const milliseconds = value < 1e10 ? value * 1e3 : value;
|
|
1840
|
+
return new Date(milliseconds).toISOString();
|
|
1841
|
+
}
|
|
1842
|
+
return void 0;
|
|
1843
|
+
}
|
|
1844
|
+
function eventTime(event, fallback) {
|
|
1845
|
+
for (const key of [
|
|
1846
|
+
"timestamp",
|
|
1847
|
+
"created_at",
|
|
1848
|
+
"started_at",
|
|
1849
|
+
"completed_at",
|
|
1850
|
+
"timestamp_ms",
|
|
1851
|
+
"started_at_ms",
|
|
1852
|
+
"completed_at_ms"
|
|
1853
|
+
]) {
|
|
1854
|
+
const time = isoTime(event[key]);
|
|
1855
|
+
if (time) return { time, source: "event" };
|
|
1856
|
+
}
|
|
1857
|
+
return { time: fallback, source: "file_mtime" };
|
|
1858
|
+
}
|
|
1859
|
+
function earlier(left, right) {
|
|
1860
|
+
return Date.parse(left) <= Date.parse(right) ? left : right;
|
|
1861
|
+
}
|
|
1862
|
+
function later(left, right) {
|
|
1863
|
+
return Date.parse(left) >= Date.parse(right) ? left : right;
|
|
1864
|
+
}
|
|
1865
|
+
function requireObject(value, sourcePath, label) {
|
|
1866
|
+
const object = objectValue(value);
|
|
1867
|
+
if (!object) throw new CodexExecStreamError(sourcePath, `${label} must be an object`);
|
|
1868
|
+
return object;
|
|
1869
|
+
}
|
|
1870
|
+
function requireString(value, sourcePath, label) {
|
|
1871
|
+
const string = stringValue(value);
|
|
1872
|
+
if (!string) throw new CodexExecStreamError(sourcePath, `${label} must be a non-empty string`);
|
|
1873
|
+
return string;
|
|
1874
|
+
}
|
|
1875
|
+
function optionalTokenCount(usage, key, sourcePath) {
|
|
1876
|
+
const value = usage?.[key];
|
|
1877
|
+
if (value === void 0) return void 0;
|
|
1878
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value < 0) {
|
|
1879
|
+
throw new CodexExecStreamError(sourcePath, `usage.${key} must be a non-negative integer`);
|
|
1880
|
+
}
|
|
1881
|
+
return value;
|
|
1882
|
+
}
|
|
1883
|
+
function exitCode(item, sourcePath) {
|
|
1884
|
+
const value = item.exit_code;
|
|
1885
|
+
if (value === void 0 || value === null) return void 0;
|
|
1886
|
+
if (typeof value !== "number" || !Number.isInteger(value)) {
|
|
1887
|
+
throw new CodexExecStreamError(sourcePath, "command_execution.exit_code must be an integer or null");
|
|
1888
|
+
}
|
|
1889
|
+
return value;
|
|
1890
|
+
}
|
|
1891
|
+
function fileChanges(item, sourcePath) {
|
|
1892
|
+
if (!Array.isArray(item.changes) || item.changes.length === 0) {
|
|
1893
|
+
throw new CodexExecStreamError(sourcePath, "file_change.changes must be a non-empty array");
|
|
1894
|
+
}
|
|
1895
|
+
return item.changes.map((raw, index) => {
|
|
1896
|
+
const change = requireObject(raw, sourcePath, `file_change.changes[${index}]`);
|
|
1897
|
+
return {
|
|
1898
|
+
path: requireString(change.path, sourcePath, `file_change.changes[${index}].path`),
|
|
1899
|
+
kind: requireString(change.kind, sourcePath, `file_change.changes[${index}].kind`)
|
|
1900
|
+
};
|
|
1901
|
+
});
|
|
1902
|
+
}
|
|
1903
|
+
function toolInput(itemType2, item, sourcePath) {
|
|
1904
|
+
if (itemType2 === "command_execution") {
|
|
1905
|
+
const command = requireString(item.command, sourcePath, "command_execution.command");
|
|
1906
|
+
const cwd = stringValue(item.cwd);
|
|
1907
|
+
return { cmd: command, ...cwd ? { cwd } : {} };
|
|
1908
|
+
}
|
|
1909
|
+
return { changes: fileChanges(item, sourcePath) };
|
|
1910
|
+
}
|
|
1911
|
+
function completedStatus(itemType2, item, sourcePath) {
|
|
1912
|
+
const status = requireString(item.status, sourcePath, `${itemType2}.status`);
|
|
1913
|
+
if (status !== "completed" && status !== "failed") {
|
|
1914
|
+
throw new CodexExecStreamError(
|
|
1915
|
+
sourcePath,
|
|
1916
|
+
`${itemType2}.status on item.completed must be "completed" or "failed"`
|
|
1917
|
+
);
|
|
1918
|
+
}
|
|
1919
|
+
const code = itemType2 === "command_execution" ? exitCode(item, sourcePath) : void 0;
|
|
1920
|
+
const failed = status === "failed" || code !== void 0 && code !== 0;
|
|
1921
|
+
return {
|
|
1922
|
+
code: failed ? "ERROR" : "OK",
|
|
1923
|
+
...failed ? { message: code === void 0 ? `${itemType2} failed` : `command exited ${code}` } : {},
|
|
1924
|
+
...code === void 0 ? {} : { exitCode: code }
|
|
1925
|
+
};
|
|
1926
|
+
}
|
|
1927
|
+
function itemType(item) {
|
|
1928
|
+
return stringValue(item.type);
|
|
1929
|
+
}
|
|
1930
|
+
function toolItemType(item) {
|
|
1931
|
+
const type = itemType(item);
|
|
1932
|
+
return type === "command_execution" || type === "file_change" ? type : void 0;
|
|
1933
|
+
}
|
|
1934
|
+
function toolName(type) {
|
|
1935
|
+
return type === "command_execution" ? "exec_command" : "apply_patch";
|
|
1936
|
+
}
|
|
1937
|
+
var CodexExecAdapter = class {
|
|
1938
|
+
harness = SERVICE4;
|
|
1939
|
+
aliases = ["codex-json"];
|
|
1940
|
+
async locate(_opts = {}) {
|
|
1941
|
+
return [];
|
|
1942
|
+
}
|
|
1943
|
+
async parse(ref, options = {}) {
|
|
1944
|
+
const fallbackTime = Number.isFinite(ref.mtimeMs) && ref.mtimeMs >= 0 ? new Date(ref.mtimeMs).toISOString() : (/* @__PURE__ */ new Date(0)).toISOString();
|
|
1945
|
+
const spans = [];
|
|
1946
|
+
let root;
|
|
1947
|
+
let threadId;
|
|
1948
|
+
let activeTurn;
|
|
1949
|
+
let turnIndex = 0;
|
|
1950
|
+
let step = 0;
|
|
1951
|
+
let recognizedEventCount = 0;
|
|
1952
|
+
let ignoredEventCount = 0;
|
|
1953
|
+
let ignoredItemCount = 0;
|
|
1954
|
+
let eventTimestampCount = 0;
|
|
1955
|
+
let terminalCount = 0;
|
|
1956
|
+
let fatalError = false;
|
|
1957
|
+
const fail = (message) => {
|
|
1958
|
+
throw new CodexExecStreamError(ref.path, message);
|
|
1959
|
+
};
|
|
1960
|
+
const touchRoot = (timed) => {
|
|
1961
|
+
if (!root) return;
|
|
1962
|
+
root.start_time = earlier(root.start_time, timed.time);
|
|
1963
|
+
root.end_time = later(root.end_time, timed.time);
|
|
1964
|
+
if (timed.source === "event") eventTimestampCount += 1;
|
|
1965
|
+
};
|
|
1966
|
+
const requireRoot = (type) => {
|
|
1967
|
+
const current = root;
|
|
1968
|
+
if (!current) throw new CodexExecStreamError(ref.path, `${type} appeared before thread.started`);
|
|
1969
|
+
return current;
|
|
1970
|
+
};
|
|
1971
|
+
const requireTurn = (type) => {
|
|
1972
|
+
requireRoot(type);
|
|
1973
|
+
const current = activeTurn;
|
|
1974
|
+
if (!current) throw new CodexExecStreamError(ref.path, `${type} appeared without an active turn.started`);
|
|
1975
|
+
return current;
|
|
1976
|
+
};
|
|
1977
|
+
const createTool = (turn, item, type, time, lifecycle) => {
|
|
1978
|
+
const id = requireString(item.id, ref.path, `${type}.id`);
|
|
1979
|
+
const name = toolName(type);
|
|
1980
|
+
const input = toolInput(type, item, ref.path);
|
|
1981
|
+
const cwd = stringValue(item.cwd);
|
|
1982
|
+
if (!ref.cwd && cwd) ref.cwd = cwd;
|
|
1983
|
+
const toolSpan = span({
|
|
1984
|
+
traceId: threadId,
|
|
1985
|
+
spanId: `tool:${turn.index}:${id}`,
|
|
1986
|
+
parentSpanId: turn.spanId,
|
|
1987
|
+
name: `tool.${name}`,
|
|
1988
|
+
kind: "TOOL",
|
|
1989
|
+
startTime: time,
|
|
1990
|
+
status: "UNSET",
|
|
1991
|
+
service: SERVICE4,
|
|
1992
|
+
agent: SERVICE4,
|
|
1993
|
+
tool: name,
|
|
1994
|
+
step: step++,
|
|
1995
|
+
extra: {
|
|
1996
|
+
...toolIoAttributes({ input, argsCaptured: true }),
|
|
1997
|
+
"traces.codex.exec_item_id": id,
|
|
1998
|
+
"traces.codex.exec_item_type": type,
|
|
1999
|
+
"traces.codex.exec_lifecycle": lifecycle
|
|
2000
|
+
}
|
|
2001
|
+
});
|
|
2002
|
+
spans.push(toolSpan);
|
|
2003
|
+
return { itemType: type, span: toolSpan };
|
|
2004
|
+
};
|
|
2005
|
+
const completeTool = (turn, item, time) => {
|
|
2006
|
+
const type = toolItemType(item);
|
|
2007
|
+
if (!type) throw new CodexExecStreamError(ref.path, "item.completed tool item has an unsupported type");
|
|
2008
|
+
const id = requireString(item.id, ref.path, `${type}.id`);
|
|
2009
|
+
if (turn.completedItemIds.has(id)) fail(`item ${id} completed more than once`);
|
|
2010
|
+
const existing = turn.pendingTools.get(id);
|
|
2011
|
+
if (existing && existing.itemType !== type) {
|
|
2012
|
+
fail(`item ${id} changed type from ${existing.itemType} to ${type}`);
|
|
2013
|
+
}
|
|
2014
|
+
const pending = existing ?? createTool(turn, item, type, time, "completed_only");
|
|
2015
|
+
const result = completedStatus(type, item, ref.path);
|
|
2016
|
+
pending.span.end_time = later(pending.span.start_time, time);
|
|
2017
|
+
pending.span.status = {
|
|
2018
|
+
code: result.code,
|
|
2019
|
+
...result.message ? { message: result.message } : {}
|
|
2020
|
+
};
|
|
2021
|
+
pending.span.attributes["traces.codex.exec_item_status"] = item.status;
|
|
2022
|
+
if (result.exitCode !== void 0) {
|
|
2023
|
+
pending.span.attributes["traces.codex.exec_exit_code"] = result.exitCode;
|
|
2024
|
+
}
|
|
2025
|
+
if (type === "command_execution") {
|
|
2026
|
+
recordToolOutput(pending.span, typeof item.aggregated_output === "string" ? item.aggregated_output : void 0);
|
|
2027
|
+
}
|
|
2028
|
+
turn.pendingTools.delete(id);
|
|
2029
|
+
turn.completedItemIds.add(id);
|
|
2030
|
+
};
|
|
2031
|
+
const closeTurn = (code, time, usage, message) => {
|
|
2032
|
+
const turn = requireTurn(code === "OK" ? "turn.completed" : "turn.failed");
|
|
2033
|
+
if (code === "OK" && turn.pendingTools.size > 0) {
|
|
2034
|
+
fail(`turn.completed left ${turn.pendingTools.size} item(s) without item.completed`);
|
|
2035
|
+
}
|
|
2036
|
+
if (code === "ERROR") {
|
|
2037
|
+
for (const pending of turn.pendingTools.values()) {
|
|
2038
|
+
pending.span.end_time = later(pending.span.start_time, time);
|
|
2039
|
+
pending.span.status = { code: "ERROR", message: message ?? "turn failed before item completion" };
|
|
2040
|
+
pending.span.attributes["traces.codex.exec_item_status"] = "interrupted";
|
|
2041
|
+
}
|
|
2042
|
+
turn.pendingTools.clear();
|
|
2043
|
+
}
|
|
2044
|
+
spans.push(span({
|
|
2045
|
+
traceId: threadId,
|
|
2046
|
+
spanId: turn.spanId,
|
|
2047
|
+
parentSpanId: root.span_id,
|
|
2048
|
+
name: "llm.turn",
|
|
2049
|
+
kind: "LLM",
|
|
2050
|
+
startTime: turn.startTime,
|
|
2051
|
+
endTime: later(turn.startTime, time),
|
|
2052
|
+
status: code,
|
|
2053
|
+
statusMessage: message,
|
|
2054
|
+
service: SERVICE4,
|
|
2055
|
+
agent: SERVICE4,
|
|
2056
|
+
inputTokens: optionalTokenCount(usage, "input_tokens", ref.path),
|
|
2057
|
+
cachedInputTokens: optionalTokenCount(usage, "cached_input_tokens", ref.path) ?? optionalTokenCount(usage, "cache_read_input_tokens", ref.path),
|
|
2058
|
+
outputTokens: optionalTokenCount(usage, "output_tokens", ref.path),
|
|
2059
|
+
reasoningTokens: optionalTokenCount(usage, "reasoning_output_tokens", ref.path),
|
|
2060
|
+
step: turn.step
|
|
2061
|
+
}));
|
|
2062
|
+
activeTurn = void 0;
|
|
2063
|
+
terminalCount += 1;
|
|
2064
|
+
if (code === "ERROR") fatalError = true;
|
|
2065
|
+
};
|
|
2066
|
+
for await (const raw of readJsonl(ref.path, sessionJsonlOptions(ref, options))) {
|
|
2067
|
+
const event = objectValue(raw);
|
|
2068
|
+
const type = event ? stringValue(event.type) : void 0;
|
|
2069
|
+
if (!event || !type || !SUPPORTED_EVENT_TYPES.has(type)) {
|
|
2070
|
+
ignoredEventCount += 1;
|
|
2071
|
+
continue;
|
|
2072
|
+
}
|
|
2073
|
+
recognizedEventCount += 1;
|
|
2074
|
+
const timed = eventTime(event, fallbackTime);
|
|
2075
|
+
if (type === "thread.started") {
|
|
2076
|
+
if (root) fail("thread.started appeared more than once");
|
|
2077
|
+
threadId = requireString(event.thread_id, ref.path, "thread.started.thread_id");
|
|
2078
|
+
ref.sessionId = threadId;
|
|
2079
|
+
root = span({
|
|
2080
|
+
traceId: threadId,
|
|
2081
|
+
spanId: `root:${threadId}`,
|
|
2082
|
+
parentSpanId: null,
|
|
2083
|
+
name: "session",
|
|
2084
|
+
kind: "AGENT",
|
|
2085
|
+
startTime: timed.time,
|
|
2086
|
+
status: "UNSET",
|
|
2087
|
+
service: SERVICE4,
|
|
2088
|
+
agent: SERVICE4,
|
|
2089
|
+
extra: {
|
|
2090
|
+
"traces.codex.stream_format": "exec-jsonl",
|
|
2091
|
+
"traces.session.role": "operator"
|
|
2092
|
+
}
|
|
2093
|
+
});
|
|
2094
|
+
spans.push(root);
|
|
2095
|
+
touchRoot(timed);
|
|
2096
|
+
continue;
|
|
2097
|
+
}
|
|
2098
|
+
touchRoot(timed);
|
|
2099
|
+
requireRoot(type);
|
|
2100
|
+
if (type === "turn.started") {
|
|
2101
|
+
if (activeTurn) fail("turn.started appeared before the prior turn reached a terminal event");
|
|
2102
|
+
activeTurn = {
|
|
2103
|
+
index: turnIndex,
|
|
2104
|
+
spanId: `llm:${turnIndex}`,
|
|
2105
|
+
startTime: timed.time,
|
|
2106
|
+
step: step++,
|
|
2107
|
+
pendingTools: /* @__PURE__ */ new Map(),
|
|
2108
|
+
completedItemIds: /* @__PURE__ */ new Set()
|
|
2109
|
+
};
|
|
2110
|
+
turnIndex += 1;
|
|
2111
|
+
continue;
|
|
2112
|
+
}
|
|
2113
|
+
if (type === "item.started") {
|
|
2114
|
+
const turn = requireTurn(type);
|
|
2115
|
+
const item = requireObject(event.item, ref.path, "item.started.item");
|
|
2116
|
+
const toolType = toolItemType(item);
|
|
2117
|
+
if (!toolType) {
|
|
2118
|
+
ignoredItemCount += 1;
|
|
2119
|
+
continue;
|
|
2120
|
+
}
|
|
2121
|
+
const id = requireString(item.id, ref.path, `${toolType}.id`);
|
|
2122
|
+
if (turn.pendingTools.has(id) || turn.completedItemIds.has(id)) {
|
|
2123
|
+
fail(`item ${id} started more than once`);
|
|
2124
|
+
}
|
|
2125
|
+
const status = stringValue(item.status);
|
|
2126
|
+
if (status !== void 0 && status !== "in_progress") {
|
|
2127
|
+
fail(`${toolType}.status on item.started must be "in_progress" when present`);
|
|
2128
|
+
}
|
|
2129
|
+
turn.pendingTools.set(id, createTool(turn, item, toolType, timed.time, "paired"));
|
|
2130
|
+
continue;
|
|
2131
|
+
}
|
|
2132
|
+
if (type === "item.completed") {
|
|
2133
|
+
const turn = requireTurn(type);
|
|
2134
|
+
const item = requireObject(event.item, ref.path, "item.completed.item");
|
|
2135
|
+
const completedType = itemType(item);
|
|
2136
|
+
if (completedType === "command_execution" || completedType === "file_change") {
|
|
2137
|
+
completeTool(turn, item, timed.time);
|
|
2138
|
+
} else if (completedType === "agent_message") {
|
|
2139
|
+
const id = requireString(item.id, ref.path, "agent_message.id");
|
|
2140
|
+
if (turn.completedItemIds.has(id)) fail(`item ${id} completed more than once`);
|
|
2141
|
+
const text = typeof item.text === "string" ? capText(item.text) : "";
|
|
2142
|
+
if (text) {
|
|
2143
|
+
spans.push(span({
|
|
2144
|
+
traceId: threadId,
|
|
2145
|
+
spanId: `message:${turn.index}:${id}`,
|
|
2146
|
+
parentSpanId: turn.spanId,
|
|
2147
|
+
name: "message.assistant",
|
|
2148
|
+
kind: "CHAIN",
|
|
2149
|
+
startTime: timed.time,
|
|
2150
|
+
service: SERVICE4,
|
|
2151
|
+
agent: SERVICE4,
|
|
2152
|
+
step: step++,
|
|
2153
|
+
content: text,
|
|
2154
|
+
extra: {
|
|
2155
|
+
"traces.codex.exec_item_id": id,
|
|
2156
|
+
"traces.codex.exec_item_type": completedType
|
|
2157
|
+
}
|
|
2158
|
+
}));
|
|
2159
|
+
}
|
|
2160
|
+
turn.completedItemIds.add(id);
|
|
2161
|
+
} else if (completedType === "error") {
|
|
2162
|
+
const id = requireString(item.id, ref.path, "error.id");
|
|
2163
|
+
if (turn.completedItemIds.has(id)) fail(`item ${id} completed more than once`);
|
|
2164
|
+
const message2 = requireString(item.message, ref.path, "error.message");
|
|
2165
|
+
spans.push(span({
|
|
2166
|
+
traceId: threadId,
|
|
2167
|
+
spanId: `error:${turn.index}:${id}`,
|
|
2168
|
+
parentSpanId: turn.spanId,
|
|
2169
|
+
name: "error.codex_item",
|
|
2170
|
+
kind: "CHAIN",
|
|
2171
|
+
startTime: timed.time,
|
|
2172
|
+
status: "ERROR",
|
|
2173
|
+
statusMessage: message2,
|
|
2174
|
+
service: SERVICE4,
|
|
2175
|
+
agent: SERVICE4,
|
|
2176
|
+
step: step++,
|
|
2177
|
+
content: capText(message2),
|
|
2178
|
+
extra: {
|
|
2179
|
+
"traces.codex.exec_item_id": id,
|
|
2180
|
+
"traces.codex.exec_item_type": completedType
|
|
2181
|
+
}
|
|
2182
|
+
}));
|
|
2183
|
+
turn.completedItemIds.add(id);
|
|
2184
|
+
} else {
|
|
2185
|
+
ignoredItemCount += 1;
|
|
2186
|
+
}
|
|
2187
|
+
continue;
|
|
2188
|
+
}
|
|
2189
|
+
if (type === "turn.completed") {
|
|
2190
|
+
const usage = event.usage === void 0 ? void 0 : requireObject(event.usage, ref.path, "turn.completed.usage");
|
|
2191
|
+
closeTurn("OK", timed.time, usage);
|
|
2192
|
+
continue;
|
|
2193
|
+
}
|
|
2194
|
+
if (type === "turn.failed") {
|
|
2195
|
+
const error = requireObject(event.error, ref.path, "turn.failed.error");
|
|
2196
|
+
const message2 = requireString(error.message, ref.path, "turn.failed.error.message");
|
|
2197
|
+
closeTurn("ERROR", timed.time, void 0, message2);
|
|
2198
|
+
continue;
|
|
2199
|
+
}
|
|
2200
|
+
const message = requireString(event.message, ref.path, "error.message");
|
|
2201
|
+
spans.push(span({
|
|
2202
|
+
traceId: threadId,
|
|
2203
|
+
spanId: `error:stream:${step}`,
|
|
2204
|
+
parentSpanId: activeTurn?.spanId ?? root.span_id,
|
|
2205
|
+
name: "error.codex_stream",
|
|
2206
|
+
kind: "CHAIN",
|
|
2207
|
+
startTime: timed.time,
|
|
2208
|
+
status: "ERROR",
|
|
2209
|
+
statusMessage: message,
|
|
2210
|
+
service: SERVICE4,
|
|
2211
|
+
agent: SERVICE4,
|
|
2212
|
+
step: step++,
|
|
2213
|
+
content: capText(message)
|
|
2214
|
+
}));
|
|
2215
|
+
if (activeTurn) closeTurn("ERROR", timed.time, void 0, message);
|
|
2216
|
+
else {
|
|
2217
|
+
fatalError = true;
|
|
2218
|
+
terminalCount += 1;
|
|
2219
|
+
}
|
|
2220
|
+
}
|
|
2221
|
+
if (recognizedEventCount === 0) {
|
|
2222
|
+
throw new CodexExecStreamError(
|
|
2223
|
+
ref.path,
|
|
2224
|
+
"no supported events found; expected codex exec --json output beginning with thread.started"
|
|
2225
|
+
);
|
|
2226
|
+
}
|
|
2227
|
+
if (!root || !threadId) {
|
|
2228
|
+
throw new CodexExecStreamError(ref.path, "thread.started was not found");
|
|
2229
|
+
}
|
|
2230
|
+
if (activeTurn) {
|
|
2231
|
+
throw new CodexExecStreamError(ref.path, "stream ended before turn.completed, turn.failed, or error");
|
|
2232
|
+
}
|
|
2233
|
+
if (terminalCount === 0) {
|
|
2234
|
+
throw new CodexExecStreamError(ref.path, "stream has no terminal turn.completed, turn.failed, or error event");
|
|
2235
|
+
}
|
|
2236
|
+
root.status = fatalError ? { code: "ERROR", message: "Codex exec stream failed" } : { code: "OK" };
|
|
2237
|
+
root.attributes["traces.codex.exec_event_count"] = recognizedEventCount;
|
|
2238
|
+
root.attributes["traces.codex.exec_ignored_event_count"] = ignoredEventCount;
|
|
2239
|
+
root.attributes["traces.codex.exec_ignored_item_count"] = ignoredItemCount;
|
|
2240
|
+
root.attributes["traces.codex.exec_event_timestamp_count"] = eventTimestampCount;
|
|
2241
|
+
root.attributes["traces.codex.exec_time_source"] = eventTimestampCount === 0 ? "file_mtime" : eventTimestampCount === recognizedEventCount ? "event" : "mixed";
|
|
2242
|
+
return spans;
|
|
2243
|
+
}
|
|
2244
|
+
};
|
|
2245
|
+
|
|
1704
2246
|
// src/adapters/copilot.ts
|
|
1705
2247
|
import { readdir as readdir5, stat as stat4 } from "fs/promises";
|
|
1706
2248
|
import { homedir as homedir4 } from "os";
|
|
1707
2249
|
import { join as join4 } from "path";
|
|
1708
|
-
var
|
|
2250
|
+
var SERVICE5 = "github-copilot";
|
|
1709
2251
|
var CopilotAdapter = class {
|
|
1710
2252
|
harness = "github-copilot";
|
|
1711
2253
|
aliases = ["copilot"];
|
|
@@ -1770,8 +2312,8 @@ var CopilotAdapter = class {
|
|
|
1770
2312
|
name: "message.assistant",
|
|
1771
2313
|
kind: "LLM",
|
|
1772
2314
|
startTime: ts,
|
|
1773
|
-
service:
|
|
1774
|
-
agent:
|
|
2315
|
+
service: SERVICE5,
|
|
2316
|
+
agent: SERVICE5,
|
|
1775
2317
|
model: d.model ?? null,
|
|
1776
2318
|
inputTokens: pendingInputTokens,
|
|
1777
2319
|
outputTokens: d.outputTokens ?? null,
|
|
@@ -1791,8 +2333,8 @@ var CopilotAdapter = class {
|
|
|
1791
2333
|
name: `tool.${name}`,
|
|
1792
2334
|
kind: "TOOL",
|
|
1793
2335
|
startTime: ts,
|
|
1794
|
-
service:
|
|
1795
|
-
agent:
|
|
2336
|
+
service: SERVICE5,
|
|
2337
|
+
agent: SERVICE5,
|
|
1796
2338
|
tool: name,
|
|
1797
2339
|
step,
|
|
1798
2340
|
extra: toolIoAttributes({ input: d.arguments })
|
|
@@ -1818,8 +2360,8 @@ var CopilotAdapter = class {
|
|
|
1818
2360
|
kind: "AGENT",
|
|
1819
2361
|
startTime: firstTimestamp ?? (/* @__PURE__ */ new Date(0)).toISOString(),
|
|
1820
2362
|
endTime: lastTimestamp,
|
|
1821
|
-
service:
|
|
1822
|
-
agent:
|
|
2363
|
+
service: SERVICE5,
|
|
2364
|
+
agent: SERVICE5
|
|
1823
2365
|
});
|
|
1824
2366
|
return [root, ...spans];
|
|
1825
2367
|
}
|
|
@@ -1829,7 +2371,7 @@ var CopilotAdapter = class {
|
|
|
1829
2371
|
import { readdir as readdir6, stat as stat5 } from "fs/promises";
|
|
1830
2372
|
import { homedir as homedir5 } from "os";
|
|
1831
2373
|
import { basename as basename4, join as join5 } from "path";
|
|
1832
|
-
var
|
|
2374
|
+
var SERVICE6 = "factory";
|
|
1833
2375
|
function textOf4(content) {
|
|
1834
2376
|
return capText(
|
|
1835
2377
|
(content ?? []).filter((b) => b.type === "text" && typeof b.text === "string").map((b) => b.text).join("")
|
|
@@ -1917,8 +2459,8 @@ var FactoryAdapter = class {
|
|
|
1917
2459
|
parentSpanId: sourceRootId,
|
|
1918
2460
|
startTime: ts,
|
|
1919
2461
|
content: text,
|
|
1920
|
-
service:
|
|
1921
|
-
agent:
|
|
2462
|
+
service: SERVICE6,
|
|
2463
|
+
agent: SERVICE6,
|
|
1922
2464
|
step
|
|
1923
2465
|
})
|
|
1924
2466
|
);
|
|
@@ -1934,8 +2476,8 @@ var FactoryAdapter = class {
|
|
|
1934
2476
|
name: `message.${role ?? "unknown"}`,
|
|
1935
2477
|
kind: "LLM",
|
|
1936
2478
|
startTime: ts,
|
|
1937
|
-
service:
|
|
1938
|
-
agent:
|
|
2479
|
+
service: SERVICE6,
|
|
2480
|
+
agent: SERVICE6,
|
|
1939
2481
|
model: settings.model ?? null,
|
|
1940
2482
|
step,
|
|
1941
2483
|
content: text || null
|
|
@@ -1953,8 +2495,8 @@ var FactoryAdapter = class {
|
|
|
1953
2495
|
name: `tool.${b.name}`,
|
|
1954
2496
|
kind: "TOOL",
|
|
1955
2497
|
startTime: ts,
|
|
1956
|
-
service:
|
|
1957
|
-
agent:
|
|
2498
|
+
service: SERVICE6,
|
|
2499
|
+
agent: SERVICE6,
|
|
1958
2500
|
tool: b.name,
|
|
1959
2501
|
step,
|
|
1960
2502
|
extra: toolIoAttributes({ input: b.input })
|
|
@@ -1985,8 +2527,8 @@ var FactoryAdapter = class {
|
|
|
1985
2527
|
kind: "AGENT",
|
|
1986
2528
|
startTime: sessionLine?.timestamp ?? firstTimestamp ?? (/* @__PURE__ */ new Date(0)).toISOString(),
|
|
1987
2529
|
endTime: lastTimestamp,
|
|
1988
|
-
service:
|
|
1989
|
-
agent:
|
|
2530
|
+
service: SERVICE6,
|
|
2531
|
+
agent: SERVICE6,
|
|
1990
2532
|
model: settings.model ?? null,
|
|
1991
2533
|
extra: settings.tokenUsage ? {
|
|
1992
2534
|
"session.input_tokens": settings.tokenUsage.inputTokens ?? 0,
|
|
@@ -2001,7 +2543,7 @@ var FactoryAdapter = class {
|
|
|
2001
2543
|
import { readdir as readdir7, stat as stat6 } from "fs/promises";
|
|
2002
2544
|
import { homedir as homedir6 } from "os";
|
|
2003
2545
|
import { basename as basename5, join as join6 } from "path";
|
|
2004
|
-
var
|
|
2546
|
+
var SERVICE7 = "forge";
|
|
2005
2547
|
function tokens(t) {
|
|
2006
2548
|
if (!t) return null;
|
|
2007
2549
|
return t.actual ?? t.approx ?? null;
|
|
@@ -2050,7 +2592,7 @@ var ForgeAdapter = class {
|
|
|
2050
2592
|
const rootId = `root:${traceId}`;
|
|
2051
2593
|
const messages = ctx.messages ?? [];
|
|
2052
2594
|
const spans = [
|
|
2053
|
-
span({ traceId, spanId: rootId, parentSpanId: null, name: "session", kind: "AGENT", startTime: (/* @__PURE__ */ new Date(0)).toISOString(), service:
|
|
2595
|
+
span({ traceId, spanId: rootId, parentSpanId: null, name: "session", kind: "AGENT", startTime: (/* @__PURE__ */ new Date(0)).toISOString(), service: SERVICE7, agent: SERVICE7 })
|
|
2054
2596
|
];
|
|
2055
2597
|
const toolByCallId = /* @__PURE__ */ new Map();
|
|
2056
2598
|
let step = 0;
|
|
@@ -2068,8 +2610,8 @@ var ForgeAdapter = class {
|
|
|
2068
2610
|
parentSpanId: rootId,
|
|
2069
2611
|
startTime: ts,
|
|
2070
2612
|
content: prompt,
|
|
2071
|
-
service:
|
|
2072
|
-
agent:
|
|
2613
|
+
service: SERVICE7,
|
|
2614
|
+
agent: SERVICE7,
|
|
2073
2615
|
step
|
|
2074
2616
|
})
|
|
2075
2617
|
);
|
|
@@ -2085,8 +2627,8 @@ var ForgeAdapter = class {
|
|
|
2085
2627
|
name: `message.${entry.text.role ?? "unknown"}`,
|
|
2086
2628
|
kind: "LLM",
|
|
2087
2629
|
startTime: ts,
|
|
2088
|
-
service:
|
|
2089
|
-
agent:
|
|
2630
|
+
service: SERVICE7,
|
|
2631
|
+
agent: SERVICE7,
|
|
2090
2632
|
model: entry.text.model ?? null,
|
|
2091
2633
|
inputTokens: tokens(raw.usage?.prompt_tokens),
|
|
2092
2634
|
outputTokens: tokens(raw.usage?.completion_tokens),
|
|
@@ -2105,8 +2647,8 @@ var ForgeAdapter = class {
|
|
|
2105
2647
|
name: `tool.${tc.name ?? "tool"}`,
|
|
2106
2648
|
kind: "TOOL",
|
|
2107
2649
|
startTime: ts,
|
|
2108
|
-
service:
|
|
2109
|
-
agent:
|
|
2650
|
+
service: SERVICE7,
|
|
2651
|
+
agent: SERVICE7,
|
|
2110
2652
|
tool: tc.name ?? "tool",
|
|
2111
2653
|
step,
|
|
2112
2654
|
extra: toolIoAttributes({ input: tc.arguments })
|
|
@@ -2306,7 +2848,7 @@ var GeminiAdapter = class extends GeminiFamilyAdapter {
|
|
|
2306
2848
|
import { readdir as readdir9, stat as stat8 } from "fs/promises";
|
|
2307
2849
|
import { homedir as homedir8 } from "os";
|
|
2308
2850
|
import { join as join8 } from "path";
|
|
2309
|
-
var
|
|
2851
|
+
var SERVICE8 = "opencode";
|
|
2310
2852
|
function isoOf(ms) {
|
|
2311
2853
|
return new Date(ms ?? 0).toISOString();
|
|
2312
2854
|
}
|
|
@@ -2366,8 +2908,8 @@ var OpencodeAdapter = class {
|
|
|
2366
2908
|
kind: "AGENT",
|
|
2367
2909
|
startTime: isoOf(messages[0]?.time?.created),
|
|
2368
2910
|
endTime: isoOf(messages.at(-1)?.time?.completed ?? messages.at(-1)?.time?.created),
|
|
2369
|
-
service:
|
|
2370
|
-
agent:
|
|
2911
|
+
service: SERVICE8,
|
|
2912
|
+
agent: SERVICE8
|
|
2371
2913
|
})
|
|
2372
2914
|
];
|
|
2373
2915
|
let step = 0;
|
|
@@ -2395,8 +2937,8 @@ var OpencodeAdapter = class {
|
|
|
2395
2937
|
spanId: `${llmId}:user`,
|
|
2396
2938
|
parentSpanId: rootId,
|
|
2397
2939
|
startTime: isoOf(msg.time?.created),
|
|
2398
|
-
service:
|
|
2399
|
-
agent:
|
|
2940
|
+
service: SERVICE8,
|
|
2941
|
+
agent: SERVICE8,
|
|
2400
2942
|
step,
|
|
2401
2943
|
content: turnText
|
|
2402
2944
|
})
|
|
@@ -2413,8 +2955,8 @@ var OpencodeAdapter = class {
|
|
|
2413
2955
|
kind: "LLM",
|
|
2414
2956
|
startTime: isoOf(msg.time?.created),
|
|
2415
2957
|
endTime: isoOf(msg.time?.completed ?? msg.time?.created),
|
|
2416
|
-
service:
|
|
2417
|
-
agent:
|
|
2958
|
+
service: SERVICE8,
|
|
2959
|
+
agent: SERVICE8,
|
|
2418
2960
|
model: msg.modelID ?? null,
|
|
2419
2961
|
inputTokens: msg.tokens?.input ?? null,
|
|
2420
2962
|
outputTokens: msg.tokens?.output ?? null,
|
|
@@ -2441,8 +2983,8 @@ var OpencodeAdapter = class {
|
|
|
2441
2983
|
endTime: isoOf(msg.time?.completed ?? msg.time?.created),
|
|
2442
2984
|
status: err ? "ERROR" : "OK",
|
|
2443
2985
|
statusMessage: err ? (part.state?.error ?? "").slice(0, 500) : void 0,
|
|
2444
|
-
service:
|
|
2445
|
-
agent:
|
|
2986
|
+
service: SERVICE8,
|
|
2987
|
+
agent: SERVICE8,
|
|
2446
2988
|
tool: part.tool,
|
|
2447
2989
|
step,
|
|
2448
2990
|
extra: toolIoAttributes({
|
|
@@ -2462,7 +3004,7 @@ var OpencodeAdapter = class {
|
|
|
2462
3004
|
import { readdir as readdir10, stat as stat9 } from "fs/promises";
|
|
2463
3005
|
import { homedir as homedir9 } from "os";
|
|
2464
3006
|
import { basename as basename6, join as join9 } from "path";
|
|
2465
|
-
var
|
|
3007
|
+
var SERVICE9 = "pi";
|
|
2466
3008
|
function isToolBlock(b) {
|
|
2467
3009
|
return typeof b.type === "string" && /tool[_-]?call|tool[_-]?use|^tool$/i.test(b.type);
|
|
2468
3010
|
}
|
|
@@ -2551,8 +3093,8 @@ var PiAdapter = class {
|
|
|
2551
3093
|
spanId: `${mid}:user`,
|
|
2552
3094
|
parentSpanId: sourceRootId,
|
|
2553
3095
|
startTime: ts,
|
|
2554
|
-
service:
|
|
2555
|
-
agent:
|
|
3096
|
+
service: SERVICE9,
|
|
3097
|
+
agent: SERVICE9,
|
|
2556
3098
|
step,
|
|
2557
3099
|
content: prompt
|
|
2558
3100
|
})
|
|
@@ -2571,8 +3113,8 @@ var PiAdapter = class {
|
|
|
2571
3113
|
startTime: ts,
|
|
2572
3114
|
status: errored ? "ERROR" : "OK",
|
|
2573
3115
|
statusMessage: errored ? msg.errorMessage.slice(0, 500) : void 0,
|
|
2574
|
-
service:
|
|
2575
|
-
agent:
|
|
3116
|
+
service: SERVICE9,
|
|
3117
|
+
agent: SERVICE9,
|
|
2576
3118
|
model: msg.model ?? null,
|
|
2577
3119
|
inputTokens: msg.usage?.input ?? null,
|
|
2578
3120
|
outputTokens: msg.usage?.output ?? null,
|
|
@@ -2593,8 +3135,8 @@ var PiAdapter = class {
|
|
|
2593
3135
|
name: `tool.${name}`,
|
|
2594
3136
|
kind: "TOOL",
|
|
2595
3137
|
startTime: ts,
|
|
2596
|
-
service:
|
|
2597
|
-
agent:
|
|
3138
|
+
service: SERVICE9,
|
|
3139
|
+
agent: SERVICE9,
|
|
2598
3140
|
tool: name,
|
|
2599
3141
|
step,
|
|
2600
3142
|
extra: toolIoAttributes({ input: b.input ?? b.args })
|
|
@@ -2628,8 +3170,8 @@ var PiAdapter = class {
|
|
|
2628
3170
|
kind: "AGENT",
|
|
2629
3171
|
startTime: sessionLine?.timestamp ?? firstTimestamp ?? (/* @__PURE__ */ new Date(0)).toISOString(),
|
|
2630
3172
|
endTime: lastTimestamp,
|
|
2631
|
-
service:
|
|
2632
|
-
agent:
|
|
3173
|
+
service: SERVICE9,
|
|
3174
|
+
agent: SERVICE9
|
|
2633
3175
|
});
|
|
2634
3176
|
return [root, ...spans];
|
|
2635
3177
|
}
|
|
@@ -2639,7 +3181,7 @@ var PiAdapter = class {
|
|
|
2639
3181
|
import { readdir as readdir11, stat as stat10 } from "fs/promises";
|
|
2640
3182
|
import { homedir as homedir10 } from "os";
|
|
2641
3183
|
import { basename as basename7, join as join10 } from "path";
|
|
2642
|
-
var
|
|
3184
|
+
var SERVICE10 = "qwen";
|
|
2643
3185
|
function textOf9(parts) {
|
|
2644
3186
|
if (!parts) return "";
|
|
2645
3187
|
return parts.filter((p) => typeof p.text === "string").map((p) => p.text).join("");
|
|
@@ -2715,8 +3257,8 @@ var QwenAdapter = class {
|
|
|
2715
3257
|
name: "llm.turn",
|
|
2716
3258
|
kind: "LLM",
|
|
2717
3259
|
startTime: ts,
|
|
2718
|
-
service:
|
|
2719
|
-
agent:
|
|
3260
|
+
service: SERVICE10,
|
|
3261
|
+
agent: SERVICE10,
|
|
2720
3262
|
model: r.model ?? null,
|
|
2721
3263
|
inputTokens: r.usageMetadata?.promptTokenCount ?? null,
|
|
2722
3264
|
outputTokens: r.usageMetadata?.candidatesTokenCount ?? null,
|
|
@@ -2735,8 +3277,8 @@ var QwenAdapter = class {
|
|
|
2735
3277
|
name: `tool.${name}`,
|
|
2736
3278
|
kind: "TOOL",
|
|
2737
3279
|
startTime: ts,
|
|
2738
|
-
service:
|
|
2739
|
-
agent:
|
|
3280
|
+
service: SERVICE10,
|
|
3281
|
+
agent: SERVICE10,
|
|
2740
3282
|
tool: name,
|
|
2741
3283
|
step,
|
|
2742
3284
|
extra: toolIoAttributes({ input: p.functionCall.args })
|
|
@@ -2756,8 +3298,8 @@ var QwenAdapter = class {
|
|
|
2756
3298
|
spanId: `user:${r.uuid ?? step}`,
|
|
2757
3299
|
parentSpanId: sourceRootId,
|
|
2758
3300
|
startTime: ts,
|
|
2759
|
-
service:
|
|
2760
|
-
agent:
|
|
3301
|
+
service: SERVICE10,
|
|
3302
|
+
agent: SERVICE10,
|
|
2761
3303
|
step,
|
|
2762
3304
|
content: prompt
|
|
2763
3305
|
})
|
|
@@ -2792,8 +3334,8 @@ var QwenAdapter = class {
|
|
|
2792
3334
|
kind: "AGENT",
|
|
2793
3335
|
startTime: firstTimestamp ?? (/* @__PURE__ */ new Date(0)).toISOString(),
|
|
2794
3336
|
endTime: lastTimestamp,
|
|
2795
|
-
service:
|
|
2796
|
-
agent:
|
|
3337
|
+
service: SERVICE10,
|
|
3338
|
+
agent: SERVICE10
|
|
2797
3339
|
});
|
|
2798
3340
|
return [root, ...spans];
|
|
2799
3341
|
}
|
|
@@ -2803,6 +3345,7 @@ var QwenAdapter = class {
|
|
|
2803
3345
|
var ADAPTERS = [
|
|
2804
3346
|
new ClaudeAdapter(),
|
|
2805
3347
|
new CodexAdapter(),
|
|
3348
|
+
new CodexExecAdapter(),
|
|
2806
3349
|
new OpencodeAdapter(),
|
|
2807
3350
|
new GeminiAdapter(),
|
|
2808
3351
|
new QwenAdapter(),
|
|
@@ -3303,6 +3846,9 @@ function summarizeTools(spans) {
|
|
|
3303
3846
|
return [...byTool.entries()].map(([name, row]) => ({ name, calls: row.calls, errors: row.errors })).sort((a, b) => b.calls - a.calls || a.name.localeCompare(b.name));
|
|
3304
3847
|
}
|
|
3305
3848
|
async function buildPolicyEvidenceRecord(ref, spans, opts = {}) {
|
|
3849
|
+
if (opts.sourceSha256 && !/^[a-f0-9]{64}$/.test(opts.sourceSha256)) {
|
|
3850
|
+
throw new Error("sourceSha256 must be a lowercase SHA-256 hex digest");
|
|
3851
|
+
}
|
|
3306
3852
|
const toolSpans = spans.filter((span2) => spanKind(span2) === "TOOL");
|
|
3307
3853
|
const erroredToolCallCount = toolSpans.filter((span2) => span2.status.code === "ERROR").length;
|
|
3308
3854
|
const pipelines = await runPipelines(spans, { minLoopOccurrences: opts.minLoopOccurrences });
|
|
@@ -3352,6 +3898,7 @@ async function buildPolicyEvidenceRecord(ref, spans, opts = {}) {
|
|
|
3352
3898
|
source: "traces",
|
|
3353
3899
|
evidenceKind: "session-summary",
|
|
3354
3900
|
...opts.otlpPath ? { otlpPath: opts.otlpPath } : {},
|
|
3901
|
+
...opts.sourceSha256 ? { sourceSha256: opts.sourceSha256 } : {},
|
|
3355
3902
|
notCampaignCell: true,
|
|
3356
3903
|
note: "This is normalized coding-agent session evidence for downstream policy mining; it is not an eval campaign cell."
|
|
3357
3904
|
}
|
|
@@ -3574,25 +4121,25 @@ import { join as join13 } from "path";
|
|
|
3574
4121
|
import {
|
|
3575
4122
|
firstNumberAttr,
|
|
3576
4123
|
LLM_CACHED_TOKEN_ATTR_KEYS,
|
|
3577
|
-
LLM_CACHED_TOKENS,
|
|
4124
|
+
LLM_CACHED_TOKENS as LLM_CACHED_TOKENS2,
|
|
3578
4125
|
LLM_CACHE_WRITE_TOKEN_ATTR_KEYS,
|
|
3579
|
-
LLM_CACHE_WRITE_TOKENS,
|
|
4126
|
+
LLM_CACHE_WRITE_TOKENS as LLM_CACHE_WRITE_TOKENS2,
|
|
3580
4127
|
LLM_COST_ATTR_KEYS,
|
|
3581
4128
|
LLM_COST_USD,
|
|
3582
4129
|
LLM_INPUT_TOKEN_ATTR_KEYS,
|
|
3583
|
-
LLM_INPUT_TOKENS,
|
|
4130
|
+
LLM_INPUT_TOKENS as LLM_INPUT_TOKENS2,
|
|
3584
4131
|
LLM_OUTPUT_TOKEN_ATTR_KEYS,
|
|
3585
|
-
LLM_OUTPUT_TOKENS,
|
|
4132
|
+
LLM_OUTPUT_TOKENS as LLM_OUTPUT_TOKENS2,
|
|
3586
4133
|
LLM_REASONING_TOKEN_ATTR_KEYS,
|
|
3587
4134
|
LLM_REASONING_TOKENS
|
|
3588
4135
|
} from "@tangle-network/agent-eval/trace-attributes";
|
|
3589
4136
|
function isObject(value) {
|
|
3590
4137
|
return value != null && typeof value === "object" && !Array.isArray(value);
|
|
3591
4138
|
}
|
|
3592
|
-
function
|
|
4139
|
+
function stringValue2(value) {
|
|
3593
4140
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
3594
4141
|
}
|
|
3595
|
-
function
|
|
4142
|
+
function objectValue2(value) {
|
|
3596
4143
|
return isObject(value) ? value : void 0;
|
|
3597
4144
|
}
|
|
3598
4145
|
function stableJson(value) {
|
|
@@ -3607,7 +4154,7 @@ function stableJson(value) {
|
|
|
3607
4154
|
function hashId(value, chars = 16) {
|
|
3608
4155
|
return createHash5("sha256").update(stableJson(value)).digest("hex").slice(0, chars);
|
|
3609
4156
|
}
|
|
3610
|
-
function
|
|
4157
|
+
function isoTime2(value) {
|
|
3611
4158
|
if (typeof value === "string") {
|
|
3612
4159
|
const trimmed = value.trim();
|
|
3613
4160
|
const normalized = /^\d{4}-\d{2}-\d{2}[ T]\d{2}:\d{2}:\d{2}(?:\.\d+)?$/.test(trimmed) ? `${trimmed.replace(" ", "T")}Z` : trimmed;
|
|
@@ -3622,7 +4169,7 @@ function isoTime(value) {
|
|
|
3622
4169
|
}
|
|
3623
4170
|
function firstTime(obj, keys) {
|
|
3624
4171
|
for (const key of keys) {
|
|
3625
|
-
const value =
|
|
4172
|
+
const value = isoTime2(obj[key]);
|
|
3626
4173
|
if (value) return value;
|
|
3627
4174
|
}
|
|
3628
4175
|
return void 0;
|
|
@@ -3683,7 +4230,7 @@ function isIntelligenceSpanRow(row) {
|
|
|
3683
4230
|
return typeof row.trace_id === "string" && typeof row.name === "string" && isObject(row.attributes) && (row.start_unix_nano != null || row.start_time != null || row.received_at != null);
|
|
3684
4231
|
}
|
|
3685
4232
|
function eventType(row) {
|
|
3686
|
-
return
|
|
4233
|
+
return stringValue2(row.type) ?? stringValue2(row.event) ?? stringValue2(row.name) ?? stringValue2(row.kind);
|
|
3687
4234
|
}
|
|
3688
4235
|
function looksLikeSandboxEvent(row) {
|
|
3689
4236
|
if (!isObject(row)) return false;
|
|
@@ -3755,6 +4302,7 @@ function policyEvidenceToSpans(records) {
|
|
|
3755
4302
|
"traces.provenance.source": record.provenance.source,
|
|
3756
4303
|
"traces.provenance.evidence_kind": record.provenance.evidenceKind,
|
|
3757
4304
|
"traces.provenance.otlp_path": record.provenance.otlpPath,
|
|
4305
|
+
"traces.provenance.source_sha256": record.provenance.sourceSha256,
|
|
3758
4306
|
[ATTR.SUBJECT_KEY]: record.repo.subjectKey,
|
|
3759
4307
|
[ATTR.GIT_REPOSITORY]: record.repo.repository,
|
|
3760
4308
|
[ATTR.GIT_BRANCH_NAME]: record.repo.branch,
|
|
@@ -3792,7 +4340,7 @@ function findStringKey(value, keys, depth = 4) {
|
|
|
3792
4340
|
if (depth < 0) return void 0;
|
|
3793
4341
|
if (isObject(value)) {
|
|
3794
4342
|
for (const key of keys) {
|
|
3795
|
-
const found =
|
|
4343
|
+
const found = stringValue2(value[key]);
|
|
3796
4344
|
if (found) return found;
|
|
3797
4345
|
}
|
|
3798
4346
|
for (const child of Object.values(value)) {
|
|
@@ -3808,7 +4356,7 @@ function findStringKey(value, keys, depth = 4) {
|
|
|
3808
4356
|
return void 0;
|
|
3809
4357
|
}
|
|
3810
4358
|
function eventValue(row, keys) {
|
|
3811
|
-
const scopes = [row,
|
|
4359
|
+
const scopes = [row, objectValue2(row.data), objectValue2(row.payload)];
|
|
3812
4360
|
for (const scope of scopes) {
|
|
3813
4361
|
if (!scope) continue;
|
|
3814
4362
|
for (const key of keys) {
|
|
@@ -3843,9 +4391,9 @@ function eventEndTimestamp(row, fallback) {
|
|
|
3843
4391
|
}
|
|
3844
4392
|
function eventStatus(row, type) {
|
|
3845
4393
|
const lowered = type.toLowerCase();
|
|
3846
|
-
const errorMessage =
|
|
4394
|
+
const errorMessage = stringValue2(row.error) ?? findStringKey(row, ["error", "errorMessage", "error_message"], 3);
|
|
3847
4395
|
if (lowered.includes("error") || errorMessage) {
|
|
3848
|
-
const message = errorMessage ??
|
|
4396
|
+
const message = errorMessage ?? stringValue2(row.message);
|
|
3849
4397
|
return { status: "ERROR", ...message ? { message: capText(message) } : {} };
|
|
3850
4398
|
}
|
|
3851
4399
|
return { status: "OK" };
|
|
@@ -3896,21 +4444,21 @@ function sandboxEventsToSpans(rows, wrapper) {
|
|
|
3896
4444
|
const { status, message } = eventStatus(row, type);
|
|
3897
4445
|
const { kind, tool } = eventKind(row, type);
|
|
3898
4446
|
const name = kind === "TOOL" && tool ? `tool.${tool}` : `event.${type}`;
|
|
3899
|
-
const
|
|
4447
|
+
const toolInput2 = kind === "TOOL" ? eventValue(row, ["toolInput", "tool_input", "input", "args", "arguments", "full_command"]) : void 0;
|
|
3900
4448
|
const extra = {
|
|
3901
4449
|
"traces.source_format": "sandbox-events",
|
|
3902
4450
|
"traces.event.type": type,
|
|
3903
4451
|
"traces.event.index": index,
|
|
3904
4452
|
...kind === "TOOL" ? toolIoAttributes({
|
|
3905
|
-
input:
|
|
4453
|
+
input: toolInput2,
|
|
3906
4454
|
output: eventValue(row, ["toolOutput", "tool_output", "output", "result"]),
|
|
3907
|
-
argsCaptured:
|
|
4455
|
+
argsCaptured: toolInput2 !== void 0
|
|
3908
4456
|
}) : {}
|
|
3909
4457
|
};
|
|
3910
4458
|
copyDefined(extra, {
|
|
3911
|
-
"traces.event.id":
|
|
3912
|
-
"traces.event.raw_type":
|
|
3913
|
-
"traces.event.raw_event":
|
|
4459
|
+
"traces.event.id": stringValue2(row.id),
|
|
4460
|
+
"traces.event.raw_type": stringValue2(row.type),
|
|
4461
|
+
"traces.event.raw_event": stringValue2(row.event)
|
|
3914
4462
|
});
|
|
3915
4463
|
spans.push(span({
|
|
3916
4464
|
traceId,
|
|
@@ -3971,11 +4519,11 @@ var COMPUTED_SPAN_ATTRIBUTES = /* @__PURE__ */ new Set([
|
|
|
3971
4519
|
"tool.name",
|
|
3972
4520
|
"llm.input_tokens",
|
|
3973
4521
|
"llm.output_tokens",
|
|
3974
|
-
|
|
3975
|
-
|
|
4522
|
+
LLM_INPUT_TOKENS2,
|
|
4523
|
+
LLM_OUTPUT_TOKENS2,
|
|
3976
4524
|
LLM_REASONING_TOKENS,
|
|
3977
|
-
|
|
3978
|
-
|
|
4525
|
+
LLM_CACHED_TOKENS2,
|
|
4526
|
+
LLM_CACHE_WRITE_TOKENS2,
|
|
3979
4527
|
LLM_COST_USD,
|
|
3980
4528
|
"step",
|
|
3981
4529
|
"content"
|
|
@@ -4001,7 +4549,7 @@ function capturedToolIo(attrs) {
|
|
|
4001
4549
|
});
|
|
4002
4550
|
}
|
|
4003
4551
|
function isToolTelemetrySpan(attrs) {
|
|
4004
|
-
const spanType =
|
|
4552
|
+
const spanType = stringValue2(attrs["span.type"])?.toLowerCase();
|
|
4005
4553
|
return spanType === "tool.execution" || spanType === "tool.blocked_on_user";
|
|
4006
4554
|
}
|
|
4007
4555
|
function otlpSpanKind(value) {
|
|
@@ -4009,7 +4557,7 @@ function otlpSpanKind(value) {
|
|
|
4009
4557
|
}
|
|
4010
4558
|
function intelligenceSpanKind(name, attrs) {
|
|
4011
4559
|
const lowered = name.toLowerCase();
|
|
4012
|
-
const spanType =
|
|
4560
|
+
const spanType = stringValue2(attrs["span.type"])?.toLowerCase();
|
|
4013
4561
|
if (spanType === "llm_request" || lowered.includes("llm")) return "LLM";
|
|
4014
4562
|
if (isToolTelemetrySpan(attrs)) return "CHAIN";
|
|
4015
4563
|
if (spanType === "tool" || lowered.includes("tool") || attrs["tool.name"] != null || attrs.tool_name != null || attrs["mcp.tool.name"] != null || attrs["tool.input"] != null || attrs["tool.output"] != null) {
|
|
@@ -4020,15 +4568,15 @@ function intelligenceSpanKind(name, attrs) {
|
|
|
4020
4568
|
}
|
|
4021
4569
|
function intelligenceSpansToSpans(rows) {
|
|
4022
4570
|
return rows.map((row, index) => {
|
|
4023
|
-
const attrs = { ...
|
|
4024
|
-
const name =
|
|
4025
|
-
const startTime = unixNanoTime(row.start_unix_nano) ??
|
|
4026
|
-
const endTime = unixNanoTime(row.end_unix_nano) ??
|
|
4027
|
-
const model =
|
|
4028
|
-
const tool =
|
|
4571
|
+
const attrs = { ...objectValue2(row.attributes) ?? {} };
|
|
4572
|
+
const name = stringValue2(row.name) ?? "intelligence.span";
|
|
4573
|
+
const startTime = unixNanoTime(row.start_unix_nano) ?? isoTime2(row.start_time) ?? stringValue2(row.start_time) ?? isoTime2(row.received_at) ?? (/* @__PURE__ */ new Date(0)).toISOString();
|
|
4574
|
+
const endTime = unixNanoTime(row.end_unix_nano) ?? isoTime2(row.end_time) ?? stringValue2(row.end_time) ?? startTime;
|
|
4575
|
+
const model = stringValue2(row.model) ?? stringValue2(attrs["llm.model_name"]) ?? stringValue2(attrs.model) ?? stringValue2(attrs["gen_ai.request.model"]);
|
|
4576
|
+
const tool = stringValue2(attrs["tool.name"]) ?? stringValue2(attrs.tool_name) ?? stringValue2(attrs["mcp.tool.name"]);
|
|
4029
4577
|
const kind = intelligenceSpanKind(name, attrs);
|
|
4030
|
-
const sourceStatus =
|
|
4031
|
-
const sessionId =
|
|
4578
|
+
const sourceStatus = objectValue2(row.status);
|
|
4579
|
+
const sessionId = stringValue2(row.session_id) ?? sessionIdFromAttributes(attrs);
|
|
4032
4580
|
const extra = {
|
|
4033
4581
|
...preserveRawAttributes(attrs),
|
|
4034
4582
|
...kind === "TOOL" ? capturedToolIo(attrs) : {},
|
|
@@ -4036,30 +4584,30 @@ function intelligenceSpansToSpans(rows) {
|
|
|
4036
4584
|
"traces.row.index": index
|
|
4037
4585
|
};
|
|
4038
4586
|
copyDefined(extra, {
|
|
4039
|
-
"traces.row.id":
|
|
4587
|
+
"traces.row.id": stringValue2(row.id),
|
|
4040
4588
|
[ATTR.SESSION_ID]: sessionId,
|
|
4041
4589
|
"session.id": sessionId,
|
|
4042
|
-
"run.id":
|
|
4043
|
-
"thread.id":
|
|
4044
|
-
"scenario.id":
|
|
4045
|
-
"generation.id":
|
|
4046
|
-
"cell.id":
|
|
4047
|
-
"project.key":
|
|
4048
|
-
"redaction.version":
|
|
4049
|
-
"traces.received_at":
|
|
4590
|
+
"run.id": stringValue2(row.run_id),
|
|
4591
|
+
"thread.id": stringValue2(row.thread_id),
|
|
4592
|
+
"scenario.id": stringValue2(row.scenario_id),
|
|
4593
|
+
"generation.id": stringValue2(row.generation),
|
|
4594
|
+
"cell.id": stringValue2(row.cell_id),
|
|
4595
|
+
"project.key": stringValue2(row.project_key),
|
|
4596
|
+
"redaction.version": stringValue2(row.redaction_version),
|
|
4597
|
+
"traces.received_at": isoTime2(row.received_at)
|
|
4050
4598
|
});
|
|
4051
4599
|
return span({
|
|
4052
|
-
traceId:
|
|
4053
|
-
spanId:
|
|
4054
|
-
parentSpanId:
|
|
4600
|
+
traceId: stringValue2(row.trace_id) ?? hashId(row, 32),
|
|
4601
|
+
spanId: stringValue2(row.id) ?? hashId({ row, field: "span" }),
|
|
4602
|
+
parentSpanId: stringValue2(row.parent_span_id) ?? null,
|
|
4055
4603
|
name,
|
|
4056
4604
|
kind,
|
|
4057
4605
|
startTime,
|
|
4058
4606
|
endTime,
|
|
4059
4607
|
status: statusCode(row.status_code ?? sourceStatus?.code),
|
|
4060
|
-
statusMessage:
|
|
4061
|
-
service:
|
|
4062
|
-
agent:
|
|
4608
|
+
statusMessage: stringValue2(row.status_message) ?? stringValue2(sourceStatus?.message),
|
|
4609
|
+
service: stringValue2(attrs["service.name"]) ?? stringValue2(attrs.service_name) ?? "tangle-intelligence",
|
|
4610
|
+
agent: stringValue2(attrs["agent.name"]) ?? stringValue2(attrs["service.name"]) ?? null,
|
|
4063
4611
|
model,
|
|
4064
4612
|
tool,
|
|
4065
4613
|
inputTokens: firstNumber(row, attrs, ["input_tokens", ...LLM_INPUT_TOKEN_ATTR_KEYS]),
|
|
@@ -4069,7 +4617,7 @@ function intelligenceSpansToSpans(rows) {
|
|
|
4069
4617
|
cacheWriteInputTokens: firstNumber(row, attrs, LLM_CACHE_WRITE_TOKEN_ATTR_KEYS),
|
|
4070
4618
|
costUsd: firstNumber(row, attrs, ["cost_usd", ...LLM_COST_ATTR_KEYS]),
|
|
4071
4619
|
step: numberValue(attrs["interaction.sequence"]) ?? index,
|
|
4072
|
-
content:
|
|
4620
|
+
content: stringValue2(attrs.user_prompt) ?? stringValue2(attrs["gen_ai.prompt.messages"]) ?? stringValue2(attrs["log.content.task"]) ?? null,
|
|
4073
4621
|
extra
|
|
4074
4622
|
});
|
|
4075
4623
|
});
|
|
@@ -4077,10 +4625,10 @@ function intelligenceSpansToSpans(rows) {
|
|
|
4077
4625
|
function openInferenceToSpans(rows) {
|
|
4078
4626
|
return rows.map((row) => {
|
|
4079
4627
|
const attrs = isObject(row.attributes) ? { ...row.attributes } : {};
|
|
4080
|
-
const resource =
|
|
4081
|
-
const resourceAttrs =
|
|
4628
|
+
const resource = objectValue2(row.resource);
|
|
4629
|
+
const resourceAttrs = objectValue2(resource?.attributes);
|
|
4082
4630
|
if (resourceAttrs) copyDefined(attrs, resourceAttrs);
|
|
4083
|
-
const name =
|
|
4631
|
+
const name = stringValue2(row.name) ?? "span";
|
|
4084
4632
|
const declaredKind = otlpSpanKind(attrs["openinference.span.kind"]) ?? otlpSpanKind(row.kind);
|
|
4085
4633
|
const kind = isToolTelemetrySpan(attrs) ? "CHAIN" : declaredKind ?? intelligenceSpanKind(name, attrs);
|
|
4086
4634
|
if (declaredKind && declaredKind !== kind) {
|
|
@@ -4088,15 +4636,15 @@ function openInferenceToSpans(rows) {
|
|
|
4088
4636
|
}
|
|
4089
4637
|
attrs["openinference.span.kind"] = kind;
|
|
4090
4638
|
if (kind === "TOOL") Object.assign(attrs, capturedToolIo(attrs));
|
|
4091
|
-
const status =
|
|
4092
|
-
const message =
|
|
4639
|
+
const status = objectValue2(row.status);
|
|
4640
|
+
const message = stringValue2(status?.message);
|
|
4093
4641
|
return {
|
|
4094
|
-
trace_id:
|
|
4095
|
-
span_id:
|
|
4096
|
-
parent_span_id:
|
|
4642
|
+
trace_id: stringValue2(row.trace_id) ?? hashId(row, 32),
|
|
4643
|
+
span_id: stringValue2(row.span_id) ?? hashId({ row, field: "span" }),
|
|
4644
|
+
parent_span_id: stringValue2(row.parent_span_id) || null,
|
|
4097
4645
|
name,
|
|
4098
|
-
start_time:
|
|
4099
|
-
end_time:
|
|
4646
|
+
start_time: stringValue2(row.start_time) ?? (/* @__PURE__ */ new Date(0)).toISOString(),
|
|
4647
|
+
end_time: stringValue2(row.end_time) ?? stringValue2(row.start_time) ?? (/* @__PURE__ */ new Date(0)).toISOString(),
|
|
4100
4648
|
status: { code: statusCode(status?.code), ...message ? { message } : {} },
|
|
4101
4649
|
attributes: attrs
|
|
4102
4650
|
};
|
|
@@ -4218,7 +4766,7 @@ function topLoopTools(row) {
|
|
|
4218
4766
|
current.maxOccurrences = Math.max(current.maxOccurrences, loop.occurrences);
|
|
4219
4767
|
byTool.set(loop.toolName, current);
|
|
4220
4768
|
}
|
|
4221
|
-
const loops = [...byTool.entries()].sort(([, a], [, b]) => b.groups - a.groups || b.maxOccurrences - a.maxOccurrences).slice(0, 3).map(([
|
|
4769
|
+
const loops = [...byTool.entries()].sort(([, a], [, b]) => b.groups - a.groups || b.maxOccurrences - a.maxOccurrences).slice(0, 3).map(([toolName4, stats]) => `${toolName4} ${stats.groups} group(s), max x${stats.maxOccurrences}`);
|
|
4222
4770
|
const suffix = row.signals.stuckLoopsOmitted > 0 ? `, +${row.signals.stuckLoopsOmitted} omitted` : "";
|
|
4223
4771
|
return loops.length > 0 ? `${loops.join(", ")}${suffix}` : `${row.signals.stuckLoopCount} loop(s)`;
|
|
4224
4772
|
}
|
|
@@ -4484,7 +5032,7 @@ async function writeInspectionReportFile(report, outPath) {
|
|
|
4484
5032
|
// src/adoption.ts
|
|
4485
5033
|
import { readFile as readFile4 } from "fs/promises";
|
|
4486
5034
|
import { join as join15 } from "path";
|
|
4487
|
-
function
|
|
5035
|
+
function toolName2(s) {
|
|
4488
5036
|
const n = s.attributes["tool.name"];
|
|
4489
5037
|
if (typeof n === "string") return n;
|
|
4490
5038
|
if (s.name.startsWith("tool.")) return s.name.slice("tool.".length);
|
|
@@ -4643,7 +5191,7 @@ async function analyzeAdoption(spans, opts = {}) {
|
|
|
4643
5191
|
sessionsWithSkillFileReference.add(group);
|
|
4644
5192
|
addCounts(skillDocumentReads, skillDocuments);
|
|
4645
5193
|
}
|
|
4646
|
-
const tn =
|
|
5194
|
+
const tn = toolName2(s);
|
|
4647
5195
|
if (tn === "Skill") {
|
|
4648
5196
|
sessionCapabilities.set(group, "supported");
|
|
4649
5197
|
const name = skillNameOf(parseInput(s));
|
|
@@ -5877,12 +6425,12 @@ function spanKind2(span2) {
|
|
|
5877
6425
|
function spanContent(span2) {
|
|
5878
6426
|
return asString(span2.attributes.content) ?? asString(span2.attributes["input.value"]) ?? asString(span2.attributes["llm.input"]) ?? asString(span2.attributes["gen_ai.prompt"]) ?? "";
|
|
5879
6427
|
}
|
|
5880
|
-
function
|
|
6428
|
+
function toolName3(span2) {
|
|
5881
6429
|
return asString(span2.attributes["tool.name"]) ?? span2.name.replace(/^tool\./, "");
|
|
5882
6430
|
}
|
|
5883
6431
|
function toolSignature(span2) {
|
|
5884
6432
|
const content = spanContent(span2).replace(/\s+/g, " ").trim();
|
|
5885
|
-
return `${
|
|
6433
|
+
return `${toolName3(span2)}:${content || span2.name}`;
|
|
5886
6434
|
}
|
|
5887
6435
|
function isTool(span2) {
|
|
5888
6436
|
return spanKind2(span2) === "TOOL" || span2.attributes["tool.name"] != null || span2.name.startsWith("tool.");
|
|
@@ -5892,15 +6440,15 @@ function isTextSpan(span2) {
|
|
|
5892
6440
|
}
|
|
5893
6441
|
function isVerification(span2) {
|
|
5894
6442
|
if (!isTool(span2)) return false;
|
|
5895
|
-
return VERIFY_RE.test(`${
|
|
6443
|
+
return VERIFY_RE.test(`${toolName3(span2)} ${span2.name} ${spanContent(span2)}`);
|
|
5896
6444
|
}
|
|
5897
6445
|
function isChange(span2) {
|
|
5898
6446
|
if (!isTool(span2)) return false;
|
|
5899
|
-
return CHANGE_RE.test(`${
|
|
6447
|
+
return CHANGE_RE.test(`${toolName3(span2)} ${span2.name} ${spanContent(span2)}`);
|
|
5900
6448
|
}
|
|
5901
6449
|
function isRead(span2) {
|
|
5902
6450
|
if (!isTool(span2)) return false;
|
|
5903
|
-
return /\b(read|cat|less|grep|rg|sed -n|nl|ls|find|git show|git diff)\b/i.test(`${
|
|
6451
|
+
return /\b(read|cat|less|grep|rg|sed -n|nl|ls|find|git show|git diff)\b/i.test(`${toolName3(span2)} ${span2.name} ${spanContent(span2)}`);
|
|
5904
6452
|
}
|
|
5905
6453
|
function actionKind(span2) {
|
|
5906
6454
|
if (isVerification(span2)) return "verify";
|
|
@@ -5912,7 +6460,7 @@ function actionKind(span2) {
|
|
|
5912
6460
|
}
|
|
5913
6461
|
function classifyLiveActions(spans) {
|
|
5914
6462
|
return orderSpans(spans).map((span2) => {
|
|
5915
|
-
const tool = isTool(span2) ?
|
|
6463
|
+
const tool = isTool(span2) ? toolName3(span2) : void 0;
|
|
5916
6464
|
return {
|
|
5917
6465
|
kind: actionKind(span2),
|
|
5918
6466
|
spanId: span2.span_id,
|
|
@@ -6807,6 +7355,8 @@ export {
|
|
|
6807
7355
|
CORRECTIVE_REACTIONS,
|
|
6808
7356
|
ClaudeAdapter,
|
|
6809
7357
|
CodexAdapter,
|
|
7358
|
+
CodexExecStreamError,
|
|
7359
|
+
CodexExecAdapter,
|
|
6810
7360
|
CopilotAdapter,
|
|
6811
7361
|
FactoryAdapter,
|
|
6812
7362
|
ForgeAdapter,
|