@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 +5 -0
- package/dist/{chunk-NHXPTFKC.js → chunk-ELWOJS65.js} +563 -117
- package/dist/cli.js +20 -6
- package/dist/index.d.ts +26 -1
- package/dist/index.js +5 -1
- package/package.json +2 -2
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
|
|
1443
|
-
if (
|
|
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
|
|
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:
|
|
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 =
|
|
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 =
|
|
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
|
|
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:
|
|
1878
|
-
agent:
|
|
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:
|
|
1899
|
-
agent:
|
|
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:
|
|
1926
|
-
agent:
|
|
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
|
|
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:
|
|
2025
|
-
agent:
|
|
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:
|
|
2042
|
-
agent:
|
|
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:
|
|
2061
|
-
agent:
|
|
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:
|
|
2093
|
-
agent:
|
|
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
|
|
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:
|
|
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:
|
|
2176
|
-
agent:
|
|
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:
|
|
2193
|
-
agent:
|
|
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:
|
|
2213
|
-
agent:
|
|
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
|
|
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:
|
|
2474
|
-
agent:
|
|
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:
|
|
2503
|
-
agent:
|
|
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:
|
|
2521
|
-
agent:
|
|
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:
|
|
2549
|
-
agent:
|
|
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
|
|
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:
|
|
2659
|
-
agent:
|
|
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:
|
|
2679
|
-
agent:
|
|
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:
|
|
2701
|
-
agent:
|
|
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:
|
|
2736
|
-
agent:
|
|
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
|
|
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:
|
|
2823
|
-
agent:
|
|
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:
|
|
2843
|
-
agent:
|
|
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:
|
|
2864
|
-
agent:
|
|
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:
|
|
2900
|
-
agent:
|
|
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
|
|
4139
|
+
function stringValue2(value) {
|
|
3697
4140
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
3698
4141
|
}
|
|
3699
|
-
function
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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,
|
|
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 =
|
|
4394
|
+
const errorMessage = stringValue2(row.error) ?? findStringKey(row, ["error", "errorMessage", "error_message"], 3);
|
|
3951
4395
|
if (lowered.includes("error") || errorMessage) {
|
|
3952
|
-
const message = errorMessage ??
|
|
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
|
|
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:
|
|
4453
|
+
input: toolInput2,
|
|
4010
4454
|
output: eventValue(row, ["toolOutput", "tool_output", "output", "result"]),
|
|
4011
|
-
argsCaptured:
|
|
4455
|
+
argsCaptured: toolInput2 !== void 0
|
|
4012
4456
|
}) : {}
|
|
4013
4457
|
};
|
|
4014
4458
|
copyDefined(extra, {
|
|
4015
|
-
"traces.event.id":
|
|
4016
|
-
"traces.event.raw_type":
|
|
4017
|
-
"traces.event.raw_event":
|
|
4459
|
+
"traces.event.id": stringValue2(row.id),
|
|
4460
|
+
"traces.event.raw_type": stringValue2(row.type),
|
|
4461
|
+
"traces.event.raw_event": stringValue2(row.event)
|
|
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 =
|
|
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 =
|
|
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 = { ...
|
|
4128
|
-
const name =
|
|
4129
|
-
const startTime = unixNanoTime(row.start_unix_nano) ??
|
|
4130
|
-
const endTime = unixNanoTime(row.end_unix_nano) ??
|
|
4131
|
-
const model =
|
|
4132
|
-
const tool =
|
|
4571
|
+
const attrs = { ...objectValue2(row.attributes) ?? {} };
|
|
4572
|
+
const name = stringValue2(row.name) ?? "intelligence.span";
|
|
4573
|
+
const startTime = unixNanoTime(row.start_unix_nano) ?? isoTime2(row.start_time) ?? stringValue2(row.start_time) ?? isoTime2(row.received_at) ?? (/* @__PURE__ */ new Date(0)).toISOString();
|
|
4574
|
+
const endTime = unixNanoTime(row.end_unix_nano) ?? isoTime2(row.end_time) ?? stringValue2(row.end_time) ?? startTime;
|
|
4575
|
+
const model = stringValue2(row.model) ?? stringValue2(attrs["llm.model_name"]) ?? stringValue2(attrs.model) ?? stringValue2(attrs["gen_ai.request.model"]);
|
|
4576
|
+
const tool = stringValue2(attrs["tool.name"]) ?? stringValue2(attrs.tool_name) ?? stringValue2(attrs["mcp.tool.name"]);
|
|
4133
4577
|
const kind = intelligenceSpanKind(name, attrs);
|
|
4134
|
-
const sourceStatus =
|
|
4135
|
-
const sessionId =
|
|
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":
|
|
4587
|
+
"traces.row.id": stringValue2(row.id),
|
|
4144
4588
|
[ATTR.SESSION_ID]: sessionId,
|
|
4145
4589
|
"session.id": sessionId,
|
|
4146
|
-
"run.id":
|
|
4147
|
-
"thread.id":
|
|
4148
|
-
"scenario.id":
|
|
4149
|
-
"generation.id":
|
|
4150
|
-
"cell.id":
|
|
4151
|
-
"project.key":
|
|
4152
|
-
"redaction.version":
|
|
4153
|
-
"traces.received_at":
|
|
4590
|
+
"run.id": stringValue2(row.run_id),
|
|
4591
|
+
"thread.id": stringValue2(row.thread_id),
|
|
4592
|
+
"scenario.id": stringValue2(row.scenario_id),
|
|
4593
|
+
"generation.id": stringValue2(row.generation),
|
|
4594
|
+
"cell.id": stringValue2(row.cell_id),
|
|
4595
|
+
"project.key": stringValue2(row.project_key),
|
|
4596
|
+
"redaction.version": stringValue2(row.redaction_version),
|
|
4597
|
+
"traces.received_at": isoTime2(row.received_at)
|
|
4154
4598
|
});
|
|
4155
4599
|
return span({
|
|
4156
|
-
traceId:
|
|
4157
|
-
spanId:
|
|
4158
|
-
parentSpanId:
|
|
4600
|
+
traceId: stringValue2(row.trace_id) ?? hashId(row, 32),
|
|
4601
|
+
spanId: stringValue2(row.id) ?? hashId({ row, field: "span" }),
|
|
4602
|
+
parentSpanId: stringValue2(row.parent_span_id) ?? null,
|
|
4159
4603
|
name,
|
|
4160
4604
|
kind,
|
|
4161
4605
|
startTime,
|
|
4162
4606
|
endTime,
|
|
4163
4607
|
status: statusCode(row.status_code ?? sourceStatus?.code),
|
|
4164
|
-
statusMessage:
|
|
4165
|
-
service:
|
|
4166
|
-
agent:
|
|
4608
|
+
statusMessage: stringValue2(row.status_message) ?? stringValue2(sourceStatus?.message),
|
|
4609
|
+
service: stringValue2(attrs["service.name"]) ?? stringValue2(attrs.service_name) ?? "tangle-intelligence",
|
|
4610
|
+
agent: stringValue2(attrs["agent.name"]) ?? stringValue2(attrs["service.name"]) ?? null,
|
|
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:
|
|
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 =
|
|
4185
|
-
const resourceAttrs =
|
|
4628
|
+
const resource = objectValue2(row.resource);
|
|
4629
|
+
const resourceAttrs = objectValue2(resource?.attributes);
|
|
4186
4630
|
if (resourceAttrs) copyDefined(attrs, resourceAttrs);
|
|
4187
|
-
const name =
|
|
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 =
|
|
4196
|
-
const message =
|
|
4639
|
+
const status = objectValue2(row.status);
|
|
4640
|
+
const message = stringValue2(status?.message);
|
|
4197
4641
|
return {
|
|
4198
|
-
trace_id:
|
|
4199
|
-
span_id:
|
|
4200
|
-
parent_span_id:
|
|
4642
|
+
trace_id: stringValue2(row.trace_id) ?? hashId(row, 32),
|
|
4643
|
+
span_id: stringValue2(row.span_id) ?? hashId({ row, field: "span" }),
|
|
4644
|
+
parent_span_id: stringValue2(row.parent_span_id) || null,
|
|
4201
4645
|
name,
|
|
4202
|
-
start_time:
|
|
4203
|
-
end_time:
|
|
4646
|
+
start_time: stringValue2(row.start_time) ?? (/* @__PURE__ */ new Date(0)).toISOString(),
|
|
4647
|
+
end_time: stringValue2(row.end_time) ?? stringValue2(row.start_time) ?? (/* @__PURE__ */ new Date(0)).toISOString(),
|
|
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(([
|
|
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
|
|
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 =
|
|
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
|
|
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 `${
|
|
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(`${
|
|
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(`${
|
|
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(`${
|
|
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) ?
|
|
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-
|
|
43
|
+
} from "./chunk-ELWOJS65.js";
|
|
44
44
|
|
|
45
45
|
// src/cli.ts
|
|
46
|
-
import {
|
|
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
|
|
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-
|
|
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.
|
|
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.
|
|
48
|
+
"@tangle-network/agent-eval": "^0.134.0"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@types/node": "^22.0.0",
|