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.cjs CHANGED
@@ -21,12 +21,28 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
23
  auditProcesses: () => auditProcesses,
24
+ buildAgentSummaryPrompt: () => buildAgentSummaryPrompt,
25
+ buildAnomalyExplanationPrompt: () => buildAnomalyExplanationPrompt,
26
+ buildFailureAnalysisPrompt: () => buildFailureAnalysisPrompt,
27
+ buildFixSuggestionPrompt: () => buildFixSuggestionPrompt,
28
+ checkConformance: () => checkConformance,
24
29
  checkGuards: () => checkGuards,
30
+ createEventEmitter: () => createEventEmitter,
31
+ createExecutionEvent: () => createExecutionEvent,
25
32
  createGraphBuilder: () => createGraphBuilder,
33
+ createInsightEngine: () => createInsightEngine,
34
+ createJsonEventWriter: () => createJsonEventWriter,
35
+ createKnowledgeStore: () => createKnowledgeStore,
36
+ createPatternEvent: () => createPatternEvent,
37
+ createPolicySource: () => createPolicySource,
38
+ createSomaEventWriter: () => createSomaEventWriter,
26
39
  createTraceStore: () => createTraceStore,
40
+ discoverProcess: () => discoverProcess,
27
41
  discoverProcessConfig: () => discoverProcessConfig,
42
+ findVariants: () => findVariants,
28
43
  findWaitingOn: () => findWaitingOn,
29
44
  formatAuditReport: () => formatAuditReport,
45
+ getBottlenecks: () => getBottlenecks,
30
46
  getChildren: () => getChildren,
31
47
  getCriticalPath: () => getCriticalPath,
32
48
  getDepth: () => getDepth,
@@ -35,6 +51,7 @@ __export(index_exports, {
35
51
  getHungNodes: () => getHungNodes,
36
52
  getNode: () => getNode,
37
53
  getParent: () => getParent,
54
+ getPathSignature: () => getPathSignature,
38
55
  getStats: () => getStats,
39
56
  getSubtree: () => getSubtree,
40
57
  getTraceTree: () => getTraceTree,
@@ -51,8 +68,321 @@ __export(index_exports, {
51
68
  });
52
69
  module.exports = __toCommonJS(index_exports);
53
70
 
71
+ // src/process-mining.ts
72
+ function getPathSignature(graph) {
73
+ const root = graph.nodes.get(graph.rootNodeId);
74
+ if (!root) return "";
75
+ const parts = [];
76
+ function walk(node) {
77
+ parts.push(`${node.type}:${node.name}`);
78
+ const childNodes = [];
79
+ for (const childId of node.children) {
80
+ const child = graph.nodes.get(childId);
81
+ if (child) childNodes.push(child);
82
+ }
83
+ childNodes.sort((a, b) => {
84
+ const keyA = `${a.type}:${a.name}`;
85
+ const keyB = `${b.type}:${b.name}`;
86
+ return keyA.localeCompare(keyB);
87
+ });
88
+ for (const child of childNodes) {
89
+ walk(child);
90
+ }
91
+ }
92
+ walk(root);
93
+ return parts.join("\u2192");
94
+ }
95
+ function stepKey(node) {
96
+ return `${node.type}:${node.name}`;
97
+ }
98
+ function discoverProcess(graphs) {
99
+ if (graphs.length === 0) {
100
+ throw new Error("discoverProcess requires at least one graph");
101
+ }
102
+ const steps = /* @__PURE__ */ new Set();
103
+ const transitionCounts = /* @__PURE__ */ new Map();
104
+ const outgoingCounts = /* @__PURE__ */ new Map();
105
+ for (const graph of graphs) {
106
+ for (const node of graph.nodes.values()) {
107
+ const parentKey = stepKey(node);
108
+ steps.add(parentKey);
109
+ for (const childId of node.children) {
110
+ const child = graph.nodes.get(childId);
111
+ if (!child) continue;
112
+ const childKey = stepKey(child);
113
+ const tKey = `${parentKey}\0${childKey}`;
114
+ transitionCounts.set(tKey, (transitionCounts.get(tKey) ?? 0) + 1);
115
+ outgoingCounts.set(parentKey, (outgoingCounts.get(parentKey) ?? 0) + 1);
116
+ }
117
+ }
118
+ }
119
+ const transitions = [];
120
+ for (const [tKey, count] of transitionCounts) {
121
+ const [from, to] = tKey.split("\0");
122
+ const outgoing = outgoingCounts.get(from) ?? count;
123
+ transitions.push({
124
+ from,
125
+ to,
126
+ count,
127
+ probability: count / outgoing
128
+ });
129
+ }
130
+ transitions.sort((a, b) => a.from.localeCompare(b.from) || a.to.localeCompare(b.to));
131
+ return {
132
+ steps: [...steps].sort(),
133
+ transitions,
134
+ totalGraphs: graphs.length,
135
+ agentId: graphs[0].agentId
136
+ };
137
+ }
138
+ function findVariants(graphs) {
139
+ if (graphs.length === 0) return [];
140
+ const groups = /* @__PURE__ */ new Map();
141
+ for (const graph of graphs) {
142
+ const sig = getPathSignature(graph);
143
+ const group = groups.get(sig) ?? [];
144
+ group.push(graph);
145
+ groups.set(sig, group);
146
+ }
147
+ const total = graphs.length;
148
+ const variants = [];
149
+ for (const [pathSignature, groupGraphs] of groups) {
150
+ variants.push({
151
+ pathSignature,
152
+ count: groupGraphs.length,
153
+ percentage: groupGraphs.length / total * 100,
154
+ graphIds: groupGraphs.map((g) => g.id),
155
+ exampleGraph: groupGraphs[0]
156
+ });
157
+ }
158
+ variants.sort((a, b) => {
159
+ const freqDiff = b.count - a.count;
160
+ if (freqDiff !== 0) return freqDiff;
161
+ return a.pathSignature.localeCompare(b.pathSignature);
162
+ });
163
+ return variants;
164
+ }
165
+ function percentile(sorted, p) {
166
+ if (sorted.length === 0) return 0;
167
+ if (sorted.length === 1) return sorted[0];
168
+ const index = p / 100 * (sorted.length - 1);
169
+ const lower = Math.floor(index);
170
+ const upper = Math.ceil(index);
171
+ if (lower === upper) return sorted[lower];
172
+ const weight = index - lower;
173
+ return sorted[lower] * (1 - weight) + sorted[upper] * weight;
174
+ }
175
+ function getBottlenecks(graphs) {
176
+ if (graphs.length === 0) return [];
177
+ const now = Date.now();
178
+ const stats = /* @__PURE__ */ new Map();
179
+ for (const graph of graphs) {
180
+ for (const node of graph.nodes.values()) {
181
+ const key = `${node.type}:${node.name}`;
182
+ const entry = stats.get(key) ?? { durations: [], nodeType: node.type, nodeName: node.name };
183
+ const end = node.endTime ?? now;
184
+ entry.durations.push(end - node.startTime);
185
+ stats.set(key, entry);
186
+ }
187
+ }
188
+ const total = graphs.length;
189
+ const bottlenecks = [];
190
+ for (const [, entry] of stats) {
191
+ const sorted = [...entry.durations].sort((a, b) => a - b);
192
+ bottlenecks.push({
193
+ nodeName: entry.nodeName,
194
+ nodeType: entry.nodeType,
195
+ occurrences: sorted.length,
196
+ durations: {
197
+ median: percentile(sorted, 50),
198
+ p95: percentile(sorted, 95),
199
+ p99: percentile(sorted, 99),
200
+ min: sorted[0],
201
+ max: sorted[sorted.length - 1]
202
+ },
203
+ percentOfGraphs: sorted.length / total * 100
204
+ });
205
+ }
206
+ bottlenecks.sort((a, b) => b.durations.p95 - a.durations.p95);
207
+ return bottlenecks;
208
+ }
209
+ function extractGraphTransitions(graph) {
210
+ const transitions = /* @__PURE__ */ new Set();
211
+ for (const node of graph.nodes.values()) {
212
+ const parentKey = stepKey(node);
213
+ for (const childId of node.children) {
214
+ const child = graph.nodes.get(childId);
215
+ if (!child) continue;
216
+ transitions.add(`${parentKey}\0${stepKey(child)}`);
217
+ }
218
+ }
219
+ return transitions;
220
+ }
221
+ function checkConformance(graph, model) {
222
+ const graphTransitions = extractGraphTransitions(graph);
223
+ const deviations = [];
224
+ const modelLookup = /* @__PURE__ */ new Map();
225
+ for (const t of model.transitions) {
226
+ modelLookup.set(`${t.from}\0${t.to}`, t);
227
+ }
228
+ let totalChecks = 0;
229
+ let deviationCount = 0;
230
+ for (const tKey of graphTransitions) {
231
+ totalChecks++;
232
+ const [from, to] = tKey.split("\0");
233
+ const modelTransition = modelLookup.get(tKey);
234
+ if (!modelTransition) {
235
+ deviationCount++;
236
+ deviations.push({
237
+ type: "unexpected-transition",
238
+ from,
239
+ to,
240
+ message: `Unexpected transition ${from} \u2192 ${to} (not in process model)`
241
+ });
242
+ } else if (modelTransition.probability < 0.1) {
243
+ deviationCount++;
244
+ deviations.push({
245
+ type: "low-frequency-path",
246
+ from,
247
+ to,
248
+ message: `Low-frequency path ${from} \u2192 ${to} (model probability: ${(modelTransition.probability * 100).toFixed(1)}%)`,
249
+ modelProbability: modelTransition.probability
250
+ });
251
+ }
252
+ }
253
+ const graphSteps = /* @__PURE__ */ new Set();
254
+ for (const node of graph.nodes.values()) {
255
+ graphSteps.add(stepKey(node));
256
+ }
257
+ for (const t of model.transitions) {
258
+ if (t.probability > 0.5) {
259
+ const tKey = `${t.from}\0${t.to}`;
260
+ if (graphSteps.has(t.from) && !graphTransitions.has(tKey)) {
261
+ totalChecks++;
262
+ deviationCount++;
263
+ deviations.push({
264
+ type: "missing-transition",
265
+ from: t.from,
266
+ to: t.to,
267
+ message: `Missing expected transition ${t.from} \u2192 ${t.to} (model probability: ${(t.probability * 100).toFixed(1)}%)`,
268
+ modelProbability: t.probability
269
+ });
270
+ }
271
+ }
272
+ }
273
+ const conformanceScore = totalChecks === 0 ? 1 : (totalChecks - deviationCount) / totalChecks;
274
+ return {
275
+ conformanceScore,
276
+ isConforming: deviations.length === 0,
277
+ deviations
278
+ };
279
+ }
280
+
281
+ // src/event-emitter.ts
282
+ var SCHEMA_VERSION = 1;
283
+ function createExecutionEvent(graph, options) {
284
+ const duration = graph.endTime !== null ? graph.endTime - graph.startTime : Date.now() - graph.startTime;
285
+ let failurePoint;
286
+ if (graph.status === "failed") {
287
+ let candidate;
288
+ for (const node of graph.nodes.values()) {
289
+ if (node.status === "failed" || node.status === "timeout") {
290
+ const errorMeta = node.metadata.error;
291
+ const fp = {
292
+ nodeId: node.id,
293
+ nodeName: node.name,
294
+ nodeType: node.type,
295
+ error: typeof errorMeta === "string" ? errorMeta : void 0
296
+ };
297
+ if (node.id !== graph.rootNodeId) {
298
+ failurePoint = fp;
299
+ break;
300
+ }
301
+ if (!candidate) candidate = fp;
302
+ }
303
+ }
304
+ if (!failurePoint) failurePoint = candidate;
305
+ }
306
+ return {
307
+ eventType: graph.status === "failed" ? "execution.failed" : "execution.completed",
308
+ graphId: graph.id,
309
+ agentId: graph.agentId,
310
+ timestamp: Date.now(),
311
+ schemaVersion: SCHEMA_VERSION,
312
+ status: graph.status,
313
+ duration,
314
+ nodeCount: graph.nodes.size,
315
+ pathSignature: getPathSignature(graph),
316
+ ...failurePoint ? { failurePoint } : {},
317
+ ...options?.processContext ? { processContext: options.processContext } : {},
318
+ ...options?.semantic ? { semantic: options.semantic } : {},
319
+ violations: options?.violations ?? []
320
+ };
321
+ }
322
+ function createPatternEvent(agentId, model, variants, bottlenecks) {
323
+ return {
324
+ eventType: "pattern.discovered",
325
+ agentId,
326
+ timestamp: Date.now(),
327
+ schemaVersion: SCHEMA_VERSION,
328
+ pattern: {
329
+ totalGraphs: model.totalGraphs,
330
+ variantCount: variants.length,
331
+ topVariants: variants.slice(0, 5).map((v) => ({
332
+ pathSignature: v.pathSignature,
333
+ count: v.count,
334
+ percentage: v.percentage
335
+ })),
336
+ topBottlenecks: bottlenecks.slice(0, 5).map((b) => ({
337
+ nodeName: b.nodeName,
338
+ nodeType: b.nodeType,
339
+ p95: b.durations.p95
340
+ })),
341
+ processModel: model
342
+ }
343
+ };
344
+ }
345
+ function createEventEmitter(config) {
346
+ const writers = config?.writers ?? [];
347
+ const knowledgeStore = config?.knowledgeStore;
348
+ const onError = config?.onError ?? (() => {
349
+ });
350
+ const subscribers = /* @__PURE__ */ new Set();
351
+ return {
352
+ async emit(event) {
353
+ if (knowledgeStore) {
354
+ try {
355
+ knowledgeStore.append(event);
356
+ } catch (err) {
357
+ onError(err);
358
+ }
359
+ }
360
+ for (const writer of writers) {
361
+ try {
362
+ await writer.writeEvent(event);
363
+ } catch (err) {
364
+ onError(err);
365
+ }
366
+ }
367
+ for (const listener of subscribers) {
368
+ try {
369
+ listener(event);
370
+ } catch (err) {
371
+ onError(err);
372
+ }
373
+ }
374
+ },
375
+ subscribe(listener) {
376
+ subscribers.add(listener);
377
+ return () => {
378
+ subscribers.delete(listener);
379
+ };
380
+ }
381
+ };
382
+ }
383
+
54
384
  // src/graph-builder.ts
55
- var import_crypto = require("crypto");
385
+ var import_node_crypto = require("crypto");
56
386
  function deepFreeze(obj) {
57
387
  if (obj === null || typeof obj !== "object") return obj;
58
388
  if (obj instanceof Map) {
@@ -83,8 +413,8 @@ function createGraphBuilder(config) {
83
413
  const generateId = config?.idGenerator ?? createCounterIdGenerator();
84
414
  const agentId = config?.agentId ?? "unknown";
85
415
  const trigger = config?.trigger ?? "manual";
86
- const spanId = (0, import_crypto.randomUUID)();
87
- const traceId = config?.traceId ?? (typeof process !== "undefined" ? process.env?.AGENTFLOW_TRACE_ID : void 0) ?? (0, import_crypto.randomUUID)();
416
+ const spanId = (0, import_node_crypto.randomUUID)();
417
+ const traceId = config?.traceId ?? (typeof process !== "undefined" ? process.env?.AGENTFLOW_TRACE_ID : void 0) ?? (0, import_node_crypto.randomUUID)();
88
418
  const parentSpanId = config?.parentSpanId ?? (typeof process !== "undefined" ? process.env?.AGENTFLOW_PARENT_SPAN_ID : void 0) ?? null;
89
419
  const graphId = generateId();
90
420
  const startTime = Date.now();
@@ -277,6 +607,336 @@ function createGraphBuilder(config) {
277
607
  return builder;
278
608
  }
279
609
 
610
+ // src/prompt-builder.ts
611
+ var ROLE = "You are analyzing execution data for an AI agent system. Provide clear, actionable analysis based on the data below.";
612
+ function fmtDuration(ms) {
613
+ if (ms < 1e3) return `${ms}ms`;
614
+ return `${(ms / 1e3).toFixed(1)}s`;
615
+ }
616
+ function fmtTime(ts) {
617
+ return new Date(ts).toISOString();
618
+ }
619
+ function durationStats(durations) {
620
+ if (durations.length === 0) return { avg: 0, p50: 0, p95: 0, min: 0, max: 0 };
621
+ const sorted = [...durations].sort((a, b) => a - b);
622
+ const sum = sorted.reduce((a, b) => a + b, 0);
623
+ return {
624
+ avg: Math.round(sum / sorted.length),
625
+ p50: sorted[Math.floor(sorted.length * 0.5)] ?? 0,
626
+ p95: sorted[Math.floor(sorted.length * 0.95)] ?? 0,
627
+ min: sorted[0] ?? 0,
628
+ max: sorted[sorted.length - 1] ?? 0
629
+ };
630
+ }
631
+ function buildFailureAnalysisPrompt(events, profile) {
632
+ const stats = durationStats(profile.recentDurations);
633
+ const failureDetails = events.map((e, i) => {
634
+ const lines = [
635
+ `Failure ${i + 1}:`,
636
+ ` Time: ${fmtTime(e.timestamp)}`,
637
+ ` Duration: ${fmtDuration(e.duration)}`,
638
+ ` Path: ${e.pathSignature}`
639
+ ];
640
+ if (e.failurePoint) {
641
+ lines.push(` Failed at: ${e.failurePoint.nodeName} (${e.failurePoint.nodeType})`);
642
+ if (e.failurePoint.error) lines.push(` Error: ${e.failurePoint.error}`);
643
+ }
644
+ if (e.violations.length > 0) {
645
+ lines.push(` Violations: ${e.violations.map((v) => v.message).join("; ")}`);
646
+ }
647
+ return lines.join("\n");
648
+ }).join("\n\n");
649
+ return `${ROLE}
650
+
651
+ ## Agent Profile
652
+ - Agent: ${profile.agentId}
653
+ - Total runs: ${profile.totalRuns}
654
+ - Failure rate: ${(profile.failureRate * 100).toFixed(1)}% (${profile.failureCount} failures / ${profile.totalRuns} total)
655
+ - Avg duration: ${fmtDuration(stats.avg)} (p50: ${fmtDuration(stats.p50)}, p95: ${fmtDuration(stats.p95)})
656
+ - Known bottlenecks: ${profile.knownBottlenecks.length > 0 ? profile.knownBottlenecks.join(", ") : "none"}
657
+ - Last conformance score: ${profile.lastConformanceScore ?? "N/A"}
658
+
659
+ ## Recent Failures (${events.length})
660
+
661
+ ${failureDetails}
662
+
663
+ ## Question
664
+ Analyze these failures. What patterns do you see? What is the most likely root cause? Are these related or independent failures?`;
665
+ }
666
+ function buildAnomalyExplanationPrompt(event, profile) {
667
+ const stats = durationStats(profile.recentDurations);
668
+ const eventDetails = [
669
+ `Time: ${fmtTime(event.timestamp)}`,
670
+ `Status: ${event.status}`,
671
+ `Duration: ${fmtDuration(event.duration)}`,
672
+ `Path: ${event.pathSignature}`,
673
+ `Node count: ${event.nodeCount}`
674
+ ];
675
+ if (event.processContext) {
676
+ eventDetails.push(`Conformance score: ${event.processContext.conformanceScore}`);
677
+ eventDetails.push(`Is anomaly: ${event.processContext.isAnomaly}`);
678
+ eventDetails.push(`Variant: ${event.processContext.variant}`);
679
+ }
680
+ if (event.failurePoint) {
681
+ eventDetails.push(`Failed at: ${event.failurePoint.nodeName} (${event.failurePoint.nodeType})`);
682
+ if (event.failurePoint.error) eventDetails.push(`Error: ${event.failurePoint.error}`);
683
+ }
684
+ if (event.violations.length > 0) {
685
+ eventDetails.push(`Violations: ${event.violations.map((v) => v.message).join("; ")}`);
686
+ }
687
+ return `${ROLE}
688
+
689
+ ## Agent Baseline (from profile)
690
+ - Agent: ${profile.agentId}
691
+ - Total runs: ${profile.totalRuns}
692
+ - Typical failure rate: ${(profile.failureRate * 100).toFixed(1)}%
693
+ - Typical duration: avg ${fmtDuration(stats.avg)}, p50 ${fmtDuration(stats.p50)}, p95 ${fmtDuration(stats.p95)}
694
+ - Last conformance score: ${profile.lastConformanceScore ?? "N/A"}
695
+ - Known bottlenecks: ${profile.knownBottlenecks.length > 0 ? profile.knownBottlenecks.join(", ") : "none"}
696
+
697
+ ## Anomalous Execution
698
+ ${eventDetails.join("\n")}
699
+
700
+ ## Question
701
+ 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?`;
702
+ }
703
+ function buildAgentSummaryPrompt(profile, recentEvents, patterns) {
704
+ const stats = durationStats(profile.recentDurations);
705
+ const recentOutcomes = recentEvents.slice(0, 10).map((e) => ` ${fmtTime(e.timestamp)} \u2014 ${e.eventType} (${fmtDuration(e.duration)})`).join("\n");
706
+ const patternSummary = patterns.length > 0 ? patterns.slice(0, 3).map((p) => {
707
+ const lines = [
708
+ ` Variants: ${p.pattern.variantCount} across ${p.pattern.totalGraphs} executions`,
709
+ ` Top variant: ${p.pattern.topVariants[0]?.pathSignature ?? "N/A"} (${p.pattern.topVariants[0]?.percentage.toFixed(0) ?? 0}%)`
710
+ ];
711
+ if (p.pattern.topBottlenecks.length > 0) {
712
+ const topB = p.pattern.topBottlenecks[0];
713
+ if (topB)
714
+ lines.push(` Top bottleneck: ${topB.nodeName} (p95: ${fmtDuration(topB.p95)})`);
715
+ }
716
+ return lines.join("\n");
717
+ }).join("\n\n") : " No patterns discovered yet.";
718
+ const dataNote = recentEvents.length === 0 && patterns.length === 0 ? "\nNote: Limited data available. Summary is based only on the profile statistics.\n" : "";
719
+ return `${ROLE}
720
+
721
+ ## Agent Profile
722
+ - Agent: ${profile.agentId}
723
+ - Total runs: ${profile.totalRuns}
724
+ - Success rate: ${((1 - profile.failureRate) * 100).toFixed(1)}% (${profile.successCount} successes, ${profile.failureCount} failures)
725
+ - Duration: avg ${fmtDuration(stats.avg)}, p50 ${fmtDuration(stats.p50)}, p95 ${fmtDuration(stats.p95)}, range ${fmtDuration(stats.min)}\u2013${fmtDuration(stats.max)}
726
+ - Known bottlenecks: ${profile.knownBottlenecks.length > 0 ? profile.knownBottlenecks.join(", ") : "none"}
727
+ - Last conformance score: ${profile.lastConformanceScore ?? "N/A"}
728
+ - Last pattern analysis: ${profile.lastPatternTimestamp ? fmtTime(profile.lastPatternTimestamp) : "never"}
729
+ ${dataNote}
730
+ ## Recent Executions (last ${recentEvents.slice(0, 10).length})
731
+ ${recentOutcomes || " No recent events."}
732
+
733
+ ## Pattern Analysis
734
+ ${patternSummary}
735
+
736
+ ## Question
737
+ 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?`;
738
+ }
739
+ function buildFixSuggestionPrompt(events, profile, patterns) {
740
+ const failureGroups = /* @__PURE__ */ new Map();
741
+ for (const e of events) {
742
+ const key = e.failurePoint?.error ?? e.pathSignature;
743
+ const group = failureGroups.get(key) ?? [];
744
+ group.push(e);
745
+ failureGroups.set(key, group);
746
+ }
747
+ const failureGroupSummary = [...failureGroups.entries()].map(([key, group]) => {
748
+ const latest = group[0];
749
+ return ` "${key}" \u2014 ${group.length} occurrence(s), latest at ${latest ? fmtTime(latest.timestamp) : "unknown"}`;
750
+ }).join("\n");
751
+ const bottleneckDetails = patterns.flatMap((p) => p.pattern.topBottlenecks).map((b) => ` ${b.nodeName} (${b.nodeType}) \u2014 p95: ${fmtDuration(b.p95)}`);
752
+ const uniqueBottlenecks = [...new Set(bottleneckDetails)].join("\n");
753
+ const conformanceIssues = events.filter((e) => e.processContext && e.processContext.conformanceScore < 0.8).map(
754
+ (e) => ` ${fmtTime(e.timestamp)}: conformance ${e.processContext?.conformanceScore}, variant "${e.processContext?.variant}"`
755
+ ).join("\n");
756
+ return `${ROLE}
757
+
758
+ ## Agent Profile
759
+ - Agent: ${profile.agentId}
760
+ - Failure rate: ${(profile.failureRate * 100).toFixed(1)}%
761
+ - Known bottlenecks: ${profile.knownBottlenecks.length > 0 ? profile.knownBottlenecks.join(", ") : "none"}
762
+
763
+ ## Failure Patterns (${events.length} failures)
764
+ ${failureGroupSummary || " No failures recorded."}
765
+
766
+ ## Bottlenecks
767
+ ${uniqueBottlenecks || " No bottlenecks detected."}
768
+
769
+ ## Conformance Issues
770
+ ${conformanceIssues || " No conformance issues."}
771
+
772
+ ## Question
773
+ 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.`;
774
+ }
775
+
776
+ // src/insight-engine.ts
777
+ var DEFAULT_CACHE_TTL_MS = 36e5;
778
+ var SCHEMA_VERSION2 = 1;
779
+ function simpleHash(input) {
780
+ let hash = 0;
781
+ for (let i = 0; i < input.length; i++) {
782
+ const char = input.charCodeAt(i);
783
+ hash = (hash << 5) - hash + char | 0;
784
+ }
785
+ return (hash >>> 0).toString(36);
786
+ }
787
+ function createInsightEngine(store, analysisFn, config) {
788
+ const cacheTtlMs = config?.cacheTtlMs ?? DEFAULT_CACHE_TTL_MS;
789
+ function checkCache(agentId, insightType, dataHash) {
790
+ const recent = store.getRecentInsights(agentId, { type: insightType, limit: 1 });
791
+ if (recent.length === 0) return null;
792
+ const cached = recent[0];
793
+ if (!cached || cached.dataHash !== dataHash) return null;
794
+ const age = Date.now() - cached.timestamp;
795
+ if (age >= cacheTtlMs) return null;
796
+ return cached;
797
+ }
798
+ function storeAndReturn(agentId, insightType, prompt, response, dataHash) {
799
+ const event = {
800
+ eventType: "insight.generated",
801
+ agentId,
802
+ timestamp: Date.now(),
803
+ schemaVersion: SCHEMA_VERSION2,
804
+ insightType,
805
+ prompt,
806
+ response,
807
+ dataHash
808
+ };
809
+ store.appendInsight(event);
810
+ return {
811
+ agentId,
812
+ insightType,
813
+ content: response,
814
+ cached: false,
815
+ timestamp: event.timestamp
816
+ };
817
+ }
818
+ function shortCircuit(agentId, insightType, content, cached, timestamp) {
819
+ return { agentId, insightType, content, cached, timestamp: timestamp ?? Date.now() };
820
+ }
821
+ return {
822
+ async explainFailures(agentId) {
823
+ const profile = store.getAgentProfile(agentId);
824
+ if (!profile) {
825
+ return shortCircuit(
826
+ agentId,
827
+ "failure-analysis",
828
+ "No data available for this agent.",
829
+ false
830
+ );
831
+ }
832
+ const events = store.getRecentEvents(agentId, { limit: 50 });
833
+ const failures = events.filter((e) => e.eventType === "execution.failed");
834
+ if (failures.length === 0) {
835
+ return shortCircuit(
836
+ agentId,
837
+ "failure-analysis",
838
+ "No recent failures found for this agent.",
839
+ false
840
+ );
841
+ }
842
+ const dataHash = simpleHash(JSON.stringify({ failures, profile }));
843
+ const cached = checkCache(agentId, "failure-analysis", dataHash);
844
+ if (cached) {
845
+ return shortCircuit(agentId, "failure-analysis", cached.response, true, cached.timestamp);
846
+ }
847
+ const prompt = buildFailureAnalysisPrompt(failures, profile);
848
+ try {
849
+ const response = await analysisFn(prompt);
850
+ return storeAndReturn(agentId, "failure-analysis", prompt, response, dataHash);
851
+ } catch (err) {
852
+ const message = err instanceof Error ? err.message : String(err);
853
+ return shortCircuit(agentId, "failure-analysis", `Analysis failed: ${message}`, false);
854
+ }
855
+ },
856
+ async explainAnomaly(agentId, event) {
857
+ const profile = store.getAgentProfile(agentId);
858
+ if (!profile) {
859
+ return shortCircuit(
860
+ agentId,
861
+ "anomaly-explanation",
862
+ "No data available for this agent.",
863
+ false
864
+ );
865
+ }
866
+ const dataHash = simpleHash(JSON.stringify({ event, profile }));
867
+ const cached = checkCache(agentId, "anomaly-explanation", dataHash);
868
+ if (cached) {
869
+ return shortCircuit(
870
+ agentId,
871
+ "anomaly-explanation",
872
+ cached.response,
873
+ true,
874
+ cached.timestamp
875
+ );
876
+ }
877
+ const prompt = buildAnomalyExplanationPrompt(event, profile);
878
+ try {
879
+ const response = await analysisFn(prompt);
880
+ return storeAndReturn(agentId, "anomaly-explanation", prompt, response, dataHash);
881
+ } catch (err) {
882
+ const message = err instanceof Error ? err.message : String(err);
883
+ return shortCircuit(agentId, "anomaly-explanation", `Analysis failed: ${message}`, false);
884
+ }
885
+ },
886
+ async summarizeAgent(agentId) {
887
+ const profile = store.getAgentProfile(agentId);
888
+ if (!profile) {
889
+ return shortCircuit(agentId, "agent-summary", "No data available for this agent.", false);
890
+ }
891
+ const recentEvents = store.getRecentEvents(agentId, { limit: 20 });
892
+ const patterns = store.getPatternHistory(agentId, { limit: 5 });
893
+ const dataHash = simpleHash(JSON.stringify({ profile, recentEvents, patterns }));
894
+ const cached = checkCache(agentId, "agent-summary", dataHash);
895
+ if (cached) {
896
+ return shortCircuit(agentId, "agent-summary", cached.response, true, cached.timestamp);
897
+ }
898
+ const prompt = buildAgentSummaryPrompt(profile, recentEvents, patterns);
899
+ try {
900
+ const response = await analysisFn(prompt);
901
+ return storeAndReturn(agentId, "agent-summary", prompt, response, dataHash);
902
+ } catch (err) {
903
+ const message = err instanceof Error ? err.message : String(err);
904
+ return shortCircuit(agentId, "agent-summary", `Analysis failed: ${message}`, false);
905
+ }
906
+ },
907
+ async suggestFixes(agentId) {
908
+ const profile = store.getAgentProfile(agentId);
909
+ if (!profile) {
910
+ return shortCircuit(agentId, "fix-suggestion", "No data available for this agent.", false);
911
+ }
912
+ const events = store.getRecentEvents(agentId, { limit: 50 });
913
+ const failures = events.filter((e) => e.eventType === "execution.failed");
914
+ const patterns = store.getPatternHistory(agentId, { limit: 5 });
915
+ if (failures.length === 0 && profile.knownBottlenecks.length === 0) {
916
+ return shortCircuit(
917
+ agentId,
918
+ "fix-suggestion",
919
+ "Agent is healthy \u2014 no failures or bottlenecks detected.",
920
+ false
921
+ );
922
+ }
923
+ const dataHash = simpleHash(JSON.stringify({ failures, profile, patterns }));
924
+ const cached = checkCache(agentId, "fix-suggestion", dataHash);
925
+ if (cached) {
926
+ return shortCircuit(agentId, "fix-suggestion", cached.response, true, cached.timestamp);
927
+ }
928
+ const prompt = buildFixSuggestionPrompt(failures, profile, patterns);
929
+ try {
930
+ const response = await analysisFn(prompt);
931
+ return storeAndReturn(agentId, "fix-suggestion", prompt, response, dataHash);
932
+ } catch (err) {
933
+ const message = err instanceof Error ? err.message : String(err);
934
+ return shortCircuit(agentId, "fix-suggestion", `Analysis failed: ${message}`, false);
935
+ }
936
+ }
937
+ };
938
+ }
939
+
280
940
  // src/graph-query.ts
281
941
  function getNode(graph, nodeId) {
282
942
  return graph.nodes.get(nodeId);
@@ -429,7 +1089,7 @@ function groupByTraceId(graphs) {
429
1089
  }
430
1090
  function stitchTrace(graphs) {
431
1091
  if (graphs.length === 0) throw new Error("No graphs to stitch");
432
- const traceId = graphs[0].traceId ?? "";
1092
+ const traceId = graphs[0]?.traceId ?? "";
433
1093
  const graphsBySpan = /* @__PURE__ */ new Map();
434
1094
  const childMap = /* @__PURE__ */ new Map();
435
1095
  let rootGraph = null;
@@ -540,6 +1200,9 @@ function checkGuards(graph, config) {
540
1200
  });
541
1201
  }
542
1202
  violations.push(...detectReasoningLoops(graph, maxReasoningSteps, now));
1203
+ if (config?.policySource) {
1204
+ violations.push(...checkPolicyViolations(graph, config.policySource, config.policyThresholds, now));
1205
+ }
543
1206
  return violations;
544
1207
  }
545
1208
  function detectReasoningLoops(graph, maxSteps, timestamp) {
@@ -574,6 +1237,40 @@ function detectReasoningLoops(graph, maxSteps, timestamp) {
574
1237
  walk(graph.rootNodeId, 0, null);
575
1238
  return violations;
576
1239
  }
1240
+ function checkPolicyViolations(graph, policySource, thresholds, timestamp) {
1241
+ const violations = [];
1242
+ const maxFailureRate = thresholds?.maxFailureRate ?? 0.5;
1243
+ const minConformance = thresholds?.minConformance ?? 0.7;
1244
+ const failureRate = policySource.recentFailureRate(graph.agentId);
1245
+ if (failureRate > maxFailureRate) {
1246
+ violations.push({
1247
+ type: "high-failure-rate",
1248
+ nodeId: graph.rootNodeId,
1249
+ message: `Agent ${graph.agentId} has a recent failure rate of ${(failureRate * 100).toFixed(0)}% (threshold: ${(maxFailureRate * 100).toFixed(0)}%)`,
1250
+ timestamp
1251
+ });
1252
+ }
1253
+ const conformanceScore = policySource.lastConformanceScore(graph.agentId);
1254
+ if (conformanceScore !== null && conformanceScore < minConformance) {
1255
+ violations.push({
1256
+ type: "conformance-drift",
1257
+ nodeId: graph.rootNodeId,
1258
+ message: `Agent ${graph.agentId} conformance score ${(conformanceScore * 100).toFixed(0)}% is below threshold ${(minConformance * 100).toFixed(0)}%`,
1259
+ timestamp
1260
+ });
1261
+ }
1262
+ for (const node of graph.nodes.values()) {
1263
+ if (node.status === "running" && policySource.isKnownBottleneck(node.name)) {
1264
+ violations.push({
1265
+ type: "known-bottleneck",
1266
+ nodeId: node.id,
1267
+ message: `Node ${node.name} (${node.type}) is a known bottleneck`,
1268
+ timestamp
1269
+ });
1270
+ }
1271
+ }
1272
+ return violations;
1273
+ }
577
1274
  function withGuards(builder, config) {
578
1275
  const logger = config?.logger ?? ((msg) => console.warn(`[AgentFlow Guard] ${msg}`));
579
1276
  const onViolation = config?.onViolation ?? "warn";
@@ -638,9 +1335,510 @@ function withGuards(builder, config) {
638
1335
  };
639
1336
  }
640
1337
 
641
- // src/live.ts
1338
+ // src/json-event-writer.ts
1339
+ var import_node_fs = require("fs");
1340
+ var import_node_path = require("path");
1341
+ function createJsonEventWriter(config) {
1342
+ const { outputDir } = config;
1343
+ function ensureDir() {
1344
+ (0, import_node_fs.mkdirSync)(outputDir, { recursive: true });
1345
+ }
1346
+ function eventFileName(event) {
1347
+ const typePart = event.eventType.replace(/\./g, "-");
1348
+ const agentId = "agentId" in event ? event.agentId : "unknown";
1349
+ return `${typePart}-${agentId}-${event.timestamp}.json`;
1350
+ }
1351
+ return {
1352
+ async write(_graph) {
1353
+ },
1354
+ async writeEvent(event) {
1355
+ ensureDir();
1356
+ const fileName = eventFileName(event);
1357
+ const filePath = (0, import_node_path.join)(outputDir, fileName);
1358
+ (0, import_node_fs.writeFileSync)(filePath, JSON.stringify(event, null, 2), "utf-8");
1359
+ }
1360
+ };
1361
+ }
1362
+
1363
+ // src/knowledge-store.ts
642
1364
  var import_node_fs2 = require("fs");
643
1365
  var import_node_path2 = require("path");
1366
+ var DEFAULT_BASE_DIR = ".agentflow/knowledge";
1367
+ var MAX_RECENT_DURATIONS = 100;
1368
+ var writeCounter = 0;
1369
+ function toDateDir(epochMs) {
1370
+ return new Date(epochMs).toISOString().slice(0, 10);
1371
+ }
1372
+ function readJson(filePath) {
1373
+ try {
1374
+ return JSON.parse((0, import_node_fs2.readFileSync)(filePath, "utf-8"));
1375
+ } catch {
1376
+ return null;
1377
+ }
1378
+ }
1379
+ function writeJsonAtomic(filePath, data) {
1380
+ const tmpPath = `${filePath}.tmp.${Date.now()}`;
1381
+ (0, import_node_fs2.writeFileSync)(tmpPath, JSON.stringify(data, null, 2), "utf-8");
1382
+ (0, import_node_fs2.renameSync)(tmpPath, filePath);
1383
+ }
1384
+ function emptyProfile(agentId) {
1385
+ return {
1386
+ agentId,
1387
+ totalRuns: 0,
1388
+ successCount: 0,
1389
+ failureCount: 0,
1390
+ failureRate: 0,
1391
+ recentDurations: [],
1392
+ lastConformanceScore: null,
1393
+ knownBottlenecks: [],
1394
+ lastPatternTimestamp: null,
1395
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
1396
+ };
1397
+ }
1398
+ function mergeExecutionEvent(profile, event) {
1399
+ const totalRuns = profile.totalRuns + 1;
1400
+ const isFailure = event.eventType === "execution.failed";
1401
+ const successCount = profile.successCount + (isFailure ? 0 : 1);
1402
+ const failureCount = profile.failureCount + (isFailure ? 1 : 0);
1403
+ const durations = [...profile.recentDurations, event.duration];
1404
+ if (durations.length > MAX_RECENT_DURATIONS) {
1405
+ durations.shift();
1406
+ }
1407
+ const conformanceScore = event.processContext?.conformanceScore ?? profile.lastConformanceScore;
1408
+ return {
1409
+ agentId: profile.agentId,
1410
+ totalRuns,
1411
+ successCount,
1412
+ failureCount,
1413
+ failureRate: failureCount / totalRuns,
1414
+ recentDurations: durations,
1415
+ lastConformanceScore: conformanceScore,
1416
+ knownBottlenecks: profile.knownBottlenecks,
1417
+ lastPatternTimestamp: profile.lastPatternTimestamp,
1418
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
1419
+ };
1420
+ }
1421
+ function mergePatternEvent(profile, event) {
1422
+ const existingBottlenecks = new Set(profile.knownBottlenecks);
1423
+ for (const b of event.pattern.topBottlenecks) {
1424
+ existingBottlenecks.add(b.nodeName);
1425
+ }
1426
+ return {
1427
+ ...profile,
1428
+ knownBottlenecks: [...existingBottlenecks],
1429
+ lastPatternTimestamp: event.timestamp,
1430
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
1431
+ };
1432
+ }
1433
+ function createKnowledgeStore(config) {
1434
+ const baseDir = config?.baseDir ?? DEFAULT_BASE_DIR;
1435
+ const eventsDir = (0, import_node_path2.join)(baseDir, "events");
1436
+ const patternsDir = (0, import_node_path2.join)(baseDir, "patterns");
1437
+ const profilesDir = (0, import_node_path2.join)(baseDir, "profiles");
1438
+ const insightsDir = (0, import_node_path2.join)(baseDir, "insights");
1439
+ function ensureDir(dir) {
1440
+ (0, import_node_fs2.mkdirSync)(dir, { recursive: true });
1441
+ }
1442
+ function profilePath(agentId) {
1443
+ return (0, import_node_path2.join)(profilesDir, `${agentId}.json`);
1444
+ }
1445
+ function appendExecutionEvent(event) {
1446
+ const dateDir = (0, import_node_path2.join)(eventsDir, event.agentId, toDateDir(event.timestamp));
1447
+ ensureDir(dateDir);
1448
+ const typePart = event.eventType.replace(/\./g, "-");
1449
+ const seq = String(writeCounter++).padStart(4, "0");
1450
+ const fileName = `${typePart}-${event.timestamp}-${seq}.json`;
1451
+ (0, import_node_fs2.writeFileSync)((0, import_node_path2.join)(dateDir, fileName), JSON.stringify(event, null, 2), "utf-8");
1452
+ ensureDir(profilesDir);
1453
+ const existing = readJson(profilePath(event.agentId)) ?? emptyProfile(event.agentId);
1454
+ const updated = mergeExecutionEvent(existing, event);
1455
+ writeJsonAtomic(profilePath(event.agentId), updated);
1456
+ }
1457
+ function appendPatternEvent(event) {
1458
+ const agentPatternDir = (0, import_node_path2.join)(patternsDir, event.agentId);
1459
+ ensureDir(agentPatternDir);
1460
+ const seq = String(writeCounter++).padStart(4, "0");
1461
+ const fileName = `${event.timestamp}-${seq}.json`;
1462
+ (0, import_node_fs2.writeFileSync)((0, import_node_path2.join)(agentPatternDir, fileName), JSON.stringify(event, null, 2), "utf-8");
1463
+ ensureDir(profilesDir);
1464
+ const existing = readJson(profilePath(event.agentId)) ?? emptyProfile(event.agentId);
1465
+ const updated = mergePatternEvent(existing, event);
1466
+ writeJsonAtomic(profilePath(event.agentId), updated);
1467
+ }
1468
+ return {
1469
+ baseDir,
1470
+ append(event) {
1471
+ if (event.eventType === "pattern.discovered" || event.eventType === "pattern.updated") {
1472
+ appendPatternEvent(event);
1473
+ } else {
1474
+ appendExecutionEvent(event);
1475
+ }
1476
+ },
1477
+ getRecentEvents(agentId, options) {
1478
+ const limit = options?.limit ?? 50;
1479
+ const since = options?.since ?? 0;
1480
+ const agentDir = (0, import_node_path2.join)(eventsDir, agentId);
1481
+ if (!(0, import_node_fs2.existsSync)(agentDir)) return [];
1482
+ const events = [];
1483
+ const dateDirs = (0, import_node_fs2.readdirSync)(agentDir).sort().reverse();
1484
+ for (const dateDir of dateDirs) {
1485
+ const fullDateDir = (0, import_node_path2.join)(agentDir, dateDir);
1486
+ let files;
1487
+ try {
1488
+ files = (0, import_node_fs2.readdirSync)(fullDateDir).filter((f) => f.endsWith(".json"));
1489
+ } catch {
1490
+ continue;
1491
+ }
1492
+ for (const file of files) {
1493
+ const event = readJson((0, import_node_path2.join)(fullDateDir, file));
1494
+ if (event && event.timestamp > since) {
1495
+ events.push(event);
1496
+ }
1497
+ }
1498
+ if (events.length >= limit * 2) break;
1499
+ }
1500
+ events.sort((a, b) => b.timestamp - a.timestamp);
1501
+ return events.slice(0, limit);
1502
+ },
1503
+ getAgentProfile(agentId) {
1504
+ return readJson(profilePath(agentId));
1505
+ },
1506
+ getPatternHistory(agentId, options) {
1507
+ const limit = options?.limit ?? 20;
1508
+ const agentPatternDir = (0, import_node_path2.join)(patternsDir, agentId);
1509
+ if (!(0, import_node_fs2.existsSync)(agentPatternDir)) return [];
1510
+ const files = (0, import_node_fs2.readdirSync)(agentPatternDir).filter((f) => f.endsWith(".json")).sort().reverse();
1511
+ const events = [];
1512
+ for (const file of files.slice(0, limit)) {
1513
+ const event = readJson((0, import_node_path2.join)(agentPatternDir, file));
1514
+ if (event) events.push(event);
1515
+ }
1516
+ return events;
1517
+ },
1518
+ compact(options) {
1519
+ let removed = 0;
1520
+ if ((0, import_node_fs2.existsSync)(eventsDir)) {
1521
+ for (const agentId of (0, import_node_fs2.readdirSync)(eventsDir)) {
1522
+ const agentDir = (0, import_node_path2.join)(eventsDir, agentId);
1523
+ let dateDirs;
1524
+ try {
1525
+ dateDirs = (0, import_node_fs2.readdirSync)(agentDir);
1526
+ } catch {
1527
+ continue;
1528
+ }
1529
+ for (const dateDir of dateDirs) {
1530
+ const fullDateDir = (0, import_node_path2.join)(agentDir, dateDir);
1531
+ let files;
1532
+ try {
1533
+ files = (0, import_node_fs2.readdirSync)(fullDateDir).filter((f) => f.endsWith(".json"));
1534
+ } catch {
1535
+ continue;
1536
+ }
1537
+ for (const file of files) {
1538
+ const parts = file.replace(".json", "").split("-");
1539
+ const tsPart = parts[parts.length - 2];
1540
+ const ts = tsPart ? Number.parseInt(tsPart, 10) : 0;
1541
+ if (!Number.isNaN(ts) && ts < options.olderThan) {
1542
+ try {
1543
+ (0, import_node_fs2.rmSync)((0, import_node_path2.join)(fullDateDir, file));
1544
+ removed++;
1545
+ } catch {
1546
+ }
1547
+ }
1548
+ }
1549
+ try {
1550
+ if ((0, import_node_fs2.readdirSync)(fullDateDir).length === 0) {
1551
+ (0, import_node_fs2.rmSync)(fullDateDir, { recursive: true });
1552
+ }
1553
+ } catch {
1554
+ }
1555
+ }
1556
+ }
1557
+ }
1558
+ if ((0, import_node_fs2.existsSync)(patternsDir)) {
1559
+ for (const agentId of (0, import_node_fs2.readdirSync)(patternsDir)) {
1560
+ const agentPatternDir = (0, import_node_path2.join)(patternsDir, agentId);
1561
+ let files;
1562
+ try {
1563
+ files = (0, import_node_fs2.readdirSync)(agentPatternDir).filter((f) => f.endsWith(".json"));
1564
+ } catch {
1565
+ continue;
1566
+ }
1567
+ for (const file of files) {
1568
+ const ts = Number.parseInt(file.split("-")[0], 10);
1569
+ if (!Number.isNaN(ts) && ts < options.olderThan) {
1570
+ try {
1571
+ (0, import_node_fs2.rmSync)((0, import_node_path2.join)(agentPatternDir, file));
1572
+ removed++;
1573
+ } catch {
1574
+ }
1575
+ }
1576
+ }
1577
+ }
1578
+ }
1579
+ if ((0, import_node_fs2.existsSync)(insightsDir)) {
1580
+ for (const agentId of (0, import_node_fs2.readdirSync)(insightsDir)) {
1581
+ const agentInsightDir = (0, import_node_path2.join)(insightsDir, agentId);
1582
+ let files;
1583
+ try {
1584
+ files = (0, import_node_fs2.readdirSync)(agentInsightDir).filter((f) => f.endsWith(".json"));
1585
+ } catch {
1586
+ continue;
1587
+ }
1588
+ for (const file of files) {
1589
+ const parts = file.replace(".json", "").split("-");
1590
+ const tsPart = parts[parts.length - 2];
1591
+ const ts = tsPart ? Number.parseInt(tsPart, 10) : 0;
1592
+ if (!Number.isNaN(ts) && ts < options.olderThan) {
1593
+ try {
1594
+ (0, import_node_fs2.rmSync)((0, import_node_path2.join)(agentInsightDir, file));
1595
+ removed++;
1596
+ } catch {
1597
+ }
1598
+ }
1599
+ }
1600
+ }
1601
+ }
1602
+ return { removed };
1603
+ },
1604
+ appendInsight(event) {
1605
+ const agentInsightDir = (0, import_node_path2.join)(insightsDir, event.agentId);
1606
+ ensureDir(agentInsightDir);
1607
+ const seq = String(writeCounter++).padStart(4, "0");
1608
+ const fileName = `${event.insightType}-${event.timestamp}-${seq}.json`;
1609
+ (0, import_node_fs2.writeFileSync)((0, import_node_path2.join)(agentInsightDir, fileName), JSON.stringify(event, null, 2), "utf-8");
1610
+ },
1611
+ getRecentInsights(agentId, options) {
1612
+ const limit = options?.limit ?? 10;
1613
+ const typeFilter = options?.type;
1614
+ const agentInsightDir = (0, import_node_path2.join)(insightsDir, agentId);
1615
+ if (!(0, import_node_fs2.existsSync)(agentInsightDir)) return [];
1616
+ const files = (0, import_node_fs2.readdirSync)(agentInsightDir).filter((f) => f.endsWith(".json"));
1617
+ const events = [];
1618
+ for (const file of files) {
1619
+ const event = readJson((0, import_node_path2.join)(agentInsightDir, file));
1620
+ if (event) {
1621
+ if (typeFilter && event.insightType !== typeFilter) continue;
1622
+ events.push(event);
1623
+ }
1624
+ }
1625
+ events.sort((a, b) => b.timestamp - a.timestamp);
1626
+ return events.slice(0, limit);
1627
+ },
1628
+ // EventWriter interface
1629
+ async write(_graph) {
1630
+ },
1631
+ async writeEvent(event) {
1632
+ this.append(event);
1633
+ }
1634
+ };
1635
+ }
1636
+
1637
+ // src/policy-source.ts
1638
+ var import_node_fs3 = require("fs");
1639
+ var import_node_path3 = require("path");
1640
+ function createPolicySource(store) {
1641
+ return {
1642
+ recentFailureRate(agentId) {
1643
+ const profile = store.getAgentProfile(agentId);
1644
+ return profile?.failureRate ?? 0;
1645
+ },
1646
+ isKnownBottleneck(nodeName) {
1647
+ const profilesDir = (0, import_node_path3.join)(store.baseDir, "profiles");
1648
+ let agentIds;
1649
+ try {
1650
+ agentIds = (0, import_node_fs3.readdirSync)(profilesDir).filter((f) => f.endsWith(".json")).map((f) => f.replace(".json", ""));
1651
+ } catch {
1652
+ return false;
1653
+ }
1654
+ for (const agentId of agentIds) {
1655
+ const profile = store.getAgentProfile(agentId);
1656
+ if (profile && profile.knownBottlenecks.includes(nodeName)) {
1657
+ return true;
1658
+ }
1659
+ }
1660
+ return false;
1661
+ },
1662
+ lastConformanceScore(agentId) {
1663
+ const profile = store.getAgentProfile(agentId);
1664
+ return profile?.lastConformanceScore ?? null;
1665
+ },
1666
+ getAgentProfile(agentId) {
1667
+ return store.getAgentProfile(agentId);
1668
+ }
1669
+ };
1670
+ }
1671
+
1672
+ // src/soma-event-writer.ts
1673
+ var import_node_fs4 = require("fs");
1674
+ var import_node_path4 = require("path");
1675
+ function compactIso(epochMs) {
1676
+ return new Date(epochMs).toISOString().slice(0, 19).replace(/:/g, "");
1677
+ }
1678
+ function isoDate(epochMs) {
1679
+ return new Date(epochMs).toISOString().slice(0, 10);
1680
+ }
1681
+ function renderFrontmatter(fields) {
1682
+ const lines = ["---"];
1683
+ for (const [key, value] of Object.entries(fields)) {
1684
+ if (value === void 0) continue;
1685
+ if (Array.isArray(value)) {
1686
+ lines.push(`${key}: [${value.map((v) => `'${v}'`).join(", ")}]`);
1687
+ } else if (typeof value === "string") {
1688
+ lines.push(`${key}: '${value}'`);
1689
+ } else {
1690
+ lines.push(`${key}: ${String(value)}`);
1691
+ }
1692
+ }
1693
+ lines.push("---");
1694
+ return lines.join("\n");
1695
+ }
1696
+ function formatDuration(ms) {
1697
+ if (ms < 1e3) return `${ms.toFixed(0)}ms`;
1698
+ if (ms < 6e4) return `${(ms / 1e3).toFixed(1)}s`;
1699
+ return `${(ms / 6e4).toFixed(1)}min`;
1700
+ }
1701
+ function executionEventToMarkdown(event) {
1702
+ const isCompleted = event.eventType === "execution.completed";
1703
+ const subtype = isCompleted ? "completed" : "failed";
1704
+ const tags = [
1705
+ "agentflow/execution",
1706
+ `agent/${event.agentId}`,
1707
+ `status/${subtype}`
1708
+ ];
1709
+ if (event.processContext?.isAnomaly) {
1710
+ tags.push("agentflow/anomaly");
1711
+ }
1712
+ const frontmatter = {
1713
+ type: "execution",
1714
+ subtype,
1715
+ name: `Execution: ${event.agentId} \u2014 ${subtype}`,
1716
+ source: "agentflow",
1717
+ created: isoDate(event.timestamp),
1718
+ alfred_tags: tags,
1719
+ agentflow_graph_id: event.graphId,
1720
+ duration_ms: event.duration,
1721
+ node_count: event.nodeCount
1722
+ };
1723
+ if (event.processContext) {
1724
+ frontmatter.conformance_score = event.processContext.conformanceScore;
1725
+ frontmatter.is_anomaly = event.processContext.isAnomaly;
1726
+ }
1727
+ const body = [];
1728
+ body.push(`# Execution: ${event.agentId} \u2014 ${subtype}
1729
+ `);
1730
+ body.push(`**Duration:** ${formatDuration(event.duration)} `);
1731
+ body.push(`**Nodes:** ${event.nodeCount} `);
1732
+ body.push(`**Status:** ${event.status}
1733
+ `);
1734
+ if (event.pathSignature) {
1735
+ body.push(`## Path
1736
+ `);
1737
+ body.push(`\`${event.pathSignature}\`
1738
+ `);
1739
+ }
1740
+ if (!isCompleted && event.failurePoint) {
1741
+ const fp = event.failurePoint;
1742
+ body.push(`## Failure Point
1743
+ `);
1744
+ body.push(`**Node:** ${fp.nodeType}:${fp.nodeName} (\`${fp.nodeId}\`) `);
1745
+ if (fp.error) {
1746
+ body.push(`**Error:** ${fp.error}
1747
+ `);
1748
+ }
1749
+ }
1750
+ if (event.processContext) {
1751
+ body.push(`## Process Context
1752
+ `);
1753
+ body.push(`**Conformance:** ${(event.processContext.conformanceScore * 100).toFixed(0)}% `);
1754
+ body.push(`**Anomaly:** ${event.processContext.isAnomaly ? "yes" : "no"}
1755
+ `);
1756
+ }
1757
+ body.push(`## Related
1758
+ `);
1759
+ body.push(`- [[agent/${event.agentId}]]`);
1760
+ return `${renderFrontmatter(frontmatter)}
1761
+
1762
+ ${body.join("\n")}`;
1763
+ }
1764
+ function patternEventToMarkdown(event) {
1765
+ const { pattern } = event;
1766
+ const tags = [
1767
+ "agentflow/pattern",
1768
+ `agent/${event.agentId}`
1769
+ ];
1770
+ const frontmatter = {
1771
+ type: "synthesis",
1772
+ subtype: "pattern-discovery",
1773
+ name: `Pattern: ${event.agentId} \u2014 ${pattern.variantCount} variants across ${pattern.totalGraphs} runs`,
1774
+ source: "agentflow",
1775
+ created: isoDate(event.timestamp),
1776
+ alfred_tags: tags,
1777
+ variant_count: pattern.variantCount,
1778
+ total_graphs: pattern.totalGraphs
1779
+ };
1780
+ const body = [];
1781
+ body.push(`# Pattern: ${event.agentId}
1782
+ `);
1783
+ body.push(`**Variants:** ${pattern.variantCount} `);
1784
+ body.push(`**Total Runs:** ${pattern.totalGraphs}
1785
+ `);
1786
+ if (pattern.topVariants.length > 0) {
1787
+ body.push(`## Top Variants
1788
+ `);
1789
+ body.push(`| Path | Count | % |`);
1790
+ body.push(`|------|-------|---|`);
1791
+ for (const v of pattern.topVariants) {
1792
+ const sig = v.pathSignature.length > 60 ? `${v.pathSignature.slice(0, 57)}...` : v.pathSignature;
1793
+ body.push(`| \`${sig}\` | ${v.count} | ${v.percentage.toFixed(1)}% |`);
1794
+ }
1795
+ body.push("");
1796
+ }
1797
+ if (pattern.topBottlenecks.length > 0) {
1798
+ body.push(`## Top Bottlenecks
1799
+ `);
1800
+ body.push(`| Node | Type | p95 |`);
1801
+ body.push(`|------|------|-----|`);
1802
+ for (const b of pattern.topBottlenecks) {
1803
+ body.push(`| ${b.nodeName} | ${b.nodeType} | ${formatDuration(b.p95)} |`);
1804
+ }
1805
+ body.push("");
1806
+ }
1807
+ body.push(`## Related
1808
+ `);
1809
+ body.push(`- [[agent/${event.agentId}]]`);
1810
+ return `${renderFrontmatter(frontmatter)}
1811
+
1812
+ ${body.join("\n")}`;
1813
+ }
1814
+ function createSomaEventWriter(config) {
1815
+ const { inboxDir } = config;
1816
+ function ensureDir() {
1817
+ (0, import_node_fs4.mkdirSync)(inboxDir, { recursive: true });
1818
+ }
1819
+ function eventFileName(event) {
1820
+ const agentId = event.agentId;
1821
+ const ts = compactIso(event.timestamp);
1822
+ if (event.eventType === "pattern.discovered" || event.eventType === "pattern.updated") {
1823
+ return `synthesis-${agentId}-${ts}.md`;
1824
+ }
1825
+ return `execution-${agentId}-${ts}.md`;
1826
+ }
1827
+ return {
1828
+ async write(_graph) {
1829
+ },
1830
+ async writeEvent(event) {
1831
+ ensureDir();
1832
+ const markdown = event.eventType === "pattern.discovered" || event.eventType === "pattern.updated" ? patternEventToMarkdown(event) : executionEventToMarkdown(event);
1833
+ const fileName = eventFileName(event);
1834
+ (0, import_node_fs4.writeFileSync)((0, import_node_path4.join)(inboxDir, fileName), markdown, "utf-8");
1835
+ }
1836
+ };
1837
+ }
1838
+
1839
+ // src/live.ts
1840
+ var import_node_fs6 = require("fs");
1841
+ var import_node_path6 = require("path");
644
1842
 
645
1843
  // src/loader.ts
646
1844
  function toNodesMap(raw) {
@@ -696,8 +1894,8 @@ function graphToJson(graph) {
696
1894
 
697
1895
  // src/process-audit.ts
698
1896
  var import_node_child_process = require("child_process");
699
- var import_node_fs = require("fs");
700
- var import_node_path = require("path");
1897
+ var import_node_fs5 = require("fs");
1898
+ var import_node_path5 = require("path");
701
1899
  function isPidAlive(pid) {
702
1900
  try {
703
1901
  process.kill(pid, 0);
@@ -708,7 +1906,7 @@ function isPidAlive(pid) {
708
1906
  }
709
1907
  function pidMatchesName(pid, name) {
710
1908
  try {
711
- const cmdline = (0, import_node_fs.readFileSync)(`/proc/${pid}/cmdline`, "utf8");
1909
+ const cmdline = (0, import_node_fs5.readFileSync)(`/proc/${pid}/cmdline`, "utf8");
712
1910
  return cmdline.includes(name);
713
1911
  } catch {
714
1912
  return false;
@@ -716,8 +1914,8 @@ function pidMatchesName(pid, name) {
716
1914
  }
717
1915
  function readPidFile(path) {
718
1916
  try {
719
- const pid = parseInt((0, import_node_fs.readFileSync)(path, "utf8").trim(), 10);
720
- return isNaN(pid) ? null : pid;
1917
+ const pid = parseInt((0, import_node_fs5.readFileSync)(path, "utf8").trim(), 10);
1918
+ return Number.isNaN(pid) ? null : pid;
721
1919
  } catch {
722
1920
  return null;
723
1921
  }
@@ -731,8 +1929,8 @@ function auditPidFile(config) {
731
1929
  pid: null,
732
1930
  alive: false,
733
1931
  matchesProcess: false,
734
- stale: !(0, import_node_fs.existsSync)(config.pidFile),
735
- reason: (0, import_node_fs.existsSync)(config.pidFile) ? "PID file exists but content is invalid" : "No PID file found"
1932
+ stale: !(0, import_node_fs5.existsSync)(config.pidFile),
1933
+ reason: (0, import_node_fs5.existsSync)(config.pidFile) ? "PID file exists but content is invalid" : "No PID file found"
736
1934
  };
737
1935
  }
738
1936
  const alive = isPidAlive(pid);
@@ -761,11 +1959,11 @@ function auditSystemd(config) {
761
1959
  const [k, ...v] = line.split("=");
762
1960
  if (k) props[k.trim()] = v.join("=").trim();
763
1961
  }
764
- const activeState = props["ActiveState"] ?? "unknown";
765
- const subState = props["SubState"] ?? "unknown";
766
- const mainPid = parseInt(props["MainPID"] ?? "0", 10);
767
- const restarts = parseInt(props["NRestarts"] ?? "0", 10);
768
- const result = props["Result"] ?? "unknown";
1962
+ const activeState = props.ActiveState ?? "unknown";
1963
+ const subState = props.SubState ?? "unknown";
1964
+ const mainPid = parseInt(props.MainPID ?? "0", 10);
1965
+ const restarts = parseInt(props.NRestarts ?? "0", 10);
1966
+ const result = props.Result ?? "unknown";
769
1967
  return {
770
1968
  unit,
771
1969
  activeState,
@@ -781,9 +1979,9 @@ function auditSystemd(config) {
781
1979
  }
782
1980
  }
783
1981
  function auditWorkers(config) {
784
- if (!config.workersFile || !(0, import_node_fs.existsSync)(config.workersFile)) return null;
1982
+ if (!config.workersFile || !(0, import_node_fs5.existsSync)(config.workersFile)) return null;
785
1983
  try {
786
- const data = JSON.parse((0, import_node_fs.readFileSync)(config.workersFile, "utf8"));
1984
+ const data = JSON.parse((0, import_node_fs5.readFileSync)(config.workersFile, "utf8"));
787
1985
  const orchPid = data.pid ?? null;
788
1986
  const orchAlive = orchPid ? isPidAlive(orchPid) : false;
789
1987
  const workers = [];
@@ -811,17 +2009,17 @@ function auditWorkers(config) {
811
2009
  }
812
2010
  function readCmdline(pid) {
813
2011
  try {
814
- return (0, import_node_fs.readFileSync)(`/proc/${pid}/cmdline`, "utf8").replace(/\0/g, " ").trim();
2012
+ return (0, import_node_fs5.readFileSync)(`/proc/${pid}/cmdline`, "utf8").replace(/\0/g, " ").trim();
815
2013
  } catch {
816
2014
  return "";
817
2015
  }
818
2016
  }
819
2017
  function getOsProcesses(processName) {
820
2018
  try {
821
- const raw = (0, import_node_child_process.execSync)(
822
- `ps -eo pid,pcpu,pmem,etime,lstart,args --no-headers`,
823
- { encoding: "utf8", timeout: 5e3 }
824
- );
2019
+ const raw = (0, import_node_child_process.execSync)(`ps -eo pid,pcpu,pmem,etime,lstart,args --no-headers`, {
2020
+ encoding: "utf8",
2021
+ timeout: 5e3
2022
+ });
825
2023
  const results = [];
826
2024
  for (const line of raw.split("\n")) {
827
2025
  if (!line.includes(processName)) continue;
@@ -829,7 +2027,7 @@ function getOsProcesses(processName) {
829
2027
  const trimmed = line.trim();
830
2028
  const parts = trimmed.split(/\s+/);
831
2029
  const pid = parseInt(parts[0] ?? "0", 10);
832
- if (isNaN(pid) || pid <= 0) continue;
2030
+ if (Number.isNaN(pid) || pid <= 0) continue;
833
2031
  const cpu = parts[1] ?? "0";
834
2032
  const mem = parts[2] ?? "0";
835
2033
  const elapsed = parts[3] ?? "";
@@ -848,30 +2046,30 @@ function discoverProcessConfig(dirs) {
848
2046
  let workersFile;
849
2047
  let processName = "";
850
2048
  for (const dir of dirs) {
851
- if (!(0, import_node_fs.existsSync)(dir)) continue;
2049
+ if (!(0, import_node_fs5.existsSync)(dir)) continue;
852
2050
  let entries;
853
2051
  try {
854
- entries = (0, import_node_fs.readdirSync)(dir);
2052
+ entries = (0, import_node_fs5.readdirSync)(dir);
855
2053
  } catch {
856
2054
  continue;
857
2055
  }
858
2056
  for (const f of entries) {
859
- const fp = (0, import_node_path.join)(dir, f);
2057
+ const fp = (0, import_node_path5.join)(dir, f);
860
2058
  try {
861
- if (!(0, import_node_fs.statSync)(fp).isFile()) continue;
2059
+ if (!(0, import_node_fs5.statSync)(fp).isFile()) continue;
862
2060
  } catch {
863
2061
  continue;
864
2062
  }
865
2063
  if (f.endsWith(".pid") && !pidFile) {
866
2064
  pidFile = fp;
867
2065
  if (!processName) {
868
- processName = (0, import_node_path.basename)(f, ".pid");
2066
+ processName = (0, import_node_path5.basename)(f, ".pid");
869
2067
  }
870
2068
  }
871
2069
  if ((f === "workers.json" || f.endsWith("-workers.json")) && !workersFile) {
872
2070
  workersFile = fp;
873
2071
  if (!processName && f !== "workers.json") {
874
- processName = (0, import_node_path.basename)(f, "-workers.json");
2072
+ processName = (0, import_node_path5.basename)(f, "-workers.json");
875
2073
  }
876
2074
  }
877
2075
  }
@@ -906,16 +2104,45 @@ function auditProcesses(config) {
906
2104
  }
907
2105
  }
908
2106
  if (systemd?.mainPid) knownPids.add(systemd.mainPid);
2107
+ const childPids = /* @__PURE__ */ new Set();
2108
+ for (const knownPid of knownPids) {
2109
+ try {
2110
+ const childrenRaw = (0, import_node_fs5.readFileSync)(
2111
+ `/proc/${knownPid}/task/${knownPid}/children`,
2112
+ "utf8"
2113
+ ).trim();
2114
+ if (childrenRaw) {
2115
+ for (const c of childrenRaw.split(/\s+/)) {
2116
+ const cp = parseInt(c, 10);
2117
+ if (!Number.isNaN(cp)) childPids.add(cp);
2118
+ }
2119
+ }
2120
+ } catch {
2121
+ }
2122
+ }
2123
+ for (const p of osProcesses) {
2124
+ if (knownPids.has(p.pid)) continue;
2125
+ try {
2126
+ const statusContent = (0, import_node_fs5.readFileSync)(`/proc/${p.pid}/status`, "utf8");
2127
+ const ppidMatch = statusContent.match(/^PPid:\s+(\d+)/m);
2128
+ if (ppidMatch) {
2129
+ const ppid = parseInt(ppidMatch[1] ?? "0", 10);
2130
+ if (knownPids.has(ppid)) childPids.add(p.pid);
2131
+ }
2132
+ } catch {
2133
+ }
2134
+ }
909
2135
  const selfPid = process.pid;
910
2136
  const selfPpid = process.ppid;
911
2137
  const orphans = osProcesses.filter(
912
- (p) => !knownPids.has(p.pid) && p.pid !== selfPid && p.pid !== selfPpid
2138
+ (p) => !knownPids.has(p.pid) && !childPids.has(p.pid) && p.pid !== selfPid && p.pid !== selfPpid
913
2139
  );
914
2140
  const problems = [];
915
2141
  if (pidFile?.stale) problems.push(`Stale PID file: ${pidFile.reason}`);
916
2142
  if (systemd?.crashLooping) problems.push("Systemd unit is crash-looping (auto-restart)");
917
2143
  if (systemd?.failed) problems.push("Systemd unit has failed");
918
- if (systemd && systemd.restarts > 10) problems.push(`High systemd restart count: ${systemd.restarts}`);
2144
+ if (systemd && systemd.restarts > 10)
2145
+ problems.push(`High systemd restart count: ${systemd.restarts}`);
919
2146
  if (pidFile?.pid && systemd?.mainPid && pidFile.pid !== systemd.mainPid) {
920
2147
  problems.push(`PID mismatch: file says ${pidFile.pid}, systemd says ${systemd.mainPid}`);
921
2148
  }
@@ -924,15 +2151,24 @@ function auditProcesses(config) {
924
2151
  if (w.stale) problems.push(`Worker "${w.name}" (pid ${w.pid}) declares running but is dead`);
925
2152
  }
926
2153
  }
927
- if (orphans.length > 0) problems.push(`${orphans.length} orphan process(es) not tracked by PID file or workers registry`);
2154
+ if (orphans.length > 0)
2155
+ problems.push(
2156
+ `${orphans.length} orphan process(es) not tracked by PID file or workers registry`
2157
+ );
928
2158
  return { pidFile, systemd, workers, osProcesses, orphans, problems };
929
2159
  }
930
2160
  function formatAuditReport(result) {
931
2161
  const lines = [];
932
2162
  lines.push("");
933
- lines.push("\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557");
934
- lines.push("\u2551 \u{1F50D} P R O C E S S A U D I T \u2551");
935
- lines.push("\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D");
2163
+ lines.push(
2164
+ "\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"
2165
+ );
2166
+ lines.push(
2167
+ "\u2551 \u{1F50D} P R O C E S S A U D I T \u2551"
2168
+ );
2169
+ lines.push(
2170
+ "\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"
2171
+ );
936
2172
  if (result.pidFile) {
937
2173
  const pf = result.pidFile;
938
2174
  const icon = pf.pid && pf.alive && pf.matchesProcess ? "\u2705" : pf.stale ? "\u26A0\uFE0F " : "\u2139\uFE0F ";
@@ -950,25 +2186,33 @@ function formatAuditReport(result) {
950
2186
  }
951
2187
  if (result.workers) {
952
2188
  const w = result.workers;
953
- lines.push(`
954
- Workers (orchestrator pid ${w.orchestratorPid ?? "unknown"} ${w.orchestratorAlive ? "\u2705" : "\u274C"})`);
2189
+ lines.push(
2190
+ `
2191
+ Workers (orchestrator pid ${w.orchestratorPid ?? "unknown"} ${w.orchestratorAlive ? "\u2705" : "\u274C"})`
2192
+ );
955
2193
  for (const worker of w.workers) {
956
2194
  const icon = worker.declaredStatus === "running" && worker.alive ? "\u{1F7E2}" : worker.stale ? "\u{1F534} STALE" : "\u26AA";
957
- lines.push(` ${icon} ${worker.name.padEnd(14)} pid=${String(worker.pid ?? "-").padEnd(8)} status=${worker.declaredStatus}`);
2195
+ lines.push(
2196
+ ` ${icon} ${worker.name.padEnd(14)} pid=${String(worker.pid ?? "-").padEnd(8)} status=${worker.declaredStatus}`
2197
+ );
958
2198
  }
959
2199
  }
960
2200
  if (result.osProcesses.length > 0) {
961
2201
  lines.push(`
962
2202
  OS Processes (${result.osProcesses.length} total)`);
963
2203
  for (const p of result.osProcesses) {
964
- lines.push(` PID ${String(p.pid).padEnd(8)} CPU=${p.cpu.padEnd(6)} MEM=${p.mem.padEnd(6)} Up=${p.elapsed.padEnd(10)} ${p.command.substring(0, 50)}`);
2204
+ lines.push(
2205
+ ` PID ${String(p.pid).padEnd(8)} CPU=${p.cpu.padEnd(6)} MEM=${p.mem.padEnd(6)} Up=${p.elapsed.padEnd(10)} ${p.command.substring(0, 50)}`
2206
+ );
965
2207
  }
966
2208
  }
967
2209
  if (result.orphans.length > 0) {
968
2210
  lines.push(`
969
2211
  \u26A0\uFE0F ${result.orphans.length} ORPHAN PROCESS(ES):`);
970
2212
  for (const p of result.orphans) {
971
- lines.push(` PID ${String(p.pid).padEnd(8)} CPU=${p.cpu.padEnd(6)} MEM=${p.mem.padEnd(6)} Up=${p.elapsed}`);
2213
+ lines.push(
2214
+ ` PID ${String(p.pid).padEnd(8)} CPU=${p.cpu.padEnd(6)} MEM=${p.mem.padEnd(6)} Up=${p.elapsed}`
2215
+ );
972
2216
  lines.push(` Started: ${p.started}`);
973
2217
  lines.push(` Command: ${p.cmdline || p.command}`);
974
2218
  }
@@ -1013,19 +2257,19 @@ function parseArgs(argv) {
1013
2257
  } else if (arg === "--refresh" || arg === "-r") {
1014
2258
  i++;
1015
2259
  const v = parseInt(args[i] ?? "", 10);
1016
- if (!isNaN(v) && v > 0) config.refreshMs = v * 1e3;
2260
+ if (!Number.isNaN(v) && v > 0) config.refreshMs = v * 1e3;
1017
2261
  i++;
1018
2262
  } else if (arg === "--recursive" || arg === "-R") {
1019
2263
  config.recursive = true;
1020
2264
  i++;
1021
2265
  } else if (!arg.startsWith("-")) {
1022
- config.dirs.push((0, import_node_path2.resolve)(arg));
2266
+ config.dirs.push((0, import_node_path6.resolve)(arg));
1023
2267
  i++;
1024
2268
  } else {
1025
2269
  i++;
1026
2270
  }
1027
2271
  }
1028
- if (config.dirs.length === 0) config.dirs.push((0, import_node_path2.resolve)("."));
2272
+ if (config.dirs.length === 0) config.dirs.push((0, import_node_path6.resolve)("."));
1029
2273
  return config;
1030
2274
  }
1031
2275
  function printUsage() {
@@ -1061,7 +2305,7 @@ function scanFiles(dirs, recursive) {
1061
2305
  const seen = /* @__PURE__ */ new Set();
1062
2306
  function scanDir(d, topLevel) {
1063
2307
  try {
1064
- const dirStat = (0, import_node_fs2.statSync)(d);
2308
+ const dirStat = (0, import_node_fs6.statSync)(d);
1065
2309
  const dirMtime = dirStat.mtime.getTime();
1066
2310
  const cachedMtime = dirMtimeCache.get(d);
1067
2311
  if (cachedMtime === dirMtime) {
@@ -1077,13 +2321,13 @@ function scanFiles(dirs, recursive) {
1077
2321
  }
1078
2322
  }
1079
2323
  const dirResults = [];
1080
- for (const f of (0, import_node_fs2.readdirSync)(d)) {
2324
+ for (const f of (0, import_node_fs6.readdirSync)(d)) {
1081
2325
  if (f.startsWith(".")) continue;
1082
- const fp = (0, import_node_path2.join)(d, f);
2326
+ const fp = (0, import_node_path6.join)(d, f);
1083
2327
  if (seen.has(fp)) continue;
1084
2328
  let stat;
1085
2329
  try {
1086
- stat = (0, import_node_fs2.statSync)(fp);
2330
+ stat = (0, import_node_fs6.statSync)(fp);
1087
2331
  } catch {
1088
2332
  continue;
1089
2333
  }
@@ -1094,12 +2338,22 @@ function scanFiles(dirs, recursive) {
1094
2338
  if (!stat.isFile()) continue;
1095
2339
  if (f.endsWith(".json")) {
1096
2340
  seen.add(fp);
1097
- const entry = { filename: f, path: fp, mtime: stat.mtime.getTime(), ext: ".json" };
2341
+ const entry = {
2342
+ filename: f,
2343
+ path: fp,
2344
+ mtime: stat.mtime.getTime(),
2345
+ ext: ".json"
2346
+ };
1098
2347
  results.push(entry);
1099
2348
  dirResults.push(entry);
1100
2349
  } else if (f.endsWith(".jsonl")) {
1101
2350
  seen.add(fp);
1102
- const entry = { filename: f, path: fp, mtime: stat.mtime.getTime(), ext: ".jsonl" };
2351
+ const entry = {
2352
+ filename: f,
2353
+ path: fp,
2354
+ mtime: stat.mtime.getTime(),
2355
+ ext: ".jsonl"
2356
+ };
1103
2357
  results.push(entry);
1104
2358
  dirResults.push(entry);
1105
2359
  }
@@ -1115,13 +2369,13 @@ function scanFiles(dirs, recursive) {
1115
2369
  }
1116
2370
  function safeReadJson(fp) {
1117
2371
  try {
1118
- return JSON.parse((0, import_node_fs2.readFileSync)(fp, "utf8"));
2372
+ return JSON.parse((0, import_node_fs6.readFileSync)(fp, "utf8"));
1119
2373
  } catch {
1120
2374
  return null;
1121
2375
  }
1122
2376
  }
1123
2377
  function nameFromFile(filename) {
1124
- return (0, import_node_path2.basename)(filename).replace(/\.(json|jsonl)$/, "").replace(/-state$/, "");
2378
+ return (0, import_node_path6.basename)(filename).replace(/\.(json|jsonl)$/, "").replace(/-state$/, "");
1125
2379
  }
1126
2380
  function normalizeStatus(val) {
1127
2381
  if (typeof val !== "string") return "unknown";
@@ -1161,7 +2415,7 @@ function findTimestamp(obj) {
1161
2415
  if (typeof val === "number") return val > 1e12 ? val : val * 1e3;
1162
2416
  if (typeof val === "string") {
1163
2417
  const d = Date.parse(val);
1164
- if (!isNaN(d)) return d;
2418
+ if (!Number.isNaN(d)) return d;
1165
2419
  }
1166
2420
  }
1167
2421
  return 0;
@@ -1193,7 +2447,7 @@ function extractDetail(obj) {
1193
2447
  }
1194
2448
  return parts.join(" | ") || "";
1195
2449
  }
1196
- function tryLoadTrace(fp, raw) {
2450
+ function tryLoadTrace(_fp, raw) {
1197
2451
  if (typeof raw !== "object" || raw === null) return null;
1198
2452
  const obj = raw;
1199
2453
  if (!("nodes" in obj)) return null;
@@ -1299,7 +2553,7 @@ function processJsonFile(file) {
1299
2553
  }
1300
2554
  function processJsonlFile(file) {
1301
2555
  try {
1302
- const content = (0, import_node_fs2.readFileSync)(file.path, "utf8").trim();
2556
+ const content = (0, import_node_fs6.readFileSync)(file.path, "utf8").trim();
1303
2557
  if (!content) return [];
1304
2558
  const lines = content.split("\n");
1305
2559
  const lineCount = lines.length;
@@ -1444,7 +2698,8 @@ function writeLine(lines, text) {
1444
2698
  }
1445
2699
  function flushLines(lines) {
1446
2700
  process.stdout.write("\x1B[H");
1447
- process.stdout.write(lines.join("\n") + "\n");
2701
+ process.stdout.write(`${lines.join("\n")}
2702
+ `);
1448
2703
  process.stdout.write("\x1B[J");
1449
2704
  }
1450
2705
  var prevFileCount = 0;
@@ -1538,7 +2793,7 @@ function render(config) {
1538
2793
  const status = fail > 0 ? "error" : running > 0 ? "running" : ok > 0 ? "ok" : "unknown";
1539
2794
  groups.push({
1540
2795
  name: groupName,
1541
- source: records[0].source,
2796
+ source: records[0]?.source ?? "trace",
1542
2797
  status,
1543
2798
  lastTs,
1544
2799
  detail: `${records.length} agents`,
@@ -1584,8 +2839,10 @@ function render(config) {
1584
2839
  cachedAuditResult = auditResult;
1585
2840
  lastAuditTime = now;
1586
2841
  } catch (err) {
1587
- process.stderr.write(`[agentflow] process audit error: ${err instanceof Error ? err.message : err}
1588
- `);
2842
+ process.stderr.write(
2843
+ `[agentflow] process audit error: ${err instanceof Error ? err.message : err}
2844
+ `
2845
+ );
1589
2846
  }
1590
2847
  }
1591
2848
  } else {
@@ -1642,7 +2899,7 @@ function render(config) {
1642
2899
  return new Date(ts).toLocaleTimeString();
1643
2900
  }
1644
2901
  function truncate(s, max) {
1645
- return s.length > max ? s.slice(0, max - 1) + "\u2026" : s;
2902
+ return s.length > max ? `${s.slice(0, max - 1)}\u2026` : s;
1646
2903
  }
1647
2904
  const termWidth = process.stdout.columns || 120;
1648
2905
  const detailWidth = Math.max(20, termWidth - 60);
@@ -1683,7 +2940,8 @@ function render(config) {
1683
2940
  if (ar.systemd) {
1684
2941
  const si = ar.systemd.activeState === "active" ? `${C.green}\u25CF${C.reset}` : ar.systemd.crashLooping ? `${C.yellow}\u25CF${C.reset}` : ar.systemd.failed ? `${C.red}\u25CF${C.reset}` : `${C.dim}\u25CB${C.reset}`;
1685
2942
  sysdLabel = ` ${C.bold}Systemd${C.reset} ${si} ${ar.systemd.activeState}`;
1686
- if (ar.systemd.restarts > 0) sysdLabel += ` ${C.dim}(${ar.systemd.restarts} restarts)${C.reset}`;
2943
+ if (ar.systemd.restarts > 0)
2944
+ sysdLabel += ` ${C.dim}(${ar.systemd.restarts} restarts)${C.reset}`;
1687
2945
  }
1688
2946
  let pidLabel = "";
1689
2947
  if (ar.pidFile?.pid) {
@@ -1692,7 +2950,10 @@ function render(config) {
1692
2950
  }
1693
2951
  writeLine(L, "");
1694
2952
  writeLine(L, ` ${C.bold}${C.under}Process Health${C.reset}`);
1695
- writeLine(L, ` ${healthIcon} ${healthLabel}${pidLabel}${sysdLabel} ${C.bold}Procs${C.reset} ${C.dim}${ar.osProcesses.length}${C.reset} ${ar.orphans.length > 0 ? `${C.red}Orphans ${ar.orphans.length}${C.reset}` : `${C.dim}Orphans 0${C.reset}`}`);
2953
+ writeLine(
2954
+ L,
2955
+ ` ${healthIcon} ${healthLabel}${pidLabel}${sysdLabel} ${C.bold}Procs${C.reset} ${C.dim}${ar.osProcesses.length}${C.reset} ${ar.orphans.length > 0 ? `${C.red}Orphans ${ar.orphans.length}${C.reset}` : `${C.dim}Orphans 0${C.reset}`}`
2956
+ );
1696
2957
  if (workerParts.length > 0) {
1697
2958
  writeLine(L, ` ${C.dim}Workers${C.reset} ${workerParts.join(" ")}`);
1698
2959
  }
@@ -1704,7 +2965,10 @@ function render(config) {
1704
2965
  if (ar.orphans.length > 0) {
1705
2966
  for (const o of ar.orphans.slice(0, 5)) {
1706
2967
  const cmd = (o.cmdline || o.command).substring(0, detailWidth);
1707
- writeLine(L, ` ${C.red}?${C.reset} ${C.dim}pid=${o.pid} cpu=${o.cpu} mem=${o.mem} up=${o.elapsed}${C.reset} ${C.dim}${cmd}${C.reset}`);
2968
+ writeLine(
2969
+ L,
2970
+ ` ${C.red}?${C.reset} ${C.dim}pid=${o.pid} cpu=${o.cpu} mem=${o.mem} up=${o.elapsed}${C.reset} ${C.dim}${cmd}${C.reset}`
2971
+ );
1708
2972
  }
1709
2973
  if (ar.orphans.length > 5) {
1710
2974
  writeLine(L, ` ${C.dim}... +${ar.orphans.length - 5} more orphans${C.reset}`);
@@ -1778,7 +3042,7 @@ function render(config) {
1778
3042
  for (let i = 0; i < Math.min(tree.length, 6); i++) {
1779
3043
  const tg = tree[i];
1780
3044
  const depth = getDistDepth(dt, tg.spanId);
1781
- const indent = " " + "\u2502 ".repeat(Math.max(0, depth - 1));
3045
+ const indent = ` ${"\u2502 ".repeat(Math.max(0, depth - 1))}`;
1782
3046
  const isLast = i === tree.length - 1 || getDistDepth(dt, tree[i + 1]?.spanId) <= depth;
1783
3047
  const conn = depth === 0 ? " " : isLast ? "\u2514\u2500 " : "\u251C\u2500 ";
1784
3048
  const gs = tg.status === "completed" ? `${C.green}\u2713${C.reset}` : tg.status === "failed" ? `${C.red}\u2717${C.reset}` : `${C.yellow}\u23F3${C.reset}`;
@@ -1799,7 +3063,7 @@ function render(config) {
1799
3063
  const t = new Date(r.lastActive).toLocaleTimeString();
1800
3064
  const agent = truncate(r.id, 26).padEnd(26);
1801
3065
  const age = Math.floor((Date.now() - r.lastActive) / 1e3);
1802
- const ageStr = age < 60 ? age + "s ago" : age < 3600 ? Math.floor(age / 60) + "m ago" : Math.floor(age / 3600) + "h ago";
3066
+ const ageStr = age < 60 ? `${age}s ago` : age < 3600 ? `${Math.floor(age / 60)}m ago` : `${Math.floor(age / 3600)}h ago`;
1803
3067
  const det = truncate(r.detail, detailWidth);
1804
3068
  writeLine(
1805
3069
  L,
@@ -1829,13 +3093,13 @@ function getDistDepth(dt, spanId, visited) {
1829
3093
  }
1830
3094
  function startLive(argv) {
1831
3095
  const config = parseArgs(argv);
1832
- const valid = config.dirs.filter((d) => (0, import_node_fs2.existsSync)(d));
3096
+ const valid = config.dirs.filter((d) => (0, import_node_fs6.existsSync)(d));
1833
3097
  if (valid.length === 0) {
1834
3098
  console.error(`No valid directories found: ${config.dirs.join(", ")}`);
1835
3099
  console.error("Specify directories containing JSON/JSONL files: agentflow live <dir> [dir...]");
1836
3100
  process.exit(1);
1837
3101
  }
1838
- const invalid = config.dirs.filter((d) => !(0, import_node_fs2.existsSync)(d));
3102
+ const invalid = config.dirs.filter((d) => !(0, import_node_fs6.existsSync)(d));
1839
3103
  if (invalid.length > 0) {
1840
3104
  console.warn(`Skipping non-existent: ${invalid.join(", ")}`);
1841
3105
  }
@@ -1847,7 +3111,7 @@ function startLive(argv) {
1847
3111
  let debounce = null;
1848
3112
  for (const dir of config.dirs) {
1849
3113
  try {
1850
- (0, import_node_fs2.watch)(dir, { recursive: config.recursive }, () => {
3114
+ (0, import_node_fs6.watch)(dir, { recursive: config.recursive }, () => {
1851
3115
  if (debounce) clearTimeout(debounce);
1852
3116
  debounce = setTimeout(() => render(config), 500);
1853
3117
  });
@@ -1864,7 +3128,7 @@ function startLive(argv) {
1864
3128
  };
1865
3129
  process.on("SIGINT", () => {
1866
3130
  cleanup();
1867
- console.log(C.dim + "Monitor stopped." + C.reset);
3131
+ console.log(`${C.dim}Monitor stopped.${C.reset}`);
1868
3132
  process.exit(0);
1869
3133
  });
1870
3134
  process.on("SIGTERM", () => {
@@ -1875,20 +3139,20 @@ function startLive(argv) {
1875
3139
 
1876
3140
  // src/runner.ts
1877
3141
  var import_node_child_process2 = require("child_process");
1878
- var import_node_fs3 = require("fs");
1879
- var import_node_path3 = require("path");
3142
+ var import_node_fs7 = require("fs");
3143
+ var import_node_path7 = require("path");
1880
3144
  function globToRegex(pattern) {
1881
3145
  const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
1882
3146
  return new RegExp(`^${escaped}$`);
1883
3147
  }
1884
3148
  function snapshotDir(dir, patterns) {
1885
3149
  const result = /* @__PURE__ */ new Map();
1886
- if (!(0, import_node_fs3.existsSync)(dir)) return result;
1887
- for (const entry of (0, import_node_fs3.readdirSync)(dir)) {
3150
+ if (!(0, import_node_fs7.existsSync)(dir)) return result;
3151
+ for (const entry of (0, import_node_fs7.readdirSync)(dir)) {
1888
3152
  if (!patterns.some((re) => re.test(entry))) continue;
1889
- const full = (0, import_node_path3.join)(dir, entry);
3153
+ const full = (0, import_node_path7.join)(dir, entry);
1890
3154
  try {
1891
- const stat = (0, import_node_fs3.statSync)(full);
3155
+ const stat = (0, import_node_fs7.statSync)(full);
1892
3156
  if (stat.isFile()) {
1893
3157
  result.set(full, stat.mtimeMs);
1894
3158
  }
@@ -1898,11 +3162,11 @@ function snapshotDir(dir, patterns) {
1898
3162
  return result;
1899
3163
  }
1900
3164
  function agentIdFromFilename(filePath) {
1901
- const base = (0, import_node_path3.basename)(filePath, ".json");
3165
+ const base = (0, import_node_path7.basename)(filePath, ".json");
1902
3166
  const cleaned = base.replace(/-state$/, "");
1903
3167
  return `alfred-${cleaned}`;
1904
3168
  }
1905
- function deriveAgentId(command) {
3169
+ function deriveAgentId(_command) {
1906
3170
  return "orchestrator";
1907
3171
  }
1908
3172
  function fileTimestamp() {
@@ -1920,7 +3184,7 @@ async function runTraced(config) {
1920
3184
  if (command.length === 0) {
1921
3185
  throw new Error("runTraced: command must not be empty");
1922
3186
  }
1923
- const resolvedTracesDir = (0, import_node_path3.resolve)(tracesDir);
3187
+ const resolvedTracesDir = (0, import_node_path7.resolve)(tracesDir);
1924
3188
  const patterns = watchPatterns.map(globToRegex);
1925
3189
  const orchestrator = createGraphBuilder({ agentId, trigger });
1926
3190
  const { traceId, spanId } = orchestrator.traceContext;
@@ -2003,23 +3267,27 @@ async function runTraced(config) {
2003
3267
  childBuilder.endNode(childRootId);
2004
3268
  allGraphs.push(childBuilder.build());
2005
3269
  }
2006
- if (!(0, import_node_fs3.existsSync)(resolvedTracesDir)) {
2007
- (0, import_node_fs3.mkdirSync)(resolvedTracesDir, { recursive: true });
3270
+ if (!(0, import_node_fs7.existsSync)(resolvedTracesDir)) {
3271
+ (0, import_node_fs7.mkdirSync)(resolvedTracesDir, { recursive: true });
2008
3272
  }
2009
3273
  const ts = fileTimestamp();
2010
3274
  const tracePaths = [];
2011
3275
  for (const graph of allGraphs) {
2012
3276
  const filename = `${graph.agentId}-${ts}.json`;
2013
- const outPath = (0, import_node_path3.join)(resolvedTracesDir, filename);
2014
- const resolvedOut = (0, import_node_path3.resolve)(outPath);
2015
- if (!resolvedOut.startsWith(resolvedTracesDir + "/") && resolvedOut !== resolvedTracesDir) {
2016
- throw new Error(`Path traversal detected: agentId "${graph.agentId}" escapes traces directory`);
3277
+ const outPath = (0, import_node_path7.join)(resolvedTracesDir, filename);
3278
+ const resolvedOut = (0, import_node_path7.resolve)(outPath);
3279
+ if (!resolvedOut.startsWith(`${resolvedTracesDir}/`) && resolvedOut !== resolvedTracesDir) {
3280
+ throw new Error(
3281
+ `Path traversal detected: agentId "${graph.agentId}" escapes traces directory`
3282
+ );
2017
3283
  }
2018
- (0, import_node_fs3.writeFileSync)(outPath, JSON.stringify(graphToJson(graph), null, 2), "utf-8");
3284
+ (0, import_node_fs7.writeFileSync)(outPath, JSON.stringify(graphToJson(graph), null, 2), "utf-8");
2019
3285
  tracePaths.push(outPath);
2020
3286
  }
2021
3287
  if (tracePaths.length > 0) {
2022
- console.log(`\u{1F50D} Run "agentflow trace show ${orchestratorGraph.id} --traces-dir ${resolvedTracesDir}" to inspect`);
3288
+ console.log(
3289
+ `\u{1F50D} Run "agentflow trace show ${orchestratorGraph.id} --traces-dir ${resolvedTracesDir}" to inspect`
3290
+ );
2023
3291
  }
2024
3292
  return {
2025
3293
  exitCode,
@@ -2033,7 +3301,7 @@ async function runTraced(config) {
2033
3301
 
2034
3302
  // src/trace-store.ts
2035
3303
  var import_promises = require("fs/promises");
2036
- var import_path = require("path");
3304
+ var import_node_path8 = require("path");
2037
3305
  function createTraceStore(dir) {
2038
3306
  async function ensureDir() {
2039
3307
  await (0, import_promises.mkdir)(dir, { recursive: true });
@@ -2050,7 +3318,7 @@ function createTraceStore(dir) {
2050
3318
  for (const file of files) {
2051
3319
  if (!file.endsWith(".json")) continue;
2052
3320
  try {
2053
- const content = await (0, import_promises.readFile)((0, import_path.join)(dir, file), "utf-8");
3321
+ const content = await (0, import_promises.readFile)((0, import_node_path8.join)(dir, file), "utf-8");
2054
3322
  const graph = loadGraph(content);
2055
3323
  graphs.push(graph);
2056
3324
  } catch {
@@ -2062,10 +3330,10 @@ function createTraceStore(dir) {
2062
3330
  async save(graph) {
2063
3331
  await ensureDir();
2064
3332
  const json = graphToJson(graph);
2065
- const filePath = (0, import_path.join)(dir, `${graph.id}.json`);
2066
- const resolvedBase = (0, import_path.resolve)(dir);
2067
- const resolvedPath = (0, import_path.resolve)(filePath);
2068
- if (!resolvedPath.startsWith(resolvedBase + "/") && resolvedPath !== resolvedBase) {
3333
+ const filePath = (0, import_node_path8.join)(dir, `${graph.id}.json`);
3334
+ const resolvedBase = (0, import_node_path8.resolve)(dir);
3335
+ const resolvedPath = (0, import_node_path8.resolve)(filePath);
3336
+ if (!resolvedPath.startsWith(`${resolvedBase}/`) && resolvedPath !== resolvedBase) {
2069
3337
  throw new Error(`Path traversal detected: "${graph.id}" escapes base directory`);
2070
3338
  }
2071
3339
  await (0, import_promises.writeFile)(filePath, JSON.stringify(json, null, 2), "utf-8");
@@ -2073,7 +3341,7 @@ function createTraceStore(dir) {
2073
3341
  },
2074
3342
  async get(graphId) {
2075
3343
  await ensureDir();
2076
- const filePath = (0, import_path.join)(dir, `${graphId}.json`);
3344
+ const filePath = (0, import_node_path8.join)(dir, `${graphId}.json`);
2077
3345
  try {
2078
3346
  const content = await (0, import_promises.readFile)(filePath, "utf-8");
2079
3347
  return loadGraph(content);
@@ -2143,7 +3411,7 @@ var STATUS_ICONS = {
2143
3411
  hung: "\u231B",
2144
3412
  timeout: "\u231B"
2145
3413
  };
2146
- function formatDuration(ms) {
3414
+ function formatDuration2(ms) {
2147
3415
  if (ms < 1e3) return `${ms}ms`;
2148
3416
  if (ms < 6e4) return `${(ms / 1e3).toFixed(1)}s`;
2149
3417
  if (ms < 36e5) return `${(ms / 6e4).toFixed(1)}m`;
@@ -2151,7 +3419,7 @@ function formatDuration(ms) {
2151
3419
  }
2152
3420
  function nodeDuration(node, graphEndTime) {
2153
3421
  const end = node.endTime ?? graphEndTime;
2154
- return formatDuration(end - node.startTime);
3422
+ return formatDuration2(end - node.startTime);
2155
3423
  }
2156
3424
  function getGenAiInfo(node) {
2157
3425
  const parts = [];
@@ -2195,7 +3463,8 @@ function toAsciiTree(graph) {
2195
3463
  const children = getChildren(graph, nodeId);
2196
3464
  const childPrefix = isRoot ? "" : prefix + (isLast ? " " : "\u2502 ");
2197
3465
  for (let i = 0; i < children.length; i++) {
2198
- renderNode(children[i].id, childPrefix, i === children.length - 1, false);
3466
+ const childId = children[i]?.id;
3467
+ if (childId) renderNode(childId, childPrefix, i === children.length - 1, false);
2199
3468
  }
2200
3469
  }
2201
3470
  renderNode(graph.rootNodeId, "", true, true);
@@ -2214,7 +3483,7 @@ function toTimeline(graph) {
2214
3483
  const tickCount = Math.min(5, Math.max(2, Math.floor(barWidth / 10)));
2215
3484
  for (let i = 0; i <= tickCount; i++) {
2216
3485
  const t = totalDuration * i / tickCount;
2217
- scaleLabels.push(formatDuration(t));
3486
+ scaleLabels.push(formatDuration2(t));
2218
3487
  }
2219
3488
  let header = "";
2220
3489
  for (let i = 0; i < scaleLabels.length; i++) {
@@ -2266,9 +3535,9 @@ function toTimeline(graph) {
2266
3535
  }
2267
3536
 
2268
3537
  // src/watch.ts
2269
- var import_node_fs5 = require("fs");
3538
+ var import_node_fs9 = require("fs");
2270
3539
  var import_node_os = require("os");
2271
- var import_node_path4 = require("path");
3540
+ var import_node_path9 = require("path");
2272
3541
 
2273
3542
  // src/watch-alerts.ts
2274
3543
  var import_node_child_process3 = require("child_process");
@@ -2389,15 +3658,15 @@ function sendCommand(payload, cmd) {
2389
3658
  }
2390
3659
 
2391
3660
  // src/watch-state.ts
2392
- var import_node_fs4 = require("fs");
3661
+ var import_node_fs8 = require("fs");
2393
3662
  function parseDuration(input) {
2394
3663
  const match = input.match(/^(\d+(?:\.\d+)?)\s*(s|m|h|d)$/i);
2395
3664
  if (!match) {
2396
3665
  const n = parseInt(input, 10);
2397
- return isNaN(n) ? 0 : n * 1e3;
3666
+ return Number.isNaN(n) ? 0 : n * 1e3;
2398
3667
  }
2399
3668
  const value = parseFloat(match[1]);
2400
- switch (match[2].toLowerCase()) {
3669
+ switch (match[2]?.toLowerCase()) {
2401
3670
  case "s":
2402
3671
  return value * 1e3;
2403
3672
  case "m":
@@ -2414,9 +3683,9 @@ function emptyState() {
2414
3683
  return { version: 1, agents: {}, lastPollTime: 0 };
2415
3684
  }
2416
3685
  function loadWatchState(filePath) {
2417
- if (!(0, import_node_fs4.existsSync)(filePath)) return emptyState();
3686
+ if (!(0, import_node_fs8.existsSync)(filePath)) return emptyState();
2418
3687
  try {
2419
- const raw = JSON.parse((0, import_node_fs4.readFileSync)(filePath, "utf8"));
3688
+ const raw = JSON.parse((0, import_node_fs8.readFileSync)(filePath, "utf8"));
2420
3689
  if (raw.version !== 1 || typeof raw.agents !== "object") return emptyState();
2421
3690
  return raw;
2422
3691
  } catch {
@@ -2424,13 +3693,13 @@ function loadWatchState(filePath) {
2424
3693
  }
2425
3694
  }
2426
3695
  function saveWatchState(filePath, state) {
2427
- const tmp = filePath + ".tmp";
3696
+ const tmp = `${filePath}.tmp`;
2428
3697
  try {
2429
- (0, import_node_fs4.writeFileSync)(tmp, JSON.stringify(state, null, 2), "utf8");
2430
- (0, import_node_fs4.renameSync)(tmp, filePath);
3698
+ (0, import_node_fs8.writeFileSync)(tmp, JSON.stringify(state, null, 2), "utf8");
3699
+ (0, import_node_fs8.renameSync)(tmp, filePath);
2431
3700
  } catch {
2432
3701
  try {
2433
- (0, import_node_fs4.writeFileSync)(filePath, JSON.stringify(state, null, 2), "utf8");
3702
+ (0, import_node_fs8.writeFileSync)(filePath, JSON.stringify(state, null, 2), "utf8");
2434
3703
  } catch {
2435
3704
  }
2436
3705
  }
@@ -2625,8 +3894,8 @@ function parseWatchArgs(argv) {
2625
3894
  i++;
2626
3895
  const val = args[i] ?? "";
2627
3896
  if (val === "telegram") {
2628
- const botToken = process.env["AGENTFLOW_TELEGRAM_BOT_TOKEN"] ?? "";
2629
- const chatId = process.env["AGENTFLOW_TELEGRAM_CHAT_ID"] ?? "";
3897
+ const botToken = process.env.AGENTFLOW_TELEGRAM_BOT_TOKEN ?? "";
3898
+ const chatId = process.env.AGENTFLOW_TELEGRAM_CHAT_ID ?? "";
2630
3899
  if (botToken && chatId) {
2631
3900
  notifyChannels.push({ type: "telegram", botToken, chatId });
2632
3901
  } else {
@@ -2643,7 +3912,7 @@ function parseWatchArgs(argv) {
2643
3912
  } else if (arg === "--poll") {
2644
3913
  i++;
2645
3914
  const v = parseInt(args[i] ?? "", 10);
2646
- if (!isNaN(v) && v > 0) pollIntervalMs = v * 1e3;
3915
+ if (!Number.isNaN(v) && v > 0) pollIntervalMs = v * 1e3;
2647
3916
  i++;
2648
3917
  } else if (arg === "--cooldown") {
2649
3918
  i++;
@@ -2658,20 +3927,20 @@ function parseWatchArgs(argv) {
2658
3927
  recursive = true;
2659
3928
  i++;
2660
3929
  } else if (!arg.startsWith("-")) {
2661
- dirs.push((0, import_node_path4.resolve)(arg));
3930
+ dirs.push((0, import_node_path9.resolve)(arg));
2662
3931
  i++;
2663
3932
  } else {
2664
3933
  i++;
2665
3934
  }
2666
3935
  }
2667
- if (dirs.length === 0) dirs.push((0, import_node_path4.resolve)("."));
3936
+ if (dirs.length === 0) dirs.push((0, import_node_path9.resolve)("."));
2668
3937
  if (alertConditions.length === 0) {
2669
3938
  alertConditions.push({ type: "error" });
2670
3939
  alertConditions.push({ type: "recovery" });
2671
3940
  }
2672
3941
  notifyChannels.unshift({ type: "stdout" });
2673
3942
  if (!stateFilePath) {
2674
- stateFilePath = (0, import_node_path4.join)(dirs[0], ".agentflow-watch-state.json");
3943
+ stateFilePath = (0, import_node_path9.join)(dirs[0], ".agentflow-watch-state.json");
2675
3944
  }
2676
3945
  return {
2677
3946
  dirs,
@@ -2679,7 +3948,7 @@ function parseWatchArgs(argv) {
2679
3948
  pollIntervalMs,
2680
3949
  alertConditions,
2681
3950
  notifyChannels,
2682
- stateFilePath: (0, import_node_path4.resolve)(stateFilePath),
3951
+ stateFilePath: (0, import_node_path9.resolve)(stateFilePath),
2683
3952
  cooldownMs
2684
3953
  };
2685
3954
  }
@@ -2733,12 +4002,12 @@ Examples:
2733
4002
  }
2734
4003
  function startWatch(argv) {
2735
4004
  const config = parseWatchArgs(argv);
2736
- const valid = config.dirs.filter((d) => (0, import_node_fs5.existsSync)(d));
4005
+ const valid = config.dirs.filter((d) => (0, import_node_fs9.existsSync)(d));
2737
4006
  if (valid.length === 0) {
2738
4007
  console.error(`No valid directories found: ${config.dirs.join(", ")}`);
2739
4008
  process.exit(1);
2740
4009
  }
2741
- const invalid = config.dirs.filter((d) => !(0, import_node_fs5.existsSync)(d));
4010
+ const invalid = config.dirs.filter((d) => !(0, import_node_fs9.existsSync)(d));
2742
4011
  if (invalid.length > 0) {
2743
4012
  console.warn(`Skipping non-existent: ${invalid.join(", ")}`);
2744
4013
  }
@@ -2759,7 +4028,7 @@ agentflow watch started`);
2759
4028
  console.log(` Poll: ${config.pollIntervalMs / 1e3}s`);
2760
4029
  console.log(` Alert on: ${condLabels.join(", ")}`);
2761
4030
  console.log(
2762
- ` Notify: stdout${channelLabels.length > 0 ? ", " + channelLabels.join(", ") : ""}`
4031
+ ` Notify: stdout${channelLabels.length > 0 ? `, ${channelLabels.join(", ")}` : ""}`
2763
4032
  );
2764
4033
  console.log(` Cooldown: ${Math.floor(config.cooldownMs / 6e4)}m`);
2765
4034
  console.log(` State: ${config.stateFilePath}`);
@@ -2818,12 +4087,28 @@ agentflow watch started`);
2818
4087
  // Annotate the CommonJS export names for ESM import in node:
2819
4088
  0 && (module.exports = {
2820
4089
  auditProcesses,
4090
+ buildAgentSummaryPrompt,
4091
+ buildAnomalyExplanationPrompt,
4092
+ buildFailureAnalysisPrompt,
4093
+ buildFixSuggestionPrompt,
4094
+ checkConformance,
2821
4095
  checkGuards,
4096
+ createEventEmitter,
4097
+ createExecutionEvent,
2822
4098
  createGraphBuilder,
4099
+ createInsightEngine,
4100
+ createJsonEventWriter,
4101
+ createKnowledgeStore,
4102
+ createPatternEvent,
4103
+ createPolicySource,
4104
+ createSomaEventWriter,
2823
4105
  createTraceStore,
4106
+ discoverProcess,
2824
4107
  discoverProcessConfig,
4108
+ findVariants,
2825
4109
  findWaitingOn,
2826
4110
  formatAuditReport,
4111
+ getBottlenecks,
2827
4112
  getChildren,
2828
4113
  getCriticalPath,
2829
4114
  getDepth,
@@ -2832,6 +4117,7 @@ agentflow watch started`);
2832
4117
  getHungNodes,
2833
4118
  getNode,
2834
4119
  getParent,
4120
+ getPathSignature,
2835
4121
  getStats,
2836
4122
  getSubtree,
2837
4123
  getTraceTree,