@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.
@@ -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 { spans: [], toolSpanByUseId: /* @__PURE__ */ new Map(), step: startStep, sawUserTurn: false };
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 ?? `${ctx.idPrefix}step${state.step}`, ctx, state);
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 llmId = `${ctx.idPrefix}${uid}`;
1036
- state.spans.push(
1037
- span({
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: llmId,
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
- content: event.content
1053
- })
1054
- );
1055
- state.step += 1;
1056
- let toolIdx = 0;
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: `${ctx.idPrefix}${uid}:tool:${toolIdx}`,
1061
- parentSpanId: llmId,
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 += 1;
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 `<session>/subagents/agent-*.jsonl`, parenting each under its Agent call. */
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
- let files;
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
- if (!file.endsWith(".jsonl")) continue;
1231
- const hash2 = basename2(file, ".jsonl");
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: `${hash2}:`,
1245
- rootParent: parent
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
- join2(subDir, file),
1335
+ file,
1251
1336
  sessionJsonlOptions(ref, options)
1252
1337
  )) {
1253
- consumeDistinctClaudeEvent(event, ctx, state, seen, join2(subDir, file));
1338
+ consumeDistinctClaudeEvent(event, ctx, state, seen, file);
1254
1339
  }
1255
1340
  const parsed = finishClaudeStream(state);
1256
- appendAll(out, parsed.spans);
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 exitCode = header.match(/^Process exited with code\s+(-?\d+)\b/im)?.[1];
1339
- if (exitCode !== void 0) return Number(exitCode) !== 0;
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 eventTime = typeof occurredAtMs === "number" && Number.isFinite(occurredAtMs) ? new Date(occurredAtMs).toISOString() : ts;
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: eventTime,
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 = eventTime;
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 = eventTime;
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 SERVICE4 = "github-copilot";
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: SERVICE4,
1774
- agent: SERVICE4,
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: SERVICE4,
1795
- agent: SERVICE4,
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: SERVICE4,
1822
- agent: SERVICE4
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 SERVICE5 = "factory";
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: SERVICE5,
1921
- agent: SERVICE5,
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: SERVICE5,
1938
- agent: SERVICE5,
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: SERVICE5,
1957
- agent: SERVICE5,
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: SERVICE5,
1989
- agent: SERVICE5,
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 SERVICE6 = "forge";
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: SERVICE6, agent: SERVICE6 })
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: SERVICE6,
2072
- agent: SERVICE6,
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: SERVICE6,
2089
- agent: SERVICE6,
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: SERVICE6,
2109
- agent: SERVICE6,
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 SERVICE7 = "opencode";
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: SERVICE7,
2370
- agent: SERVICE7
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: SERVICE7,
2399
- agent: SERVICE7,
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: SERVICE7,
2417
- agent: SERVICE7,
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: SERVICE7,
2445
- agent: SERVICE7,
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 SERVICE8 = "pi";
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: SERVICE8,
2555
- agent: SERVICE8,
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: SERVICE8,
2575
- agent: SERVICE8,
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: SERVICE8,
2597
- agent: SERVICE8,
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: SERVICE8,
2632
- agent: SERVICE8
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 SERVICE9 = "qwen";
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: SERVICE9,
2719
- agent: SERVICE9,
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: SERVICE9,
2739
- agent: SERVICE9,
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: SERVICE9,
2760
- agent: SERVICE9,
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: SERVICE9,
2796
- agent: SERVICE9
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 stringValue(value) {
4139
+ function stringValue2(value) {
3593
4140
  return typeof value === "string" && value.length > 0 ? value : void 0;
3594
4141
  }
3595
- function objectValue(value) {
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 isoTime(value) {
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 = isoTime(obj[key]);
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 stringValue(row.type) ?? stringValue(row.event) ?? stringValue(row.name) ?? stringValue(row.kind);
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 = stringValue(value[key]);
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, objectValue(row.data), objectValue(row.payload)];
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 = stringValue(row.error) ?? findStringKey(row, ["error", "errorMessage", "error_message"], 3);
4394
+ const errorMessage = stringValue2(row.error) ?? findStringKey(row, ["error", "errorMessage", "error_message"], 3);
3847
4395
  if (lowered.includes("error") || errorMessage) {
3848
- const message = errorMessage ?? stringValue(row.message);
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 toolInput = kind === "TOOL" ? eventValue(row, ["toolInput", "tool_input", "input", "args", "arguments", "full_command"]) : void 0;
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: toolInput,
4453
+ input: toolInput2,
3906
4454
  output: eventValue(row, ["toolOutput", "tool_output", "output", "result"]),
3907
- argsCaptured: toolInput !== void 0
4455
+ argsCaptured: toolInput2 !== void 0
3908
4456
  }) : {}
3909
4457
  };
3910
4458
  copyDefined(extra, {
3911
- "traces.event.id": stringValue(row.id),
3912
- "traces.event.raw_type": stringValue(row.type),
3913
- "traces.event.raw_event": stringValue(row.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
- LLM_INPUT_TOKENS,
3975
- LLM_OUTPUT_TOKENS,
4522
+ LLM_INPUT_TOKENS2,
4523
+ LLM_OUTPUT_TOKENS2,
3976
4524
  LLM_REASONING_TOKENS,
3977
- LLM_CACHED_TOKENS,
3978
- LLM_CACHE_WRITE_TOKENS,
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 = stringValue(attrs["span.type"])?.toLowerCase();
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 = stringValue(attrs["span.type"])?.toLowerCase();
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 = { ...objectValue(row.attributes) ?? {} };
4024
- const name = stringValue(row.name) ?? "intelligence.span";
4025
- const startTime = unixNanoTime(row.start_unix_nano) ?? isoTime(row.start_time) ?? stringValue(row.start_time) ?? isoTime(row.received_at) ?? (/* @__PURE__ */ new Date(0)).toISOString();
4026
- const endTime = unixNanoTime(row.end_unix_nano) ?? isoTime(row.end_time) ?? stringValue(row.end_time) ?? startTime;
4027
- const model = stringValue(row.model) ?? stringValue(attrs["llm.model_name"]) ?? stringValue(attrs.model) ?? stringValue(attrs["gen_ai.request.model"]);
4028
- const tool = stringValue(attrs["tool.name"]) ?? stringValue(attrs.tool_name) ?? stringValue(attrs["mcp.tool.name"]);
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 = objectValue(row.status);
4031
- const sessionId = stringValue(row.session_id) ?? sessionIdFromAttributes(attrs);
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": stringValue(row.id),
4587
+ "traces.row.id": stringValue2(row.id),
4040
4588
  [ATTR.SESSION_ID]: sessionId,
4041
4589
  "session.id": sessionId,
4042
- "run.id": stringValue(row.run_id),
4043
- "thread.id": stringValue(row.thread_id),
4044
- "scenario.id": stringValue(row.scenario_id),
4045
- "generation.id": stringValue(row.generation),
4046
- "cell.id": stringValue(row.cell_id),
4047
- "project.key": stringValue(row.project_key),
4048
- "redaction.version": stringValue(row.redaction_version),
4049
- "traces.received_at": isoTime(row.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: stringValue(row.trace_id) ?? hashId(row, 32),
4053
- spanId: stringValue(row.id) ?? hashId({ row, field: "span" }),
4054
- parentSpanId: stringValue(row.parent_span_id) ?? null,
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: stringValue(row.status_message) ?? stringValue(sourceStatus?.message),
4061
- service: stringValue(attrs["service.name"]) ?? stringValue(attrs.service_name) ?? "tangle-intelligence",
4062
- agent: stringValue(attrs["agent.name"]) ?? stringValue(attrs["service.name"]) ?? null,
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: stringValue(attrs.user_prompt) ?? stringValue(attrs["gen_ai.prompt.messages"]) ?? stringValue(attrs["log.content.task"]) ?? null,
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 = objectValue(row.resource);
4081
- const resourceAttrs = objectValue(resource?.attributes);
4628
+ const resource = objectValue2(row.resource);
4629
+ const resourceAttrs = objectValue2(resource?.attributes);
4082
4630
  if (resourceAttrs) copyDefined(attrs, resourceAttrs);
4083
- const name = stringValue(row.name) ?? "span";
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 = objectValue(row.status);
4092
- const message = stringValue(status?.message);
4639
+ const status = objectValue2(row.status);
4640
+ const message = stringValue2(status?.message);
4093
4641
  return {
4094
- trace_id: stringValue(row.trace_id) ?? hashId(row, 32),
4095
- span_id: stringValue(row.span_id) ?? hashId({ row, field: "span" }),
4096
- parent_span_id: stringValue(row.parent_span_id) || null,
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: stringValue(row.start_time) ?? (/* @__PURE__ */ new Date(0)).toISOString(),
4099
- end_time: stringValue(row.end_time) ?? stringValue(row.start_time) ?? (/* @__PURE__ */ new Date(0)).toISOString(),
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(([toolName3, stats]) => `${toolName3} ${stats.groups} group(s), max x${stats.maxOccurrences}`);
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 toolName(s) {
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 = toolName(s);
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 toolName2(span2) {
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 `${toolName2(span2)}:${content || span2.name}`;
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(`${toolName2(span2)} ${span2.name} ${spanContent(span2)}`);
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(`${toolName2(span2)} ${span2.name} ${spanContent(span2)}`);
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(`${toolName2(span2)} ${span2.name} ${spanContent(span2)}`);
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) ? toolName2(span2) : void 0;
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,