agentflow-core 0.7.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,13 +24,330 @@ import {
23
24
  stitchTrace,
24
25
  toAsciiTree,
25
26
  toTimeline
26
- } from "./chunk-5PRHVYYD.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";
32
+
33
+ // src/process-mining.ts
34
+ function getPathSignature(graph) {
35
+ const root = graph.nodes.get(graph.rootNodeId);
36
+ if (!root) return "";
37
+ const parts = [];
38
+ function walk(node) {
39
+ parts.push(`${node.type}:${node.name}`);
40
+ const childNodes = [];
41
+ for (const childId of node.children) {
42
+ const child = graph.nodes.get(childId);
43
+ if (child) childNodes.push(child);
44
+ }
45
+ childNodes.sort((a, b) => {
46
+ const keyA = `${a.type}:${a.name}`;
47
+ const keyB = `${b.type}:${b.name}`;
48
+ return keyA.localeCompare(keyB);
49
+ });
50
+ for (const child of childNodes) {
51
+ walk(child);
52
+ }
53
+ }
54
+ walk(root);
55
+ return parts.join("\u2192");
56
+ }
57
+ function stepKey(node) {
58
+ return `${node.type}:${node.name}`;
59
+ }
60
+ function discoverProcess(graphs) {
61
+ if (graphs.length === 0) {
62
+ throw new Error("discoverProcess requires at least one graph");
63
+ }
64
+ const steps = /* @__PURE__ */ new Set();
65
+ const transitionCounts = /* @__PURE__ */ new Map();
66
+ const outgoingCounts = /* @__PURE__ */ new Map();
67
+ for (const graph of graphs) {
68
+ for (const node of graph.nodes.values()) {
69
+ const parentKey = stepKey(node);
70
+ steps.add(parentKey);
71
+ for (const childId of node.children) {
72
+ const child = graph.nodes.get(childId);
73
+ if (!child) continue;
74
+ const childKey = stepKey(child);
75
+ const tKey = `${parentKey}\0${childKey}`;
76
+ transitionCounts.set(tKey, (transitionCounts.get(tKey) ?? 0) + 1);
77
+ outgoingCounts.set(parentKey, (outgoingCounts.get(parentKey) ?? 0) + 1);
78
+ }
79
+ }
80
+ }
81
+ const transitions = [];
82
+ for (const [tKey, count] of transitionCounts) {
83
+ const [from, to] = tKey.split("\0");
84
+ const outgoing = outgoingCounts.get(from) ?? count;
85
+ transitions.push({
86
+ from,
87
+ to,
88
+ count,
89
+ probability: count / outgoing
90
+ });
91
+ }
92
+ transitions.sort((a, b) => a.from.localeCompare(b.from) || a.to.localeCompare(b.to));
93
+ return {
94
+ steps: [...steps].sort(),
95
+ transitions,
96
+ totalGraphs: graphs.length,
97
+ agentId: graphs[0]?.agentId ?? ""
98
+ };
99
+ }
100
+ function findVariants(graphs) {
101
+ if (graphs.length === 0) return [];
102
+ const groups = /* @__PURE__ */ new Map();
103
+ for (const graph of graphs) {
104
+ const sig = getPathSignature(graph);
105
+ const group = groups.get(sig) ?? [];
106
+ group.push(graph);
107
+ groups.set(sig, group);
108
+ }
109
+ const total = graphs.length;
110
+ const variants = [];
111
+ for (const [pathSignature, groupGraphs] of groups) {
112
+ variants.push({
113
+ pathSignature,
114
+ count: groupGraphs.length,
115
+ percentage: groupGraphs.length / total * 100,
116
+ graphIds: groupGraphs.map((g) => g.id),
117
+ exampleGraph: groupGraphs[0]
118
+ });
119
+ }
120
+ variants.sort((a, b) => {
121
+ const freqDiff = b.count - a.count;
122
+ if (freqDiff !== 0) return freqDiff;
123
+ return a.pathSignature.localeCompare(b.pathSignature);
124
+ });
125
+ return variants;
126
+ }
127
+ function percentile(sorted, p) {
128
+ if (sorted.length === 0) return 0;
129
+ if (sorted.length === 1) return sorted[0] ?? 0;
130
+ const index = p / 100 * (sorted.length - 1);
131
+ const lower = Math.floor(index);
132
+ const upper = Math.ceil(index);
133
+ if (lower === upper) return sorted[lower] ?? 0;
134
+ const weight = index - lower;
135
+ return (sorted[lower] ?? 0) * (1 - weight) + (sorted[upper] ?? 0) * weight;
136
+ }
137
+ function getBottlenecks(graphs) {
138
+ if (graphs.length === 0) return [];
139
+ const now = Date.now();
140
+ const stats = /* @__PURE__ */ new Map();
141
+ for (const graph of graphs) {
142
+ for (const node of graph.nodes.values()) {
143
+ const key = `${node.type}:${node.name}`;
144
+ const entry = stats.get(key) ?? { durations: [], nodeType: node.type, nodeName: node.name };
145
+ const end = node.endTime ?? now;
146
+ entry.durations.push(end - node.startTime);
147
+ stats.set(key, entry);
148
+ }
149
+ }
150
+ const total = graphs.length;
151
+ const bottlenecks = [];
152
+ for (const [, entry] of stats) {
153
+ const sorted = [...entry.durations].sort((a, b) => a - b);
154
+ bottlenecks.push({
155
+ nodeName: entry.nodeName,
156
+ nodeType: entry.nodeType,
157
+ occurrences: sorted.length,
158
+ durations: {
159
+ median: percentile(sorted, 50),
160
+ p95: percentile(sorted, 95),
161
+ p99: percentile(sorted, 99),
162
+ min: sorted[0] ?? 0,
163
+ max: sorted[sorted.length - 1] ?? 0
164
+ },
165
+ percentOfGraphs: sorted.length / total * 100
166
+ });
167
+ }
168
+ bottlenecks.sort((a, b) => b.durations.p95 - a.durations.p95);
169
+ return bottlenecks;
170
+ }
171
+ function extractGraphTransitions(graph) {
172
+ const transitions = /* @__PURE__ */ new Set();
173
+ for (const node of graph.nodes.values()) {
174
+ const parentKey = stepKey(node);
175
+ for (const childId of node.children) {
176
+ const child = graph.nodes.get(childId);
177
+ if (!child) continue;
178
+ transitions.add(`${parentKey}\0${stepKey(child)}`);
179
+ }
180
+ }
181
+ return transitions;
182
+ }
183
+ function checkConformance(graph, model) {
184
+ const graphTransitions = extractGraphTransitions(graph);
185
+ const deviations = [];
186
+ const modelLookup = /* @__PURE__ */ new Map();
187
+ for (const t of model.transitions) {
188
+ modelLookup.set(`${t.from}\0${t.to}`, t);
189
+ }
190
+ let totalChecks = 0;
191
+ let deviationCount = 0;
192
+ for (const tKey of graphTransitions) {
193
+ totalChecks++;
194
+ const [from, to] = tKey.split("\0");
195
+ const modelTransition = modelLookup.get(tKey);
196
+ if (!modelTransition) {
197
+ deviationCount++;
198
+ deviations.push({
199
+ type: "unexpected-transition",
200
+ from,
201
+ to,
202
+ message: `Unexpected transition ${from} \u2192 ${to} (not in process model)`
203
+ });
204
+ } else if (modelTransition.probability < 0.1) {
205
+ deviationCount++;
206
+ deviations.push({
207
+ type: "low-frequency-path",
208
+ from,
209
+ to,
210
+ message: `Low-frequency path ${from} \u2192 ${to} (model probability: ${(modelTransition.probability * 100).toFixed(1)}%)`,
211
+ modelProbability: modelTransition.probability
212
+ });
213
+ }
214
+ }
215
+ const graphSteps = /* @__PURE__ */ new Set();
216
+ for (const node of graph.nodes.values()) {
217
+ graphSteps.add(stepKey(node));
218
+ }
219
+ for (const t of model.transitions) {
220
+ if (t.probability > 0.5) {
221
+ const tKey = `${t.from}\0${t.to}`;
222
+ if (graphSteps.has(t.from) && !graphTransitions.has(tKey)) {
223
+ totalChecks++;
224
+ deviationCount++;
225
+ deviations.push({
226
+ type: "missing-transition",
227
+ from: t.from,
228
+ to: t.to,
229
+ message: `Missing expected transition ${t.from} \u2192 ${t.to} (model probability: ${(t.probability * 100).toFixed(1)}%)`,
230
+ modelProbability: t.probability
231
+ });
232
+ }
233
+ }
234
+ }
235
+ const conformanceScore = totalChecks === 0 ? 1 : (totalChecks - deviationCount) / totalChecks;
236
+ return {
237
+ conformanceScore,
238
+ isConforming: deviations.length === 0,
239
+ deviations
240
+ };
241
+ }
242
+
243
+ // src/event-emitter.ts
244
+ var SCHEMA_VERSION = 1;
245
+ function createExecutionEvent(graph, options) {
246
+ const duration = graph.endTime !== null ? graph.endTime - graph.startTime : Date.now() - graph.startTime;
247
+ let failurePoint;
248
+ if (graph.status === "failed") {
249
+ let candidate;
250
+ for (const node of graph.nodes.values()) {
251
+ if (node.status === "failed" || node.status === "timeout") {
252
+ const errorMeta = node.metadata.error;
253
+ const fp = {
254
+ nodeId: node.id,
255
+ nodeName: node.name,
256
+ nodeType: node.type,
257
+ error: typeof errorMeta === "string" ? errorMeta : void 0
258
+ };
259
+ if (node.id !== graph.rootNodeId) {
260
+ failurePoint = fp;
261
+ break;
262
+ }
263
+ if (!candidate) candidate = fp;
264
+ }
265
+ }
266
+ if (!failurePoint) failurePoint = candidate;
267
+ }
268
+ return {
269
+ eventType: graph.status === "failed" ? "execution.failed" : "execution.completed",
270
+ graphId: graph.id,
271
+ agentId: graph.agentId,
272
+ timestamp: Date.now(),
273
+ schemaVersion: SCHEMA_VERSION,
274
+ status: graph.status,
275
+ duration,
276
+ nodeCount: graph.nodes.size,
277
+ pathSignature: getPathSignature(graph),
278
+ ...failurePoint ? { failurePoint } : {},
279
+ ...options?.processContext ? { processContext: options.processContext } : {},
280
+ ...options?.semantic ? { semantic: options.semantic } : {},
281
+ violations: options?.violations ?? []
282
+ };
283
+ }
284
+ function createPatternEvent(agentId, model, variants, bottlenecks) {
285
+ return {
286
+ eventType: "pattern.discovered",
287
+ agentId,
288
+ timestamp: Date.now(),
289
+ schemaVersion: SCHEMA_VERSION,
290
+ pattern: {
291
+ totalGraphs: model.totalGraphs,
292
+ variantCount: variants.length,
293
+ topVariants: variants.slice(0, 5).map((v) => ({
294
+ pathSignature: v.pathSignature,
295
+ count: v.count,
296
+ percentage: v.percentage
297
+ })),
298
+ topBottlenecks: bottlenecks.slice(0, 5).map((b) => ({
299
+ nodeName: b.nodeName,
300
+ nodeType: b.nodeType,
301
+ p95: b.durations.p95
302
+ })),
303
+ processModel: model
304
+ }
305
+ };
306
+ }
307
+ function createEventEmitter(config) {
308
+ const writers = config?.writers ?? [];
309
+ const knowledgeStore = config?.knowledgeStore;
310
+ const onError = config?.onError ?? (() => {
311
+ });
312
+ const subscribers = /* @__PURE__ */ new Set();
313
+ return {
314
+ async emit(event) {
315
+ if (knowledgeStore) {
316
+ try {
317
+ knowledgeStore.append(event);
318
+ } catch (err) {
319
+ onError(err);
320
+ }
321
+ }
322
+ for (const writer of writers) {
323
+ try {
324
+ await writer.writeEvent(event);
325
+ } catch (err) {
326
+ onError(err);
327
+ }
328
+ }
329
+ for (const listener of subscribers) {
330
+ try {
331
+ listener(event);
332
+ } catch (err) {
333
+ onError(err);
334
+ }
335
+ }
336
+ },
337
+ subscribe(listener) {
338
+ subscribers.add(listener);
339
+ return () => {
340
+ subscribers.delete(listener);
341
+ };
342
+ }
343
+ };
344
+ }
31
345
 
32
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
+ }
33
351
  var DEFAULT_TIMEOUTS = {
34
352
  tool: 3e4,
35
353
  // 30s
@@ -56,22 +374,36 @@ function checkGuards(graph, config) {
56
374
  const timeoutThreshold = timeouts[node.type];
57
375
  const elapsed = now - node.startTime;
58
376
  if (elapsed > timeoutThreshold) {
377
+ const explanation = {
378
+ rule: "timeout",
379
+ threshold: timeoutThreshold,
380
+ actual: elapsed,
381
+ source: "static"
382
+ };
59
383
  violations.push({
60
384
  type: "timeout",
61
385
  nodeId: node.id,
62
- message: `Node ${node.id} (${node.type}: ${node.name}) has been running for ${elapsed}ms, exceeding timeout of ${timeoutThreshold}ms`,
63
- timestamp: now
386
+ message: explainMessage(explanation),
387
+ timestamp: now,
388
+ explanation
64
389
  });
65
390
  }
66
391
  }
67
392
  }
68
393
  const depth = getDepth(graph);
69
394
  if (depth > maxDepth) {
395
+ const explanation = {
396
+ rule: "max-depth",
397
+ threshold: maxDepth,
398
+ actual: depth,
399
+ source: "static"
400
+ };
70
401
  violations.push({
71
402
  type: "spawn-explosion",
72
403
  nodeId: graph.rootNodeId,
73
- message: `Graph depth ${depth} exceeds maximum depth of ${maxDepth}`,
74
- timestamp: now
404
+ message: explainMessage(explanation),
405
+ timestamp: now,
406
+ explanation
75
407
  });
76
408
  }
77
409
  let agentCount = 0;
@@ -81,14 +413,26 @@ function checkGuards(graph, config) {
81
413
  }
82
414
  }
83
415
  if (agentCount > maxAgentSpawns) {
416
+ const explanation = {
417
+ rule: "max-agent-spawns",
418
+ threshold: maxAgentSpawns,
419
+ actual: agentCount,
420
+ source: "static"
421
+ };
84
422
  violations.push({
85
423
  type: "spawn-explosion",
86
424
  nodeId: graph.rootNodeId,
87
- message: `Total agent/subagent count ${agentCount} exceeds maximum of ${maxAgentSpawns}`,
88
- timestamp: now
425
+ message: explainMessage(explanation),
426
+ timestamp: now,
427
+ explanation
89
428
  });
90
429
  }
91
430
  violations.push(...detectReasoningLoops(graph, maxReasoningSteps, now));
431
+ if (config?.policySource) {
432
+ violations.push(
433
+ ...checkPolicyViolations(graph, config.policySource, config.policyThresholds, now)
434
+ );
435
+ }
92
436
  return violations;
93
437
  }
94
438
  function detectReasoningLoops(graph, maxSteps, timestamp) {
@@ -108,11 +452,18 @@ function detectReasoningLoops(graph, maxSteps, timestamp) {
108
452
  }
109
453
  if (newCount > maxSteps && !reported.has(newType)) {
110
454
  reported.add(newType);
455
+ const explanation = {
456
+ rule: "max-reasoning-steps",
457
+ threshold: maxSteps,
458
+ actual: newCount,
459
+ source: "static"
460
+ };
111
461
  violations.push({
112
462
  type: "reasoning-loop",
113
463
  nodeId: node.id,
114
- message: `Detected ${newCount} consecutive ${newType} nodes along path to ${node.name}`,
115
- timestamp
464
+ message: explainMessage(explanation),
465
+ timestamp,
466
+ explanation
116
467
  });
117
468
  }
118
469
  const children = getChildren(graph, nodeId);
@@ -123,6 +474,61 @@ function detectReasoningLoops(graph, maxSteps, timestamp) {
123
474
  walk(graph.rootNodeId, 0, null);
124
475
  return violations;
125
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
+ }
126
532
  function withGuards(builder, config) {
127
533
  const logger = config?.logger ?? ((msg) => console.warn(`[AgentFlow Guard] ${msg}`));
128
534
  const onViolation = config?.onViolation ?? "warn";
@@ -186,14 +592,977 @@ function withGuards(builder, config) {
186
592
  }
187
593
  };
188
594
  }
595
+
596
+ // src/prompt-builder.ts
597
+ var ROLE = "You are analyzing execution data for an AI agent system. Provide clear, actionable analysis based on the data below.";
598
+ function fmtDuration(ms) {
599
+ if (ms < 1e3) return `${ms}ms`;
600
+ return `${(ms / 1e3).toFixed(1)}s`;
601
+ }
602
+ function fmtTime(ts) {
603
+ return new Date(ts).toISOString();
604
+ }
605
+ function durationStats(durations) {
606
+ if (durations.length === 0) return { avg: 0, p50: 0, p95: 0, min: 0, max: 0 };
607
+ const sorted = [...durations].sort((a, b) => a - b);
608
+ const sum = sorted.reduce((a, b) => a + b, 0);
609
+ return {
610
+ avg: Math.round(sum / sorted.length),
611
+ p50: sorted[Math.floor(sorted.length * 0.5)] ?? 0,
612
+ p95: sorted[Math.floor(sorted.length * 0.95)] ?? 0,
613
+ min: sorted[0] ?? 0,
614
+ max: sorted[sorted.length - 1] ?? 0
615
+ };
616
+ }
617
+ function buildFailureAnalysisPrompt(events, profile) {
618
+ const stats = durationStats(profile.recentDurations);
619
+ const failureDetails = events.map((e, i) => {
620
+ const lines = [
621
+ `Failure ${i + 1}:`,
622
+ ` Time: ${fmtTime(e.timestamp)}`,
623
+ ` Duration: ${fmtDuration(e.duration)}`,
624
+ ` Path: ${e.pathSignature}`
625
+ ];
626
+ if (e.failurePoint) {
627
+ lines.push(` Failed at: ${e.failurePoint.nodeName} (${e.failurePoint.nodeType})`);
628
+ if (e.failurePoint.error) lines.push(` Error: ${e.failurePoint.error}`);
629
+ }
630
+ if (e.violations.length > 0) {
631
+ lines.push(` Violations: ${e.violations.map((v) => v.message).join("; ")}`);
632
+ }
633
+ return lines.join("\n");
634
+ }).join("\n\n");
635
+ return `${ROLE}
636
+
637
+ ## Agent Profile
638
+ - Agent: ${profile.agentId}
639
+ - Total runs: ${profile.totalRuns}
640
+ - Failure rate: ${(profile.failureRate * 100).toFixed(1)}% (${profile.failureCount} failures / ${profile.totalRuns} total)
641
+ - Avg duration: ${fmtDuration(stats.avg)} (p50: ${fmtDuration(stats.p50)}, p95: ${fmtDuration(stats.p95)})
642
+ - Known bottlenecks: ${profile.knownBottlenecks.length > 0 ? profile.knownBottlenecks.join(", ") : "none"}
643
+ - Last conformance score: ${profile.lastConformanceScore ?? "N/A"}
644
+
645
+ ## Recent Failures (${events.length})
646
+
647
+ ${failureDetails}
648
+
649
+ ## Question
650
+ Analyze these failures. What patterns do you see? What is the most likely root cause? Are these related or independent failures?`;
651
+ }
652
+ function buildAnomalyExplanationPrompt(event, profile) {
653
+ const stats = durationStats(profile.recentDurations);
654
+ const eventDetails = [
655
+ `Time: ${fmtTime(event.timestamp)}`,
656
+ `Status: ${event.status}`,
657
+ `Duration: ${fmtDuration(event.duration)}`,
658
+ `Path: ${event.pathSignature}`,
659
+ `Node count: ${event.nodeCount}`
660
+ ];
661
+ if (event.processContext) {
662
+ eventDetails.push(`Conformance score: ${event.processContext.conformanceScore}`);
663
+ eventDetails.push(`Is anomaly: ${event.processContext.isAnomaly}`);
664
+ eventDetails.push(`Variant: ${event.processContext.variant}`);
665
+ }
666
+ if (event.failurePoint) {
667
+ eventDetails.push(`Failed at: ${event.failurePoint.nodeName} (${event.failurePoint.nodeType})`);
668
+ if (event.failurePoint.error) eventDetails.push(`Error: ${event.failurePoint.error}`);
669
+ }
670
+ if (event.violations.length > 0) {
671
+ eventDetails.push(`Violations: ${event.violations.map((v) => v.message).join("; ")}`);
672
+ }
673
+ return `${ROLE}
674
+
675
+ ## Agent Baseline (from profile)
676
+ - Agent: ${profile.agentId}
677
+ - Total runs: ${profile.totalRuns}
678
+ - Typical failure rate: ${(profile.failureRate * 100).toFixed(1)}%
679
+ - Typical duration: avg ${fmtDuration(stats.avg)}, p50 ${fmtDuration(stats.p50)}, p95 ${fmtDuration(stats.p95)}
680
+ - Last conformance score: ${profile.lastConformanceScore ?? "N/A"}
681
+ - Known bottlenecks: ${profile.knownBottlenecks.length > 0 ? profile.knownBottlenecks.join(", ") : "none"}
682
+
683
+ ## Anomalous Execution
684
+ ${eventDetails.join("\n")}
685
+
686
+ ## Question
687
+ This execution has been flagged as anomalous. Explain what is unusual about it compared to the agent's typical behavior. What might have caused this deviation?`;
688
+ }
689
+ function buildAgentSummaryPrompt(profile, recentEvents, patterns) {
690
+ const stats = durationStats(profile.recentDurations);
691
+ const recentOutcomes = recentEvents.slice(0, 10).map((e) => ` ${fmtTime(e.timestamp)} \u2014 ${e.eventType} (${fmtDuration(e.duration)})`).join("\n");
692
+ const patternSummary = patterns.length > 0 ? patterns.slice(0, 3).map((p) => {
693
+ const lines = [
694
+ ` Variants: ${p.pattern.variantCount} across ${p.pattern.totalGraphs} executions`,
695
+ ` Top variant: ${p.pattern.topVariants[0]?.pathSignature ?? "N/A"} (${p.pattern.topVariants[0]?.percentage.toFixed(0) ?? 0}%)`
696
+ ];
697
+ if (p.pattern.topBottlenecks.length > 0) {
698
+ const topB = p.pattern.topBottlenecks[0];
699
+ if (topB)
700
+ lines.push(` Top bottleneck: ${topB.nodeName} (p95: ${fmtDuration(topB.p95)})`);
701
+ }
702
+ return lines.join("\n");
703
+ }).join("\n\n") : " No patterns discovered yet.";
704
+ const dataNote = recentEvents.length === 0 && patterns.length === 0 ? "\nNote: Limited data available. Summary is based only on the profile statistics.\n" : "";
705
+ return `${ROLE}
706
+
707
+ ## Agent Profile
708
+ - Agent: ${profile.agentId}
709
+ - Total runs: ${profile.totalRuns}
710
+ - Success rate: ${((1 - profile.failureRate) * 100).toFixed(1)}% (${profile.successCount} successes, ${profile.failureCount} failures)
711
+ - Duration: avg ${fmtDuration(stats.avg)}, p50 ${fmtDuration(stats.p50)}, p95 ${fmtDuration(stats.p95)}, range ${fmtDuration(stats.min)}\u2013${fmtDuration(stats.max)}
712
+ - Known bottlenecks: ${profile.knownBottlenecks.length > 0 ? profile.knownBottlenecks.join(", ") : "none"}
713
+ - Last conformance score: ${profile.lastConformanceScore ?? "N/A"}
714
+ - Last pattern analysis: ${profile.lastPatternTimestamp ? fmtTime(profile.lastPatternTimestamp) : "never"}
715
+ ${dataNote}
716
+ ## Recent Executions (last ${recentEvents.slice(0, 10).length})
717
+ ${recentOutcomes || " No recent events."}
718
+
719
+ ## Pattern Analysis
720
+ ${patternSummary}
721
+
722
+ ## Question
723
+ Provide a health summary for this agent. What are the key observations? Is the agent healthy, degrading, or in trouble? What should the operator pay attention to?`;
724
+ }
725
+ function buildFixSuggestionPrompt(events, profile, patterns) {
726
+ const failureGroups = /* @__PURE__ */ new Map();
727
+ for (const e of events) {
728
+ const key = e.failurePoint?.error ?? e.pathSignature;
729
+ const group = failureGroups.get(key) ?? [];
730
+ group.push(e);
731
+ failureGroups.set(key, group);
732
+ }
733
+ const failureGroupSummary = [...failureGroups.entries()].map(([key, group]) => {
734
+ const latest = group[0];
735
+ return ` "${key}" \u2014 ${group.length} occurrence(s), latest at ${latest ? fmtTime(latest.timestamp) : "unknown"}`;
736
+ }).join("\n");
737
+ const bottleneckDetails = patterns.flatMap((p) => p.pattern.topBottlenecks).map((b) => ` ${b.nodeName} (${b.nodeType}) \u2014 p95: ${fmtDuration(b.p95)}`);
738
+ const uniqueBottlenecks = [...new Set(bottleneckDetails)].join("\n");
739
+ const conformanceIssues = events.filter((e) => e.processContext && e.processContext.conformanceScore < 0.8).map(
740
+ (e) => ` ${fmtTime(e.timestamp)}: conformance ${e.processContext?.conformanceScore}, variant "${e.processContext?.variant}"`
741
+ ).join("\n");
742
+ return `${ROLE}
743
+
744
+ ## Agent Profile
745
+ - Agent: ${profile.agentId}
746
+ - Failure rate: ${(profile.failureRate * 100).toFixed(1)}%
747
+ - Known bottlenecks: ${profile.knownBottlenecks.length > 0 ? profile.knownBottlenecks.join(", ") : "none"}
748
+
749
+ ## Failure Patterns (${events.length} failures)
750
+ ${failureGroupSummary || " No failures recorded."}
751
+
752
+ ## Bottlenecks
753
+ ${uniqueBottlenecks || " No bottlenecks detected."}
754
+
755
+ ## Conformance Issues
756
+ ${conformanceIssues || " No conformance issues."}
757
+
758
+ ## Question
759
+ Based on the failure patterns, bottlenecks, and conformance issues above, provide specific, actionable recommendations to improve this agent's reliability and performance. Prioritize by impact.`;
760
+ }
761
+
762
+ // src/insight-engine.ts
763
+ var DEFAULT_CACHE_TTL_MS = 36e5;
764
+ var SCHEMA_VERSION2 = 1;
765
+ function simpleHash(input) {
766
+ let hash = 0;
767
+ for (let i = 0; i < input.length; i++) {
768
+ const char = input.charCodeAt(i);
769
+ hash = (hash << 5) - hash + char | 0;
770
+ }
771
+ return (hash >>> 0).toString(36);
772
+ }
773
+ function createInsightEngine(store, analysisFn, config) {
774
+ const cacheTtlMs = config?.cacheTtlMs ?? DEFAULT_CACHE_TTL_MS;
775
+ function checkCache(agentId, insightType, dataHash) {
776
+ const recent = store.getRecentInsights(agentId, { type: insightType, limit: 1 });
777
+ if (recent.length === 0) return null;
778
+ const cached = recent[0];
779
+ if (!cached || cached.dataHash !== dataHash) return null;
780
+ const age = Date.now() - cached.timestamp;
781
+ if (age >= cacheTtlMs) return null;
782
+ return cached;
783
+ }
784
+ function storeAndReturn(agentId, insightType, prompt, response, dataHash) {
785
+ const event = {
786
+ eventType: "insight.generated",
787
+ agentId,
788
+ timestamp: Date.now(),
789
+ schemaVersion: SCHEMA_VERSION2,
790
+ insightType,
791
+ prompt,
792
+ response,
793
+ dataHash
794
+ };
795
+ store.appendInsight(event);
796
+ return {
797
+ agentId,
798
+ insightType,
799
+ content: response,
800
+ cached: false,
801
+ timestamp: event.timestamp
802
+ };
803
+ }
804
+ function shortCircuit(agentId, insightType, content, cached, timestamp) {
805
+ return { agentId, insightType, content, cached, timestamp: timestamp ?? Date.now() };
806
+ }
807
+ return {
808
+ async explainFailures(agentId) {
809
+ const profile = store.getAgentProfile(agentId);
810
+ if (!profile) {
811
+ return shortCircuit(
812
+ agentId,
813
+ "failure-analysis",
814
+ "No data available for this agent.",
815
+ false
816
+ );
817
+ }
818
+ const events = store.getRecentEvents(agentId, { limit: 50 });
819
+ const failures = events.filter((e) => e.eventType === "execution.failed");
820
+ if (failures.length === 0) {
821
+ return shortCircuit(
822
+ agentId,
823
+ "failure-analysis",
824
+ "No recent failures found for this agent.",
825
+ false
826
+ );
827
+ }
828
+ const dataHash = simpleHash(JSON.stringify({ failures, profile }));
829
+ const cached = checkCache(agentId, "failure-analysis", dataHash);
830
+ if (cached) {
831
+ return shortCircuit(agentId, "failure-analysis", cached.response, true, cached.timestamp);
832
+ }
833
+ const prompt = buildFailureAnalysisPrompt(failures, profile);
834
+ try {
835
+ const response = await analysisFn(prompt);
836
+ return storeAndReturn(agentId, "failure-analysis", prompt, response, dataHash);
837
+ } catch (err) {
838
+ const message = err instanceof Error ? err.message : String(err);
839
+ return shortCircuit(agentId, "failure-analysis", `Analysis failed: ${message}`, false);
840
+ }
841
+ },
842
+ async explainAnomaly(agentId, event) {
843
+ const profile = store.getAgentProfile(agentId);
844
+ if (!profile) {
845
+ return shortCircuit(
846
+ agentId,
847
+ "anomaly-explanation",
848
+ "No data available for this agent.",
849
+ false
850
+ );
851
+ }
852
+ const dataHash = simpleHash(JSON.stringify({ event, profile }));
853
+ const cached = checkCache(agentId, "anomaly-explanation", dataHash);
854
+ if (cached) {
855
+ return shortCircuit(
856
+ agentId,
857
+ "anomaly-explanation",
858
+ cached.response,
859
+ true,
860
+ cached.timestamp
861
+ );
862
+ }
863
+ const prompt = buildAnomalyExplanationPrompt(event, profile);
864
+ try {
865
+ const response = await analysisFn(prompt);
866
+ return storeAndReturn(agentId, "anomaly-explanation", prompt, response, dataHash);
867
+ } catch (err) {
868
+ const message = err instanceof Error ? err.message : String(err);
869
+ return shortCircuit(agentId, "anomaly-explanation", `Analysis failed: ${message}`, false);
870
+ }
871
+ },
872
+ async summarizeAgent(agentId) {
873
+ const profile = store.getAgentProfile(agentId);
874
+ if (!profile) {
875
+ return shortCircuit(agentId, "agent-summary", "No data available for this agent.", false);
876
+ }
877
+ const recentEvents = store.getRecentEvents(agentId, { limit: 20 });
878
+ const patterns = store.getPatternHistory(agentId, { limit: 5 });
879
+ const dataHash = simpleHash(JSON.stringify({ profile, recentEvents, patterns }));
880
+ const cached = checkCache(agentId, "agent-summary", dataHash);
881
+ if (cached) {
882
+ return shortCircuit(agentId, "agent-summary", cached.response, true, cached.timestamp);
883
+ }
884
+ const prompt = buildAgentSummaryPrompt(profile, recentEvents, patterns);
885
+ try {
886
+ const response = await analysisFn(prompt);
887
+ return storeAndReturn(agentId, "agent-summary", prompt, response, dataHash);
888
+ } catch (err) {
889
+ const message = err instanceof Error ? err.message : String(err);
890
+ return shortCircuit(agentId, "agent-summary", `Analysis failed: ${message}`, false);
891
+ }
892
+ },
893
+ async suggestFixes(agentId) {
894
+ const profile = store.getAgentProfile(agentId);
895
+ if (!profile) {
896
+ return shortCircuit(agentId, "fix-suggestion", "No data available for this agent.", false);
897
+ }
898
+ const events = store.getRecentEvents(agentId, { limit: 50 });
899
+ const failures = events.filter((e) => e.eventType === "execution.failed");
900
+ const patterns = store.getPatternHistory(agentId, { limit: 5 });
901
+ if (failures.length === 0 && profile.knownBottlenecks.length === 0) {
902
+ return shortCircuit(
903
+ agentId,
904
+ "fix-suggestion",
905
+ "Agent is healthy \u2014 no failures or bottlenecks detected.",
906
+ false
907
+ );
908
+ }
909
+ const dataHash = simpleHash(JSON.stringify({ failures, profile, patterns }));
910
+ const cached = checkCache(agentId, "fix-suggestion", dataHash);
911
+ if (cached) {
912
+ return shortCircuit(agentId, "fix-suggestion", cached.response, true, cached.timestamp);
913
+ }
914
+ const prompt = buildFixSuggestionPrompt(failures, profile, patterns);
915
+ try {
916
+ const response = await analysisFn(prompt);
917
+ return storeAndReturn(agentId, "fix-suggestion", prompt, response, dataHash);
918
+ } catch (err) {
919
+ const message = err instanceof Error ? err.message : String(err);
920
+ return shortCircuit(agentId, "fix-suggestion", `Analysis failed: ${message}`, false);
921
+ }
922
+ }
923
+ };
924
+ }
925
+
926
+ // src/json-event-writer.ts
927
+ import { mkdirSync, writeFileSync } from "fs";
928
+ import { join } from "path";
929
+ function createJsonEventWriter(config) {
930
+ const { outputDir } = config;
931
+ function ensureDir() {
932
+ mkdirSync(outputDir, { recursive: true });
933
+ }
934
+ function eventFileName(event) {
935
+ const typePart = event.eventType.replace(/\./g, "-");
936
+ const agentId = "agentId" in event ? event.agentId : "unknown";
937
+ return `${typePart}-${agentId}-${event.timestamp}.json`;
938
+ }
939
+ return {
940
+ async write(_graph) {
941
+ },
942
+ async writeEvent(event) {
943
+ ensureDir();
944
+ const fileName = eventFileName(event);
945
+ const filePath = join(outputDir, fileName);
946
+ writeFileSync(filePath, JSON.stringify(event, null, 2), "utf-8");
947
+ }
948
+ };
949
+ }
950
+
951
+ // src/knowledge-store.ts
952
+ import {
953
+ existsSync,
954
+ mkdirSync as mkdirSync2,
955
+ readdirSync,
956
+ readFileSync,
957
+ renameSync,
958
+ rmSync,
959
+ writeFileSync as writeFileSync2
960
+ } from "fs";
961
+ import { join as join2 } from "path";
962
+ var DEFAULT_BASE_DIR = ".agentflow/knowledge";
963
+ var MAX_RECENT_DURATIONS = 100;
964
+ var writeCounter = 0;
965
+ function toDateDir(epochMs) {
966
+ return new Date(epochMs).toISOString().slice(0, 10);
967
+ }
968
+ function readJson(filePath) {
969
+ try {
970
+ return JSON.parse(readFileSync(filePath, "utf-8"));
971
+ } catch {
972
+ return null;
973
+ }
974
+ }
975
+ function writeJsonAtomic(filePath, data) {
976
+ const tmpPath = `${filePath}.tmp.${Date.now()}`;
977
+ writeFileSync2(tmpPath, JSON.stringify(data, null, 2), "utf-8");
978
+ renameSync(tmpPath, filePath);
979
+ }
980
+ function emptyProfile(agentId) {
981
+ return {
982
+ agentId,
983
+ totalRuns: 0,
984
+ successCount: 0,
985
+ failureCount: 0,
986
+ failureRate: 0,
987
+ recentDurations: [],
988
+ lastConformanceScore: null,
989
+ knownBottlenecks: [],
990
+ lastPatternTimestamp: null,
991
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
992
+ };
993
+ }
994
+ function mergeExecutionEvent(profile, event) {
995
+ const totalRuns = profile.totalRuns + 1;
996
+ const isFailure = event.eventType === "execution.failed";
997
+ const successCount = profile.successCount + (isFailure ? 0 : 1);
998
+ const failureCount = profile.failureCount + (isFailure ? 1 : 0);
999
+ const durations = [...profile.recentDurations, event.duration];
1000
+ if (durations.length > MAX_RECENT_DURATIONS) {
1001
+ durations.shift();
1002
+ }
1003
+ const conformanceScore = event.processContext?.conformanceScore ?? profile.lastConformanceScore;
1004
+ return {
1005
+ agentId: profile.agentId,
1006
+ totalRuns,
1007
+ successCount,
1008
+ failureCount,
1009
+ failureRate: failureCount / totalRuns,
1010
+ recentDurations: durations,
1011
+ lastConformanceScore: conformanceScore,
1012
+ knownBottlenecks: profile.knownBottlenecks,
1013
+ lastPatternTimestamp: profile.lastPatternTimestamp,
1014
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
1015
+ };
1016
+ }
1017
+ function mergePatternEvent(profile, event) {
1018
+ const existingBottlenecks = new Set(profile.knownBottlenecks);
1019
+ for (const b of event.pattern.topBottlenecks) {
1020
+ existingBottlenecks.add(b.nodeName);
1021
+ }
1022
+ return {
1023
+ ...profile,
1024
+ knownBottlenecks: [...existingBottlenecks],
1025
+ lastPatternTimestamp: event.timestamp,
1026
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
1027
+ };
1028
+ }
1029
+ function createKnowledgeStore(config) {
1030
+ const baseDir = config?.baseDir ?? DEFAULT_BASE_DIR;
1031
+ const eventsDir = join2(baseDir, "events");
1032
+ const patternsDir = join2(baseDir, "patterns");
1033
+ const profilesDir = join2(baseDir, "profiles");
1034
+ const insightsDir = join2(baseDir, "insights");
1035
+ function ensureDir(dir) {
1036
+ mkdirSync2(dir, { recursive: true });
1037
+ }
1038
+ function profilePath(agentId) {
1039
+ const safe = agentId.replace(/[/\\]/g, "_").replace(/\.\./g, "_");
1040
+ return join2(profilesDir, `${safe}.json`);
1041
+ }
1042
+ function appendExecutionEvent(event) {
1043
+ const dateDir = join2(eventsDir, event.agentId, toDateDir(event.timestamp));
1044
+ ensureDir(dateDir);
1045
+ const typePart = event.eventType.replace(/\./g, "-");
1046
+ const seq = String(writeCounter++).padStart(4, "0");
1047
+ const fileName = `${typePart}-${event.timestamp}-${seq}.json`;
1048
+ writeFileSync2(join2(dateDir, fileName), JSON.stringify(event, null, 2), "utf-8");
1049
+ ensureDir(profilesDir);
1050
+ const existing = readJson(profilePath(event.agentId)) ?? emptyProfile(event.agentId);
1051
+ const updated = mergeExecutionEvent(existing, event);
1052
+ writeJsonAtomic(profilePath(event.agentId), updated);
1053
+ }
1054
+ function appendPatternEvent(event) {
1055
+ const agentPatternDir = join2(patternsDir, event.agentId);
1056
+ ensureDir(agentPatternDir);
1057
+ const seq = String(writeCounter++).padStart(4, "0");
1058
+ const fileName = `${event.timestamp}-${seq}.json`;
1059
+ writeFileSync2(join2(agentPatternDir, fileName), JSON.stringify(event, null, 2), "utf-8");
1060
+ ensureDir(profilesDir);
1061
+ const existing = readJson(profilePath(event.agentId)) ?? emptyProfile(event.agentId);
1062
+ const updated = mergePatternEvent(existing, event);
1063
+ writeJsonAtomic(profilePath(event.agentId), updated);
1064
+ }
1065
+ return {
1066
+ baseDir,
1067
+ append(event) {
1068
+ if (event.eventType === "pattern.discovered" || event.eventType === "pattern.updated") {
1069
+ appendPatternEvent(event);
1070
+ } else {
1071
+ appendExecutionEvent(event);
1072
+ }
1073
+ },
1074
+ getRecentEvents(agentId, options) {
1075
+ const limit = options?.limit ?? 50;
1076
+ const since = options?.since ?? 0;
1077
+ const agentDir = join2(eventsDir, agentId);
1078
+ if (!existsSync(agentDir)) return [];
1079
+ const events = [];
1080
+ const dateDirs = readdirSync(agentDir).sort().reverse();
1081
+ for (const dateDir of dateDirs) {
1082
+ const fullDateDir = join2(agentDir, dateDir);
1083
+ let files;
1084
+ try {
1085
+ files = readdirSync(fullDateDir).filter((f) => f.endsWith(".json"));
1086
+ } catch {
1087
+ continue;
1088
+ }
1089
+ for (const file of files) {
1090
+ const event = readJson(join2(fullDateDir, file));
1091
+ if (event && event.timestamp > since) {
1092
+ events.push(event);
1093
+ }
1094
+ }
1095
+ if (events.length >= limit * 2) break;
1096
+ }
1097
+ events.sort((a, b) => b.timestamp - a.timestamp);
1098
+ return events.slice(0, limit);
1099
+ },
1100
+ getAgentProfile(agentId) {
1101
+ return readJson(profilePath(agentId));
1102
+ },
1103
+ getPatternHistory(agentId, options) {
1104
+ const limit = options?.limit ?? 20;
1105
+ const agentPatternDir = join2(patternsDir, agentId);
1106
+ if (!existsSync(agentPatternDir)) return [];
1107
+ const files = readdirSync(agentPatternDir).filter((f) => f.endsWith(".json")).sort().reverse();
1108
+ const events = [];
1109
+ for (const file of files.slice(0, limit)) {
1110
+ const event = readJson(join2(agentPatternDir, file));
1111
+ if (event) events.push(event);
1112
+ }
1113
+ return events;
1114
+ },
1115
+ compact(options) {
1116
+ let removed = 0;
1117
+ if (existsSync(eventsDir)) {
1118
+ for (const agentId of readdirSync(eventsDir)) {
1119
+ const agentDir = join2(eventsDir, agentId);
1120
+ let dateDirs;
1121
+ try {
1122
+ dateDirs = readdirSync(agentDir);
1123
+ } catch {
1124
+ continue;
1125
+ }
1126
+ for (const dateDir of dateDirs) {
1127
+ const fullDateDir = join2(agentDir, dateDir);
1128
+ let files;
1129
+ try {
1130
+ files = readdirSync(fullDateDir).filter((f) => f.endsWith(".json"));
1131
+ } catch {
1132
+ continue;
1133
+ }
1134
+ for (const file of files) {
1135
+ const parts = file.replace(".json", "").split("-");
1136
+ const tsPart = parts[parts.length - 2];
1137
+ const ts = tsPart ? Number.parseInt(tsPart, 10) : 0;
1138
+ if (!Number.isNaN(ts) && ts < options.olderThan) {
1139
+ try {
1140
+ rmSync(join2(fullDateDir, file));
1141
+ removed++;
1142
+ } catch {
1143
+ }
1144
+ }
1145
+ }
1146
+ try {
1147
+ if (readdirSync(fullDateDir).length === 0) {
1148
+ rmSync(fullDateDir, { recursive: true });
1149
+ }
1150
+ } catch {
1151
+ }
1152
+ }
1153
+ }
1154
+ }
1155
+ if (existsSync(patternsDir)) {
1156
+ for (const agentId of readdirSync(patternsDir)) {
1157
+ const agentPatternDir = join2(patternsDir, agentId);
1158
+ let files;
1159
+ try {
1160
+ files = readdirSync(agentPatternDir).filter((f) => f.endsWith(".json"));
1161
+ } catch {
1162
+ continue;
1163
+ }
1164
+ for (const file of files) {
1165
+ const ts = Number.parseInt(file.split("-")[0] ?? "", 10);
1166
+ if (!Number.isNaN(ts) && ts < options.olderThan) {
1167
+ try {
1168
+ rmSync(join2(agentPatternDir, file));
1169
+ removed++;
1170
+ } catch {
1171
+ }
1172
+ }
1173
+ }
1174
+ }
1175
+ }
1176
+ if (existsSync(insightsDir)) {
1177
+ for (const agentId of readdirSync(insightsDir)) {
1178
+ const agentInsightDir = join2(insightsDir, agentId);
1179
+ let files;
1180
+ try {
1181
+ files = readdirSync(agentInsightDir).filter((f) => f.endsWith(".json"));
1182
+ } catch {
1183
+ continue;
1184
+ }
1185
+ for (const file of files) {
1186
+ const parts = file.replace(".json", "").split("-");
1187
+ const tsPart = parts[parts.length - 2];
1188
+ const ts = tsPart ? Number.parseInt(tsPart, 10) : 0;
1189
+ if (!Number.isNaN(ts) && ts < options.olderThan) {
1190
+ try {
1191
+ rmSync(join2(agentInsightDir, file));
1192
+ removed++;
1193
+ } catch {
1194
+ }
1195
+ }
1196
+ }
1197
+ }
1198
+ }
1199
+ return { removed };
1200
+ },
1201
+ appendInsight(event) {
1202
+ const agentInsightDir = join2(insightsDir, event.agentId);
1203
+ ensureDir(agentInsightDir);
1204
+ const seq = String(writeCounter++).padStart(4, "0");
1205
+ const fileName = `${event.insightType}-${event.timestamp}-${seq}.json`;
1206
+ writeFileSync2(join2(agentInsightDir, fileName), JSON.stringify(event, null, 2), "utf-8");
1207
+ },
1208
+ getRecentInsights(agentId, options) {
1209
+ const limit = options?.limit ?? 10;
1210
+ const typeFilter = options?.type;
1211
+ const agentInsightDir = join2(insightsDir, agentId);
1212
+ if (!existsSync(agentInsightDir)) return [];
1213
+ const files = readdirSync(agentInsightDir).filter((f) => f.endsWith(".json"));
1214
+ const events = [];
1215
+ for (const file of files) {
1216
+ const event = readJson(join2(agentInsightDir, file));
1217
+ if (event) {
1218
+ if (typeFilter && event.insightType !== typeFilter) continue;
1219
+ events.push(event);
1220
+ }
1221
+ }
1222
+ events.sort((a, b) => b.timestamp - a.timestamp);
1223
+ return events.slice(0, limit);
1224
+ },
1225
+ // EventWriter interface
1226
+ async write(_graph) {
1227
+ },
1228
+ async writeEvent(event) {
1229
+ this.append(event);
1230
+ }
1231
+ };
1232
+ }
1233
+
1234
+ // src/policy-source.ts
1235
+ import { readdirSync as readdirSync2 } from "fs";
1236
+ import { join as join3 } from "path";
1237
+ function createPolicySource(store) {
1238
+ return {
1239
+ recentFailureRate(agentId) {
1240
+ const profile = store.getAgentProfile(agentId);
1241
+ return profile?.failureRate ?? 0;
1242
+ },
1243
+ isKnownBottleneck(nodeName) {
1244
+ const profilesDir = join3(store.baseDir, "profiles");
1245
+ let agentIds;
1246
+ try {
1247
+ agentIds = readdirSync2(profilesDir).filter((f) => f.endsWith(".json")).map((f) => f.replace(".json", ""));
1248
+ } catch {
1249
+ return false;
1250
+ }
1251
+ for (const agentId of agentIds) {
1252
+ const profile = store.getAgentProfile(agentId);
1253
+ if (profile?.knownBottlenecks.includes(nodeName)) {
1254
+ return true;
1255
+ }
1256
+ }
1257
+ return false;
1258
+ },
1259
+ lastConformanceScore(agentId) {
1260
+ const profile = store.getAgentProfile(agentId);
1261
+ return profile?.lastConformanceScore ?? null;
1262
+ },
1263
+ getAgentProfile(agentId) {
1264
+ return store.getAgentProfile(agentId);
1265
+ }
1266
+ };
1267
+ }
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
+
1381
+ // src/soma-event-writer.ts
1382
+ import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "fs";
1383
+ import { join as join4 } from "path";
1384
+ function compactIso(epochMs) {
1385
+ return new Date(epochMs).toISOString().slice(0, 19).replace(/:/g, "");
1386
+ }
1387
+ function isoDate(epochMs) {
1388
+ return new Date(epochMs).toISOString().slice(0, 10);
1389
+ }
1390
+ function renderFrontmatter(fields) {
1391
+ const lines = ["---"];
1392
+ for (const [key, value] of Object.entries(fields)) {
1393
+ if (value === void 0) continue;
1394
+ if (Array.isArray(value)) {
1395
+ lines.push(`${key}: [${value.map((v) => `'${v}'`).join(", ")}]`);
1396
+ } else if (typeof value === "string") {
1397
+ lines.push(`${key}: '${value}'`);
1398
+ } else {
1399
+ lines.push(`${key}: ${String(value)}`);
1400
+ }
1401
+ }
1402
+ lines.push("---");
1403
+ return lines.join("\n");
1404
+ }
1405
+ function formatDuration(ms) {
1406
+ if (ms < 1e3) return `${ms.toFixed(0)}ms`;
1407
+ if (ms < 6e4) return `${(ms / 1e3).toFixed(1)}s`;
1408
+ return `${(ms / 6e4).toFixed(1)}min`;
1409
+ }
1410
+ function executionEventToMarkdown(event) {
1411
+ const isCompleted = event.eventType === "execution.completed";
1412
+ const subtype = isCompleted ? "completed" : "failed";
1413
+ const tags = ["agentflow/execution", `agent/${event.agentId}`, `status/${subtype}`];
1414
+ if (event.processContext?.isAnomaly) {
1415
+ tags.push("agentflow/anomaly");
1416
+ }
1417
+ const frontmatter = {
1418
+ type: "execution",
1419
+ subtype,
1420
+ name: `Execution: ${event.agentId} \u2014 ${subtype}`,
1421
+ source: "agentflow",
1422
+ created: isoDate(event.timestamp),
1423
+ alfred_tags: tags,
1424
+ agentflow_graph_id: event.graphId,
1425
+ duration_ms: event.duration,
1426
+ node_count: event.nodeCount
1427
+ };
1428
+ if (event.processContext) {
1429
+ frontmatter.conformance_score = event.processContext.conformanceScore;
1430
+ frontmatter.is_anomaly = event.processContext.isAnomaly;
1431
+ }
1432
+ const body = [];
1433
+ body.push(`# Execution: ${event.agentId} \u2014 ${subtype}
1434
+ `);
1435
+ body.push(`**Duration:** ${formatDuration(event.duration)} `);
1436
+ body.push(`**Nodes:** ${event.nodeCount} `);
1437
+ body.push(`**Status:** ${event.status}
1438
+ `);
1439
+ if (event.pathSignature) {
1440
+ body.push(`## Path
1441
+ `);
1442
+ body.push(`\`${event.pathSignature}\`
1443
+ `);
1444
+ }
1445
+ if (!isCompleted && event.failurePoint) {
1446
+ const fp = event.failurePoint;
1447
+ body.push(`## Failure Point
1448
+ `);
1449
+ body.push(`**Node:** ${fp.nodeType}:${fp.nodeName} (\`${fp.nodeId}\`) `);
1450
+ if (fp.error) {
1451
+ body.push(`**Error:** ${fp.error}
1452
+ `);
1453
+ }
1454
+ }
1455
+ if (event.processContext) {
1456
+ body.push(`## Process Context
1457
+ `);
1458
+ body.push(`**Conformance:** ${(event.processContext.conformanceScore * 100).toFixed(0)}% `);
1459
+ body.push(`**Anomaly:** ${event.processContext.isAnomaly ? "yes" : "no"}
1460
+ `);
1461
+ }
1462
+ body.push(`## Related
1463
+ `);
1464
+ body.push(`- [[agent/${event.agentId}]]`);
1465
+ return `${renderFrontmatter(frontmatter)}
1466
+
1467
+ ${body.join("\n")}`;
1468
+ }
1469
+ function patternEventToMarkdown(event) {
1470
+ const { pattern } = event;
1471
+ const tags = ["agentflow/pattern", `agent/${event.agentId}`];
1472
+ const frontmatter = {
1473
+ type: "synthesis",
1474
+ subtype: "pattern-discovery",
1475
+ name: `Pattern: ${event.agentId} \u2014 ${pattern.variantCount} variants across ${pattern.totalGraphs} runs`,
1476
+ source: "agentflow",
1477
+ created: isoDate(event.timestamp),
1478
+ alfred_tags: tags,
1479
+ variant_count: pattern.variantCount,
1480
+ total_graphs: pattern.totalGraphs
1481
+ };
1482
+ const body = [];
1483
+ body.push(`# Pattern: ${event.agentId}
1484
+ `);
1485
+ body.push(`**Variants:** ${pattern.variantCount} `);
1486
+ body.push(`**Total Runs:** ${pattern.totalGraphs}
1487
+ `);
1488
+ if (pattern.topVariants.length > 0) {
1489
+ body.push(`## Top Variants
1490
+ `);
1491
+ body.push(`| Path | Count | % |`);
1492
+ body.push(`|------|-------|---|`);
1493
+ for (const v of pattern.topVariants) {
1494
+ const sig = v.pathSignature.length > 60 ? `${v.pathSignature.slice(0, 57)}...` : v.pathSignature;
1495
+ body.push(`| \`${sig}\` | ${v.count} | ${v.percentage.toFixed(1)}% |`);
1496
+ }
1497
+ body.push("");
1498
+ }
1499
+ if (pattern.topBottlenecks.length > 0) {
1500
+ body.push(`## Top Bottlenecks
1501
+ `);
1502
+ body.push(`| Node | Type | p95 |`);
1503
+ body.push(`|------|------|-----|`);
1504
+ for (const b of pattern.topBottlenecks) {
1505
+ body.push(`| ${b.nodeName} | ${b.nodeType} | ${formatDuration(b.p95)} |`);
1506
+ }
1507
+ body.push("");
1508
+ }
1509
+ body.push(`## Related
1510
+ `);
1511
+ body.push(`- [[agent/${event.agentId}]]`);
1512
+ return `${renderFrontmatter(frontmatter)}
1513
+
1514
+ ${body.join("\n")}`;
1515
+ }
1516
+ function createSomaEventWriter(config) {
1517
+ const { inboxDir } = config;
1518
+ function ensureDir() {
1519
+ mkdirSync3(inboxDir, { recursive: true });
1520
+ }
1521
+ function eventFileName(event) {
1522
+ const agentId = event.agentId;
1523
+ const ts = compactIso(event.timestamp);
1524
+ if (event.eventType === "pattern.discovered" || event.eventType === "pattern.updated") {
1525
+ return `synthesis-${agentId}-${ts}.md`;
1526
+ }
1527
+ return `execution-${agentId}-${ts}.md`;
1528
+ }
1529
+ return {
1530
+ async write(_graph) {
1531
+ },
1532
+ async writeEvent(event) {
1533
+ ensureDir();
1534
+ const markdown = event.eventType === "pattern.discovered" || event.eventType === "pattern.updated" ? patternEventToMarkdown(event) : executionEventToMarkdown(event);
1535
+ const fileName = eventFileName(event);
1536
+ writeFileSync3(join4(inboxDir, fileName), markdown, "utf-8");
1537
+ }
1538
+ };
1539
+ }
189
1540
  export {
190
1541
  auditProcesses,
1542
+ buildAgentSummaryPrompt,
1543
+ buildAnomalyExplanationPrompt,
1544
+ buildFailureAnalysisPrompt,
1545
+ buildFixSuggestionPrompt,
1546
+ checkConformance,
191
1547
  checkGuards,
1548
+ createEventEmitter,
1549
+ createExecutionEvent,
192
1550
  createGraphBuilder,
1551
+ createInsightEngine,
1552
+ createJsonEventWriter,
1553
+ createKnowledgeStore,
1554
+ createPatternEvent,
1555
+ createPolicySource,
1556
+ createSomaEventWriter,
193
1557
  createTraceStore,
1558
+ discoverAllProcessConfigs,
1559
+ discoverProcess,
194
1560
  discoverProcessConfig,
1561
+ findVariants,
195
1562
  findWaitingOn,
196
1563
  formatAuditReport,
1564
+ formatReceipt,
1565
+ getBottlenecks,
197
1566
  getChildren,
198
1567
  getCriticalPath,
199
1568
  getDepth,
@@ -202,6 +1571,7 @@ export {
202
1571
  getHungNodes,
203
1572
  getNode,
204
1573
  getParent,
1574
+ getPathSignature,
205
1575
  getStats,
206
1576
  getSubtree,
207
1577
  getTraceTree,
@@ -213,6 +1583,7 @@ export {
213
1583
  startWatch,
214
1584
  stitchTrace,
215
1585
  toAsciiTree,
1586
+ toReceipt,
216
1587
  toTimeline,
217
1588
  withGuards
218
1589
  };