@tangle-network/traces 0.9.7 → 0.9.9

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 CHANGED
@@ -81,6 +81,7 @@ The deterministic pass (free, no key) surfaces:
81
81
  |---|---|---|
82
82
  | `claude-code` (`claude`, `claudish`, `openclaw`, `nanoclaw`) | `~/.claude/projects/<cwd>/*.jsonl` (+ subagent sidechains) | verified |
83
83
  | `codex` (`codex-acp`) | `~/.codex/sessions/**/rollout-*.jsonl` | verified |
84
+ | `codex-exec` (`codex-json`) | explicit `codex exec --json` JSONL file | fixture |
84
85
  | `opencode` | `~/.local/share/opencode/storage/` | verified |
85
86
  | `gemini` (`gemini-cli`) | `~/.gemini/tmp/<hash>/chats/session-*.json` | verified |
86
87
  | `pi` | `~/.pi/agent/sessions/<cwd>/*.jsonl` | verified |
@@ -105,6 +106,7 @@ traces convert --harness claude-code --last 1 --otlp spans.jsonl # OTLP only
105
106
  traces index --all --since 24h --out session-index.json
106
107
  traces inspect session-index.json --out inspection-report.md
107
108
  traces evidence --harness codex --last 20 --out policy-evidence.jsonl
109
+ traces evidence --harness codex-exec --session /tmp/codex.jsonl --cwd "$PWD" --out policy-evidence.jsonl
108
110
  traces export policy-evidence.jsonl --out spans.openinference.jsonl
109
111
  traces watch --all # live observer; loops + semantic findings
110
112
  traces stream --all --mode findings # low-volume semantic feed
@@ -249,10 +251,13 @@ traces evidence --all --since 24h --out policy-evidence.jsonl --otlp spans.otlp.
249
251
 
250
252
  `--last` follows recent file activity and is useful for live inspection.
251
253
  For a reproducible report, run `traces list` first and pass its session ID with `--session`.
254
+ Ephemeral `codex exec --json` output has no discovery location, so select its file with `--harness codex-exec --session <path>`.
255
+ The adapter rejects empty, incomplete, or other JSONL formats instead of emitting zero-valued session evidence.
252
256
 
253
257
  Each JSONL row is one session:
254
258
 
255
259
  - session provenance: harness, session id, cwd, path, mtime
260
+ - explicit-session source binding: `provenance.sourceSha256` over the exact stable input bytes
256
261
  - repo labels: `tangle.subject.key`, `git.repository`, branch, commit
257
262
  - behavior metrics: span counts, LLM turns, tool calls, errored tool calls, tokens, models, tool histogram
258
263
  - mining signals: stuck loops and tool error rate
@@ -1439,8 +1439,8 @@ function explicitOutputError(value) {
1439
1439
  const outputStart = text.indexOf("\nOutput:\n");
1440
1440
  const header = outputStart >= 0 ? text.slice(0, outputStart) : text;
1441
1441
  if (/^Chunk ID:/i.test(header)) {
1442
- const exitCode = header.match(/^Process exited with code\s+(-?\d+)\b/im)?.[1];
1443
- 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;
1444
1444
  }
1445
1445
  if (/^Script completed\s*\nWall time:/i.test(header)) return false;
1446
1446
  if (/^Script failed\s*\nWall time:/i.test(header)) return true;
@@ -1716,7 +1716,7 @@ var CodexAdapter = class {
1716
1716
  } else if (l.type === "event_msg" && l.payload?.type === "sub_agent_activity") {
1717
1717
  const threadId = l.payload.agent_thread_id;
1718
1718
  const occurredAtMs = l.payload.occurred_at_ms;
1719
- 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;
1720
1720
  if (l.payload.kind === "started" && threadId && !subagentByThreadId.has(threadId)) {
1721
1721
  const agentPath = l.payload.agent_path ?? "subagent";
1722
1722
  const subagentType = agentPath.split("/").filter(Boolean).at(-1) ?? "subagent";
@@ -1726,7 +1726,7 @@ var CodexAdapter = class {
1726
1726
  parentSpanId: lastLlm,
1727
1727
  name: "tool.Agent",
1728
1728
  kind: "TOOL",
1729
- startTime: eventTime,
1729
+ startTime: eventTime2,
1730
1730
  service: SERVICE3,
1731
1731
  agent: SERVICE3,
1732
1732
  tool: "Agent",
@@ -1749,13 +1749,13 @@ var CodexAdapter = class {
1749
1749
  } else if (l.payload.kind === "completed" && threadId) {
1750
1750
  const toolSpan = subagentByThreadId.get(threadId);
1751
1751
  if (toolSpan) {
1752
- toolSpan.end_time = eventTime;
1752
+ toolSpan.end_time = eventTime2;
1753
1753
  toolSpan.status = { code: "OK" };
1754
1754
  }
1755
1755
  } else if (["interrupted", "failed", "timed_out"].includes(l.payload.kind ?? "") && threadId) {
1756
1756
  const toolSpan = subagentByThreadId.get(threadId);
1757
1757
  if (toolSpan) {
1758
- toolSpan.end_time = eventTime;
1758
+ toolSpan.end_time = eventTime2;
1759
1759
  toolSpan.status = { code: "ERROR", message: `subagent ${l.payload.kind}` };
1760
1760
  }
1761
1761
  }
@@ -1805,11 +1805,449 @@ var CodexAdapter = class {
1805
1805
  }
1806
1806
  };
1807
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
+
1808
2246
  // src/adapters/copilot.ts
1809
2247
  import { readdir as readdir5, stat as stat4 } from "fs/promises";
1810
2248
  import { homedir as homedir4 } from "os";
1811
2249
  import { join as join4 } from "path";
1812
- var SERVICE4 = "github-copilot";
2250
+ var SERVICE5 = "github-copilot";
1813
2251
  var CopilotAdapter = class {
1814
2252
  harness = "github-copilot";
1815
2253
  aliases = ["copilot"];
@@ -1874,8 +2312,8 @@ var CopilotAdapter = class {
1874
2312
  name: "message.assistant",
1875
2313
  kind: "LLM",
1876
2314
  startTime: ts,
1877
- service: SERVICE4,
1878
- agent: SERVICE4,
2315
+ service: SERVICE5,
2316
+ agent: SERVICE5,
1879
2317
  model: d.model ?? null,
1880
2318
  inputTokens: pendingInputTokens,
1881
2319
  outputTokens: d.outputTokens ?? null,
@@ -1895,8 +2333,8 @@ var CopilotAdapter = class {
1895
2333
  name: `tool.${name}`,
1896
2334
  kind: "TOOL",
1897
2335
  startTime: ts,
1898
- service: SERVICE4,
1899
- agent: SERVICE4,
2336
+ service: SERVICE5,
2337
+ agent: SERVICE5,
1900
2338
  tool: name,
1901
2339
  step,
1902
2340
  extra: toolIoAttributes({ input: d.arguments })
@@ -1922,8 +2360,8 @@ var CopilotAdapter = class {
1922
2360
  kind: "AGENT",
1923
2361
  startTime: firstTimestamp ?? (/* @__PURE__ */ new Date(0)).toISOString(),
1924
2362
  endTime: lastTimestamp,
1925
- service: SERVICE4,
1926
- agent: SERVICE4
2363
+ service: SERVICE5,
2364
+ agent: SERVICE5
1927
2365
  });
1928
2366
  return [root, ...spans];
1929
2367
  }
@@ -1933,7 +2371,7 @@ var CopilotAdapter = class {
1933
2371
  import { readdir as readdir6, stat as stat5 } from "fs/promises";
1934
2372
  import { homedir as homedir5 } from "os";
1935
2373
  import { basename as basename4, join as join5 } from "path";
1936
- var SERVICE5 = "factory";
2374
+ var SERVICE6 = "factory";
1937
2375
  function textOf4(content) {
1938
2376
  return capText(
1939
2377
  (content ?? []).filter((b) => b.type === "text" && typeof b.text === "string").map((b) => b.text).join("")
@@ -2021,8 +2459,8 @@ var FactoryAdapter = class {
2021
2459
  parentSpanId: sourceRootId,
2022
2460
  startTime: ts,
2023
2461
  content: text,
2024
- service: SERVICE5,
2025
- agent: SERVICE5,
2462
+ service: SERVICE6,
2463
+ agent: SERVICE6,
2026
2464
  step
2027
2465
  })
2028
2466
  );
@@ -2038,8 +2476,8 @@ var FactoryAdapter = class {
2038
2476
  name: `message.${role ?? "unknown"}`,
2039
2477
  kind: "LLM",
2040
2478
  startTime: ts,
2041
- service: SERVICE5,
2042
- agent: SERVICE5,
2479
+ service: SERVICE6,
2480
+ agent: SERVICE6,
2043
2481
  model: settings.model ?? null,
2044
2482
  step,
2045
2483
  content: text || null
@@ -2057,8 +2495,8 @@ var FactoryAdapter = class {
2057
2495
  name: `tool.${b.name}`,
2058
2496
  kind: "TOOL",
2059
2497
  startTime: ts,
2060
- service: SERVICE5,
2061
- agent: SERVICE5,
2498
+ service: SERVICE6,
2499
+ agent: SERVICE6,
2062
2500
  tool: b.name,
2063
2501
  step,
2064
2502
  extra: toolIoAttributes({ input: b.input })
@@ -2089,8 +2527,8 @@ var FactoryAdapter = class {
2089
2527
  kind: "AGENT",
2090
2528
  startTime: sessionLine?.timestamp ?? firstTimestamp ?? (/* @__PURE__ */ new Date(0)).toISOString(),
2091
2529
  endTime: lastTimestamp,
2092
- service: SERVICE5,
2093
- agent: SERVICE5,
2530
+ service: SERVICE6,
2531
+ agent: SERVICE6,
2094
2532
  model: settings.model ?? null,
2095
2533
  extra: settings.tokenUsage ? {
2096
2534
  "session.input_tokens": settings.tokenUsage.inputTokens ?? 0,
@@ -2105,7 +2543,7 @@ var FactoryAdapter = class {
2105
2543
  import { readdir as readdir7, stat as stat6 } from "fs/promises";
2106
2544
  import { homedir as homedir6 } from "os";
2107
2545
  import { basename as basename5, join as join6 } from "path";
2108
- var SERVICE6 = "forge";
2546
+ var SERVICE7 = "forge";
2109
2547
  function tokens(t) {
2110
2548
  if (!t) return null;
2111
2549
  return t.actual ?? t.approx ?? null;
@@ -2154,7 +2592,7 @@ var ForgeAdapter = class {
2154
2592
  const rootId = `root:${traceId}`;
2155
2593
  const messages = ctx.messages ?? [];
2156
2594
  const spans = [
2157
- 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 })
2158
2596
  ];
2159
2597
  const toolByCallId = /* @__PURE__ */ new Map();
2160
2598
  let step = 0;
@@ -2172,8 +2610,8 @@ var ForgeAdapter = class {
2172
2610
  parentSpanId: rootId,
2173
2611
  startTime: ts,
2174
2612
  content: prompt,
2175
- service: SERVICE6,
2176
- agent: SERVICE6,
2613
+ service: SERVICE7,
2614
+ agent: SERVICE7,
2177
2615
  step
2178
2616
  })
2179
2617
  );
@@ -2189,8 +2627,8 @@ var ForgeAdapter = class {
2189
2627
  name: `message.${entry.text.role ?? "unknown"}`,
2190
2628
  kind: "LLM",
2191
2629
  startTime: ts,
2192
- service: SERVICE6,
2193
- agent: SERVICE6,
2630
+ service: SERVICE7,
2631
+ agent: SERVICE7,
2194
2632
  model: entry.text.model ?? null,
2195
2633
  inputTokens: tokens(raw.usage?.prompt_tokens),
2196
2634
  outputTokens: tokens(raw.usage?.completion_tokens),
@@ -2209,8 +2647,8 @@ var ForgeAdapter = class {
2209
2647
  name: `tool.${tc.name ?? "tool"}`,
2210
2648
  kind: "TOOL",
2211
2649
  startTime: ts,
2212
- service: SERVICE6,
2213
- agent: SERVICE6,
2650
+ service: SERVICE7,
2651
+ agent: SERVICE7,
2214
2652
  tool: tc.name ?? "tool",
2215
2653
  step,
2216
2654
  extra: toolIoAttributes({ input: tc.arguments })
@@ -2410,7 +2848,7 @@ var GeminiAdapter = class extends GeminiFamilyAdapter {
2410
2848
  import { readdir as readdir9, stat as stat8 } from "fs/promises";
2411
2849
  import { homedir as homedir8 } from "os";
2412
2850
  import { join as join8 } from "path";
2413
- var SERVICE7 = "opencode";
2851
+ var SERVICE8 = "opencode";
2414
2852
  function isoOf(ms) {
2415
2853
  return new Date(ms ?? 0).toISOString();
2416
2854
  }
@@ -2470,8 +2908,8 @@ var OpencodeAdapter = class {
2470
2908
  kind: "AGENT",
2471
2909
  startTime: isoOf(messages[0]?.time?.created),
2472
2910
  endTime: isoOf(messages.at(-1)?.time?.completed ?? messages.at(-1)?.time?.created),
2473
- service: SERVICE7,
2474
- agent: SERVICE7
2911
+ service: SERVICE8,
2912
+ agent: SERVICE8
2475
2913
  })
2476
2914
  ];
2477
2915
  let step = 0;
@@ -2499,8 +2937,8 @@ var OpencodeAdapter = class {
2499
2937
  spanId: `${llmId}:user`,
2500
2938
  parentSpanId: rootId,
2501
2939
  startTime: isoOf(msg.time?.created),
2502
- service: SERVICE7,
2503
- agent: SERVICE7,
2940
+ service: SERVICE8,
2941
+ agent: SERVICE8,
2504
2942
  step,
2505
2943
  content: turnText
2506
2944
  })
@@ -2517,8 +2955,8 @@ var OpencodeAdapter = class {
2517
2955
  kind: "LLM",
2518
2956
  startTime: isoOf(msg.time?.created),
2519
2957
  endTime: isoOf(msg.time?.completed ?? msg.time?.created),
2520
- service: SERVICE7,
2521
- agent: SERVICE7,
2958
+ service: SERVICE8,
2959
+ agent: SERVICE8,
2522
2960
  model: msg.modelID ?? null,
2523
2961
  inputTokens: msg.tokens?.input ?? null,
2524
2962
  outputTokens: msg.tokens?.output ?? null,
@@ -2545,8 +2983,8 @@ var OpencodeAdapter = class {
2545
2983
  endTime: isoOf(msg.time?.completed ?? msg.time?.created),
2546
2984
  status: err ? "ERROR" : "OK",
2547
2985
  statusMessage: err ? (part.state?.error ?? "").slice(0, 500) : void 0,
2548
- service: SERVICE7,
2549
- agent: SERVICE7,
2986
+ service: SERVICE8,
2987
+ agent: SERVICE8,
2550
2988
  tool: part.tool,
2551
2989
  step,
2552
2990
  extra: toolIoAttributes({
@@ -2566,7 +3004,7 @@ var OpencodeAdapter = class {
2566
3004
  import { readdir as readdir10, stat as stat9 } from "fs/promises";
2567
3005
  import { homedir as homedir9 } from "os";
2568
3006
  import { basename as basename6, join as join9 } from "path";
2569
- var SERVICE8 = "pi";
3007
+ var SERVICE9 = "pi";
2570
3008
  function isToolBlock(b) {
2571
3009
  return typeof b.type === "string" && /tool[_-]?call|tool[_-]?use|^tool$/i.test(b.type);
2572
3010
  }
@@ -2655,8 +3093,8 @@ var PiAdapter = class {
2655
3093
  spanId: `${mid}:user`,
2656
3094
  parentSpanId: sourceRootId,
2657
3095
  startTime: ts,
2658
- service: SERVICE8,
2659
- agent: SERVICE8,
3096
+ service: SERVICE9,
3097
+ agent: SERVICE9,
2660
3098
  step,
2661
3099
  content: prompt
2662
3100
  })
@@ -2675,8 +3113,8 @@ var PiAdapter = class {
2675
3113
  startTime: ts,
2676
3114
  status: errored ? "ERROR" : "OK",
2677
3115
  statusMessage: errored ? msg.errorMessage.slice(0, 500) : void 0,
2678
- service: SERVICE8,
2679
- agent: SERVICE8,
3116
+ service: SERVICE9,
3117
+ agent: SERVICE9,
2680
3118
  model: msg.model ?? null,
2681
3119
  inputTokens: msg.usage?.input ?? null,
2682
3120
  outputTokens: msg.usage?.output ?? null,
@@ -2697,8 +3135,8 @@ var PiAdapter = class {
2697
3135
  name: `tool.${name}`,
2698
3136
  kind: "TOOL",
2699
3137
  startTime: ts,
2700
- service: SERVICE8,
2701
- agent: SERVICE8,
3138
+ service: SERVICE9,
3139
+ agent: SERVICE9,
2702
3140
  tool: name,
2703
3141
  step,
2704
3142
  extra: toolIoAttributes({ input: b.input ?? b.args })
@@ -2732,8 +3170,8 @@ var PiAdapter = class {
2732
3170
  kind: "AGENT",
2733
3171
  startTime: sessionLine?.timestamp ?? firstTimestamp ?? (/* @__PURE__ */ new Date(0)).toISOString(),
2734
3172
  endTime: lastTimestamp,
2735
- service: SERVICE8,
2736
- agent: SERVICE8
3173
+ service: SERVICE9,
3174
+ agent: SERVICE9
2737
3175
  });
2738
3176
  return [root, ...spans];
2739
3177
  }
@@ -2743,7 +3181,7 @@ var PiAdapter = class {
2743
3181
  import { readdir as readdir11, stat as stat10 } from "fs/promises";
2744
3182
  import { homedir as homedir10 } from "os";
2745
3183
  import { basename as basename7, join as join10 } from "path";
2746
- var SERVICE9 = "qwen";
3184
+ var SERVICE10 = "qwen";
2747
3185
  function textOf9(parts) {
2748
3186
  if (!parts) return "";
2749
3187
  return parts.filter((p) => typeof p.text === "string").map((p) => p.text).join("");
@@ -2819,8 +3257,8 @@ var QwenAdapter = class {
2819
3257
  name: "llm.turn",
2820
3258
  kind: "LLM",
2821
3259
  startTime: ts,
2822
- service: SERVICE9,
2823
- agent: SERVICE9,
3260
+ service: SERVICE10,
3261
+ agent: SERVICE10,
2824
3262
  model: r.model ?? null,
2825
3263
  inputTokens: r.usageMetadata?.promptTokenCount ?? null,
2826
3264
  outputTokens: r.usageMetadata?.candidatesTokenCount ?? null,
@@ -2839,8 +3277,8 @@ var QwenAdapter = class {
2839
3277
  name: `tool.${name}`,
2840
3278
  kind: "TOOL",
2841
3279
  startTime: ts,
2842
- service: SERVICE9,
2843
- agent: SERVICE9,
3280
+ service: SERVICE10,
3281
+ agent: SERVICE10,
2844
3282
  tool: name,
2845
3283
  step,
2846
3284
  extra: toolIoAttributes({ input: p.functionCall.args })
@@ -2860,8 +3298,8 @@ var QwenAdapter = class {
2860
3298
  spanId: `user:${r.uuid ?? step}`,
2861
3299
  parentSpanId: sourceRootId,
2862
3300
  startTime: ts,
2863
- service: SERVICE9,
2864
- agent: SERVICE9,
3301
+ service: SERVICE10,
3302
+ agent: SERVICE10,
2865
3303
  step,
2866
3304
  content: prompt
2867
3305
  })
@@ -2896,8 +3334,8 @@ var QwenAdapter = class {
2896
3334
  kind: "AGENT",
2897
3335
  startTime: firstTimestamp ?? (/* @__PURE__ */ new Date(0)).toISOString(),
2898
3336
  endTime: lastTimestamp,
2899
- service: SERVICE9,
2900
- agent: SERVICE9
3337
+ service: SERVICE10,
3338
+ agent: SERVICE10
2901
3339
  });
2902
3340
  return [root, ...spans];
2903
3341
  }
@@ -2907,6 +3345,7 @@ var QwenAdapter = class {
2907
3345
  var ADAPTERS = [
2908
3346
  new ClaudeAdapter(),
2909
3347
  new CodexAdapter(),
3348
+ new CodexExecAdapter(),
2910
3349
  new OpencodeAdapter(),
2911
3350
  new GeminiAdapter(),
2912
3351
  new QwenAdapter(),
@@ -3407,6 +3846,9 @@ function summarizeTools(spans) {
3407
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));
3408
3847
  }
3409
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
+ }
3410
3852
  const toolSpans = spans.filter((span2) => spanKind(span2) === "TOOL");
3411
3853
  const erroredToolCallCount = toolSpans.filter((span2) => span2.status.code === "ERROR").length;
3412
3854
  const pipelines = await runPipelines(spans, { minLoopOccurrences: opts.minLoopOccurrences });
@@ -3456,6 +3898,7 @@ async function buildPolicyEvidenceRecord(ref, spans, opts = {}) {
3456
3898
  source: "traces",
3457
3899
  evidenceKind: "session-summary",
3458
3900
  ...opts.otlpPath ? { otlpPath: opts.otlpPath } : {},
3901
+ ...opts.sourceSha256 ? { sourceSha256: opts.sourceSha256 } : {},
3459
3902
  notCampaignCell: true,
3460
3903
  note: "This is normalized coding-agent session evidence for downstream policy mining; it is not an eval campaign cell."
3461
3904
  }
@@ -3693,10 +4136,10 @@ import {
3693
4136
  function isObject(value) {
3694
4137
  return value != null && typeof value === "object" && !Array.isArray(value);
3695
4138
  }
3696
- function stringValue(value) {
4139
+ function stringValue2(value) {
3697
4140
  return typeof value === "string" && value.length > 0 ? value : void 0;
3698
4141
  }
3699
- function objectValue(value) {
4142
+ function objectValue2(value) {
3700
4143
  return isObject(value) ? value : void 0;
3701
4144
  }
3702
4145
  function stableJson(value) {
@@ -3711,7 +4154,7 @@ function stableJson(value) {
3711
4154
  function hashId(value, chars = 16) {
3712
4155
  return createHash5("sha256").update(stableJson(value)).digest("hex").slice(0, chars);
3713
4156
  }
3714
- function isoTime(value) {
4157
+ function isoTime2(value) {
3715
4158
  if (typeof value === "string") {
3716
4159
  const trimmed = value.trim();
3717
4160
  const normalized = /^\d{4}-\d{2}-\d{2}[ T]\d{2}:\d{2}:\d{2}(?:\.\d+)?$/.test(trimmed) ? `${trimmed.replace(" ", "T")}Z` : trimmed;
@@ -3726,7 +4169,7 @@ function isoTime(value) {
3726
4169
  }
3727
4170
  function firstTime(obj, keys) {
3728
4171
  for (const key of keys) {
3729
- const value = isoTime(obj[key]);
4172
+ const value = isoTime2(obj[key]);
3730
4173
  if (value) return value;
3731
4174
  }
3732
4175
  return void 0;
@@ -3787,7 +4230,7 @@ function isIntelligenceSpanRow(row) {
3787
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);
3788
4231
  }
3789
4232
  function eventType(row) {
3790
- 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);
3791
4234
  }
3792
4235
  function looksLikeSandboxEvent(row) {
3793
4236
  if (!isObject(row)) return false;
@@ -3859,6 +4302,7 @@ function policyEvidenceToSpans(records) {
3859
4302
  "traces.provenance.source": record.provenance.source,
3860
4303
  "traces.provenance.evidence_kind": record.provenance.evidenceKind,
3861
4304
  "traces.provenance.otlp_path": record.provenance.otlpPath,
4305
+ "traces.provenance.source_sha256": record.provenance.sourceSha256,
3862
4306
  [ATTR.SUBJECT_KEY]: record.repo.subjectKey,
3863
4307
  [ATTR.GIT_REPOSITORY]: record.repo.repository,
3864
4308
  [ATTR.GIT_BRANCH_NAME]: record.repo.branch,
@@ -3896,7 +4340,7 @@ function findStringKey(value, keys, depth = 4) {
3896
4340
  if (depth < 0) return void 0;
3897
4341
  if (isObject(value)) {
3898
4342
  for (const key of keys) {
3899
- const found = stringValue(value[key]);
4343
+ const found = stringValue2(value[key]);
3900
4344
  if (found) return found;
3901
4345
  }
3902
4346
  for (const child of Object.values(value)) {
@@ -3912,7 +4356,7 @@ function findStringKey(value, keys, depth = 4) {
3912
4356
  return void 0;
3913
4357
  }
3914
4358
  function eventValue(row, keys) {
3915
- const scopes = [row, objectValue(row.data), objectValue(row.payload)];
4359
+ const scopes = [row, objectValue2(row.data), objectValue2(row.payload)];
3916
4360
  for (const scope of scopes) {
3917
4361
  if (!scope) continue;
3918
4362
  for (const key of keys) {
@@ -3947,9 +4391,9 @@ function eventEndTimestamp(row, fallback) {
3947
4391
  }
3948
4392
  function eventStatus(row, type) {
3949
4393
  const lowered = type.toLowerCase();
3950
- 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);
3951
4395
  if (lowered.includes("error") || errorMessage) {
3952
- const message = errorMessage ?? stringValue(row.message);
4396
+ const message = errorMessage ?? stringValue2(row.message);
3953
4397
  return { status: "ERROR", ...message ? { message: capText(message) } : {} };
3954
4398
  }
3955
4399
  return { status: "OK" };
@@ -4000,21 +4444,21 @@ function sandboxEventsToSpans(rows, wrapper) {
4000
4444
  const { status, message } = eventStatus(row, type);
4001
4445
  const { kind, tool } = eventKind(row, type);
4002
4446
  const name = kind === "TOOL" && tool ? `tool.${tool}` : `event.${type}`;
4003
- 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;
4004
4448
  const extra = {
4005
4449
  "traces.source_format": "sandbox-events",
4006
4450
  "traces.event.type": type,
4007
4451
  "traces.event.index": index,
4008
4452
  ...kind === "TOOL" ? toolIoAttributes({
4009
- input: toolInput,
4453
+ input: toolInput2,
4010
4454
  output: eventValue(row, ["toolOutput", "tool_output", "output", "result"]),
4011
- argsCaptured: toolInput !== void 0
4455
+ argsCaptured: toolInput2 !== void 0
4012
4456
  }) : {}
4013
4457
  };
4014
4458
  copyDefined(extra, {
4015
- "traces.event.id": stringValue(row.id),
4016
- "traces.event.raw_type": stringValue(row.type),
4017
- "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)
4018
4462
  });
4019
4463
  spans.push(span({
4020
4464
  traceId,
@@ -4105,7 +4549,7 @@ function capturedToolIo(attrs) {
4105
4549
  });
4106
4550
  }
4107
4551
  function isToolTelemetrySpan(attrs) {
4108
- const spanType = stringValue(attrs["span.type"])?.toLowerCase();
4552
+ const spanType = stringValue2(attrs["span.type"])?.toLowerCase();
4109
4553
  return spanType === "tool.execution" || spanType === "tool.blocked_on_user";
4110
4554
  }
4111
4555
  function otlpSpanKind(value) {
@@ -4113,7 +4557,7 @@ function otlpSpanKind(value) {
4113
4557
  }
4114
4558
  function intelligenceSpanKind(name, attrs) {
4115
4559
  const lowered = name.toLowerCase();
4116
- const spanType = stringValue(attrs["span.type"])?.toLowerCase();
4560
+ const spanType = stringValue2(attrs["span.type"])?.toLowerCase();
4117
4561
  if (spanType === "llm_request" || lowered.includes("llm")) return "LLM";
4118
4562
  if (isToolTelemetrySpan(attrs)) return "CHAIN";
4119
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) {
@@ -4124,15 +4568,15 @@ function intelligenceSpanKind(name, attrs) {
4124
4568
  }
4125
4569
  function intelligenceSpansToSpans(rows) {
4126
4570
  return rows.map((row, index) => {
4127
- const attrs = { ...objectValue(row.attributes) ?? {} };
4128
- const name = stringValue(row.name) ?? "intelligence.span";
4129
- const startTime = unixNanoTime(row.start_unix_nano) ?? isoTime(row.start_time) ?? stringValue(row.start_time) ?? isoTime(row.received_at) ?? (/* @__PURE__ */ new Date(0)).toISOString();
4130
- const endTime = unixNanoTime(row.end_unix_nano) ?? isoTime(row.end_time) ?? stringValue(row.end_time) ?? startTime;
4131
- const model = stringValue(row.model) ?? stringValue(attrs["llm.model_name"]) ?? stringValue(attrs.model) ?? stringValue(attrs["gen_ai.request.model"]);
4132
- 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"]);
4133
4577
  const kind = intelligenceSpanKind(name, attrs);
4134
- const sourceStatus = objectValue(row.status);
4135
- const sessionId = stringValue(row.session_id) ?? sessionIdFromAttributes(attrs);
4578
+ const sourceStatus = objectValue2(row.status);
4579
+ const sessionId = stringValue2(row.session_id) ?? sessionIdFromAttributes(attrs);
4136
4580
  const extra = {
4137
4581
  ...preserveRawAttributes(attrs),
4138
4582
  ...kind === "TOOL" ? capturedToolIo(attrs) : {},
@@ -4140,30 +4584,30 @@ function intelligenceSpansToSpans(rows) {
4140
4584
  "traces.row.index": index
4141
4585
  };
4142
4586
  copyDefined(extra, {
4143
- "traces.row.id": stringValue(row.id),
4587
+ "traces.row.id": stringValue2(row.id),
4144
4588
  [ATTR.SESSION_ID]: sessionId,
4145
4589
  "session.id": sessionId,
4146
- "run.id": stringValue(row.run_id),
4147
- "thread.id": stringValue(row.thread_id),
4148
- "scenario.id": stringValue(row.scenario_id),
4149
- "generation.id": stringValue(row.generation),
4150
- "cell.id": stringValue(row.cell_id),
4151
- "project.key": stringValue(row.project_key),
4152
- "redaction.version": stringValue(row.redaction_version),
4153
- "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)
4154
4598
  });
4155
4599
  return span({
4156
- traceId: stringValue(row.trace_id) ?? hashId(row, 32),
4157
- spanId: stringValue(row.id) ?? hashId({ row, field: "span" }),
4158
- 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,
4159
4603
  name,
4160
4604
  kind,
4161
4605
  startTime,
4162
4606
  endTime,
4163
4607
  status: statusCode(row.status_code ?? sourceStatus?.code),
4164
- statusMessage: stringValue(row.status_message) ?? stringValue(sourceStatus?.message),
4165
- service: stringValue(attrs["service.name"]) ?? stringValue(attrs.service_name) ?? "tangle-intelligence",
4166
- 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,
4167
4611
  model,
4168
4612
  tool,
4169
4613
  inputTokens: firstNumber(row, attrs, ["input_tokens", ...LLM_INPUT_TOKEN_ATTR_KEYS]),
@@ -4173,7 +4617,7 @@ function intelligenceSpansToSpans(rows) {
4173
4617
  cacheWriteInputTokens: firstNumber(row, attrs, LLM_CACHE_WRITE_TOKEN_ATTR_KEYS),
4174
4618
  costUsd: firstNumber(row, attrs, ["cost_usd", ...LLM_COST_ATTR_KEYS]),
4175
4619
  step: numberValue(attrs["interaction.sequence"]) ?? index,
4176
- 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,
4177
4621
  extra
4178
4622
  });
4179
4623
  });
@@ -4181,10 +4625,10 @@ function intelligenceSpansToSpans(rows) {
4181
4625
  function openInferenceToSpans(rows) {
4182
4626
  return rows.map((row) => {
4183
4627
  const attrs = isObject(row.attributes) ? { ...row.attributes } : {};
4184
- const resource = objectValue(row.resource);
4185
- const resourceAttrs = objectValue(resource?.attributes);
4628
+ const resource = objectValue2(row.resource);
4629
+ const resourceAttrs = objectValue2(resource?.attributes);
4186
4630
  if (resourceAttrs) copyDefined(attrs, resourceAttrs);
4187
- const name = stringValue(row.name) ?? "span";
4631
+ const name = stringValue2(row.name) ?? "span";
4188
4632
  const declaredKind = otlpSpanKind(attrs["openinference.span.kind"]) ?? otlpSpanKind(row.kind);
4189
4633
  const kind = isToolTelemetrySpan(attrs) ? "CHAIN" : declaredKind ?? intelligenceSpanKind(name, attrs);
4190
4634
  if (declaredKind && declaredKind !== kind) {
@@ -4192,15 +4636,15 @@ function openInferenceToSpans(rows) {
4192
4636
  }
4193
4637
  attrs["openinference.span.kind"] = kind;
4194
4638
  if (kind === "TOOL") Object.assign(attrs, capturedToolIo(attrs));
4195
- const status = objectValue(row.status);
4196
- const message = stringValue(status?.message);
4639
+ const status = objectValue2(row.status);
4640
+ const message = stringValue2(status?.message);
4197
4641
  return {
4198
- trace_id: stringValue(row.trace_id) ?? hashId(row, 32),
4199
- span_id: stringValue(row.span_id) ?? hashId({ row, field: "span" }),
4200
- 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,
4201
4645
  name,
4202
- start_time: stringValue(row.start_time) ?? (/* @__PURE__ */ new Date(0)).toISOString(),
4203
- 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(),
4204
4648
  status: { code: statusCode(status?.code), ...message ? { message } : {} },
4205
4649
  attributes: attrs
4206
4650
  };
@@ -4322,7 +4766,7 @@ function topLoopTools(row) {
4322
4766
  current.maxOccurrences = Math.max(current.maxOccurrences, loop.occurrences);
4323
4767
  byTool.set(loop.toolName, current);
4324
4768
  }
4325
- 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}`);
4326
4770
  const suffix = row.signals.stuckLoopsOmitted > 0 ? `, +${row.signals.stuckLoopsOmitted} omitted` : "";
4327
4771
  return loops.length > 0 ? `${loops.join(", ")}${suffix}` : `${row.signals.stuckLoopCount} loop(s)`;
4328
4772
  }
@@ -4588,7 +5032,7 @@ async function writeInspectionReportFile(report, outPath) {
4588
5032
  // src/adoption.ts
4589
5033
  import { readFile as readFile4 } from "fs/promises";
4590
5034
  import { join as join15 } from "path";
4591
- function toolName(s) {
5035
+ function toolName2(s) {
4592
5036
  const n = s.attributes["tool.name"];
4593
5037
  if (typeof n === "string") return n;
4594
5038
  if (s.name.startsWith("tool.")) return s.name.slice("tool.".length);
@@ -4747,7 +5191,7 @@ async function analyzeAdoption(spans, opts = {}) {
4747
5191
  sessionsWithSkillFileReference.add(group);
4748
5192
  addCounts(skillDocumentReads, skillDocuments);
4749
5193
  }
4750
- const tn = toolName(s);
5194
+ const tn = toolName2(s);
4751
5195
  if (tn === "Skill") {
4752
5196
  sessionCapabilities.set(group, "supported");
4753
5197
  const name = skillNameOf(parseInput(s));
@@ -5981,12 +6425,12 @@ function spanKind2(span2) {
5981
6425
  function spanContent(span2) {
5982
6426
  return asString(span2.attributes.content) ?? asString(span2.attributes["input.value"]) ?? asString(span2.attributes["llm.input"]) ?? asString(span2.attributes["gen_ai.prompt"]) ?? "";
5983
6427
  }
5984
- function toolName2(span2) {
6428
+ function toolName3(span2) {
5985
6429
  return asString(span2.attributes["tool.name"]) ?? span2.name.replace(/^tool\./, "");
5986
6430
  }
5987
6431
  function toolSignature(span2) {
5988
6432
  const content = spanContent(span2).replace(/\s+/g, " ").trim();
5989
- return `${toolName2(span2)}:${content || span2.name}`;
6433
+ return `${toolName3(span2)}:${content || span2.name}`;
5990
6434
  }
5991
6435
  function isTool(span2) {
5992
6436
  return spanKind2(span2) === "TOOL" || span2.attributes["tool.name"] != null || span2.name.startsWith("tool.");
@@ -5996,15 +6440,15 @@ function isTextSpan(span2) {
5996
6440
  }
5997
6441
  function isVerification(span2) {
5998
6442
  if (!isTool(span2)) return false;
5999
- return VERIFY_RE.test(`${toolName2(span2)} ${span2.name} ${spanContent(span2)}`);
6443
+ return VERIFY_RE.test(`${toolName3(span2)} ${span2.name} ${spanContent(span2)}`);
6000
6444
  }
6001
6445
  function isChange(span2) {
6002
6446
  if (!isTool(span2)) return false;
6003
- return CHANGE_RE.test(`${toolName2(span2)} ${span2.name} ${spanContent(span2)}`);
6447
+ return CHANGE_RE.test(`${toolName3(span2)} ${span2.name} ${spanContent(span2)}`);
6004
6448
  }
6005
6449
  function isRead(span2) {
6006
6450
  if (!isTool(span2)) return false;
6007
- 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)}`);
6008
6452
  }
6009
6453
  function actionKind(span2) {
6010
6454
  if (isVerification(span2)) return "verify";
@@ -6016,7 +6460,7 @@ function actionKind(span2) {
6016
6460
  }
6017
6461
  function classifyLiveActions(spans) {
6018
6462
  return orderSpans(spans).map((span2) => {
6019
- const tool = isTool(span2) ? toolName2(span2) : void 0;
6463
+ const tool = isTool(span2) ? toolName3(span2) : void 0;
6020
6464
  return {
6021
6465
  kind: actionKind(span2),
6022
6466
  spanId: span2.span_id,
@@ -6911,6 +7355,8 @@ export {
6911
7355
  CORRECTIVE_REACTIONS,
6912
7356
  ClaudeAdapter,
6913
7357
  CodexAdapter,
7358
+ CodexExecStreamError,
7359
+ CodexExecAdapter,
6914
7360
  CopilotAdapter,
6915
7361
  FactoryAdapter,
6916
7362
  ForgeAdapter,
package/dist/cli.js CHANGED
@@ -40,10 +40,11 @@ import {
40
40
  writePolicyEvidenceFile,
41
41
  writeSessionIndexFile,
42
42
  writeTraceEvidenceExportFile
43
- } from "./chunk-NHXPTFKC.js";
43
+ } from "./chunk-ELWOJS65.js";
44
44
 
45
45
  // src/cli.ts
46
- import { readFileSync } from "fs";
46
+ import { createHash } from "crypto";
47
+ import { createReadStream, readFileSync } from "fs";
47
48
  import { readFile, stat } from "fs/promises";
48
49
  import { basename, resolve } from "path";
49
50
  import {
@@ -351,10 +352,22 @@ async function cmdConvert(args) {
351
352
  const path = await writeOtlpFile(spans, args.otlp);
352
353
  console.log(`wrote ${spans.length} spans \u2192 ${path}`);
353
354
  }
354
- async function collectSessionRows(args) {
355
+ async function sourceSha256(path) {
356
+ const hash = createHash("sha256");
357
+ for await (const chunk of createReadStream(path)) hash.update(chunk);
358
+ return hash.digest("hex");
359
+ }
360
+ async function collectSessionRows(args, bindExplicitSource = false) {
355
361
  if (args.session) {
356
362
  const { adapter, ref } = await resolveSelectedSession(args);
357
- return [{ ref, spans: await parseSession(adapter, ref) }];
363
+ if (!bindExplicitSource) return [{ ref, spans: await parseSession(adapter, ref) }];
364
+ const before = await sourceSha256(ref.path);
365
+ const spans = await parseSession(adapter, ref);
366
+ const after = await sourceSha256(ref.path);
367
+ if (before !== after) {
368
+ throw new Error(`session source changed while parsing; refusing unbound evidence: ${ref.path}`);
369
+ }
370
+ return [{ ref, spans, sourceSha256: after }];
358
371
  }
359
372
  const groups = await discover({ ...args, last: args.last || 20 });
360
373
  const rows = [];
@@ -364,7 +377,7 @@ async function collectSessionRows(args) {
364
377
  return rows;
365
378
  }
366
379
  async function cmdEvidence(args) {
367
- const rows = (await collectSessionRows(args)).filter((row) => row.spans.length > 0);
380
+ const rows = (await collectSessionRows(args, true)).filter((row) => row.spans.length > 0);
368
381
  if (rows.length === 0) throw new Error("no spans found for the given selection");
369
382
  const otlpPath = args.otlp ? await writeOtlpFile(rows.flatMap((row) => row.spans), args.otlp) : void 0;
370
383
  const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
@@ -373,7 +386,8 @@ async function cmdEvidence(args) {
373
386
  generatedAt,
374
387
  minLoopOccurrences: args.minLoop,
375
388
  maxLoopExamples: 25,
376
- otlpPath
389
+ otlpPath,
390
+ sourceSha256: row.sourceSha256
377
391
  })
378
392
  ));
379
393
  if (args.out) {
package/dist/index.d.ts CHANGED
@@ -440,6 +440,27 @@ declare class CodexAdapter implements HarnessTraceAdapter {
440
440
  parse(ref: SessionRef, options?: ParseOptions): Promise<OtlpSpan[]>;
441
441
  }
442
442
 
443
+ /**
444
+ * Ephemeral `codex exec --json` adapter.
445
+ *
446
+ * Unlike Codex rollout files under `~/.codex/sessions`, this stream has no
447
+ * durable discovery path and normally carries no timestamps, model name, or
448
+ * user prompt. Callers select it explicitly with `--session`; event order is
449
+ * preserved with `step`, and missing timestamps use the source file mtime
450
+ * without inventing durations.
451
+ */
452
+
453
+ declare class CodexExecStreamError extends Error {
454
+ readonly sourcePath: string;
455
+ constructor(sourcePath: string, message: string);
456
+ }
457
+ declare class CodexExecAdapter implements HarnessTraceAdapter {
458
+ readonly harness = "codex-exec";
459
+ readonly aliases: readonly ["codex-json"];
460
+ locate(_opts?: LocateOptions): Promise<SessionRef[]>;
461
+ parse(ref: SessionRef, options?: ParseOptions): Promise<OtlpSpan[]>;
462
+ }
463
+
443
464
  /**
444
465
  * OpenCode adapter — `~/.local/share/opencode/storage/`.
445
466
  *
@@ -1025,6 +1046,8 @@ interface PolicyEvidenceRecord {
1025
1046
  readonly source: 'traces';
1026
1047
  readonly evidenceKind: 'session-summary';
1027
1048
  readonly otlpPath?: string;
1049
+ /** SHA-256 of the stable explicit session file consumed by the CLI. */
1050
+ readonly sourceSha256?: string;
1028
1051
  readonly notCampaignCell: true;
1029
1052
  readonly note: string;
1030
1053
  };
@@ -1034,6 +1057,8 @@ interface BuildPolicyEvidenceOptions {
1034
1057
  readonly minLoopOccurrences?: number;
1035
1058
  readonly maxLoopExamples?: number;
1036
1059
  readonly otlpPath?: string;
1060
+ /** SHA-256 of the exact stable source bytes used to produce this record. */
1061
+ readonly sourceSha256?: string;
1037
1062
  }
1038
1063
  interface CollectPolicyEvidenceOptions extends ScanOptions, BuildPolicyEvidenceOptions {
1039
1064
  }
@@ -1780,4 +1805,4 @@ interface ExecuteOptions {
1780
1805
  /** Send the final, new outbound items (or write their dry-run OTLP). */
1781
1806
  declare function executeUpload(plan: UploadPlan, opts?: ExecuteOptions): Promise<UploadResult>;
1782
1807
 
1783
- export { ACTOR_ATTR, ATTR, type Actor, type AdoptionOptions, type AdoptionReport, AmpAdapter, type AnalyzeOptions, type AnalyzeResult, type BuildPolicyEvidenceOptions, type BuildSessionIndexOptions, type BuildTraceFindingPacketOptions, CODING_REDACTION_RULES, CONTENT_CAP, CORRECTIVE_REACTIONS, ClaudeAdapter, CodexAdapter, type CollectOptions, type CollectPolicyEvidenceOptions, type CollectSessionIndexOptions, CopilotAdapter, type CorruptionMode, DEFAULT_HARNESS, EmptySessionError, type ExecuteOptions, type ExternalAnalysisResult, type ExternalAnalyzer, FactoryAdapter, ForgeAdapter, GeminiAdapter, type HarnessTraceAdapter, INGEST_SOURCE_CLI, type InspectSessionIndexOptions, JsonSourceError, type JsonlCorruptionReceipt, JsonlParseError, type JsonlReadOptions, type LiveBatchOptions, type LocateOptions, type ObservedLoop, type ObserverOptions, OpencodeAdapter, type OtlpSpan, type OtlpSpanKind, type OtlpStatusCode, type ParseOptions, PartialUploadError, PiAdapter, type PipelineOptions, type PipelineReport, type PlanOptions, type PolicyEvidenceLoopSummary, type PolicyEvidenceRecord, type PolicyEvidenceToolSummary, QwenAdapter, REACTIONS, type Reaction, type ReactionReport, type ReactionSessionReport, type Redactor, type RepoAttrs, type RepoResolution, type RepoResolutionSource, type RunResult, type RuntimeTrace, type ScanOptions, type ScannedSession, type SessionBatch, type SessionCorruptionReceipt, type SessionIdentityConflict, type SessionIdentityIndex, type SessionIntegrity, type SessionRef, type SpanInput, type SpanRedaction, TRACES_REDACTION_RULES, type TraceAgenticRoute, type TraceAgenticRouteReason, type TraceAgenticRouteReasonCode, type TraceContextIndex, type TraceContextRoot, type TraceEvidenceExportOptions, type TraceEvidenceExportResult, type TraceEvidenceFormatOption, type TraceEvidenceInputFormat, type TraceEvidenceRow, type TraceExecutionOptions, type TraceFindingPacket, type TraceImprovementArtifacts, type TraceImprovementOptions, type TraceImprovementResult, type TraceIndexedFile, type TraceInspectionArea, type TraceInspectionFinding, type TraceInspectionReport, type TraceInspectionSeverity, type TraceInvestigationOptions, type TraceInvestigationResult, type TraceLiveAction, type TraceLiveActionKind, type TraceLiveAnalysisContext, type TraceLiveAnalyst, type TraceLiveBatch, type TraceLiveEvidence, type TraceLiveFinding, type TraceLiveLoop, type TraceLiveSeverity, type TraceSessionIndex, type TraceSessionIndexRow, type TraceStoreInvestigationOptions, type TraceStoreInvestigationResult, type TraceStreamEvent, type TraceStreamOptions, type TraceStreamReplayOptions, type TraceStreamSession, type TracesConfig, type TriggerPair, type UploadBackend, type UploadIdentity, type UploadItem, type UploadPlan, type UploadRecord, type UploadResult, type UploadState, type UserPromptInput, alreadyUploaded, analyzeAdoption, analyzeLiveBatch, analyzeReactions, analyzeSpans, applyRedactor, buildPolicyEvidenceRecord, buildSessionIndex, buildSessionIndexFromRows, buildTraceFindingPacket, capText, classifyLiveActions, classifyReaction, claudeActor, codexActor, collectPolicyEvidence, collectSessionIndex, collectSessions, commandAnalyzer, commandRedactor, countSkillRunsJsonl, cwdMatchesSelection, defaultTraceLiveAnalysts, equivalentGitCwds, executeUpload, exportTraceEvidenceFile, exportTraceEvidenceRows, exportTraceEvidenceText, haloAnalyzer, indexSessionIdsByTrace, inspectSessionIndex, knownHarnesses, listAdapters, loadState, loadTracesConfig, locateSessions, looksLikeAgentPrompt, mergeTracesConfig, normalizeRemote, outboundHash, parseIsoToEpochMs, parseSession, parseSince, planTraceAgenticRoute, planUpload, readJsonFile, readJsonl, readSessionIndexFile, recordSessionCorruption, redactSpans, renderInspectionReport, resolveAdapter, resolveRepoAttrs, resolveSessionRepoAttrs, runCommand, runExternalAnalyzers, runPipelines, runTraceImprovement, runTraceInvestigation, runTraceStoreInvestigation, saveReport, saveState, scanSessions, selectAdapters, serializeInspectionReport, serializePolicyEvidence, serializeSessionIndex, serializeSpans, serializeTraceStreamEvent, sessionHash, sessionIdFromAttributes, sessionIntegrityAttributes, span, stampRepoAttrs, stampSessionIdentity, stampSessionIntegrity, stampSpanWorkdirRepoAttrs, streamSessions, summarizeOtlpExecution, summarizeSpanExecution, takeJsonl, textIsSynthetic, toOpenInferenceSpan, toRuntimeStore, toTraceSpanEvents, traceAgenticKinds, traceStreamEventsFromSpans, uploadKey, userPromptSpan, watchSessions, writeInspectionReportFile, writeOtlpFile, writePolicyEvidenceFile, writeSessionIndexFile, writeTraceEvidenceExportFile, writeTraceImprovementArtifacts };
1808
+ export { ACTOR_ATTR, ATTR, type Actor, type AdoptionOptions, type AdoptionReport, AmpAdapter, type AnalyzeOptions, type AnalyzeResult, type BuildPolicyEvidenceOptions, type BuildSessionIndexOptions, type BuildTraceFindingPacketOptions, CODING_REDACTION_RULES, CONTENT_CAP, CORRECTIVE_REACTIONS, ClaudeAdapter, CodexAdapter, CodexExecAdapter, CodexExecStreamError, type CollectOptions, type CollectPolicyEvidenceOptions, type CollectSessionIndexOptions, CopilotAdapter, type CorruptionMode, DEFAULT_HARNESS, EmptySessionError, type ExecuteOptions, type ExternalAnalysisResult, type ExternalAnalyzer, FactoryAdapter, ForgeAdapter, GeminiAdapter, type HarnessTraceAdapter, INGEST_SOURCE_CLI, type InspectSessionIndexOptions, JsonSourceError, type JsonlCorruptionReceipt, JsonlParseError, type JsonlReadOptions, type LiveBatchOptions, type LocateOptions, type ObservedLoop, type ObserverOptions, OpencodeAdapter, type OtlpSpan, type OtlpSpanKind, type OtlpStatusCode, type ParseOptions, PartialUploadError, PiAdapter, type PipelineOptions, type PipelineReport, type PlanOptions, type PolicyEvidenceLoopSummary, type PolicyEvidenceRecord, type PolicyEvidenceToolSummary, QwenAdapter, REACTIONS, type Reaction, type ReactionReport, type ReactionSessionReport, type Redactor, type RepoAttrs, type RepoResolution, type RepoResolutionSource, type RunResult, type RuntimeTrace, type ScanOptions, type ScannedSession, type SessionBatch, type SessionCorruptionReceipt, type SessionIdentityConflict, type SessionIdentityIndex, type SessionIntegrity, type SessionRef, type SpanInput, type SpanRedaction, TRACES_REDACTION_RULES, type TraceAgenticRoute, type TraceAgenticRouteReason, type TraceAgenticRouteReasonCode, type TraceContextIndex, type TraceContextRoot, type TraceEvidenceExportOptions, type TraceEvidenceExportResult, type TraceEvidenceFormatOption, type TraceEvidenceInputFormat, type TraceEvidenceRow, type TraceExecutionOptions, type TraceFindingPacket, type TraceImprovementArtifacts, type TraceImprovementOptions, type TraceImprovementResult, type TraceIndexedFile, type TraceInspectionArea, type TraceInspectionFinding, type TraceInspectionReport, type TraceInspectionSeverity, type TraceInvestigationOptions, type TraceInvestigationResult, type TraceLiveAction, type TraceLiveActionKind, type TraceLiveAnalysisContext, type TraceLiveAnalyst, type TraceLiveBatch, type TraceLiveEvidence, type TraceLiveFinding, type TraceLiveLoop, type TraceLiveSeverity, type TraceSessionIndex, type TraceSessionIndexRow, type TraceStoreInvestigationOptions, type TraceStoreInvestigationResult, type TraceStreamEvent, type TraceStreamOptions, type TraceStreamReplayOptions, type TraceStreamSession, type TracesConfig, type TriggerPair, type UploadBackend, type UploadIdentity, type UploadItem, type UploadPlan, type UploadRecord, type UploadResult, type UploadState, type UserPromptInput, alreadyUploaded, analyzeAdoption, analyzeLiveBatch, analyzeReactions, analyzeSpans, applyRedactor, buildPolicyEvidenceRecord, buildSessionIndex, buildSessionIndexFromRows, buildTraceFindingPacket, capText, classifyLiveActions, classifyReaction, claudeActor, codexActor, collectPolicyEvidence, collectSessionIndex, collectSessions, commandAnalyzer, commandRedactor, countSkillRunsJsonl, cwdMatchesSelection, defaultTraceLiveAnalysts, equivalentGitCwds, executeUpload, exportTraceEvidenceFile, exportTraceEvidenceRows, exportTraceEvidenceText, haloAnalyzer, indexSessionIdsByTrace, inspectSessionIndex, knownHarnesses, listAdapters, loadState, loadTracesConfig, locateSessions, looksLikeAgentPrompt, mergeTracesConfig, normalizeRemote, outboundHash, parseIsoToEpochMs, parseSession, parseSince, planTraceAgenticRoute, planUpload, readJsonFile, readJsonl, readSessionIndexFile, recordSessionCorruption, redactSpans, renderInspectionReport, resolveAdapter, resolveRepoAttrs, resolveSessionRepoAttrs, runCommand, runExternalAnalyzers, runPipelines, runTraceImprovement, runTraceInvestigation, runTraceStoreInvestigation, saveReport, saveState, scanSessions, selectAdapters, serializeInspectionReport, serializePolicyEvidence, serializeSessionIndex, serializeSpans, serializeTraceStreamEvent, sessionHash, sessionIdFromAttributes, sessionIntegrityAttributes, span, stampRepoAttrs, stampSessionIdentity, stampSessionIntegrity, stampSpanWorkdirRepoAttrs, streamSessions, summarizeOtlpExecution, summarizeSpanExecution, takeJsonl, textIsSynthetic, toOpenInferenceSpan, toRuntimeStore, toTraceSpanEvents, traceAgenticKinds, traceStreamEventsFromSpans, uploadKey, userPromptSpan, watchSessions, writeInspectionReportFile, writeOtlpFile, writePolicyEvidenceFile, writeSessionIndexFile, writeTraceEvidenceExportFile, writeTraceImprovementArtifacts };
package/dist/index.js CHANGED
@@ -7,6 +7,8 @@ import {
7
7
  CORRECTIVE_REACTIONS,
8
8
  ClaudeAdapter,
9
9
  CodexAdapter,
10
+ CodexExecAdapter,
11
+ CodexExecStreamError,
10
12
  CopilotAdapter,
11
13
  DEFAULT_HARNESS,
12
14
  EmptySessionError,
@@ -117,7 +119,7 @@ import {
117
119
  writeSessionIndexFile,
118
120
  writeTraceEvidenceExportFile,
119
121
  writeTraceImprovementArtifacts
120
- } from "./chunk-NHXPTFKC.js";
122
+ } from "./chunk-ELWOJS65.js";
121
123
 
122
124
  // src/collect.ts
123
125
  async function collectSessions(opts = {}) {
@@ -151,6 +153,8 @@ export {
151
153
  CORRECTIVE_REACTIONS,
152
154
  ClaudeAdapter,
153
155
  CodexAdapter,
156
+ CodexExecAdapter,
157
+ CodexExecStreamError,
154
158
  CopilotAdapter,
155
159
  DEFAULT_HARNESS,
156
160
  DEFAULT_REDACTION_RULES,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/traces",
3
- "version": "0.9.7",
3
+ "version": "0.9.9",
4
4
  "description": "Point it at your coding-agent session traces (Claude Code, Codex, OpenCode, Gemini, Pi, …) and get failure-mode + efficiency findings. CLI + SDK over the @tangle-network/agent-eval analyst suite — observe live sessions, run your own analysts, redact, and upload to the Tangle Intelligence Platform.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -45,7 +45,7 @@
45
45
  },
46
46
  "dependencies": {
47
47
  "@ax-llm/ax": "^23.0.1",
48
- "@tangle-network/agent-eval": "^0.133.0"
48
+ "@tangle-network/agent-eval": "^0.134.0"
49
49
  },
50
50
  "devDependencies": {
51
51
  "@types/node": "^22.0.0",