@tangle-network/traces 0.9.10 → 0.9.12
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 -3
- package/dist/{chunk-ELWOJS65.js → chunk-YDUN3M5J.js} +188 -31
- package/dist/cli.js +38 -5
- package/dist/index.d.ts +6 -6
- package/dist/index.js +1 -1
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -98,7 +98,7 @@ Every adapter captures the full conversation: the **user's prompt** and the **as
|
|
|
98
98
|
```bash
|
|
99
99
|
traces list --harness claude-code --last 20 # discover sessions
|
|
100
100
|
traces analyze --harness codex --session <id> # pin one ID printed by `list`
|
|
101
|
-
traces analyze --harness codex --
|
|
101
|
+
traces analyze --harness codex --current # this session, even when a child wrote later
|
|
102
102
|
traces investigate --all --last 10 --out report.md # explicit investigation alias
|
|
103
103
|
traces improve --all --last 10 --dir .traces/improvement
|
|
104
104
|
traces analyze --all --since 2026-06-18 --out report.md
|
|
@@ -121,7 +121,8 @@ traces upload --since 24h # upload last day to the Inte
|
|
|
121
121
|
| `--harness <id>` | Harness or alias (default: `claude-code`) |
|
|
122
122
|
| `--all` | Every known harness |
|
|
123
123
|
| `--last <n>` | Most-recent N sessions |
|
|
124
|
-
| `--
|
|
124
|
+
| `--current` | Active Codex session from `CODEX_THREAD_ID` |
|
|
125
|
+
| `--session <id\|path>` | One listed session ID or explicit session file |
|
|
125
126
|
| `--cwd <dir>` | Filter by working directory |
|
|
126
127
|
| `--since <t>` | `upload`: window, `30m`/`2h`/`7d` or ISO (default 24h); `analyze`: ISO cutoff |
|
|
127
128
|
| `--out <path>` | Write the report to a file |
|
|
@@ -249,7 +250,8 @@ It is intentionally read-only: it points to repeated-call loops, high tool-error
|
|
|
249
250
|
traces evidence --all --since 24h --out policy-evidence.jsonl --otlp spans.otlp.jsonl
|
|
250
251
|
```
|
|
251
252
|
|
|
252
|
-
`--last` follows recent file activity and
|
|
253
|
+
`--last` follows recent file activity and may select a child session in multi-agent work.
|
|
254
|
+
Use `--harness codex --current` for the invoking Codex session, or select an exact ID.
|
|
253
255
|
For a reproducible report, run `traces list` first and pass its session ID with `--session`.
|
|
254
256
|
Ephemeral `codex exec --json` output has no discovery location, so select its file with `--harness codex-exec --session <path>`.
|
|
255
257
|
The adapter rejects empty, incomplete, or other JSONL formats instead of emitting zero-valued session evidence.
|
|
@@ -1397,11 +1397,11 @@ function numericStatus(value) {
|
|
|
1397
1397
|
if (typeof value === "string" && /^-?\d+$/.test(value.trim())) return Number(value);
|
|
1398
1398
|
return void 0;
|
|
1399
1399
|
}
|
|
1400
|
-
function explicitOutputError(value) {
|
|
1400
|
+
function explicitOutputError(value, timeoutIsError = true) {
|
|
1401
1401
|
if (Array.isArray(value)) {
|
|
1402
1402
|
let observedSuccess = false;
|
|
1403
1403
|
for (const item of value) {
|
|
1404
|
-
const status = explicitOutputError(item);
|
|
1404
|
+
const status = explicitOutputError(item, timeoutIsError);
|
|
1405
1405
|
if (status === true) return true;
|
|
1406
1406
|
if (status === false) observedSuccess = true;
|
|
1407
1407
|
}
|
|
@@ -1416,13 +1416,13 @@ function explicitOutputError(value) {
|
|
|
1416
1416
|
}
|
|
1417
1417
|
}
|
|
1418
1418
|
for (const key of ["timed_out", "timedOut"]) {
|
|
1419
|
-
if (row[key] === true) return true;
|
|
1419
|
+
if (row[key] === true && timeoutIsError) return true;
|
|
1420
1420
|
}
|
|
1421
1421
|
if (typeof row.succeeded === "boolean" && ("value" in row || "error" in row)) {
|
|
1422
1422
|
return !row.succeeded;
|
|
1423
1423
|
}
|
|
1424
1424
|
if ((row.type === "input_text" || row.type === "text") && typeof row.text === "string") {
|
|
1425
|
-
return explicitOutputError(row.text);
|
|
1425
|
+
return explicitOutputError(row.text, timeoutIsError);
|
|
1426
1426
|
}
|
|
1427
1427
|
return void 0;
|
|
1428
1428
|
}
|
|
@@ -1431,7 +1431,7 @@ function explicitOutputError(value) {
|
|
|
1431
1431
|
if (!text) return void 0;
|
|
1432
1432
|
if (text.startsWith("{") && text.endsWith("}") || text.startsWith("[") && text.endsWith("]")) {
|
|
1433
1433
|
try {
|
|
1434
|
-
const parsedStatus = explicitOutputError(JSON.parse(text));
|
|
1434
|
+
const parsedStatus = explicitOutputError(JSON.parse(text), timeoutIsError);
|
|
1435
1435
|
if (parsedStatus !== void 0) return parsedStatus;
|
|
1436
1436
|
} catch {
|
|
1437
1437
|
}
|
|
@@ -1451,10 +1451,39 @@ function explicitOutputError(value) {
|
|
|
1451
1451
|
if (/^<tool_error>[\s\S]*<\/tool_error>$/i.test(text)) return true;
|
|
1452
1452
|
return void 0;
|
|
1453
1453
|
}
|
|
1454
|
-
function
|
|
1455
|
-
|
|
1454
|
+
function explicitTimeout(value) {
|
|
1455
|
+
if (Array.isArray(value)) return value.some(explicitTimeout);
|
|
1456
|
+
if (value && typeof value === "object") {
|
|
1457
|
+
const row = value;
|
|
1458
|
+
if (row.timed_out === true || row.timedOut === true) return true;
|
|
1459
|
+
if ((row.type === "input_text" || row.type === "text") && typeof row.text === "string") {
|
|
1460
|
+
return explicitTimeout(row.text);
|
|
1461
|
+
}
|
|
1462
|
+
return false;
|
|
1463
|
+
}
|
|
1464
|
+
if (typeof value !== "string") return false;
|
|
1465
|
+
const text = value.trim();
|
|
1466
|
+
if (!text || !(text.startsWith("{") && text.endsWith("}") || text.startsWith("[") && text.endsWith("]"))) {
|
|
1467
|
+
return false;
|
|
1468
|
+
}
|
|
1469
|
+
try {
|
|
1470
|
+
return explicitTimeout(JSON.parse(text));
|
|
1471
|
+
} catch {
|
|
1472
|
+
return false;
|
|
1473
|
+
}
|
|
1474
|
+
}
|
|
1475
|
+
function isWaitAgentOperation(name) {
|
|
1476
|
+
return name === "wait_agent" || name.endsWith("__wait_agent");
|
|
1477
|
+
}
|
|
1478
|
+
function outputStatus(name, output) {
|
|
1479
|
+
const waitAgent = isWaitAgentOperation(name);
|
|
1480
|
+
const error = explicitOutputError(output, !waitAgent) === true;
|
|
1456
1481
|
const message = typeof output === "string" ? output : JSON.stringify(output ?? "");
|
|
1457
|
-
return {
|
|
1482
|
+
return {
|
|
1483
|
+
error,
|
|
1484
|
+
message: error ? message.slice(0, 500) : "",
|
|
1485
|
+
...!error && waitAgent && explicitTimeout(output) ? { pollOutcome: "timeout" } : {}
|
|
1486
|
+
};
|
|
1458
1487
|
}
|
|
1459
1488
|
function singleNestedToolName(input) {
|
|
1460
1489
|
if (!input) return null;
|
|
@@ -1476,9 +1505,18 @@ function spawnedSessionIds(output) {
|
|
|
1476
1505
|
const ids = [...text.matchAll(/["']?agent_id["']?\s*:\s*["']([0-9a-f-]{36})["']/gi)].map((match) => match[1]).filter((id) => /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(id));
|
|
1477
1506
|
return [...new Set(ids)];
|
|
1478
1507
|
}
|
|
1508
|
+
var DIRECT_AGENT_OPERATIONS = /* @__PURE__ */ new Set([
|
|
1509
|
+
"spawn_agent",
|
|
1510
|
+
"send_message",
|
|
1511
|
+
"followup_task",
|
|
1512
|
+
"wait_agent",
|
|
1513
|
+
"interrupt_agent",
|
|
1514
|
+
"list_agents"
|
|
1515
|
+
]);
|
|
1479
1516
|
function multiAgentOperation(name) {
|
|
1480
1517
|
const prefix = "multi_agent_v1__";
|
|
1481
|
-
|
|
1518
|
+
if (name.startsWith(prefix)) return name.slice(prefix.length);
|
|
1519
|
+
return DIRECT_AGENT_OPERATIONS.has(name) ? name : null;
|
|
1482
1520
|
}
|
|
1483
1521
|
function setAgentSessionIds(toolSpan, ids) {
|
|
1484
1522
|
if (ids.length === 0) return;
|
|
@@ -1489,6 +1527,23 @@ function setAgentSessionIds(toolSpan, ids) {
|
|
|
1489
1527
|
toolSpan.attributes["traces.child_session_ids"] = JSON.stringify(unique);
|
|
1490
1528
|
}
|
|
1491
1529
|
}
|
|
1530
|
+
function recordSubagentLifecycle(agentSpan, kind, at, eventId) {
|
|
1531
|
+
const raw = agentSpan.attributes["traces.codex.subagent_lifecycle"];
|
|
1532
|
+
let entries = [];
|
|
1533
|
+
if (typeof raw === "string") {
|
|
1534
|
+
try {
|
|
1535
|
+
const parsed = JSON.parse(raw);
|
|
1536
|
+
if (Array.isArray(parsed)) entries = parsed;
|
|
1537
|
+
} catch {
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
entries.push({ kind, at, ...eventId ? { eventId } : {} });
|
|
1541
|
+
agentSpan.attributes["traces.codex.subagent_lifecycle"] = JSON.stringify(entries);
|
|
1542
|
+
if (kind === "interrupted") {
|
|
1543
|
+
const count2 = agentSpan.attributes["traces.codex.subagent_interruption_count"];
|
|
1544
|
+
agentSpan.attributes["traces.codex.subagent_interruption_count"] = (typeof count2 === "number" ? count2 : 0) + 1;
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1492
1547
|
var verificationCommand = /\b(?:pnpm|npm|yarn|bun)\s+(?:run\s+)?(?:test|typecheck|lint|build|check)(?::[A-Za-z0-9:_-]+)?\b|\b(?:vitest|jest|pytest|tsc|biome|eslint|sha256sum|pdfinfo|pdftotext)\b|\bgo\s+test\b|\bcargo\s+(?:test|check|clippy|build)\b|\bgit\s+(?:status|diff|show|merge-tree)\b|\bgh-drew\s+pr\s+(?:view|checks)\b/i;
|
|
1493
1548
|
function hasReadOnlyCurl(input) {
|
|
1494
1549
|
if (!/\bcurl\b/i.test(input)) return false;
|
|
@@ -1500,6 +1555,7 @@ function classifyNestedTool(name, input) {
|
|
|
1500
1555
|
return name === "exec_command" && input && (verificationCommand.test(input) || hasReadOnlyCurl(input)) ? "exec_command.verify" : name;
|
|
1501
1556
|
}
|
|
1502
1557
|
function isExpectedBlockingTool(name, input) {
|
|
1558
|
+
if (isWaitAgentOperation(name)) return true;
|
|
1503
1559
|
if (!input) return false;
|
|
1504
1560
|
if (name === "wait") return /\bcell_id\b["']?\s*:/.test(input);
|
|
1505
1561
|
if (name === "write_stdin") return /\bsession_id\b["']?\s*:/.test(input);
|
|
@@ -1615,6 +1671,7 @@ var CodexAdapter = class {
|
|
|
1615
1671
|
service: SERVICE3,
|
|
1616
1672
|
agent: SERVICE3,
|
|
1617
1673
|
model,
|
|
1674
|
+
status: "UNSET",
|
|
1618
1675
|
extra: {
|
|
1619
1676
|
"traces.session.role": sessionRole,
|
|
1620
1677
|
...parentSessionId ? { "traces.parent_session_id": parentSessionId } : {},
|
|
@@ -1627,6 +1684,9 @@ var CodexAdapter = class {
|
|
|
1627
1684
|
const spans = [root];
|
|
1628
1685
|
const toolByCallId = /* @__PURE__ */ new Map();
|
|
1629
1686
|
const subagentByThreadId = /* @__PURE__ */ new Map();
|
|
1687
|
+
const subagentThreadIdByPath = /* @__PURE__ */ new Map();
|
|
1688
|
+
const seenAgentMessages = /* @__PURE__ */ new Set();
|
|
1689
|
+
let activeTaskTurnId;
|
|
1630
1690
|
let step = 0;
|
|
1631
1691
|
let lastLlm = rootId;
|
|
1632
1692
|
let sawUserTurn = false;
|
|
@@ -1640,6 +1700,16 @@ var CodexAdapter = class {
|
|
|
1640
1700
|
}
|
|
1641
1701
|
lastTimestamp = l.timestamp;
|
|
1642
1702
|
const ts = l.timestamp ?? (/* @__PURE__ */ new Date(0)).toISOString();
|
|
1703
|
+
if (l.type === "event_msg" && l.payload?.type === "task_started") {
|
|
1704
|
+
activeTaskTurnId = l.payload.turn_id ?? null;
|
|
1705
|
+
root.status = { code: "UNSET" };
|
|
1706
|
+
} else if (l.type === "event_msg" && l.payload?.type === "task_complete") {
|
|
1707
|
+
const completedTurnId = l.payload.turn_id ?? null;
|
|
1708
|
+
if (activeTaskTurnId !== void 0 && activeTaskTurnId === completedTurnId) {
|
|
1709
|
+
activeTaskTurnId = void 0;
|
|
1710
|
+
root.status = { code: "OK" };
|
|
1711
|
+
}
|
|
1712
|
+
}
|
|
1643
1713
|
if (!model && l.type === "turn_context" && l.payload?.model) {
|
|
1644
1714
|
model = l.payload.model;
|
|
1645
1715
|
for (const pending of awaitingModel) pending.attributes["llm.model_name"] = model;
|
|
@@ -1705,9 +1775,11 @@ var CodexAdapter = class {
|
|
|
1705
1775
|
} else if (l.type === "response_item" && (l.payload?.type === "function_call_output" || l.payload?.type === "custom_tool_call_output")) {
|
|
1706
1776
|
const t = toolByCallId.get(l.payload.call_id ?? "");
|
|
1707
1777
|
if (t) {
|
|
1708
|
-
const
|
|
1778
|
+
const name = String(t.attributes["tool.name"] ?? "");
|
|
1779
|
+
const { error, message, pollOutcome } = outputStatus(name, l.payload.output);
|
|
1709
1780
|
t.end_time = ts;
|
|
1710
1781
|
t.status = error ? { code: "ERROR", message } : { code: "OK" };
|
|
1782
|
+
if (pollOutcome) t.attributes["traces.poll.outcome"] = pollOutcome;
|
|
1711
1783
|
recordToolOutput(t, l.payload.output);
|
|
1712
1784
|
if (t.attributes["traces.codex.agent_operation"] === "spawn_agent") {
|
|
1713
1785
|
setAgentSessionIds(t, spawnedSessionIds(l.payload.output));
|
|
@@ -1717,13 +1789,15 @@ var CodexAdapter = class {
|
|
|
1717
1789
|
const threadId = l.payload.agent_thread_id;
|
|
1718
1790
|
const occurredAtMs = l.payload.occurred_at_ms;
|
|
1719
1791
|
const eventTime2 = typeof occurredAtMs === "number" && Number.isFinite(occurredAtMs) ? new Date(occurredAtMs).toISOString() : ts;
|
|
1792
|
+
const eventCallSpan = toolByCallId.get(l.payload.event_id ?? "");
|
|
1793
|
+
if (eventCallSpan && threadId) setAgentSessionIds(eventCallSpan, [threadId]);
|
|
1720
1794
|
if (l.payload.kind === "started" && threadId && !subagentByThreadId.has(threadId)) {
|
|
1721
1795
|
const agentPath = l.payload.agent_path ?? "subagent";
|
|
1722
1796
|
const subagentType = agentPath.split("/").filter(Boolean).at(-1) ?? "subagent";
|
|
1723
1797
|
const toolSpan = span({
|
|
1724
1798
|
traceId,
|
|
1725
1799
|
spanId: `subagent:${threadId}`,
|
|
1726
|
-
parentSpanId: lastLlm,
|
|
1800
|
+
parentSpanId: eventCallSpan?.span_id ?? lastLlm,
|
|
1727
1801
|
name: "tool.Agent",
|
|
1728
1802
|
kind: "TOOL",
|
|
1729
1803
|
startTime: eventTime2,
|
|
@@ -1731,6 +1805,7 @@ var CodexAdapter = class {
|
|
|
1731
1805
|
agent: SERVICE3,
|
|
1732
1806
|
tool: "Agent",
|
|
1733
1807
|
step,
|
|
1808
|
+
status: "UNSET",
|
|
1734
1809
|
extra: {
|
|
1735
1810
|
...toolIoAttributes({
|
|
1736
1811
|
input: {
|
|
@@ -1745,19 +1820,80 @@ var CodexAdapter = class {
|
|
|
1745
1820
|
});
|
|
1746
1821
|
spans.push(toolSpan);
|
|
1747
1822
|
subagentByThreadId.set(threadId, toolSpan);
|
|
1823
|
+
subagentThreadIdByPath.set(agentPath, threadId);
|
|
1824
|
+
recordSubagentLifecycle(toolSpan, "started", eventTime2, l.payload.event_id);
|
|
1748
1825
|
step += 1;
|
|
1749
1826
|
} else if (l.payload.kind === "completed" && threadId) {
|
|
1750
1827
|
const toolSpan = subagentByThreadId.get(threadId);
|
|
1751
1828
|
if (toolSpan) {
|
|
1829
|
+
recordSubagentLifecycle(toolSpan, "completed", eventTime2, l.payload.event_id);
|
|
1752
1830
|
toolSpan.end_time = eventTime2;
|
|
1753
1831
|
toolSpan.status = { code: "OK" };
|
|
1754
1832
|
}
|
|
1755
1833
|
} else if (["interrupted", "failed", "timed_out"].includes(l.payload.kind ?? "") && threadId) {
|
|
1756
1834
|
const toolSpan = subagentByThreadId.get(threadId);
|
|
1757
1835
|
if (toolSpan) {
|
|
1836
|
+
recordSubagentLifecycle(toolSpan, l.payload.kind, eventTime2, l.payload.event_id);
|
|
1758
1837
|
toolSpan.end_time = eventTime2;
|
|
1759
1838
|
toolSpan.status = { code: "ERROR", message: `subagent ${l.payload.kind}` };
|
|
1760
1839
|
}
|
|
1840
|
+
} else if (l.payload.kind === "interacted" && threadId) {
|
|
1841
|
+
const toolSpan = subagentByThreadId.get(threadId);
|
|
1842
|
+
if (toolSpan) {
|
|
1843
|
+
recordSubagentLifecycle(toolSpan, "interacted", eventTime2, l.payload.event_id);
|
|
1844
|
+
const operation = eventCallSpan?.attributes["traces.codex.agent_operation"];
|
|
1845
|
+
if (operation === "followup_task" || operation === "send_input") {
|
|
1846
|
+
toolSpan.end_time = eventTime2;
|
|
1847
|
+
toolSpan.status = { code: "UNSET" };
|
|
1848
|
+
}
|
|
1849
|
+
}
|
|
1850
|
+
}
|
|
1851
|
+
} else if (l.type === "response_item" && l.payload?.type === "agent_message") {
|
|
1852
|
+
const messageIdentity = JSON.stringify([
|
|
1853
|
+
l.timestamp ?? null,
|
|
1854
|
+
l.payload.author ?? null,
|
|
1855
|
+
l.payload.recipient ?? null,
|
|
1856
|
+
l.payload.content ?? null
|
|
1857
|
+
]);
|
|
1858
|
+
if (seenAgentMessages.has(messageIdentity)) continue;
|
|
1859
|
+
seenAgentMessages.add(messageIdentity);
|
|
1860
|
+
const text = textOf3(l.payload.content);
|
|
1861
|
+
const author = l.payload.author;
|
|
1862
|
+
const recipient = l.payload.recipient;
|
|
1863
|
+
const messageType = /^Message Type:\s*FINAL_ANSWER\b/m.test(text) ? "final" : /^Message Type:\s*MESSAGE\b/m.test(text) ? "progress" : "unknown";
|
|
1864
|
+
const threadId = author ? subagentThreadIdByPath.get(author) : void 0;
|
|
1865
|
+
const agentSpan = threadId ? subagentByThreadId.get(threadId) : void 0;
|
|
1866
|
+
const messageSpan = span({
|
|
1867
|
+
traceId,
|
|
1868
|
+
spanId: `msg:${step}:agent`,
|
|
1869
|
+
parentSpanId: agentSpan?.span_id ?? rootId,
|
|
1870
|
+
name: `message.agent.${messageType}`,
|
|
1871
|
+
kind: "CHAIN",
|
|
1872
|
+
startTime: ts,
|
|
1873
|
+
service: SERVICE3,
|
|
1874
|
+
agent: SERVICE3,
|
|
1875
|
+
step,
|
|
1876
|
+
content: text,
|
|
1877
|
+
extra: {
|
|
1878
|
+
"traces.codex.agent_message_type": messageType,
|
|
1879
|
+
...author ? { "traces.codex.agent_message_author": author } : {},
|
|
1880
|
+
...recipient ? { "traces.codex.agent_message_recipient": recipient } : {},
|
|
1881
|
+
...threadId ? {
|
|
1882
|
+
"traces.codex.agent_thread_id": threadId,
|
|
1883
|
+
"traces.codex.agent_session_ids": JSON.stringify([threadId]),
|
|
1884
|
+
"traces.codex.agent_session_count": 1
|
|
1885
|
+
} : {}
|
|
1886
|
+
}
|
|
1887
|
+
});
|
|
1888
|
+
spans.push(messageSpan);
|
|
1889
|
+
step += 1;
|
|
1890
|
+
if (messageType === "progress" && agentSpan) {
|
|
1891
|
+
agentSpan.end_time = ts;
|
|
1892
|
+
agentSpan.status = { code: "UNSET" };
|
|
1893
|
+
} else if (messageType === "final" && agentSpan) {
|
|
1894
|
+
recordSubagentLifecycle(agentSpan, "final_answer", ts);
|
|
1895
|
+
agentSpan.end_time = ts;
|
|
1896
|
+
agentSpan.status = { code: "OK" };
|
|
1761
1897
|
}
|
|
1762
1898
|
} else if (l.type === "response_item" && l.payload?.type === "message" && l.payload.role === "user") {
|
|
1763
1899
|
const prompt = textOf3(l.payload.content);
|
|
@@ -5107,6 +5243,20 @@ function sessionIdFromRun(record) {
|
|
|
5107
5243
|
}
|
|
5108
5244
|
return void 0;
|
|
5109
5245
|
}
|
|
5246
|
+
function canonicalJson2(value) {
|
|
5247
|
+
if (Array.isArray(value)) return `[${value.map(canonicalJson2).join(",")}]`;
|
|
5248
|
+
if (value && typeof value === "object") {
|
|
5249
|
+
const record = value;
|
|
5250
|
+
return `{${Object.keys(record).sort().map((key) => `${JSON.stringify(key)}:${canonicalJson2(record[key])}`).join(",")}}`;
|
|
5251
|
+
}
|
|
5252
|
+
return JSON.stringify(value) ?? "null";
|
|
5253
|
+
}
|
|
5254
|
+
function skillRunIdentity(record) {
|
|
5255
|
+
for (const key of ["id", "runId", "run_id", "skillRunId", "skill_run_id"]) {
|
|
5256
|
+
if (typeof record[key] === "string" && record[key]) return `id:${record[key]}`;
|
|
5257
|
+
}
|
|
5258
|
+
return `row:${canonicalJson2(record)}`;
|
|
5259
|
+
}
|
|
5110
5260
|
function addCounts(target, names) {
|
|
5111
5261
|
for (const name of names) target[name] = (target[name] ?? 0) + 1;
|
|
5112
5262
|
}
|
|
@@ -5131,30 +5281,37 @@ async function readLoopRuns(cwds, sessionIds2) {
|
|
|
5131
5281
|
const linkedCounts = {};
|
|
5132
5282
|
const unlinkedCounts = {};
|
|
5133
5283
|
let filesRead = 0;
|
|
5134
|
-
const
|
|
5284
|
+
const seenCwds = /* @__PURE__ */ new Set();
|
|
5135
5285
|
for (const cwd of cwds) {
|
|
5136
|
-
if (!cwd ||
|
|
5137
|
-
|
|
5138
|
-
const
|
|
5139
|
-
|
|
5140
|
-
|
|
5141
|
-
raw
|
|
5142
|
-
} catch {
|
|
5143
|
-
continue;
|
|
5144
|
-
}
|
|
5145
|
-
filesRead += 1;
|
|
5146
|
-
for (const line of raw.split("\n")) {
|
|
5147
|
-
if (!line.trim()) continue;
|
|
5148
|
-
let value;
|
|
5286
|
+
if (!cwd || seenCwds.has(cwd)) continue;
|
|
5287
|
+
seenCwds.add(cwd);
|
|
5288
|
+
const firstSourceByRun = /* @__PURE__ */ new Map();
|
|
5289
|
+
for (const artifactDir of [".agent", ".evolve"]) {
|
|
5290
|
+
const path = join15(cwd, artifactDir, "skill-runs.jsonl");
|
|
5291
|
+
let raw;
|
|
5149
5292
|
try {
|
|
5150
|
-
|
|
5293
|
+
raw = await readFile4(path, "utf8");
|
|
5151
5294
|
} catch {
|
|
5152
5295
|
continue;
|
|
5153
5296
|
}
|
|
5154
|
-
|
|
5155
|
-
const
|
|
5156
|
-
|
|
5157
|
-
|
|
5297
|
+
filesRead += 1;
|
|
5298
|
+
for (const line of raw.split("\n")) {
|
|
5299
|
+
if (!line.trim()) continue;
|
|
5300
|
+
let value;
|
|
5301
|
+
try {
|
|
5302
|
+
value = JSON.parse(line);
|
|
5303
|
+
} catch {
|
|
5304
|
+
continue;
|
|
5305
|
+
}
|
|
5306
|
+
if (!value || typeof value !== "object") continue;
|
|
5307
|
+
const record = value;
|
|
5308
|
+
const identity = skillRunIdentity(record);
|
|
5309
|
+
const firstSource = firstSourceByRun.get(identity);
|
|
5310
|
+
if (firstSource && firstSource !== path) continue;
|
|
5311
|
+
firstSourceByRun.set(identity, path);
|
|
5312
|
+
const target = sessionIds2.has(sessionIdFromRun(record) ?? "") ? linkedCounts : unlinkedCounts;
|
|
5313
|
+
addCounts(target, skillNamesFromRun(record));
|
|
5314
|
+
}
|
|
5158
5315
|
}
|
|
5159
5316
|
}
|
|
5160
5317
|
return { linkedCounts, unlinkedCounts, filesRead };
|
|
@@ -5877,7 +6034,7 @@ function renderAdoption(ar) {
|
|
|
5877
6034
|
);
|
|
5878
6035
|
if (ar.totalUnlinkedLoopDispatchedRuns > 0) {
|
|
5879
6036
|
lines.push(
|
|
5880
|
-
`- **Unlinked repository history:** ${ar.totalUnlinkedLoopDispatchedRuns} run(s) from ${ar.skillRunFilesRead}
|
|
6037
|
+
`- **Unlinked repository history:** ${ar.totalUnlinkedLoopDispatchedRuns} run(s) from ${ar.skillRunFilesRead} current or legacy \`skill-runs.jsonl\` file(s); unassigned to these sessions.`
|
|
5881
6038
|
);
|
|
5882
6039
|
}
|
|
5883
6040
|
if (ar.totalSkillDocumentReads > 0) {
|
package/dist/cli.js
CHANGED
|
@@ -40,7 +40,7 @@ import {
|
|
|
40
40
|
writePolicyEvidenceFile,
|
|
41
41
|
writeSessionIndexFile,
|
|
42
42
|
writeTraceEvidenceExportFile
|
|
43
|
-
} from "./chunk-
|
|
43
|
+
} from "./chunk-YDUN3M5J.js";
|
|
44
44
|
|
|
45
45
|
// src/cli.ts
|
|
46
46
|
import { createHash } from "crypto";
|
|
@@ -68,6 +68,7 @@ function parseArgs(argv) {
|
|
|
68
68
|
harnessExplicit: false,
|
|
69
69
|
all: false,
|
|
70
70
|
last: 0,
|
|
71
|
+
current: false,
|
|
71
72
|
llm: false,
|
|
72
73
|
interval: 5,
|
|
73
74
|
window: 30,
|
|
@@ -95,6 +96,9 @@ function parseArgs(argv) {
|
|
|
95
96
|
case "--last":
|
|
96
97
|
a.last = Number(next());
|
|
97
98
|
break;
|
|
99
|
+
case "--current":
|
|
100
|
+
a.current = true;
|
|
101
|
+
break;
|
|
98
102
|
case "--session":
|
|
99
103
|
a.session = next();
|
|
100
104
|
break;
|
|
@@ -194,6 +198,33 @@ function parseArgs(argv) {
|
|
|
194
198
|
}
|
|
195
199
|
return a;
|
|
196
200
|
}
|
|
201
|
+
var CURRENT_SESSION_COMMANDS = /* @__PURE__ */ new Set([
|
|
202
|
+
"analyze",
|
|
203
|
+
"investigate",
|
|
204
|
+
"improve",
|
|
205
|
+
"convert",
|
|
206
|
+
"index",
|
|
207
|
+
"evidence",
|
|
208
|
+
"stream"
|
|
209
|
+
]);
|
|
210
|
+
function applyCurrentSessionSelection(args) {
|
|
211
|
+
if (!args.current) return args;
|
|
212
|
+
if (!CURRENT_SESSION_COMMANDS.has(args.command)) {
|
|
213
|
+
throw new Error(`--current is not supported by ${args.command}`);
|
|
214
|
+
}
|
|
215
|
+
if (args.input) throw new Error("--current cannot be combined with an input file");
|
|
216
|
+
if (args.supervisorRunDir) throw new Error("--current cannot be combined with --supervisor-run-dir");
|
|
217
|
+
if (args.session) throw new Error("--current cannot be combined with --session");
|
|
218
|
+
if (args.last > 0) throw new Error("--current cannot be combined with --last");
|
|
219
|
+
if (args.all) throw new Error("--current cannot be combined with --all");
|
|
220
|
+
const adapter = resolveAdapter(args.harness);
|
|
221
|
+
if (adapter?.harness !== "codex") {
|
|
222
|
+
throw new Error("--current uses CODEX_THREAD_ID and requires --harness codex");
|
|
223
|
+
}
|
|
224
|
+
const sessionId = process.env.CODEX_THREAD_ID?.trim();
|
|
225
|
+
if (!sessionId) throw new Error("--current requires CODEX_THREAD_ID from the active Codex session");
|
|
226
|
+
return { ...args, session: sessionId };
|
|
227
|
+
}
|
|
197
228
|
async function confirm(question) {
|
|
198
229
|
if (!process.stdin.isTTY) return false;
|
|
199
230
|
const rl = (await import("readline/promises")).createInterface({
|
|
@@ -239,7 +270,7 @@ async function resolveSelectedSession(args) {
|
|
|
239
270
|
sessionId: args.session,
|
|
240
271
|
path: args.session,
|
|
241
272
|
// --session is an explicit file; honor --cwd so adoption can find the
|
|
242
|
-
// project's
|
|
273
|
+
// project's current or legacy skill-run log (locate() infers cwd in the scan path).
|
|
243
274
|
cwd: args.cwd ?? null,
|
|
244
275
|
mtimeMs: st.mtimeMs
|
|
245
276
|
}
|
|
@@ -855,6 +886,7 @@ Options:
|
|
|
855
886
|
--harness <id> Harness or alias (default: claude-code). Known: ${knownHarnesses().join(", ")}
|
|
856
887
|
--all Sweep every known harness
|
|
857
888
|
--last <n> Most-recent N sessions
|
|
889
|
+
--current Analyze the active Codex session named by CODEX_THREAD_ID
|
|
858
890
|
--session <id|path> Analyze one listed session ID or one explicit harness session file
|
|
859
891
|
--cwd <dir> Filter sessions by working directory
|
|
860
892
|
--supervisor-run-dir <dir>
|
|
@@ -896,12 +928,13 @@ async function main() {
|
|
|
896
928
|
console.log(`traces ${packageVersion()}`);
|
|
897
929
|
return;
|
|
898
930
|
}
|
|
899
|
-
const
|
|
900
|
-
if (
|
|
901
|
-
if (
|
|
931
|
+
const parsedArgs = parseArgs(rawArgs);
|
|
932
|
+
if (parsedArgs.help) {
|
|
933
|
+
if (parsedArgs.command === "export" || parsedArgs.command === "help" && parsedArgs.input === "export") usageExport();
|
|
902
934
|
else usage();
|
|
903
935
|
return;
|
|
904
936
|
}
|
|
937
|
+
const args = applyCurrentSessionSelection(parsedArgs);
|
|
905
938
|
switch (args.command) {
|
|
906
939
|
case "help":
|
|
907
940
|
if (args.input === "export") usageExport();
|
package/dist/index.d.ts
CHANGED
|
@@ -836,9 +836,9 @@ declare function analyzeReactions(spans: readonly OtlpSpan[]): ReactionReport;
|
|
|
836
836
|
* 2. per-skill invocation frequency
|
|
837
837
|
* 3. subagent (Task/Agent) spawn frequency
|
|
838
838
|
*
|
|
839
|
-
* A repository's `.
|
|
840
|
-
* evidence, unless a row carries the selected
|
|
841
|
-
* from being attributed to the current trace.
|
|
839
|
+
* A repository's `.agent/skill-runs.jsonl` (or legacy `.evolve` log) is
|
|
840
|
+
* historical context, not session evidence, unless a row carries the selected
|
|
841
|
+
* session ID. This keeps old runs from being attributed to the current trace.
|
|
842
842
|
*/
|
|
843
843
|
|
|
844
844
|
interface AdoptionReport {
|
|
@@ -879,7 +879,7 @@ interface AdoptionReport {
|
|
|
879
879
|
loopDispatchedRuns: Record<string, number>;
|
|
880
880
|
/** Total session-linked loop-dispatched skill runs. */
|
|
881
881
|
totalLoopDispatchedRuns: number;
|
|
882
|
-
/**
|
|
882
|
+
/** Current or legacy `skill-runs.jsonl` files that were read. */
|
|
883
883
|
skillRunFilesRead: number;
|
|
884
884
|
/** Same-repository loop history without a selected session ID. Never treat
|
|
885
885
|
* this as evidence that a selected session used a skill. */
|
|
@@ -889,8 +889,8 @@ interface AdoptionReport {
|
|
|
889
889
|
}
|
|
890
890
|
declare function countSkillRunsJsonl(raw: string): Record<string, number>;
|
|
891
891
|
interface AdoptionOptions {
|
|
892
|
-
/** Session cwds to probe for
|
|
893
|
-
* cwds of the analyzed sessions. */
|
|
892
|
+
/** Session cwds to probe for current and legacy skill-run logs. The CLI
|
|
893
|
+
* passes the cwds of the analyzed sessions. */
|
|
894
894
|
cwds?: readonly string[];
|
|
895
895
|
}
|
|
896
896
|
declare function analyzeAdoption(spans: readonly OtlpSpan[], opts?: AdoptionOptions): Promise<AdoptionReport>;
|
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tangle-network/traces",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.12",
|
|
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,10 +45,11 @@
|
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"@ax-llm/ax": "^23.0.1",
|
|
48
|
-
"@tangle-network/agent-eval": "
|
|
48
|
+
"@tangle-network/agent-eval": "0.135.1"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@types/node": "^22.0.0",
|
|
52
|
+
"postcss": "^8.5.24",
|
|
52
53
|
"tsup": "^8.0.0",
|
|
53
54
|
"tsx": "^4.19.0",
|
|
54
55
|
"typescript": "^5.7.0",
|