@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 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 --last 1 # $0 findings with actions + checks
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
- | `--session <path>` | One explicit session file |
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 is useful for live inspection.
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 outputIsError(output) {
1455
- const error = explicitOutputError(output) === true;
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 { error, message: error ? message.slice(0, 500) : "" };
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
- return name.startsWith(prefix) ? name.slice(prefix.length) : null;
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 { error, message } = outputIsError(l.payload.output);
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 seen = /* @__PURE__ */ new Set();
5284
+ const seenCwds = /* @__PURE__ */ new Set();
5135
5285
  for (const cwd of cwds) {
5136
- if (!cwd || seen.has(cwd)) continue;
5137
- seen.add(cwd);
5138
- const path = join15(cwd, ".evolve", "skill-runs.jsonl");
5139
- let raw;
5140
- try {
5141
- raw = await readFile4(path, "utf8");
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
- value = JSON.parse(line);
5293
+ raw = await readFile4(path, "utf8");
5151
5294
  } catch {
5152
5295
  continue;
5153
5296
  }
5154
- if (!value || typeof value !== "object") continue;
5155
- const record = value;
5156
- const target = sessionIds2.has(sessionIdFromRun(record) ?? "") ? linkedCounts : unlinkedCounts;
5157
- addCounts(target, skillNamesFromRun(record));
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} \`.evolve/skill-runs.jsonl\` file(s); not attributed to these sessions.`
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-ELWOJS65.js";
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 .evolve/skill-runs.jsonl (locate() infers cwd in the scan path).
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 args = parseArgs(rawArgs);
900
- if (args.help) {
901
- if (args.command === "export" || args.command === "help" && args.input === "export") usageExport();
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 `.evolve/skill-runs.jsonl` is historical context, not session
840
- * evidence, unless a row carries the selected session ID. This keeps old runs
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
- /** `.evolve/skill-runs.jsonl` files that were read. */
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 `.evolve/skill-runs.jsonl`. The CLI passes the
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
@@ -119,7 +119,7 @@ import {
119
119
  writeSessionIndexFile,
120
120
  writeTraceEvidenceExportFile,
121
121
  writeTraceImprovementArtifacts
122
- } from "./chunk-ELWOJS65.js";
122
+ } from "./chunk-YDUN3M5J.js";
123
123
 
124
124
  // src/collect.ts
125
125
  async function collectSessions(opts = {}) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/traces",
3
- "version": "0.9.10",
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": "^0.134.1"
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",