agentflow-core 0.7.0 → 0.8.0

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
@@ -23,12 +23,655 @@ import {
23
23
  stitchTrace,
24
24
  toAsciiTree,
25
25
  toTimeline
26
- } from "./chunk-5PRHVYYD.js";
26
+ } from "./chunk-6X5HU5LB.js";
27
27
  import {
28
28
  graphToJson,
29
29
  loadGraph
30
30
  } from "./chunk-DY7YHFIB.js";
31
31
 
32
+ // src/process-mining.ts
33
+ function getPathSignature(graph) {
34
+ const root = graph.nodes.get(graph.rootNodeId);
35
+ if (!root) return "";
36
+ const parts = [];
37
+ function walk(node) {
38
+ parts.push(`${node.type}:${node.name}`);
39
+ const childNodes = [];
40
+ for (const childId of node.children) {
41
+ const child = graph.nodes.get(childId);
42
+ if (child) childNodes.push(child);
43
+ }
44
+ childNodes.sort((a, b) => {
45
+ const keyA = `${a.type}:${a.name}`;
46
+ const keyB = `${b.type}:${b.name}`;
47
+ return keyA.localeCompare(keyB);
48
+ });
49
+ for (const child of childNodes) {
50
+ walk(child);
51
+ }
52
+ }
53
+ walk(root);
54
+ return parts.join("\u2192");
55
+ }
56
+ function stepKey(node) {
57
+ return `${node.type}:${node.name}`;
58
+ }
59
+ function discoverProcess(graphs) {
60
+ if (graphs.length === 0) {
61
+ throw new Error("discoverProcess requires at least one graph");
62
+ }
63
+ const steps = /* @__PURE__ */ new Set();
64
+ const transitionCounts = /* @__PURE__ */ new Map();
65
+ const outgoingCounts = /* @__PURE__ */ new Map();
66
+ for (const graph of graphs) {
67
+ for (const node of graph.nodes.values()) {
68
+ const parentKey = stepKey(node);
69
+ steps.add(parentKey);
70
+ for (const childId of node.children) {
71
+ const child = graph.nodes.get(childId);
72
+ if (!child) continue;
73
+ const childKey = stepKey(child);
74
+ const tKey = `${parentKey}\0${childKey}`;
75
+ transitionCounts.set(tKey, (transitionCounts.get(tKey) ?? 0) + 1);
76
+ outgoingCounts.set(parentKey, (outgoingCounts.get(parentKey) ?? 0) + 1);
77
+ }
78
+ }
79
+ }
80
+ const transitions = [];
81
+ for (const [tKey, count] of transitionCounts) {
82
+ const [from, to] = tKey.split("\0");
83
+ const outgoing = outgoingCounts.get(from) ?? count;
84
+ transitions.push({
85
+ from,
86
+ to,
87
+ count,
88
+ probability: count / outgoing
89
+ });
90
+ }
91
+ transitions.sort((a, b) => a.from.localeCompare(b.from) || a.to.localeCompare(b.to));
92
+ return {
93
+ steps: [...steps].sort(),
94
+ transitions,
95
+ totalGraphs: graphs.length,
96
+ agentId: graphs[0].agentId
97
+ };
98
+ }
99
+ function findVariants(graphs) {
100
+ if (graphs.length === 0) return [];
101
+ const groups = /* @__PURE__ */ new Map();
102
+ for (const graph of graphs) {
103
+ const sig = getPathSignature(graph);
104
+ const group = groups.get(sig) ?? [];
105
+ group.push(graph);
106
+ groups.set(sig, group);
107
+ }
108
+ const total = graphs.length;
109
+ const variants = [];
110
+ for (const [pathSignature, groupGraphs] of groups) {
111
+ variants.push({
112
+ pathSignature,
113
+ count: groupGraphs.length,
114
+ percentage: groupGraphs.length / total * 100,
115
+ graphIds: groupGraphs.map((g) => g.id),
116
+ exampleGraph: groupGraphs[0]
117
+ });
118
+ }
119
+ variants.sort((a, b) => {
120
+ const freqDiff = b.count - a.count;
121
+ if (freqDiff !== 0) return freqDiff;
122
+ return a.pathSignature.localeCompare(b.pathSignature);
123
+ });
124
+ return variants;
125
+ }
126
+ function percentile(sorted, p) {
127
+ if (sorted.length === 0) return 0;
128
+ if (sorted.length === 1) return sorted[0];
129
+ const index = p / 100 * (sorted.length - 1);
130
+ const lower = Math.floor(index);
131
+ const upper = Math.ceil(index);
132
+ if (lower === upper) return sorted[lower];
133
+ const weight = index - lower;
134
+ return sorted[lower] * (1 - weight) + sorted[upper] * weight;
135
+ }
136
+ function getBottlenecks(graphs) {
137
+ if (graphs.length === 0) return [];
138
+ const now = Date.now();
139
+ const stats = /* @__PURE__ */ new Map();
140
+ for (const graph of graphs) {
141
+ for (const node of graph.nodes.values()) {
142
+ const key = `${node.type}:${node.name}`;
143
+ const entry = stats.get(key) ?? { durations: [], nodeType: node.type, nodeName: node.name };
144
+ const end = node.endTime ?? now;
145
+ entry.durations.push(end - node.startTime);
146
+ stats.set(key, entry);
147
+ }
148
+ }
149
+ const total = graphs.length;
150
+ const bottlenecks = [];
151
+ for (const [, entry] of stats) {
152
+ const sorted = [...entry.durations].sort((a, b) => a - b);
153
+ bottlenecks.push({
154
+ nodeName: entry.nodeName,
155
+ nodeType: entry.nodeType,
156
+ occurrences: sorted.length,
157
+ durations: {
158
+ median: percentile(sorted, 50),
159
+ p95: percentile(sorted, 95),
160
+ p99: percentile(sorted, 99),
161
+ min: sorted[0],
162
+ max: sorted[sorted.length - 1]
163
+ },
164
+ percentOfGraphs: sorted.length / total * 100
165
+ });
166
+ }
167
+ bottlenecks.sort((a, b) => b.durations.p95 - a.durations.p95);
168
+ return bottlenecks;
169
+ }
170
+ function extractGraphTransitions(graph) {
171
+ const transitions = /* @__PURE__ */ new Set();
172
+ for (const node of graph.nodes.values()) {
173
+ const parentKey = stepKey(node);
174
+ for (const childId of node.children) {
175
+ const child = graph.nodes.get(childId);
176
+ if (!child) continue;
177
+ transitions.add(`${parentKey}\0${stepKey(child)}`);
178
+ }
179
+ }
180
+ return transitions;
181
+ }
182
+ function checkConformance(graph, model) {
183
+ const graphTransitions = extractGraphTransitions(graph);
184
+ const deviations = [];
185
+ const modelLookup = /* @__PURE__ */ new Map();
186
+ for (const t of model.transitions) {
187
+ modelLookup.set(`${t.from}\0${t.to}`, t);
188
+ }
189
+ let totalChecks = 0;
190
+ let deviationCount = 0;
191
+ for (const tKey of graphTransitions) {
192
+ totalChecks++;
193
+ const [from, to] = tKey.split("\0");
194
+ const modelTransition = modelLookup.get(tKey);
195
+ if (!modelTransition) {
196
+ deviationCount++;
197
+ deviations.push({
198
+ type: "unexpected-transition",
199
+ from,
200
+ to,
201
+ message: `Unexpected transition ${from} \u2192 ${to} (not in process model)`
202
+ });
203
+ } else if (modelTransition.probability < 0.1) {
204
+ deviationCount++;
205
+ deviations.push({
206
+ type: "low-frequency-path",
207
+ from,
208
+ to,
209
+ message: `Low-frequency path ${from} \u2192 ${to} (model probability: ${(modelTransition.probability * 100).toFixed(1)}%)`,
210
+ modelProbability: modelTransition.probability
211
+ });
212
+ }
213
+ }
214
+ const graphSteps = /* @__PURE__ */ new Set();
215
+ for (const node of graph.nodes.values()) {
216
+ graphSteps.add(stepKey(node));
217
+ }
218
+ for (const t of model.transitions) {
219
+ if (t.probability > 0.5) {
220
+ const tKey = `${t.from}\0${t.to}`;
221
+ if (graphSteps.has(t.from) && !graphTransitions.has(tKey)) {
222
+ totalChecks++;
223
+ deviationCount++;
224
+ deviations.push({
225
+ type: "missing-transition",
226
+ from: t.from,
227
+ to: t.to,
228
+ message: `Missing expected transition ${t.from} \u2192 ${t.to} (model probability: ${(t.probability * 100).toFixed(1)}%)`,
229
+ modelProbability: t.probability
230
+ });
231
+ }
232
+ }
233
+ }
234
+ const conformanceScore = totalChecks === 0 ? 1 : (totalChecks - deviationCount) / totalChecks;
235
+ return {
236
+ conformanceScore,
237
+ isConforming: deviations.length === 0,
238
+ deviations
239
+ };
240
+ }
241
+
242
+ // src/event-emitter.ts
243
+ var SCHEMA_VERSION = 1;
244
+ function createExecutionEvent(graph, options) {
245
+ const duration = graph.endTime !== null ? graph.endTime - graph.startTime : Date.now() - graph.startTime;
246
+ let failurePoint;
247
+ if (graph.status === "failed") {
248
+ let candidate;
249
+ for (const node of graph.nodes.values()) {
250
+ if (node.status === "failed" || node.status === "timeout") {
251
+ const errorMeta = node.metadata.error;
252
+ const fp = {
253
+ nodeId: node.id,
254
+ nodeName: node.name,
255
+ nodeType: node.type,
256
+ error: typeof errorMeta === "string" ? errorMeta : void 0
257
+ };
258
+ if (node.id !== graph.rootNodeId) {
259
+ failurePoint = fp;
260
+ break;
261
+ }
262
+ if (!candidate) candidate = fp;
263
+ }
264
+ }
265
+ if (!failurePoint) failurePoint = candidate;
266
+ }
267
+ return {
268
+ eventType: graph.status === "failed" ? "execution.failed" : "execution.completed",
269
+ graphId: graph.id,
270
+ agentId: graph.agentId,
271
+ timestamp: Date.now(),
272
+ schemaVersion: SCHEMA_VERSION,
273
+ status: graph.status,
274
+ duration,
275
+ nodeCount: graph.nodes.size,
276
+ pathSignature: getPathSignature(graph),
277
+ ...failurePoint ? { failurePoint } : {},
278
+ ...options?.processContext ? { processContext: options.processContext } : {},
279
+ ...options?.semantic ? { semantic: options.semantic } : {},
280
+ violations: options?.violations ?? []
281
+ };
282
+ }
283
+ function createPatternEvent(agentId, model, variants, bottlenecks) {
284
+ return {
285
+ eventType: "pattern.discovered",
286
+ agentId,
287
+ timestamp: Date.now(),
288
+ schemaVersion: SCHEMA_VERSION,
289
+ pattern: {
290
+ totalGraphs: model.totalGraphs,
291
+ variantCount: variants.length,
292
+ topVariants: variants.slice(0, 5).map((v) => ({
293
+ pathSignature: v.pathSignature,
294
+ count: v.count,
295
+ percentage: v.percentage
296
+ })),
297
+ topBottlenecks: bottlenecks.slice(0, 5).map((b) => ({
298
+ nodeName: b.nodeName,
299
+ nodeType: b.nodeType,
300
+ p95: b.durations.p95
301
+ })),
302
+ processModel: model
303
+ }
304
+ };
305
+ }
306
+ function createEventEmitter(config) {
307
+ const writers = config?.writers ?? [];
308
+ const knowledgeStore = config?.knowledgeStore;
309
+ const onError = config?.onError ?? (() => {
310
+ });
311
+ const subscribers = /* @__PURE__ */ new Set();
312
+ return {
313
+ async emit(event) {
314
+ if (knowledgeStore) {
315
+ try {
316
+ knowledgeStore.append(event);
317
+ } catch (err) {
318
+ onError(err);
319
+ }
320
+ }
321
+ for (const writer of writers) {
322
+ try {
323
+ await writer.writeEvent(event);
324
+ } catch (err) {
325
+ onError(err);
326
+ }
327
+ }
328
+ for (const listener of subscribers) {
329
+ try {
330
+ listener(event);
331
+ } catch (err) {
332
+ onError(err);
333
+ }
334
+ }
335
+ },
336
+ subscribe(listener) {
337
+ subscribers.add(listener);
338
+ return () => {
339
+ subscribers.delete(listener);
340
+ };
341
+ }
342
+ };
343
+ }
344
+
345
+ // src/prompt-builder.ts
346
+ var ROLE = "You are analyzing execution data for an AI agent system. Provide clear, actionable analysis based on the data below.";
347
+ function fmtDuration(ms) {
348
+ if (ms < 1e3) return `${ms}ms`;
349
+ return `${(ms / 1e3).toFixed(1)}s`;
350
+ }
351
+ function fmtTime(ts) {
352
+ return new Date(ts).toISOString();
353
+ }
354
+ function durationStats(durations) {
355
+ if (durations.length === 0) return { avg: 0, p50: 0, p95: 0, min: 0, max: 0 };
356
+ const sorted = [...durations].sort((a, b) => a - b);
357
+ const sum = sorted.reduce((a, b) => a + b, 0);
358
+ return {
359
+ avg: Math.round(sum / sorted.length),
360
+ p50: sorted[Math.floor(sorted.length * 0.5)] ?? 0,
361
+ p95: sorted[Math.floor(sorted.length * 0.95)] ?? 0,
362
+ min: sorted[0] ?? 0,
363
+ max: sorted[sorted.length - 1] ?? 0
364
+ };
365
+ }
366
+ function buildFailureAnalysisPrompt(events, profile) {
367
+ const stats = durationStats(profile.recentDurations);
368
+ const failureDetails = events.map((e, i) => {
369
+ const lines = [
370
+ `Failure ${i + 1}:`,
371
+ ` Time: ${fmtTime(e.timestamp)}`,
372
+ ` Duration: ${fmtDuration(e.duration)}`,
373
+ ` Path: ${e.pathSignature}`
374
+ ];
375
+ if (e.failurePoint) {
376
+ lines.push(` Failed at: ${e.failurePoint.nodeName} (${e.failurePoint.nodeType})`);
377
+ if (e.failurePoint.error) lines.push(` Error: ${e.failurePoint.error}`);
378
+ }
379
+ if (e.violations.length > 0) {
380
+ lines.push(` Violations: ${e.violations.map((v) => v.message).join("; ")}`);
381
+ }
382
+ return lines.join("\n");
383
+ }).join("\n\n");
384
+ return `${ROLE}
385
+
386
+ ## Agent Profile
387
+ - Agent: ${profile.agentId}
388
+ - Total runs: ${profile.totalRuns}
389
+ - Failure rate: ${(profile.failureRate * 100).toFixed(1)}% (${profile.failureCount} failures / ${profile.totalRuns} total)
390
+ - Avg duration: ${fmtDuration(stats.avg)} (p50: ${fmtDuration(stats.p50)}, p95: ${fmtDuration(stats.p95)})
391
+ - Known bottlenecks: ${profile.knownBottlenecks.length > 0 ? profile.knownBottlenecks.join(", ") : "none"}
392
+ - Last conformance score: ${profile.lastConformanceScore ?? "N/A"}
393
+
394
+ ## Recent Failures (${events.length})
395
+
396
+ ${failureDetails}
397
+
398
+ ## Question
399
+ Analyze these failures. What patterns do you see? What is the most likely root cause? Are these related or independent failures?`;
400
+ }
401
+ function buildAnomalyExplanationPrompt(event, profile) {
402
+ const stats = durationStats(profile.recentDurations);
403
+ const eventDetails = [
404
+ `Time: ${fmtTime(event.timestamp)}`,
405
+ `Status: ${event.status}`,
406
+ `Duration: ${fmtDuration(event.duration)}`,
407
+ `Path: ${event.pathSignature}`,
408
+ `Node count: ${event.nodeCount}`
409
+ ];
410
+ if (event.processContext) {
411
+ eventDetails.push(`Conformance score: ${event.processContext.conformanceScore}`);
412
+ eventDetails.push(`Is anomaly: ${event.processContext.isAnomaly}`);
413
+ eventDetails.push(`Variant: ${event.processContext.variant}`);
414
+ }
415
+ if (event.failurePoint) {
416
+ eventDetails.push(`Failed at: ${event.failurePoint.nodeName} (${event.failurePoint.nodeType})`);
417
+ if (event.failurePoint.error) eventDetails.push(`Error: ${event.failurePoint.error}`);
418
+ }
419
+ if (event.violations.length > 0) {
420
+ eventDetails.push(`Violations: ${event.violations.map((v) => v.message).join("; ")}`);
421
+ }
422
+ return `${ROLE}
423
+
424
+ ## Agent Baseline (from profile)
425
+ - Agent: ${profile.agentId}
426
+ - Total runs: ${profile.totalRuns}
427
+ - Typical failure rate: ${(profile.failureRate * 100).toFixed(1)}%
428
+ - Typical duration: avg ${fmtDuration(stats.avg)}, p50 ${fmtDuration(stats.p50)}, p95 ${fmtDuration(stats.p95)}
429
+ - Last conformance score: ${profile.lastConformanceScore ?? "N/A"}
430
+ - Known bottlenecks: ${profile.knownBottlenecks.length > 0 ? profile.knownBottlenecks.join(", ") : "none"}
431
+
432
+ ## Anomalous Execution
433
+ ${eventDetails.join("\n")}
434
+
435
+ ## Question
436
+ 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?`;
437
+ }
438
+ function buildAgentSummaryPrompt(profile, recentEvents, patterns) {
439
+ const stats = durationStats(profile.recentDurations);
440
+ const recentOutcomes = recentEvents.slice(0, 10).map((e) => ` ${fmtTime(e.timestamp)} \u2014 ${e.eventType} (${fmtDuration(e.duration)})`).join("\n");
441
+ const patternSummary = patterns.length > 0 ? patterns.slice(0, 3).map((p) => {
442
+ const lines = [
443
+ ` Variants: ${p.pattern.variantCount} across ${p.pattern.totalGraphs} executions`,
444
+ ` Top variant: ${p.pattern.topVariants[0]?.pathSignature ?? "N/A"} (${p.pattern.topVariants[0]?.percentage.toFixed(0) ?? 0}%)`
445
+ ];
446
+ if (p.pattern.topBottlenecks.length > 0) {
447
+ const topB = p.pattern.topBottlenecks[0];
448
+ if (topB)
449
+ lines.push(` Top bottleneck: ${topB.nodeName} (p95: ${fmtDuration(topB.p95)})`);
450
+ }
451
+ return lines.join("\n");
452
+ }).join("\n\n") : " No patterns discovered yet.";
453
+ const dataNote = recentEvents.length === 0 && patterns.length === 0 ? "\nNote: Limited data available. Summary is based only on the profile statistics.\n" : "";
454
+ return `${ROLE}
455
+
456
+ ## Agent Profile
457
+ - Agent: ${profile.agentId}
458
+ - Total runs: ${profile.totalRuns}
459
+ - Success rate: ${((1 - profile.failureRate) * 100).toFixed(1)}% (${profile.successCount} successes, ${profile.failureCount} failures)
460
+ - Duration: avg ${fmtDuration(stats.avg)}, p50 ${fmtDuration(stats.p50)}, p95 ${fmtDuration(stats.p95)}, range ${fmtDuration(stats.min)}\u2013${fmtDuration(stats.max)}
461
+ - Known bottlenecks: ${profile.knownBottlenecks.length > 0 ? profile.knownBottlenecks.join(", ") : "none"}
462
+ - Last conformance score: ${profile.lastConformanceScore ?? "N/A"}
463
+ - Last pattern analysis: ${profile.lastPatternTimestamp ? fmtTime(profile.lastPatternTimestamp) : "never"}
464
+ ${dataNote}
465
+ ## Recent Executions (last ${recentEvents.slice(0, 10).length})
466
+ ${recentOutcomes || " No recent events."}
467
+
468
+ ## Pattern Analysis
469
+ ${patternSummary}
470
+
471
+ ## Question
472
+ 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?`;
473
+ }
474
+ function buildFixSuggestionPrompt(events, profile, patterns) {
475
+ const failureGroups = /* @__PURE__ */ new Map();
476
+ for (const e of events) {
477
+ const key = e.failurePoint?.error ?? e.pathSignature;
478
+ const group = failureGroups.get(key) ?? [];
479
+ group.push(e);
480
+ failureGroups.set(key, group);
481
+ }
482
+ const failureGroupSummary = [...failureGroups.entries()].map(([key, group]) => {
483
+ const latest = group[0];
484
+ return ` "${key}" \u2014 ${group.length} occurrence(s), latest at ${latest ? fmtTime(latest.timestamp) : "unknown"}`;
485
+ }).join("\n");
486
+ const bottleneckDetails = patterns.flatMap((p) => p.pattern.topBottlenecks).map((b) => ` ${b.nodeName} (${b.nodeType}) \u2014 p95: ${fmtDuration(b.p95)}`);
487
+ const uniqueBottlenecks = [...new Set(bottleneckDetails)].join("\n");
488
+ const conformanceIssues = events.filter((e) => e.processContext && e.processContext.conformanceScore < 0.8).map(
489
+ (e) => ` ${fmtTime(e.timestamp)}: conformance ${e.processContext?.conformanceScore}, variant "${e.processContext?.variant}"`
490
+ ).join("\n");
491
+ return `${ROLE}
492
+
493
+ ## Agent Profile
494
+ - Agent: ${profile.agentId}
495
+ - Failure rate: ${(profile.failureRate * 100).toFixed(1)}%
496
+ - Known bottlenecks: ${profile.knownBottlenecks.length > 0 ? profile.knownBottlenecks.join(", ") : "none"}
497
+
498
+ ## Failure Patterns (${events.length} failures)
499
+ ${failureGroupSummary || " No failures recorded."}
500
+
501
+ ## Bottlenecks
502
+ ${uniqueBottlenecks || " No bottlenecks detected."}
503
+
504
+ ## Conformance Issues
505
+ ${conformanceIssues || " No conformance issues."}
506
+
507
+ ## Question
508
+ 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.`;
509
+ }
510
+
511
+ // src/insight-engine.ts
512
+ var DEFAULT_CACHE_TTL_MS = 36e5;
513
+ var SCHEMA_VERSION2 = 1;
514
+ function simpleHash(input) {
515
+ let hash = 0;
516
+ for (let i = 0; i < input.length; i++) {
517
+ const char = input.charCodeAt(i);
518
+ hash = (hash << 5) - hash + char | 0;
519
+ }
520
+ return (hash >>> 0).toString(36);
521
+ }
522
+ function createInsightEngine(store, analysisFn, config) {
523
+ const cacheTtlMs = config?.cacheTtlMs ?? DEFAULT_CACHE_TTL_MS;
524
+ function checkCache(agentId, insightType, dataHash) {
525
+ const recent = store.getRecentInsights(agentId, { type: insightType, limit: 1 });
526
+ if (recent.length === 0) return null;
527
+ const cached = recent[0];
528
+ if (!cached || cached.dataHash !== dataHash) return null;
529
+ const age = Date.now() - cached.timestamp;
530
+ if (age >= cacheTtlMs) return null;
531
+ return cached;
532
+ }
533
+ function storeAndReturn(agentId, insightType, prompt, response, dataHash) {
534
+ const event = {
535
+ eventType: "insight.generated",
536
+ agentId,
537
+ timestamp: Date.now(),
538
+ schemaVersion: SCHEMA_VERSION2,
539
+ insightType,
540
+ prompt,
541
+ response,
542
+ dataHash
543
+ };
544
+ store.appendInsight(event);
545
+ return {
546
+ agentId,
547
+ insightType,
548
+ content: response,
549
+ cached: false,
550
+ timestamp: event.timestamp
551
+ };
552
+ }
553
+ function shortCircuit(agentId, insightType, content, cached, timestamp) {
554
+ return { agentId, insightType, content, cached, timestamp: timestamp ?? Date.now() };
555
+ }
556
+ return {
557
+ async explainFailures(agentId) {
558
+ const profile = store.getAgentProfile(agentId);
559
+ if (!profile) {
560
+ return shortCircuit(
561
+ agentId,
562
+ "failure-analysis",
563
+ "No data available for this agent.",
564
+ false
565
+ );
566
+ }
567
+ const events = store.getRecentEvents(agentId, { limit: 50 });
568
+ const failures = events.filter((e) => e.eventType === "execution.failed");
569
+ if (failures.length === 0) {
570
+ return shortCircuit(
571
+ agentId,
572
+ "failure-analysis",
573
+ "No recent failures found for this agent.",
574
+ false
575
+ );
576
+ }
577
+ const dataHash = simpleHash(JSON.stringify({ failures, profile }));
578
+ const cached = checkCache(agentId, "failure-analysis", dataHash);
579
+ if (cached) {
580
+ return shortCircuit(agentId, "failure-analysis", cached.response, true, cached.timestamp);
581
+ }
582
+ const prompt = buildFailureAnalysisPrompt(failures, profile);
583
+ try {
584
+ const response = await analysisFn(prompt);
585
+ return storeAndReturn(agentId, "failure-analysis", prompt, response, dataHash);
586
+ } catch (err) {
587
+ const message = err instanceof Error ? err.message : String(err);
588
+ return shortCircuit(agentId, "failure-analysis", `Analysis failed: ${message}`, false);
589
+ }
590
+ },
591
+ async explainAnomaly(agentId, event) {
592
+ const profile = store.getAgentProfile(agentId);
593
+ if (!profile) {
594
+ return shortCircuit(
595
+ agentId,
596
+ "anomaly-explanation",
597
+ "No data available for this agent.",
598
+ false
599
+ );
600
+ }
601
+ const dataHash = simpleHash(JSON.stringify({ event, profile }));
602
+ const cached = checkCache(agentId, "anomaly-explanation", dataHash);
603
+ if (cached) {
604
+ return shortCircuit(
605
+ agentId,
606
+ "anomaly-explanation",
607
+ cached.response,
608
+ true,
609
+ cached.timestamp
610
+ );
611
+ }
612
+ const prompt = buildAnomalyExplanationPrompt(event, profile);
613
+ try {
614
+ const response = await analysisFn(prompt);
615
+ return storeAndReturn(agentId, "anomaly-explanation", prompt, response, dataHash);
616
+ } catch (err) {
617
+ const message = err instanceof Error ? err.message : String(err);
618
+ return shortCircuit(agentId, "anomaly-explanation", `Analysis failed: ${message}`, false);
619
+ }
620
+ },
621
+ async summarizeAgent(agentId) {
622
+ const profile = store.getAgentProfile(agentId);
623
+ if (!profile) {
624
+ return shortCircuit(agentId, "agent-summary", "No data available for this agent.", false);
625
+ }
626
+ const recentEvents = store.getRecentEvents(agentId, { limit: 20 });
627
+ const patterns = store.getPatternHistory(agentId, { limit: 5 });
628
+ const dataHash = simpleHash(JSON.stringify({ profile, recentEvents, patterns }));
629
+ const cached = checkCache(agentId, "agent-summary", dataHash);
630
+ if (cached) {
631
+ return shortCircuit(agentId, "agent-summary", cached.response, true, cached.timestamp);
632
+ }
633
+ const prompt = buildAgentSummaryPrompt(profile, recentEvents, patterns);
634
+ try {
635
+ const response = await analysisFn(prompt);
636
+ return storeAndReturn(agentId, "agent-summary", prompt, response, dataHash);
637
+ } catch (err) {
638
+ const message = err instanceof Error ? err.message : String(err);
639
+ return shortCircuit(agentId, "agent-summary", `Analysis failed: ${message}`, false);
640
+ }
641
+ },
642
+ async suggestFixes(agentId) {
643
+ const profile = store.getAgentProfile(agentId);
644
+ if (!profile) {
645
+ return shortCircuit(agentId, "fix-suggestion", "No data available for this agent.", false);
646
+ }
647
+ const events = store.getRecentEvents(agentId, { limit: 50 });
648
+ const failures = events.filter((e) => e.eventType === "execution.failed");
649
+ const patterns = store.getPatternHistory(agentId, { limit: 5 });
650
+ if (failures.length === 0 && profile.knownBottlenecks.length === 0) {
651
+ return shortCircuit(
652
+ agentId,
653
+ "fix-suggestion",
654
+ "Agent is healthy \u2014 no failures or bottlenecks detected.",
655
+ false
656
+ );
657
+ }
658
+ const dataHash = simpleHash(JSON.stringify({ failures, profile, patterns }));
659
+ const cached = checkCache(agentId, "fix-suggestion", dataHash);
660
+ if (cached) {
661
+ return shortCircuit(agentId, "fix-suggestion", cached.response, true, cached.timestamp);
662
+ }
663
+ const prompt = buildFixSuggestionPrompt(failures, profile, patterns);
664
+ try {
665
+ const response = await analysisFn(prompt);
666
+ return storeAndReturn(agentId, "fix-suggestion", prompt, response, dataHash);
667
+ } catch (err) {
668
+ const message = err instanceof Error ? err.message : String(err);
669
+ return shortCircuit(agentId, "fix-suggestion", `Analysis failed: ${message}`, false);
670
+ }
671
+ }
672
+ };
673
+ }
674
+
32
675
  // src/guards.ts
33
676
  var DEFAULT_TIMEOUTS = {
34
677
  tool: 3e4,
@@ -89,6 +732,9 @@ function checkGuards(graph, config) {
89
732
  });
90
733
  }
91
734
  violations.push(...detectReasoningLoops(graph, maxReasoningSteps, now));
735
+ if (config?.policySource) {
736
+ violations.push(...checkPolicyViolations(graph, config.policySource, config.policyThresholds, now));
737
+ }
92
738
  return violations;
93
739
  }
94
740
  function detectReasoningLoops(graph, maxSteps, timestamp) {
@@ -123,6 +769,40 @@ function detectReasoningLoops(graph, maxSteps, timestamp) {
123
769
  walk(graph.rootNodeId, 0, null);
124
770
  return violations;
125
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
+ }
126
806
  function withGuards(builder, config) {
127
807
  const logger = config?.logger ?? ((msg) => console.warn(`[AgentFlow Guard] ${msg}`));
128
808
  const onViolation = config?.onViolation ?? "warn";
@@ -186,14 +866,539 @@ function withGuards(builder, config) {
186
866
  }
187
867
  };
188
868
  }
869
+
870
+ // src/json-event-writer.ts
871
+ import { mkdirSync, writeFileSync } from "fs";
872
+ import { join } from "path";
873
+ function createJsonEventWriter(config) {
874
+ const { outputDir } = config;
875
+ function ensureDir() {
876
+ mkdirSync(outputDir, { recursive: true });
877
+ }
878
+ function eventFileName(event) {
879
+ const typePart = event.eventType.replace(/\./g, "-");
880
+ const agentId = "agentId" in event ? event.agentId : "unknown";
881
+ return `${typePart}-${agentId}-${event.timestamp}.json`;
882
+ }
883
+ return {
884
+ async write(_graph) {
885
+ },
886
+ async writeEvent(event) {
887
+ ensureDir();
888
+ const fileName = eventFileName(event);
889
+ const filePath = join(outputDir, fileName);
890
+ writeFileSync(filePath, JSON.stringify(event, null, 2), "utf-8");
891
+ }
892
+ };
893
+ }
894
+
895
+ // src/knowledge-store.ts
896
+ import {
897
+ existsSync,
898
+ mkdirSync as mkdirSync2,
899
+ readdirSync,
900
+ readFileSync,
901
+ renameSync,
902
+ rmSync,
903
+ writeFileSync as writeFileSync2
904
+ } from "fs";
905
+ import { join as join2 } from "path";
906
+ var DEFAULT_BASE_DIR = ".agentflow/knowledge";
907
+ var MAX_RECENT_DURATIONS = 100;
908
+ var writeCounter = 0;
909
+ function toDateDir(epochMs) {
910
+ return new Date(epochMs).toISOString().slice(0, 10);
911
+ }
912
+ function readJson(filePath) {
913
+ try {
914
+ return JSON.parse(readFileSync(filePath, "utf-8"));
915
+ } catch {
916
+ return null;
917
+ }
918
+ }
919
+ function writeJsonAtomic(filePath, data) {
920
+ const tmpPath = `${filePath}.tmp.${Date.now()}`;
921
+ writeFileSync2(tmpPath, JSON.stringify(data, null, 2), "utf-8");
922
+ renameSync(tmpPath, filePath);
923
+ }
924
+ function emptyProfile(agentId) {
925
+ return {
926
+ agentId,
927
+ totalRuns: 0,
928
+ successCount: 0,
929
+ failureCount: 0,
930
+ failureRate: 0,
931
+ recentDurations: [],
932
+ lastConformanceScore: null,
933
+ knownBottlenecks: [],
934
+ lastPatternTimestamp: null,
935
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
936
+ };
937
+ }
938
+ function mergeExecutionEvent(profile, event) {
939
+ const totalRuns = profile.totalRuns + 1;
940
+ const isFailure = event.eventType === "execution.failed";
941
+ const successCount = profile.successCount + (isFailure ? 0 : 1);
942
+ const failureCount = profile.failureCount + (isFailure ? 1 : 0);
943
+ const durations = [...profile.recentDurations, event.duration];
944
+ if (durations.length > MAX_RECENT_DURATIONS) {
945
+ durations.shift();
946
+ }
947
+ const conformanceScore = event.processContext?.conformanceScore ?? profile.lastConformanceScore;
948
+ return {
949
+ agentId: profile.agentId,
950
+ totalRuns,
951
+ successCount,
952
+ failureCount,
953
+ failureRate: failureCount / totalRuns,
954
+ recentDurations: durations,
955
+ lastConformanceScore: conformanceScore,
956
+ knownBottlenecks: profile.knownBottlenecks,
957
+ lastPatternTimestamp: profile.lastPatternTimestamp,
958
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
959
+ };
960
+ }
961
+ function mergePatternEvent(profile, event) {
962
+ const existingBottlenecks = new Set(profile.knownBottlenecks);
963
+ for (const b of event.pattern.topBottlenecks) {
964
+ existingBottlenecks.add(b.nodeName);
965
+ }
966
+ return {
967
+ ...profile,
968
+ knownBottlenecks: [...existingBottlenecks],
969
+ lastPatternTimestamp: event.timestamp,
970
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
971
+ };
972
+ }
973
+ function createKnowledgeStore(config) {
974
+ const baseDir = config?.baseDir ?? DEFAULT_BASE_DIR;
975
+ const eventsDir = join2(baseDir, "events");
976
+ const patternsDir = join2(baseDir, "patterns");
977
+ const profilesDir = join2(baseDir, "profiles");
978
+ const insightsDir = join2(baseDir, "insights");
979
+ function ensureDir(dir) {
980
+ mkdirSync2(dir, { recursive: true });
981
+ }
982
+ function profilePath(agentId) {
983
+ return join2(profilesDir, `${agentId}.json`);
984
+ }
985
+ function appendExecutionEvent(event) {
986
+ const dateDir = join2(eventsDir, event.agentId, toDateDir(event.timestamp));
987
+ ensureDir(dateDir);
988
+ const typePart = event.eventType.replace(/\./g, "-");
989
+ const seq = String(writeCounter++).padStart(4, "0");
990
+ const fileName = `${typePart}-${event.timestamp}-${seq}.json`;
991
+ writeFileSync2(join2(dateDir, fileName), JSON.stringify(event, null, 2), "utf-8");
992
+ ensureDir(profilesDir);
993
+ const existing = readJson(profilePath(event.agentId)) ?? emptyProfile(event.agentId);
994
+ const updated = mergeExecutionEvent(existing, event);
995
+ writeJsonAtomic(profilePath(event.agentId), updated);
996
+ }
997
+ function appendPatternEvent(event) {
998
+ const agentPatternDir = join2(patternsDir, event.agentId);
999
+ ensureDir(agentPatternDir);
1000
+ const seq = String(writeCounter++).padStart(4, "0");
1001
+ const fileName = `${event.timestamp}-${seq}.json`;
1002
+ writeFileSync2(join2(agentPatternDir, fileName), JSON.stringify(event, null, 2), "utf-8");
1003
+ ensureDir(profilesDir);
1004
+ const existing = readJson(profilePath(event.agentId)) ?? emptyProfile(event.agentId);
1005
+ const updated = mergePatternEvent(existing, event);
1006
+ writeJsonAtomic(profilePath(event.agentId), updated);
1007
+ }
1008
+ return {
1009
+ baseDir,
1010
+ append(event) {
1011
+ if (event.eventType === "pattern.discovered" || event.eventType === "pattern.updated") {
1012
+ appendPatternEvent(event);
1013
+ } else {
1014
+ appendExecutionEvent(event);
1015
+ }
1016
+ },
1017
+ getRecentEvents(agentId, options) {
1018
+ const limit = options?.limit ?? 50;
1019
+ const since = options?.since ?? 0;
1020
+ const agentDir = join2(eventsDir, agentId);
1021
+ if (!existsSync(agentDir)) return [];
1022
+ const events = [];
1023
+ const dateDirs = readdirSync(agentDir).sort().reverse();
1024
+ for (const dateDir of dateDirs) {
1025
+ const fullDateDir = join2(agentDir, dateDir);
1026
+ let files;
1027
+ try {
1028
+ files = readdirSync(fullDateDir).filter((f) => f.endsWith(".json"));
1029
+ } catch {
1030
+ continue;
1031
+ }
1032
+ for (const file of files) {
1033
+ const event = readJson(join2(fullDateDir, file));
1034
+ if (event && event.timestamp > since) {
1035
+ events.push(event);
1036
+ }
1037
+ }
1038
+ if (events.length >= limit * 2) break;
1039
+ }
1040
+ events.sort((a, b) => b.timestamp - a.timestamp);
1041
+ return events.slice(0, limit);
1042
+ },
1043
+ getAgentProfile(agentId) {
1044
+ return readJson(profilePath(agentId));
1045
+ },
1046
+ getPatternHistory(agentId, options) {
1047
+ const limit = options?.limit ?? 20;
1048
+ const agentPatternDir = join2(patternsDir, agentId);
1049
+ if (!existsSync(agentPatternDir)) return [];
1050
+ const files = readdirSync(agentPatternDir).filter((f) => f.endsWith(".json")).sort().reverse();
1051
+ const events = [];
1052
+ for (const file of files.slice(0, limit)) {
1053
+ const event = readJson(join2(agentPatternDir, file));
1054
+ if (event) events.push(event);
1055
+ }
1056
+ return events;
1057
+ },
1058
+ compact(options) {
1059
+ let removed = 0;
1060
+ if (existsSync(eventsDir)) {
1061
+ for (const agentId of readdirSync(eventsDir)) {
1062
+ const agentDir = join2(eventsDir, agentId);
1063
+ let dateDirs;
1064
+ try {
1065
+ dateDirs = readdirSync(agentDir);
1066
+ } catch {
1067
+ continue;
1068
+ }
1069
+ for (const dateDir of dateDirs) {
1070
+ const fullDateDir = join2(agentDir, dateDir);
1071
+ let files;
1072
+ try {
1073
+ files = readdirSync(fullDateDir).filter((f) => f.endsWith(".json"));
1074
+ } catch {
1075
+ continue;
1076
+ }
1077
+ for (const file of files) {
1078
+ const parts = file.replace(".json", "").split("-");
1079
+ const tsPart = parts[parts.length - 2];
1080
+ const ts = tsPart ? Number.parseInt(tsPart, 10) : 0;
1081
+ if (!Number.isNaN(ts) && ts < options.olderThan) {
1082
+ try {
1083
+ rmSync(join2(fullDateDir, file));
1084
+ removed++;
1085
+ } catch {
1086
+ }
1087
+ }
1088
+ }
1089
+ try {
1090
+ if (readdirSync(fullDateDir).length === 0) {
1091
+ rmSync(fullDateDir, { recursive: true });
1092
+ }
1093
+ } catch {
1094
+ }
1095
+ }
1096
+ }
1097
+ }
1098
+ if (existsSync(patternsDir)) {
1099
+ for (const agentId of readdirSync(patternsDir)) {
1100
+ const agentPatternDir = join2(patternsDir, agentId);
1101
+ let files;
1102
+ try {
1103
+ files = readdirSync(agentPatternDir).filter((f) => f.endsWith(".json"));
1104
+ } catch {
1105
+ continue;
1106
+ }
1107
+ for (const file of files) {
1108
+ const ts = Number.parseInt(file.split("-")[0], 10);
1109
+ if (!Number.isNaN(ts) && ts < options.olderThan) {
1110
+ try {
1111
+ rmSync(join2(agentPatternDir, file));
1112
+ removed++;
1113
+ } catch {
1114
+ }
1115
+ }
1116
+ }
1117
+ }
1118
+ }
1119
+ if (existsSync(insightsDir)) {
1120
+ for (const agentId of readdirSync(insightsDir)) {
1121
+ const agentInsightDir = join2(insightsDir, agentId);
1122
+ let files;
1123
+ try {
1124
+ files = readdirSync(agentInsightDir).filter((f) => f.endsWith(".json"));
1125
+ } catch {
1126
+ continue;
1127
+ }
1128
+ for (const file of files) {
1129
+ const parts = file.replace(".json", "").split("-");
1130
+ const tsPart = parts[parts.length - 2];
1131
+ const ts = tsPart ? Number.parseInt(tsPart, 10) : 0;
1132
+ if (!Number.isNaN(ts) && ts < options.olderThan) {
1133
+ try {
1134
+ rmSync(join2(agentInsightDir, file));
1135
+ removed++;
1136
+ } catch {
1137
+ }
1138
+ }
1139
+ }
1140
+ }
1141
+ }
1142
+ return { removed };
1143
+ },
1144
+ appendInsight(event) {
1145
+ const agentInsightDir = join2(insightsDir, event.agentId);
1146
+ ensureDir(agentInsightDir);
1147
+ const seq = String(writeCounter++).padStart(4, "0");
1148
+ const fileName = `${event.insightType}-${event.timestamp}-${seq}.json`;
1149
+ writeFileSync2(join2(agentInsightDir, fileName), JSON.stringify(event, null, 2), "utf-8");
1150
+ },
1151
+ getRecentInsights(agentId, options) {
1152
+ const limit = options?.limit ?? 10;
1153
+ const typeFilter = options?.type;
1154
+ const agentInsightDir = join2(insightsDir, agentId);
1155
+ if (!existsSync(agentInsightDir)) return [];
1156
+ const files = readdirSync(agentInsightDir).filter((f) => f.endsWith(".json"));
1157
+ const events = [];
1158
+ for (const file of files) {
1159
+ const event = readJson(join2(agentInsightDir, file));
1160
+ if (event) {
1161
+ if (typeFilter && event.insightType !== typeFilter) continue;
1162
+ events.push(event);
1163
+ }
1164
+ }
1165
+ events.sort((a, b) => b.timestamp - a.timestamp);
1166
+ return events.slice(0, limit);
1167
+ },
1168
+ // EventWriter interface
1169
+ async write(_graph) {
1170
+ },
1171
+ async writeEvent(event) {
1172
+ this.append(event);
1173
+ }
1174
+ };
1175
+ }
1176
+
1177
+ // src/policy-source.ts
1178
+ import { readdirSync as readdirSync2 } from "fs";
1179
+ import { join as join3 } from "path";
1180
+ function createPolicySource(store) {
1181
+ return {
1182
+ recentFailureRate(agentId) {
1183
+ const profile = store.getAgentProfile(agentId);
1184
+ return profile?.failureRate ?? 0;
1185
+ },
1186
+ isKnownBottleneck(nodeName) {
1187
+ const profilesDir = join3(store.baseDir, "profiles");
1188
+ let agentIds;
1189
+ try {
1190
+ agentIds = readdirSync2(profilesDir).filter((f) => f.endsWith(".json")).map((f) => f.replace(".json", ""));
1191
+ } catch {
1192
+ return false;
1193
+ }
1194
+ for (const agentId of agentIds) {
1195
+ const profile = store.getAgentProfile(agentId);
1196
+ if (profile && profile.knownBottlenecks.includes(nodeName)) {
1197
+ return true;
1198
+ }
1199
+ }
1200
+ return false;
1201
+ },
1202
+ lastConformanceScore(agentId) {
1203
+ const profile = store.getAgentProfile(agentId);
1204
+ return profile?.lastConformanceScore ?? null;
1205
+ },
1206
+ getAgentProfile(agentId) {
1207
+ return store.getAgentProfile(agentId);
1208
+ }
1209
+ };
1210
+ }
1211
+
1212
+ // src/soma-event-writer.ts
1213
+ import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "fs";
1214
+ import { join as join4 } from "path";
1215
+ function compactIso(epochMs) {
1216
+ return new Date(epochMs).toISOString().slice(0, 19).replace(/:/g, "");
1217
+ }
1218
+ function isoDate(epochMs) {
1219
+ return new Date(epochMs).toISOString().slice(0, 10);
1220
+ }
1221
+ function renderFrontmatter(fields) {
1222
+ const lines = ["---"];
1223
+ for (const [key, value] of Object.entries(fields)) {
1224
+ if (value === void 0) continue;
1225
+ if (Array.isArray(value)) {
1226
+ lines.push(`${key}: [${value.map((v) => `'${v}'`).join(", ")}]`);
1227
+ } else if (typeof value === "string") {
1228
+ lines.push(`${key}: '${value}'`);
1229
+ } else {
1230
+ lines.push(`${key}: ${String(value)}`);
1231
+ }
1232
+ }
1233
+ lines.push("---");
1234
+ return lines.join("\n");
1235
+ }
1236
+ function formatDuration(ms) {
1237
+ if (ms < 1e3) return `${ms.toFixed(0)}ms`;
1238
+ if (ms < 6e4) return `${(ms / 1e3).toFixed(1)}s`;
1239
+ return `${(ms / 6e4).toFixed(1)}min`;
1240
+ }
1241
+ function executionEventToMarkdown(event) {
1242
+ const isCompleted = event.eventType === "execution.completed";
1243
+ const subtype = isCompleted ? "completed" : "failed";
1244
+ const tags = [
1245
+ "agentflow/execution",
1246
+ `agent/${event.agentId}`,
1247
+ `status/${subtype}`
1248
+ ];
1249
+ if (event.processContext?.isAnomaly) {
1250
+ tags.push("agentflow/anomaly");
1251
+ }
1252
+ const frontmatter = {
1253
+ type: "execution",
1254
+ subtype,
1255
+ name: `Execution: ${event.agentId} \u2014 ${subtype}`,
1256
+ source: "agentflow",
1257
+ created: isoDate(event.timestamp),
1258
+ alfred_tags: tags,
1259
+ agentflow_graph_id: event.graphId,
1260
+ duration_ms: event.duration,
1261
+ node_count: event.nodeCount
1262
+ };
1263
+ if (event.processContext) {
1264
+ frontmatter.conformance_score = event.processContext.conformanceScore;
1265
+ frontmatter.is_anomaly = event.processContext.isAnomaly;
1266
+ }
1267
+ const body = [];
1268
+ body.push(`# Execution: ${event.agentId} \u2014 ${subtype}
1269
+ `);
1270
+ body.push(`**Duration:** ${formatDuration(event.duration)} `);
1271
+ body.push(`**Nodes:** ${event.nodeCount} `);
1272
+ body.push(`**Status:** ${event.status}
1273
+ `);
1274
+ if (event.pathSignature) {
1275
+ body.push(`## Path
1276
+ `);
1277
+ body.push(`\`${event.pathSignature}\`
1278
+ `);
1279
+ }
1280
+ if (!isCompleted && event.failurePoint) {
1281
+ const fp = event.failurePoint;
1282
+ body.push(`## Failure Point
1283
+ `);
1284
+ body.push(`**Node:** ${fp.nodeType}:${fp.nodeName} (\`${fp.nodeId}\`) `);
1285
+ if (fp.error) {
1286
+ body.push(`**Error:** ${fp.error}
1287
+ `);
1288
+ }
1289
+ }
1290
+ if (event.processContext) {
1291
+ body.push(`## Process Context
1292
+ `);
1293
+ body.push(`**Conformance:** ${(event.processContext.conformanceScore * 100).toFixed(0)}% `);
1294
+ body.push(`**Anomaly:** ${event.processContext.isAnomaly ? "yes" : "no"}
1295
+ `);
1296
+ }
1297
+ body.push(`## Related
1298
+ `);
1299
+ body.push(`- [[agent/${event.agentId}]]`);
1300
+ return `${renderFrontmatter(frontmatter)}
1301
+
1302
+ ${body.join("\n")}`;
1303
+ }
1304
+ function patternEventToMarkdown(event) {
1305
+ const { pattern } = event;
1306
+ const tags = [
1307
+ "agentflow/pattern",
1308
+ `agent/${event.agentId}`
1309
+ ];
1310
+ const frontmatter = {
1311
+ type: "synthesis",
1312
+ subtype: "pattern-discovery",
1313
+ name: `Pattern: ${event.agentId} \u2014 ${pattern.variantCount} variants across ${pattern.totalGraphs} runs`,
1314
+ source: "agentflow",
1315
+ created: isoDate(event.timestamp),
1316
+ alfred_tags: tags,
1317
+ variant_count: pattern.variantCount,
1318
+ total_graphs: pattern.totalGraphs
1319
+ };
1320
+ const body = [];
1321
+ body.push(`# Pattern: ${event.agentId}
1322
+ `);
1323
+ body.push(`**Variants:** ${pattern.variantCount} `);
1324
+ body.push(`**Total Runs:** ${pattern.totalGraphs}
1325
+ `);
1326
+ if (pattern.topVariants.length > 0) {
1327
+ body.push(`## Top Variants
1328
+ `);
1329
+ body.push(`| Path | Count | % |`);
1330
+ body.push(`|------|-------|---|`);
1331
+ for (const v of pattern.topVariants) {
1332
+ const sig = v.pathSignature.length > 60 ? `${v.pathSignature.slice(0, 57)}...` : v.pathSignature;
1333
+ body.push(`| \`${sig}\` | ${v.count} | ${v.percentage.toFixed(1)}% |`);
1334
+ }
1335
+ body.push("");
1336
+ }
1337
+ if (pattern.topBottlenecks.length > 0) {
1338
+ body.push(`## Top Bottlenecks
1339
+ `);
1340
+ body.push(`| Node | Type | p95 |`);
1341
+ body.push(`|------|------|-----|`);
1342
+ for (const b of pattern.topBottlenecks) {
1343
+ body.push(`| ${b.nodeName} | ${b.nodeType} | ${formatDuration(b.p95)} |`);
1344
+ }
1345
+ body.push("");
1346
+ }
1347
+ body.push(`## Related
1348
+ `);
1349
+ body.push(`- [[agent/${event.agentId}]]`);
1350
+ return `${renderFrontmatter(frontmatter)}
1351
+
1352
+ ${body.join("\n")}`;
1353
+ }
1354
+ function createSomaEventWriter(config) {
1355
+ const { inboxDir } = config;
1356
+ function ensureDir() {
1357
+ mkdirSync3(inboxDir, { recursive: true });
1358
+ }
1359
+ function eventFileName(event) {
1360
+ const agentId = event.agentId;
1361
+ const ts = compactIso(event.timestamp);
1362
+ if (event.eventType === "pattern.discovered" || event.eventType === "pattern.updated") {
1363
+ return `synthesis-${agentId}-${ts}.md`;
1364
+ }
1365
+ return `execution-${agentId}-${ts}.md`;
1366
+ }
1367
+ return {
1368
+ async write(_graph) {
1369
+ },
1370
+ async writeEvent(event) {
1371
+ ensureDir();
1372
+ const markdown = event.eventType === "pattern.discovered" || event.eventType === "pattern.updated" ? patternEventToMarkdown(event) : executionEventToMarkdown(event);
1373
+ const fileName = eventFileName(event);
1374
+ writeFileSync3(join4(inboxDir, fileName), markdown, "utf-8");
1375
+ }
1376
+ };
1377
+ }
189
1378
  export {
190
1379
  auditProcesses,
1380
+ buildAgentSummaryPrompt,
1381
+ buildAnomalyExplanationPrompt,
1382
+ buildFailureAnalysisPrompt,
1383
+ buildFixSuggestionPrompt,
1384
+ checkConformance,
191
1385
  checkGuards,
1386
+ createEventEmitter,
1387
+ createExecutionEvent,
192
1388
  createGraphBuilder,
1389
+ createInsightEngine,
1390
+ createJsonEventWriter,
1391
+ createKnowledgeStore,
1392
+ createPatternEvent,
1393
+ createPolicySource,
1394
+ createSomaEventWriter,
193
1395
  createTraceStore,
1396
+ discoverProcess,
194
1397
  discoverProcessConfig,
1398
+ findVariants,
195
1399
  findWaitingOn,
196
1400
  formatAuditReport,
1401
+ getBottlenecks,
197
1402
  getChildren,
198
1403
  getCriticalPath,
199
1404
  getDepth,
@@ -202,6 +1407,7 @@ export {
202
1407
  getHungNodes,
203
1408
  getNode,
204
1409
  getParent,
1410
+ getPathSignature,
205
1411
  getStats,
206
1412
  getSubtree,
207
1413
  getTraceTree,