agentflow-core 0.8.0 → 0.8.1

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/dist/index.js CHANGED
@@ -2,6 +2,7 @@ import {
2
2
  auditProcesses,
3
3
  createGraphBuilder,
4
4
  createTraceStore,
5
+ discoverAllProcessConfigs,
5
6
  discoverProcessConfig,
6
7
  findWaitingOn,
7
8
  formatAuditReport,
@@ -23,11 +24,11 @@ import {
23
24
  stitchTrace,
24
25
  toAsciiTree,
25
26
  toTimeline
26
- } from "./chunk-6X5HU5LB.js";
27
+ } from "./chunk-NVFWBTAZ.js";
27
28
  import {
28
29
  graphToJson,
29
30
  loadGraph
30
- } from "./chunk-DY7YHFIB.js";
31
+ } from "./chunk-BYWLDTZK.js";
31
32
 
32
33
  // src/process-mining.ts
33
34
  function getPathSignature(graph) {
@@ -93,7 +94,7 @@ function discoverProcess(graphs) {
93
94
  steps: [...steps].sort(),
94
95
  transitions,
95
96
  totalGraphs: graphs.length,
96
- agentId: graphs[0].agentId
97
+ agentId: graphs[0]?.agentId ?? ""
97
98
  };
98
99
  }
99
100
  function findVariants(graphs) {
@@ -125,13 +126,13 @@ function findVariants(graphs) {
125
126
  }
126
127
  function percentile(sorted, p) {
127
128
  if (sorted.length === 0) return 0;
128
- if (sorted.length === 1) return sorted[0];
129
+ if (sorted.length === 1) return sorted[0] ?? 0;
129
130
  const index = p / 100 * (sorted.length - 1);
130
131
  const lower = Math.floor(index);
131
132
  const upper = Math.ceil(index);
132
- if (lower === upper) return sorted[lower];
133
+ if (lower === upper) return sorted[lower] ?? 0;
133
134
  const weight = index - lower;
134
- return sorted[lower] * (1 - weight) + sorted[upper] * weight;
135
+ return (sorted[lower] ?? 0) * (1 - weight) + (sorted[upper] ?? 0) * weight;
135
136
  }
136
137
  function getBottlenecks(graphs) {
137
138
  if (graphs.length === 0) return [];
@@ -158,8 +159,8 @@ function getBottlenecks(graphs) {
158
159
  median: percentile(sorted, 50),
159
160
  p95: percentile(sorted, 95),
160
161
  p99: percentile(sorted, 99),
161
- min: sorted[0],
162
- max: sorted[sorted.length - 1]
162
+ min: sorted[0] ?? 0,
163
+ max: sorted[sorted.length - 1] ?? 0
163
164
  },
164
165
  percentOfGraphs: sorted.length / total * 100
165
166
  });
@@ -342,6 +343,256 @@ function createEventEmitter(config) {
342
343
  };
343
344
  }
344
345
 
346
+ // src/guards.ts
347
+ function explainMessage(explanation) {
348
+ const sourceLabel = explanation.source === "static" ? "static config" : explanation.source === "soma-policy" ? `soma-policy${explanation.evidence ? ` (${explanation.evidence})` : ""}` : explanation.source === "assertion" ? "assertion" : "adaptive";
349
+ return `This run was stopped because ${explanation.rule} reached ${explanation.actual}, which exceeds the limit of ${explanation.threshold}. Source: ${sourceLabel}.`;
350
+ }
351
+ var DEFAULT_TIMEOUTS = {
352
+ tool: 3e4,
353
+ // 30s
354
+ agent: 3e5,
355
+ // 5m
356
+ subagent: 3e5,
357
+ // 5m
358
+ wait: 6e5,
359
+ // 10m
360
+ decision: 3e4,
361
+ // 30s
362
+ custom: 3e4
363
+ // 30s
364
+ };
365
+ function checkGuards(graph, config) {
366
+ const violations = [];
367
+ const now = Date.now();
368
+ const timeouts = { ...DEFAULT_TIMEOUTS, ...config?.timeouts };
369
+ const maxReasoningSteps = config?.maxReasoningSteps ?? 25;
370
+ const maxDepth = config?.maxDepth ?? 10;
371
+ const maxAgentSpawns = config?.maxAgentSpawns ?? 50;
372
+ for (const node of graph.nodes.values()) {
373
+ if (node.status === "running" && node.endTime === null) {
374
+ const timeoutThreshold = timeouts[node.type];
375
+ const elapsed = now - node.startTime;
376
+ if (elapsed > timeoutThreshold) {
377
+ const explanation = {
378
+ rule: "timeout",
379
+ threshold: timeoutThreshold,
380
+ actual: elapsed,
381
+ source: "static"
382
+ };
383
+ violations.push({
384
+ type: "timeout",
385
+ nodeId: node.id,
386
+ message: explainMessage(explanation),
387
+ timestamp: now,
388
+ explanation
389
+ });
390
+ }
391
+ }
392
+ }
393
+ const depth = getDepth(graph);
394
+ if (depth > maxDepth) {
395
+ const explanation = {
396
+ rule: "max-depth",
397
+ threshold: maxDepth,
398
+ actual: depth,
399
+ source: "static"
400
+ };
401
+ violations.push({
402
+ type: "spawn-explosion",
403
+ nodeId: graph.rootNodeId,
404
+ message: explainMessage(explanation),
405
+ timestamp: now,
406
+ explanation
407
+ });
408
+ }
409
+ let agentCount = 0;
410
+ for (const node of graph.nodes.values()) {
411
+ if (node.type === "agent" || node.type === "subagent") {
412
+ agentCount++;
413
+ }
414
+ }
415
+ if (agentCount > maxAgentSpawns) {
416
+ const explanation = {
417
+ rule: "max-agent-spawns",
418
+ threshold: maxAgentSpawns,
419
+ actual: agentCount,
420
+ source: "static"
421
+ };
422
+ violations.push({
423
+ type: "spawn-explosion",
424
+ nodeId: graph.rootNodeId,
425
+ message: explainMessage(explanation),
426
+ timestamp: now,
427
+ explanation
428
+ });
429
+ }
430
+ violations.push(...detectReasoningLoops(graph, maxReasoningSteps, now));
431
+ if (config?.policySource) {
432
+ violations.push(
433
+ ...checkPolicyViolations(graph, config.policySource, config.policyThresholds, now)
434
+ );
435
+ }
436
+ return violations;
437
+ }
438
+ function detectReasoningLoops(graph, maxSteps, timestamp) {
439
+ const violations = [];
440
+ const reported = /* @__PURE__ */ new Set();
441
+ function walk(nodeId, consecutiveCount, consecutiveType) {
442
+ const node = getNode(graph, nodeId);
443
+ if (!node) return;
444
+ let newCount;
445
+ let newType;
446
+ if (node.type === consecutiveType) {
447
+ newCount = consecutiveCount + 1;
448
+ newType = node.type;
449
+ } else {
450
+ newCount = 1;
451
+ newType = node.type;
452
+ }
453
+ if (newCount > maxSteps && !reported.has(newType)) {
454
+ reported.add(newType);
455
+ const explanation = {
456
+ rule: "max-reasoning-steps",
457
+ threshold: maxSteps,
458
+ actual: newCount,
459
+ source: "static"
460
+ };
461
+ violations.push({
462
+ type: "reasoning-loop",
463
+ nodeId: node.id,
464
+ message: explainMessage(explanation),
465
+ timestamp,
466
+ explanation
467
+ });
468
+ }
469
+ const children = getChildren(graph, nodeId);
470
+ for (const child of children) {
471
+ walk(child.id, newCount, newType);
472
+ }
473
+ }
474
+ walk(graph.rootNodeId, 0, null);
475
+ return violations;
476
+ }
477
+ function checkPolicyViolations(graph, policySource, thresholds, timestamp) {
478
+ const violations = [];
479
+ const maxFailureRate = thresholds?.maxFailureRate ?? 0.5;
480
+ const minConformance = thresholds?.minConformance ?? 0.7;
481
+ const failureRate = policySource.recentFailureRate(graph.agentId);
482
+ if (failureRate > maxFailureRate) {
483
+ const explanation = {
484
+ rule: "max-failure-rate",
485
+ threshold: maxFailureRate,
486
+ actual: failureRate,
487
+ source: "soma-policy"
488
+ };
489
+ violations.push({
490
+ type: "high-failure-rate",
491
+ nodeId: graph.rootNodeId,
492
+ message: explainMessage(explanation),
493
+ timestamp,
494
+ explanation
495
+ });
496
+ }
497
+ const conformanceScore = policySource.lastConformanceScore(graph.agentId);
498
+ if (conformanceScore !== null && conformanceScore < minConformance) {
499
+ const explanation = {
500
+ rule: "min-conformance",
501
+ threshold: minConformance,
502
+ actual: conformanceScore,
503
+ source: "soma-policy"
504
+ };
505
+ violations.push({
506
+ type: "conformance-drift",
507
+ nodeId: graph.rootNodeId,
508
+ message: explainMessage(explanation),
509
+ timestamp,
510
+ explanation
511
+ });
512
+ }
513
+ for (const node of graph.nodes.values()) {
514
+ if (node.status === "running" && policySource.isKnownBottleneck(node.name)) {
515
+ const explanation = {
516
+ rule: "known-bottleneck",
517
+ threshold: "flagged",
518
+ actual: "running",
519
+ source: "soma-policy"
520
+ };
521
+ violations.push({
522
+ type: "known-bottleneck",
523
+ nodeId: node.id,
524
+ message: explainMessage(explanation),
525
+ timestamp,
526
+ explanation
527
+ });
528
+ }
529
+ }
530
+ return violations;
531
+ }
532
+ function withGuards(builder, config) {
533
+ const logger = config?.logger ?? ((msg) => console.warn(`[AgentFlow Guard] ${msg}`));
534
+ const onViolation = config?.onViolation ?? "warn";
535
+ function handleViolations(violations) {
536
+ if (violations.length === 0) return;
537
+ for (const violation of violations) {
538
+ const message = `Guard violation: ${violation.message}`;
539
+ switch (onViolation) {
540
+ case "warn":
541
+ logger(message);
542
+ break;
543
+ case "error":
544
+ logger(message);
545
+ builder.pushEvent({
546
+ eventType: "custom",
547
+ nodeId: violation.nodeId,
548
+ data: {
549
+ guardViolation: violation.type,
550
+ message: violation.message,
551
+ severity: "error"
552
+ }
553
+ });
554
+ break;
555
+ case "abort":
556
+ throw new Error(`AgentFlow guard violation: ${violation.message}`);
557
+ default:
558
+ logger(message);
559
+ }
560
+ }
561
+ }
562
+ return {
563
+ get graphId() {
564
+ return builder.graphId;
565
+ },
566
+ get traceContext() {
567
+ return builder.traceContext;
568
+ },
569
+ startNode: (opts) => builder.startNode(opts),
570
+ endNode: (nodeId, status) => {
571
+ builder.endNode(nodeId, status);
572
+ const snapshot = builder.getSnapshot();
573
+ const violations = checkGuards(snapshot, config);
574
+ handleViolations(violations);
575
+ },
576
+ failNode: (nodeId, error) => {
577
+ builder.failNode(nodeId, error);
578
+ const snapshot = builder.getSnapshot();
579
+ const violations = checkGuards(snapshot, config);
580
+ handleViolations(violations);
581
+ },
582
+ addEdge: (from, to, type) => builder.addEdge(from, to, type),
583
+ pushEvent: (event) => builder.pushEvent(event),
584
+ updateState: (nodeId, state) => builder.updateState(nodeId, state),
585
+ withParent: (parentId, fn) => builder.withParent(parentId, fn),
586
+ getSnapshot: () => builder.getSnapshot(),
587
+ build: () => {
588
+ const snapshot = builder.getSnapshot();
589
+ const violations = checkGuards(snapshot, config);
590
+ handleViolations(violations);
591
+ return builder.build();
592
+ }
593
+ };
594
+ }
595
+
345
596
  // src/prompt-builder.ts
346
597
  var ROLE = "You are analyzing execution data for an AI agent system. Provide clear, actionable analysis based on the data below.";
347
598
  function fmtDuration(ms) {
@@ -672,201 +923,6 @@ function createInsightEngine(store, analysisFn, config) {
672
923
  };
673
924
  }
674
925
 
675
- // src/guards.ts
676
- var DEFAULT_TIMEOUTS = {
677
- tool: 3e4,
678
- // 30s
679
- agent: 3e5,
680
- // 5m
681
- subagent: 3e5,
682
- // 5m
683
- wait: 6e5,
684
- // 10m
685
- decision: 3e4,
686
- // 30s
687
- custom: 3e4
688
- // 30s
689
- };
690
- function checkGuards(graph, config) {
691
- const violations = [];
692
- const now = Date.now();
693
- const timeouts = { ...DEFAULT_TIMEOUTS, ...config?.timeouts };
694
- const maxReasoningSteps = config?.maxReasoningSteps ?? 25;
695
- const maxDepth = config?.maxDepth ?? 10;
696
- const maxAgentSpawns = config?.maxAgentSpawns ?? 50;
697
- for (const node of graph.nodes.values()) {
698
- if (node.status === "running" && node.endTime === null) {
699
- const timeoutThreshold = timeouts[node.type];
700
- const elapsed = now - node.startTime;
701
- if (elapsed > timeoutThreshold) {
702
- violations.push({
703
- type: "timeout",
704
- nodeId: node.id,
705
- message: `Node ${node.id} (${node.type}: ${node.name}) has been running for ${elapsed}ms, exceeding timeout of ${timeoutThreshold}ms`,
706
- timestamp: now
707
- });
708
- }
709
- }
710
- }
711
- const depth = getDepth(graph);
712
- if (depth > maxDepth) {
713
- violations.push({
714
- type: "spawn-explosion",
715
- nodeId: graph.rootNodeId,
716
- message: `Graph depth ${depth} exceeds maximum depth of ${maxDepth}`,
717
- timestamp: now
718
- });
719
- }
720
- let agentCount = 0;
721
- for (const node of graph.nodes.values()) {
722
- if (node.type === "agent" || node.type === "subagent") {
723
- agentCount++;
724
- }
725
- }
726
- if (agentCount > maxAgentSpawns) {
727
- violations.push({
728
- type: "spawn-explosion",
729
- nodeId: graph.rootNodeId,
730
- message: `Total agent/subagent count ${agentCount} exceeds maximum of ${maxAgentSpawns}`,
731
- timestamp: now
732
- });
733
- }
734
- violations.push(...detectReasoningLoops(graph, maxReasoningSteps, now));
735
- if (config?.policySource) {
736
- violations.push(...checkPolicyViolations(graph, config.policySource, config.policyThresholds, now));
737
- }
738
- return violations;
739
- }
740
- function detectReasoningLoops(graph, maxSteps, timestamp) {
741
- const violations = [];
742
- const reported = /* @__PURE__ */ new Set();
743
- function walk(nodeId, consecutiveCount, consecutiveType) {
744
- const node = getNode(graph, nodeId);
745
- if (!node) return;
746
- let newCount;
747
- let newType;
748
- if (node.type === consecutiveType) {
749
- newCount = consecutiveCount + 1;
750
- newType = node.type;
751
- } else {
752
- newCount = 1;
753
- newType = node.type;
754
- }
755
- if (newCount > maxSteps && !reported.has(newType)) {
756
- reported.add(newType);
757
- violations.push({
758
- type: "reasoning-loop",
759
- nodeId: node.id,
760
- message: `Detected ${newCount} consecutive ${newType} nodes along path to ${node.name}`,
761
- timestamp
762
- });
763
- }
764
- const children = getChildren(graph, nodeId);
765
- for (const child of children) {
766
- walk(child.id, newCount, newType);
767
- }
768
- }
769
- walk(graph.rootNodeId, 0, null);
770
- return violations;
771
- }
772
- function checkPolicyViolations(graph, policySource, thresholds, timestamp) {
773
- const violations = [];
774
- const maxFailureRate = thresholds?.maxFailureRate ?? 0.5;
775
- const minConformance = thresholds?.minConformance ?? 0.7;
776
- const failureRate = policySource.recentFailureRate(graph.agentId);
777
- if (failureRate > maxFailureRate) {
778
- violations.push({
779
- type: "high-failure-rate",
780
- nodeId: graph.rootNodeId,
781
- message: `Agent ${graph.agentId} has a recent failure rate of ${(failureRate * 100).toFixed(0)}% (threshold: ${(maxFailureRate * 100).toFixed(0)}%)`,
782
- timestamp
783
- });
784
- }
785
- const conformanceScore = policySource.lastConformanceScore(graph.agentId);
786
- if (conformanceScore !== null && conformanceScore < minConformance) {
787
- violations.push({
788
- type: "conformance-drift",
789
- nodeId: graph.rootNodeId,
790
- message: `Agent ${graph.agentId} conformance score ${(conformanceScore * 100).toFixed(0)}% is below threshold ${(minConformance * 100).toFixed(0)}%`,
791
- timestamp
792
- });
793
- }
794
- for (const node of graph.nodes.values()) {
795
- if (node.status === "running" && policySource.isKnownBottleneck(node.name)) {
796
- violations.push({
797
- type: "known-bottleneck",
798
- nodeId: node.id,
799
- message: `Node ${node.name} (${node.type}) is a known bottleneck`,
800
- timestamp
801
- });
802
- }
803
- }
804
- return violations;
805
- }
806
- function withGuards(builder, config) {
807
- const logger = config?.logger ?? ((msg) => console.warn(`[AgentFlow Guard] ${msg}`));
808
- const onViolation = config?.onViolation ?? "warn";
809
- function handleViolations(violations) {
810
- if (violations.length === 0) return;
811
- for (const violation of violations) {
812
- const message = `Guard violation: ${violation.message}`;
813
- switch (onViolation) {
814
- case "warn":
815
- logger(message);
816
- break;
817
- case "error":
818
- logger(message);
819
- builder.pushEvent({
820
- eventType: "custom",
821
- nodeId: violation.nodeId,
822
- data: {
823
- guardViolation: violation.type,
824
- message: violation.message,
825
- severity: "error"
826
- }
827
- });
828
- break;
829
- case "abort":
830
- throw new Error(`AgentFlow guard violation: ${violation.message}`);
831
- default:
832
- logger(message);
833
- }
834
- }
835
- }
836
- return {
837
- get graphId() {
838
- return builder.graphId;
839
- },
840
- get traceContext() {
841
- return builder.traceContext;
842
- },
843
- startNode: (opts) => builder.startNode(opts),
844
- endNode: (nodeId, status) => {
845
- builder.endNode(nodeId, status);
846
- const snapshot = builder.getSnapshot();
847
- const violations = checkGuards(snapshot, config);
848
- handleViolations(violations);
849
- },
850
- failNode: (nodeId, error) => {
851
- builder.failNode(nodeId, error);
852
- const snapshot = builder.getSnapshot();
853
- const violations = checkGuards(snapshot, config);
854
- handleViolations(violations);
855
- },
856
- addEdge: (from, to, type) => builder.addEdge(from, to, type),
857
- pushEvent: (event) => builder.pushEvent(event),
858
- updateState: (nodeId, state) => builder.updateState(nodeId, state),
859
- withParent: (parentId, fn) => builder.withParent(parentId, fn),
860
- getSnapshot: () => builder.getSnapshot(),
861
- build: () => {
862
- const snapshot = builder.getSnapshot();
863
- const violations = checkGuards(snapshot, config);
864
- handleViolations(violations);
865
- return builder.build();
866
- }
867
- };
868
- }
869
-
870
926
  // src/json-event-writer.ts
871
927
  import { mkdirSync, writeFileSync } from "fs";
872
928
  import { join } from "path";
@@ -980,7 +1036,8 @@ function createKnowledgeStore(config) {
980
1036
  mkdirSync2(dir, { recursive: true });
981
1037
  }
982
1038
  function profilePath(agentId) {
983
- return join2(profilesDir, `${agentId}.json`);
1039
+ const safe = agentId.replace(/[/\\]/g, "_").replace(/\.\./g, "_");
1040
+ return join2(profilesDir, `${safe}.json`);
984
1041
  }
985
1042
  function appendExecutionEvent(event) {
986
1043
  const dateDir = join2(eventsDir, event.agentId, toDateDir(event.timestamp));
@@ -1105,7 +1162,7 @@ function createKnowledgeStore(config) {
1105
1162
  continue;
1106
1163
  }
1107
1164
  for (const file of files) {
1108
- const ts = Number.parseInt(file.split("-")[0], 10);
1165
+ const ts = Number.parseInt(file.split("-")[0] ?? "", 10);
1109
1166
  if (!Number.isNaN(ts) && ts < options.olderThan) {
1110
1167
  try {
1111
1168
  rmSync(join2(agentPatternDir, file));
@@ -1193,7 +1250,7 @@ function createPolicySource(store) {
1193
1250
  }
1194
1251
  for (const agentId of agentIds) {
1195
1252
  const profile = store.getAgentProfile(agentId);
1196
- if (profile && profile.knownBottlenecks.includes(nodeName)) {
1253
+ if (profile?.knownBottlenecks.includes(nodeName)) {
1197
1254
  return true;
1198
1255
  }
1199
1256
  }
@@ -1209,6 +1266,118 @@ function createPolicySource(store) {
1209
1266
  };
1210
1267
  }
1211
1268
 
1269
+ // src/receipts.ts
1270
+ function extractTokenCost(node) {
1271
+ const semantic = node.metadata?.semantic;
1272
+ if (semantic?.tokenCost !== void 0 && semantic.tokenCost !== null) {
1273
+ return semantic.tokenCost;
1274
+ }
1275
+ if (node.state?.tokenCost !== void 0 && node.state.tokenCost !== null) {
1276
+ return node.state.tokenCost;
1277
+ }
1278
+ return null;
1279
+ }
1280
+ function extractError(node) {
1281
+ if (node.state?.error !== void 0 && node.state.error !== null) {
1282
+ return String(node.state.error);
1283
+ }
1284
+ if (node.metadata?.error !== void 0 && node.metadata.error !== null) {
1285
+ return String(node.metadata.error);
1286
+ }
1287
+ return null;
1288
+ }
1289
+ function nodeDuration(node) {
1290
+ if (node.endTime === null) return null;
1291
+ return node.endTime - node.startTime;
1292
+ }
1293
+ function toReceipt(graph) {
1294
+ const nodes = [...graph.nodes.values()];
1295
+ nodes.sort((a, b) => a.startTime - b.startTime);
1296
+ const steps = nodes.map((node) => ({
1297
+ nodeId: node.id,
1298
+ name: node.name,
1299
+ type: node.type,
1300
+ status: node.status,
1301
+ durationMs: nodeDuration(node),
1302
+ tokenCost: extractTokenCost(node),
1303
+ error: extractError(node)
1304
+ }));
1305
+ let succeeded = 0;
1306
+ let failed = 0;
1307
+ const skipped = 0;
1308
+ for (const node of nodes) {
1309
+ if (node.status === "completed") {
1310
+ succeeded++;
1311
+ } else if (node.status === "failed" || node.status === "hung" || node.status === "timeout") {
1312
+ failed++;
1313
+ }
1314
+ }
1315
+ const attempted = nodes.length;
1316
+ let totalTokenCost = null;
1317
+ for (const step of steps) {
1318
+ if (step.tokenCost !== null) {
1319
+ totalTokenCost = (totalTokenCost ?? 0) + step.tokenCost;
1320
+ }
1321
+ }
1322
+ const totalDurationMs = graph.endTime !== null ? graph.endTime - graph.startTime : null;
1323
+ return {
1324
+ runId: graph.id,
1325
+ agentId: graph.agentId,
1326
+ status: graph.status,
1327
+ startTime: graph.startTime,
1328
+ endTime: graph.endTime,
1329
+ totalDurationMs,
1330
+ totalTokenCost,
1331
+ steps,
1332
+ summary: { attempted, succeeded, failed, skipped }
1333
+ };
1334
+ }
1335
+ function formatReceipt(receipt) {
1336
+ const lines = [];
1337
+ lines.push("=== Run Receipt ===");
1338
+ lines.push(`Run: ${receipt.runId}`);
1339
+ lines.push(`Agent: ${receipt.agentId}`);
1340
+ lines.push(`Status: ${receipt.status}`);
1341
+ lines.push(
1342
+ `Duration: ${receipt.totalDurationMs !== null ? `${receipt.totalDurationMs}ms` : "\u2014"}`
1343
+ );
1344
+ lines.push("");
1345
+ const s = receipt.summary;
1346
+ lines.push(
1347
+ `Summary: ${s.attempted} attempted, ${s.succeeded} succeeded, ${s.failed} failed, ${s.skipped} skipped`
1348
+ );
1349
+ lines.push("");
1350
+ const nameWidth = Math.max(4, ...receipt.steps.map((st) => st.name.length));
1351
+ const typeWidth = Math.max(4, ...receipt.steps.map((st) => st.type.length));
1352
+ const pad = (str, width) => str.padEnd(width);
1353
+ const padNum = (str, width) => str.padStart(width);
1354
+ const idxWidth = Math.max(2, String(receipt.steps.length).length);
1355
+ const statusWidth = 9;
1356
+ const durWidth = 10;
1357
+ const tokWidth = 8;
1358
+ lines.push(
1359
+ ` ${padNum("#", idxWidth)} | ${pad("Step", nameWidth)} | ${pad("Type", typeWidth)} | ${pad("Status", statusWidth)} | ${padNum("Duration", durWidth)} | ${padNum("Tokens", tokWidth)}`
1360
+ );
1361
+ lines.push(
1362
+ `${"-".repeat(idxWidth + 1)}|${"-".repeat(nameWidth + 2)}|${"-".repeat(typeWidth + 2)}|${"-".repeat(statusWidth + 2)}|${"-".repeat(durWidth + 2)}|${"-".repeat(tokWidth + 2)}`
1363
+ );
1364
+ for (let i = 0; i < receipt.steps.length; i++) {
1365
+ const step = receipt.steps[i];
1366
+ const durStr = step.durationMs !== null ? `${step.durationMs}ms` : "\u2014";
1367
+ const tokStr = step.tokenCost !== null ? String(step.tokenCost) : "\u2014";
1368
+ lines.push(
1369
+ ` ${padNum(String(i + 1), idxWidth)} | ${pad(step.name, nameWidth)} | ${pad(step.type, typeWidth)} | ${pad(step.status, statusWidth)} | ${padNum(durStr, durWidth)} | ${padNum(tokStr, tokWidth)}`
1370
+ );
1371
+ }
1372
+ lines.push("");
1373
+ if (receipt.totalTokenCost !== null) {
1374
+ lines.push(`Total token cost: ${receipt.totalTokenCost}`);
1375
+ } else {
1376
+ lines.push("Total token cost: no cost data");
1377
+ }
1378
+ return lines.join("\n");
1379
+ }
1380
+
1212
1381
  // src/soma-event-writer.ts
1213
1382
  import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "fs";
1214
1383
  import { join as join4 } from "path";
@@ -1241,11 +1410,7 @@ function formatDuration(ms) {
1241
1410
  function executionEventToMarkdown(event) {
1242
1411
  const isCompleted = event.eventType === "execution.completed";
1243
1412
  const subtype = isCompleted ? "completed" : "failed";
1244
- const tags = [
1245
- "agentflow/execution",
1246
- `agent/${event.agentId}`,
1247
- `status/${subtype}`
1248
- ];
1413
+ const tags = ["agentflow/execution", `agent/${event.agentId}`, `status/${subtype}`];
1249
1414
  if (event.processContext?.isAnomaly) {
1250
1415
  tags.push("agentflow/anomaly");
1251
1416
  }
@@ -1303,10 +1468,7 @@ ${body.join("\n")}`;
1303
1468
  }
1304
1469
  function patternEventToMarkdown(event) {
1305
1470
  const { pattern } = event;
1306
- const tags = [
1307
- "agentflow/pattern",
1308
- `agent/${event.agentId}`
1309
- ];
1471
+ const tags = ["agentflow/pattern", `agent/${event.agentId}`];
1310
1472
  const frontmatter = {
1311
1473
  type: "synthesis",
1312
1474
  subtype: "pattern-discovery",
@@ -1393,11 +1555,13 @@ export {
1393
1555
  createPolicySource,
1394
1556
  createSomaEventWriter,
1395
1557
  createTraceStore,
1558
+ discoverAllProcessConfigs,
1396
1559
  discoverProcess,
1397
1560
  discoverProcessConfig,
1398
1561
  findVariants,
1399
1562
  findWaitingOn,
1400
1563
  formatAuditReport,
1564
+ formatReceipt,
1401
1565
  getBottlenecks,
1402
1566
  getChildren,
1403
1567
  getCriticalPath,
@@ -1419,6 +1583,7 @@ export {
1419
1583
  startWatch,
1420
1584
  stitchTrace,
1421
1585
  toAsciiTree,
1586
+ toReceipt,
1422
1587
  toTimeline,
1423
1588
  withGuards
1424
1589
  };
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  graphToJson,
3
3
  loadGraph
4
- } from "./chunk-DY7YHFIB.js";
4
+ } from "./chunk-BYWLDTZK.js";
5
5
  export {
6
6
  graphToJson,
7
7
  loadGraph