@tangle-network/traces 0.9.3 → 0.9.5

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.
@@ -4455,9 +4455,9 @@ function skillDocumentNames(s) {
4455
4455
  }
4456
4456
  return [...names];
4457
4457
  }
4458
- function telemetryStatus(measured, unsupported, unknown) {
4459
- if (measured > 0 && unsupported === 0 && unknown === 0) return "measured";
4460
- if (measured > 0) return "partial";
4458
+ function telemetryStatus(measured2, unsupported, unknown) {
4459
+ if (measured2 > 0 && unsupported === 0 && unknown === 0) return "measured";
4460
+ if (measured2 > 0) return "partial";
4461
4461
  if (unsupported > 0 && unknown === 0) return "unsupported";
4462
4462
  return "unknown";
4463
4463
  }
@@ -4684,6 +4684,12 @@ import {
4684
4684
  } from "@tangle-network/agent-eval/analyst";
4685
4685
  import { OtlpFileTraceStore } from "@tangle-network/agent-eval/traces";
4686
4686
  var GENERATED_TRACE_FILE_CEILING = 512 * 1024 * 1024;
4687
+ function mergeCostProvenance(first, second, totalCostUsd) {
4688
+ if (!first || !second || first.kind === "uncaptured" || second.kind === "uncaptured") {
4689
+ return { kind: "uncaptured", usd: null };
4690
+ }
4691
+ return first.kind === "estimated" || second.kind === "estimated" ? { kind: "estimated", usd: totalCostUsd } : { kind: "observed", usd: totalCostUsd };
4692
+ }
4687
4693
  async function analyzeSpans(spans, opts = {}) {
4688
4694
  if (spans.length === 0) throw new Error("analyzeSpans: no spans to analyze");
4689
4695
  const otlpPath = await writeOtlpFile(spans, opts.otlpOutPath);
@@ -4716,7 +4722,13 @@ async function analyzeSpans(spans, opts = {}) {
4716
4722
  });
4717
4723
  result.findings.push(...agResult.findings);
4718
4724
  result.per_analyst.push(...agResult.per_analyst);
4719
- result.total_cost_usd += agResult.total_cost_usd;
4725
+ const totalCostUsd = result.total_cost_usd + agResult.total_cost_usd;
4726
+ result.total_cost_provenance = mergeCostProvenance(
4727
+ result.total_cost_provenance,
4728
+ agResult.total_cost_provenance,
4729
+ totalCostUsd
4730
+ );
4731
+ result.total_cost_usd = totalCostUsd;
4720
4732
  }
4721
4733
  return { otlpPath, execution, result };
4722
4734
  }
@@ -4842,9 +4854,12 @@ function decimal(value, digits = 1) {
4842
4854
  maximumFractionDigits: digits
4843
4855
  });
4844
4856
  }
4857
+ function measured(value, render) {
4858
+ return value === null ? "not captured" : render(value);
4859
+ }
4845
4860
  function distributionRow(label, distribution, unit = "") {
4846
4861
  if (distribution.n === 0) return `| ${label} | 0 | not captured | not captured | not captured | not captured | not captured |`;
4847
- const value = (number) => `${decimal(number)}${unit}`;
4862
+ const value = (number) => measured(number, (captured) => `${decimal(captured)}${unit}`);
4848
4863
  return `| ${label} | ${count(distribution.n)} | ${value(distribution.min)} | ${value(distribution.mean)} | ${value(distribution.p50)} | ${value(distribution.p95)} | ${value(distribution.max)} |`;
4849
4864
  }
4850
4865
  function tokenRows(usage) {
@@ -4856,14 +4871,23 @@ function tokenRows(usage) {
4856
4871
  ["Cache write", usage.totals.cacheWrite, usage.cacheWrite]
4857
4872
  ];
4858
4873
  return rows.map(
4859
- ([label, total, values]) => `| ${label} | ${count(total)} | ${count(values.n)} | ${decimal(values.mean)} | ${decimal(values.p50)} | ${decimal(values.p95)} | ${count(values.max)} |`
4874
+ ([label, total, values]) => `| ${label} | ${count(total)} | ${count(values.n)} | ${measured(values.mean, decimal)} | ${measured(values.p50, decimal)} | ${measured(values.p95, decimal)} | ${measured(values.max, count)} |`
4860
4875
  );
4861
4876
  }
4877
+ function analysisCost(result) {
4878
+ const cost = `$${result.total_cost_usd.toFixed(4)}`;
4879
+ if (result.total_cost_provenance?.kind === "observed") return `Analysis cost: ${cost} observed.`;
4880
+ if (result.total_cost_provenance?.kind === "estimated") return `Analysis cost: ${cost} estimated.`;
4881
+ return `Known analysis cost: ${cost}; additional cost was not captured.`;
4882
+ }
4862
4883
  function renderExecution(report) {
4863
4884
  const execution = report.execution;
4864
4885
  const lines = ["## execution facts", ""];
4865
4886
  lines.push(
4866
- `- **Sessions:** ${count(execution.durationMs.n)} | **Model-call runs:** ${count(execution.modelCalls.runs)} | **Model-call events:** ${count(execution.modelCalls.events)} | **Sessions with tool errors:** ${count(execution.failures.runs)}/${count(execution.failures.reportingRuns)} (${decimal(execution.failures.fraction * 100, 2)}%)`
4887
+ `- **Sessions:** ${count(execution.durationMs.n)} | **Model-call runs:** ${count(execution.modelCalls.runs)} | **Model-call events:** ${count(execution.modelCalls.events)} | **Sessions with execution errors:** ${count(execution.executionErrors.runs)}/${count(execution.executionErrors.reportingRuns)} (${measured(execution.executionErrors.fraction, (fraction) => `${decimal(fraction * 100, 2)}%`)})`
4888
+ );
4889
+ lines.push(
4890
+ `- **Terminal outcomes:** ${count(execution.terminalOutcomes.succeeded)} succeeded | ${count(execution.terminalOutcomes.failed)} failed | ${count(execution.terminalOutcomes.cancelled)} cancelled | ${count(execution.terminalOutcomes.incomplete)} incomplete | ${count(execution.terminalOutcomes.unknown)} unknown`
4867
4891
  );
4868
4892
  lines.push("- **Task quality:** not measured; these traces do not include comparable task outcome labels.");
4869
4893
  lines.push("");
@@ -4936,7 +4960,7 @@ function renderReport(result, meta) {
4936
4960
  lines.push("");
4937
4961
  const sessionSummary = meta.unassignedTraceCount ? `${meta.sessionCount} identified session(s) + ${meta.unassignedTraceCount} ${plural(meta.unassignedTraceCount, "trace")} without a single stable session identity` : `${meta.sessionCount} session(s)`;
4938
4962
  lines.push(
4939
- `${sessionSummary}, ${meta.spanCount} spans \u2192 **${findingSummary}** across ${result.per_analyst.length} analyst(s). Analysis cost: $${result.total_cost_usd.toFixed(4)}.`
4963
+ `${sessionSummary}, ${meta.spanCount} spans \u2192 **${findingSummary}** across ${result.per_analyst.length} analyst(s). ${analysisCost(result)}`
4940
4964
  );
4941
4965
  lines.push("");
4942
4966
  if (meta.unassignedTraceCount) {
@@ -6516,7 +6540,7 @@ async function sessionMeta(item) {
6516
6540
  if (branch) meta[ATTR.GIT_BRANCH] = branch;
6517
6541
  return meta;
6518
6542
  }
6519
- var msToNano = (iso2) => parseIsoToEpochMs(iso2) * 1e6;
6543
+ var msToNano = (epochMs) => (BigInt(epochMs) * 1000000n).toString();
6520
6544
  function toTraceSpanEvents(spans, rootMeta) {
6521
6545
  return spans.map((s) => {
6522
6546
  const attributes = {};
@@ -6527,14 +6551,15 @@ function toTraceSpanEvents(spans, rootMeta) {
6527
6551
  attributes[ATTR.SESSION_ID] = s.trace_id;
6528
6552
  attributes[ATTR.INGEST_SOURCE] = INGEST_SOURCE_CLI;
6529
6553
  if (s.parent_span_id === null) Object.assign(attributes, rootMeta);
6530
- const startNano = msToNano(s.start_time);
6554
+ const startMs = parseIsoToEpochMs(s.start_time);
6555
+ const endMs = Math.max(startMs, parseIsoToEpochMs(s.end_time));
6531
6556
  return {
6532
6557
  traceId: s.trace_id,
6533
6558
  spanId: s.span_id,
6534
6559
  ...s.parent_span_id ? { parentSpanId: s.parent_span_id } : {},
6535
6560
  name: s.name,
6536
- startTimeUnixNano: startNano,
6537
- endTimeUnixNano: Math.max(startNano, msToNano(s.end_time)),
6561
+ startTimeUnixNano: msToNano(startMs),
6562
+ endTimeUnixNano: msToNano(endMs),
6538
6563
  attributes,
6539
6564
  ...s.status.code === "UNSET" ? {} : { status: { code: s.status.code, ...s.status.message ? { message: s.status.message } : {} } }
6540
6565
  };
package/dist/cli.js CHANGED
@@ -40,12 +40,20 @@ import {
40
40
  writePolicyEvidenceFile,
41
41
  writeSessionIndexFile,
42
42
  writeTraceEvidenceExportFile
43
- } from "./chunk-BKGWYQR4.js";
43
+ } from "./chunk-S4TEBT7Q.js";
44
44
 
45
45
  // src/cli.ts
46
46
  import { readFileSync } from "fs";
47
47
  import { readFile, stat } from "fs/promises";
48
48
  import { basename, resolve } from "path";
49
+ import {
50
+ analyzeSupervisorRun,
51
+ findSupervisorRunDirs,
52
+ isUnavailable,
53
+ renderSupervisorRollupMarkdown,
54
+ renderSupervisorRunMarkdown,
55
+ rollupSupervisorRuns
56
+ } from "@tangle-network/agent-eval/supervisor-run";
49
57
  function packageVersion() {
50
58
  const pkg = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
51
59
  if (typeof pkg.version !== "string" || !pkg.version) throw new Error("package.json is missing version");
@@ -92,6 +100,9 @@ function parseArgs(argv) {
92
100
  case "--cwd":
93
101
  a.cwd = next();
94
102
  break;
103
+ case "--supervisor-run-dir":
104
+ a.supervisorRunDir = next();
105
+ break;
95
106
  case "--since":
96
107
  a.since = next();
97
108
  break;
@@ -453,6 +464,7 @@ async function loadExportAttributes(args) {
453
464
  return attributes;
454
465
  }
455
466
  async function cmdAnalyze(args) {
467
+ if (args.supervisorRunDir) return cmdAnalyzeSupervisorRun(args.supervisorRunDir, args);
456
468
  const result = await investigate(args, { loadDefaultConfig: false });
457
469
  if (args.out) {
458
470
  await saveReport(args.out, result.report);
@@ -463,6 +475,32 @@ async function cmdAnalyze(args) {
463
475
  console.log(result.report);
464
476
  }
465
477
  }
478
+ async function cmdAnalyzeSupervisorRun(runDir, args) {
479
+ const nested = await findSupervisorRunDirs(runDir);
480
+ let markdown;
481
+ if (nested.length > 0) {
482
+ markdown = renderSupervisorRollupMarkdown(
483
+ rollupSupervisorRuns(await Promise.all(nested.map((dir) => analyzeSupervisorRun(dir)))),
484
+ `Supervisor rollup \u2014 ${runDir}`
485
+ );
486
+ } else {
487
+ const report = await analyzeSupervisorRun(runDir);
488
+ if (isUnavailable(report.orchestration.workersSpawned)) {
489
+ throw new Error(
490
+ `no supervisor run found at ${runDir} \u2014 expected <runDir>/ws/.loops/supervisor/<id>, or a parent directory containing such runs`
491
+ );
492
+ }
493
+ markdown = renderSupervisorRunMarkdown(report);
494
+ }
495
+ if (args.out) {
496
+ await saveReport(args.out, markdown);
497
+ console.log(
498
+ `supervisor report \u2192 ${args.out} (${nested.length > 0 ? `${nested.length} runs` : "1 run"})`
499
+ );
500
+ } else {
501
+ console.log(markdown);
502
+ }
503
+ }
466
504
  async function collectImportedSpans(args) {
467
505
  if (!args.input) throw new Error("input file missing");
468
506
  const attributes = await loadExportAttributes(args);
@@ -787,6 +825,7 @@ function usage() {
787
825
  Commands:
788
826
  list List discovered sessions
789
827
  analyze Run analyst suite + loop/waste pipelines over sessions or an input file
828
+ (--supervisor-run-dir <dir> reports a supervision tree instead)
790
829
  investigate Run typed investigation flow, including BYO config + evidence-backed actions
791
830
  improve Write findings, evidence, report, and canonical trace artifacts
792
831
  convert Emit OTLP-JSONL only (HALO: use analyze --analyzer halo)
@@ -804,6 +843,10 @@ Options:
804
843
  --last <n> Most-recent N sessions
805
844
  --session <id|path> Analyze one listed session ID or one explicit harness session file
806
845
  --cwd <dir> Filter sessions by working directory
846
+ --supervisor-run-dir <dir>
847
+ analyze: report a SUPERVISION TREE instead of harness sessions \u2014
848
+ steers, spawn waves, concurrency, idle wall, cost by role,
849
+ accepted vs rejected. Rolls up when the dir holds many runs.
807
850
  --since <t> upload: window, 30m / 2h / 7d or an ISO date (default 24h); analyze: ISO cutoff
808
851
  --out <path> Write report to a file
809
852
  --dir <path> improve: write artifacts to this directory
package/dist/index.js CHANGED
@@ -117,7 +117,7 @@ import {
117
117
  writeSessionIndexFile,
118
118
  writeTraceEvidenceExportFile,
119
119
  writeTraceImprovementArtifacts
120
- } from "./chunk-BKGWYQR4.js";
120
+ } from "./chunk-S4TEBT7Q.js";
121
121
 
122
122
  // src/collect.ts
123
123
  async function collectSessions(opts = {}) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/traces",
3
- "version": "0.9.3",
3
+ "version": "0.9.5",
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.122.8"
48
+ "@tangle-network/agent-eval": "^0.133.0"
49
49
  },
50
50
  "devDependencies": {
51
51
  "@types/node": "^22.0.0",