agentflow-core 0.7.0 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -21,12 +21,30 @@ 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
+ discoverAllProcessConfigs: () => discoverAllProcessConfigs,
41
+ discoverProcess: () => discoverProcess,
27
42
  discoverProcessConfig: () => discoverProcessConfig,
43
+ findVariants: () => findVariants,
28
44
  findWaitingOn: () => findWaitingOn,
29
45
  formatAuditReport: () => formatAuditReport,
46
+ formatReceipt: () => formatReceipt,
47
+ getBottlenecks: () => getBottlenecks,
30
48
  getChildren: () => getChildren,
31
49
  getCriticalPath: () => getCriticalPath,
32
50
  getDepth: () => getDepth,
@@ -35,6 +53,7 @@ __export(index_exports, {
35
53
  getHungNodes: () => getHungNodes,
36
54
  getNode: () => getNode,
37
55
  getParent: () => getParent,
56
+ getPathSignature: () => getPathSignature,
38
57
  getStats: () => getStats,
39
58
  getSubtree: () => getSubtree,
40
59
  getTraceTree: () => getTraceTree,
@@ -46,13 +65,327 @@ __export(index_exports, {
46
65
  startWatch: () => startWatch,
47
66
  stitchTrace: () => stitchTrace,
48
67
  toAsciiTree: () => toAsciiTree,
68
+ toReceipt: () => toReceipt,
49
69
  toTimeline: () => toTimeline,
50
70
  withGuards: () => withGuards
51
71
  });
52
72
  module.exports = __toCommonJS(index_exports);
53
73
 
74
+ // src/process-mining.ts
75
+ function getPathSignature(graph) {
76
+ const root = graph.nodes.get(graph.rootNodeId);
77
+ if (!root) return "";
78
+ const parts = [];
79
+ function walk(node) {
80
+ parts.push(`${node.type}:${node.name}`);
81
+ const childNodes = [];
82
+ for (const childId of node.children) {
83
+ const child = graph.nodes.get(childId);
84
+ if (child) childNodes.push(child);
85
+ }
86
+ childNodes.sort((a, b) => {
87
+ const keyA = `${a.type}:${a.name}`;
88
+ const keyB = `${b.type}:${b.name}`;
89
+ return keyA.localeCompare(keyB);
90
+ });
91
+ for (const child of childNodes) {
92
+ walk(child);
93
+ }
94
+ }
95
+ walk(root);
96
+ return parts.join("\u2192");
97
+ }
98
+ function stepKey(node) {
99
+ return `${node.type}:${node.name}`;
100
+ }
101
+ function discoverProcess(graphs) {
102
+ if (graphs.length === 0) {
103
+ throw new Error("discoverProcess requires at least one graph");
104
+ }
105
+ const steps = /* @__PURE__ */ new Set();
106
+ const transitionCounts = /* @__PURE__ */ new Map();
107
+ const outgoingCounts = /* @__PURE__ */ new Map();
108
+ for (const graph of graphs) {
109
+ for (const node of graph.nodes.values()) {
110
+ const parentKey = stepKey(node);
111
+ steps.add(parentKey);
112
+ for (const childId of node.children) {
113
+ const child = graph.nodes.get(childId);
114
+ if (!child) continue;
115
+ const childKey = stepKey(child);
116
+ const tKey = `${parentKey}\0${childKey}`;
117
+ transitionCounts.set(tKey, (transitionCounts.get(tKey) ?? 0) + 1);
118
+ outgoingCounts.set(parentKey, (outgoingCounts.get(parentKey) ?? 0) + 1);
119
+ }
120
+ }
121
+ }
122
+ const transitions = [];
123
+ for (const [tKey, count] of transitionCounts) {
124
+ const [from, to] = tKey.split("\0");
125
+ const outgoing = outgoingCounts.get(from) ?? count;
126
+ transitions.push({
127
+ from,
128
+ to,
129
+ count,
130
+ probability: count / outgoing
131
+ });
132
+ }
133
+ transitions.sort((a, b) => a.from.localeCompare(b.from) || a.to.localeCompare(b.to));
134
+ return {
135
+ steps: [...steps].sort(),
136
+ transitions,
137
+ totalGraphs: graphs.length,
138
+ agentId: graphs[0]?.agentId ?? ""
139
+ };
140
+ }
141
+ function findVariants(graphs) {
142
+ if (graphs.length === 0) return [];
143
+ const groups = /* @__PURE__ */ new Map();
144
+ for (const graph of graphs) {
145
+ const sig = getPathSignature(graph);
146
+ const group = groups.get(sig) ?? [];
147
+ group.push(graph);
148
+ groups.set(sig, group);
149
+ }
150
+ const total = graphs.length;
151
+ const variants = [];
152
+ for (const [pathSignature, groupGraphs] of groups) {
153
+ variants.push({
154
+ pathSignature,
155
+ count: groupGraphs.length,
156
+ percentage: groupGraphs.length / total * 100,
157
+ graphIds: groupGraphs.map((g) => g.id),
158
+ exampleGraph: groupGraphs[0]
159
+ });
160
+ }
161
+ variants.sort((a, b) => {
162
+ const freqDiff = b.count - a.count;
163
+ if (freqDiff !== 0) return freqDiff;
164
+ return a.pathSignature.localeCompare(b.pathSignature);
165
+ });
166
+ return variants;
167
+ }
168
+ function percentile(sorted, p) {
169
+ if (sorted.length === 0) return 0;
170
+ if (sorted.length === 1) return sorted[0] ?? 0;
171
+ const index = p / 100 * (sorted.length - 1);
172
+ const lower = Math.floor(index);
173
+ const upper = Math.ceil(index);
174
+ if (lower === upper) return sorted[lower] ?? 0;
175
+ const weight = index - lower;
176
+ return (sorted[lower] ?? 0) * (1 - weight) + (sorted[upper] ?? 0) * weight;
177
+ }
178
+ function getBottlenecks(graphs) {
179
+ if (graphs.length === 0) return [];
180
+ const now = Date.now();
181
+ const stats = /* @__PURE__ */ new Map();
182
+ for (const graph of graphs) {
183
+ for (const node of graph.nodes.values()) {
184
+ const key = `${node.type}:${node.name}`;
185
+ const entry = stats.get(key) ?? { durations: [], nodeType: node.type, nodeName: node.name };
186
+ const end = node.endTime ?? now;
187
+ entry.durations.push(end - node.startTime);
188
+ stats.set(key, entry);
189
+ }
190
+ }
191
+ const total = graphs.length;
192
+ const bottlenecks = [];
193
+ for (const [, entry] of stats) {
194
+ const sorted = [...entry.durations].sort((a, b) => a - b);
195
+ bottlenecks.push({
196
+ nodeName: entry.nodeName,
197
+ nodeType: entry.nodeType,
198
+ occurrences: sorted.length,
199
+ durations: {
200
+ median: percentile(sorted, 50),
201
+ p95: percentile(sorted, 95),
202
+ p99: percentile(sorted, 99),
203
+ min: sorted[0] ?? 0,
204
+ max: sorted[sorted.length - 1] ?? 0
205
+ },
206
+ percentOfGraphs: sorted.length / total * 100
207
+ });
208
+ }
209
+ bottlenecks.sort((a, b) => b.durations.p95 - a.durations.p95);
210
+ return bottlenecks;
211
+ }
212
+ function extractGraphTransitions(graph) {
213
+ const transitions = /* @__PURE__ */ new Set();
214
+ for (const node of graph.nodes.values()) {
215
+ const parentKey = stepKey(node);
216
+ for (const childId of node.children) {
217
+ const child = graph.nodes.get(childId);
218
+ if (!child) continue;
219
+ transitions.add(`${parentKey}\0${stepKey(child)}`);
220
+ }
221
+ }
222
+ return transitions;
223
+ }
224
+ function checkConformance(graph, model) {
225
+ const graphTransitions = extractGraphTransitions(graph);
226
+ const deviations = [];
227
+ const modelLookup = /* @__PURE__ */ new Map();
228
+ for (const t of model.transitions) {
229
+ modelLookup.set(`${t.from}\0${t.to}`, t);
230
+ }
231
+ let totalChecks = 0;
232
+ let deviationCount = 0;
233
+ for (const tKey of graphTransitions) {
234
+ totalChecks++;
235
+ const [from, to] = tKey.split("\0");
236
+ const modelTransition = modelLookup.get(tKey);
237
+ if (!modelTransition) {
238
+ deviationCount++;
239
+ deviations.push({
240
+ type: "unexpected-transition",
241
+ from,
242
+ to,
243
+ message: `Unexpected transition ${from} \u2192 ${to} (not in process model)`
244
+ });
245
+ } else if (modelTransition.probability < 0.1) {
246
+ deviationCount++;
247
+ deviations.push({
248
+ type: "low-frequency-path",
249
+ from,
250
+ to,
251
+ message: `Low-frequency path ${from} \u2192 ${to} (model probability: ${(modelTransition.probability * 100).toFixed(1)}%)`,
252
+ modelProbability: modelTransition.probability
253
+ });
254
+ }
255
+ }
256
+ const graphSteps = /* @__PURE__ */ new Set();
257
+ for (const node of graph.nodes.values()) {
258
+ graphSteps.add(stepKey(node));
259
+ }
260
+ for (const t of model.transitions) {
261
+ if (t.probability > 0.5) {
262
+ const tKey = `${t.from}\0${t.to}`;
263
+ if (graphSteps.has(t.from) && !graphTransitions.has(tKey)) {
264
+ totalChecks++;
265
+ deviationCount++;
266
+ deviations.push({
267
+ type: "missing-transition",
268
+ from: t.from,
269
+ to: t.to,
270
+ message: `Missing expected transition ${t.from} \u2192 ${t.to} (model probability: ${(t.probability * 100).toFixed(1)}%)`,
271
+ modelProbability: t.probability
272
+ });
273
+ }
274
+ }
275
+ }
276
+ const conformanceScore = totalChecks === 0 ? 1 : (totalChecks - deviationCount) / totalChecks;
277
+ return {
278
+ conformanceScore,
279
+ isConforming: deviations.length === 0,
280
+ deviations
281
+ };
282
+ }
283
+
284
+ // src/event-emitter.ts
285
+ var SCHEMA_VERSION = 1;
286
+ function createExecutionEvent(graph, options) {
287
+ const duration = graph.endTime !== null ? graph.endTime - graph.startTime : Date.now() - graph.startTime;
288
+ let failurePoint;
289
+ if (graph.status === "failed") {
290
+ let candidate;
291
+ for (const node of graph.nodes.values()) {
292
+ if (node.status === "failed" || node.status === "timeout") {
293
+ const errorMeta = node.metadata.error;
294
+ const fp = {
295
+ nodeId: node.id,
296
+ nodeName: node.name,
297
+ nodeType: node.type,
298
+ error: typeof errorMeta === "string" ? errorMeta : void 0
299
+ };
300
+ if (node.id !== graph.rootNodeId) {
301
+ failurePoint = fp;
302
+ break;
303
+ }
304
+ if (!candidate) candidate = fp;
305
+ }
306
+ }
307
+ if (!failurePoint) failurePoint = candidate;
308
+ }
309
+ return {
310
+ eventType: graph.status === "failed" ? "execution.failed" : "execution.completed",
311
+ graphId: graph.id,
312
+ agentId: graph.agentId,
313
+ timestamp: Date.now(),
314
+ schemaVersion: SCHEMA_VERSION,
315
+ status: graph.status,
316
+ duration,
317
+ nodeCount: graph.nodes.size,
318
+ pathSignature: getPathSignature(graph),
319
+ ...failurePoint ? { failurePoint } : {},
320
+ ...options?.processContext ? { processContext: options.processContext } : {},
321
+ ...options?.semantic ? { semantic: options.semantic } : {},
322
+ violations: options?.violations ?? []
323
+ };
324
+ }
325
+ function createPatternEvent(agentId, model, variants, bottlenecks) {
326
+ return {
327
+ eventType: "pattern.discovered",
328
+ agentId,
329
+ timestamp: Date.now(),
330
+ schemaVersion: SCHEMA_VERSION,
331
+ pattern: {
332
+ totalGraphs: model.totalGraphs,
333
+ variantCount: variants.length,
334
+ topVariants: variants.slice(0, 5).map((v) => ({
335
+ pathSignature: v.pathSignature,
336
+ count: v.count,
337
+ percentage: v.percentage
338
+ })),
339
+ topBottlenecks: bottlenecks.slice(0, 5).map((b) => ({
340
+ nodeName: b.nodeName,
341
+ nodeType: b.nodeType,
342
+ p95: b.durations.p95
343
+ })),
344
+ processModel: model
345
+ }
346
+ };
347
+ }
348
+ function createEventEmitter(config) {
349
+ const writers = config?.writers ?? [];
350
+ const knowledgeStore = config?.knowledgeStore;
351
+ const onError = config?.onError ?? (() => {
352
+ });
353
+ const subscribers = /* @__PURE__ */ new Set();
354
+ return {
355
+ async emit(event) {
356
+ if (knowledgeStore) {
357
+ try {
358
+ knowledgeStore.append(event);
359
+ } catch (err) {
360
+ onError(err);
361
+ }
362
+ }
363
+ for (const writer of writers) {
364
+ try {
365
+ await writer.writeEvent(event);
366
+ } catch (err) {
367
+ onError(err);
368
+ }
369
+ }
370
+ for (const listener of subscribers) {
371
+ try {
372
+ listener(event);
373
+ } catch (err) {
374
+ onError(err);
375
+ }
376
+ }
377
+ },
378
+ subscribe(listener) {
379
+ subscribers.add(listener);
380
+ return () => {
381
+ subscribers.delete(listener);
382
+ };
383
+ }
384
+ };
385
+ }
386
+
54
387
  // src/graph-builder.ts
55
- var import_crypto = require("crypto");
388
+ var import_node_crypto = require("crypto");
56
389
  function deepFreeze(obj) {
57
390
  if (obj === null || typeof obj !== "object") return obj;
58
391
  if (obj instanceof Map) {
@@ -83,8 +416,8 @@ function createGraphBuilder(config) {
83
416
  const generateId = config?.idGenerator ?? createCounterIdGenerator();
84
417
  const agentId = config?.agentId ?? "unknown";
85
418
  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)();
419
+ const spanId = (0, import_node_crypto.randomUUID)();
420
+ const traceId = config?.traceId ?? (typeof process !== "undefined" ? process.env?.AGENTFLOW_TRACE_ID : void 0) ?? (0, import_node_crypto.randomUUID)();
88
421
  const parentSpanId = config?.parentSpanId ?? (typeof process !== "undefined" ? process.env?.AGENTFLOW_PARENT_SPAN_ID : void 0) ?? null;
89
422
  const graphId = generateId();
90
423
  const startTime = Date.now();
@@ -308,13 +641,13 @@ function getHungNodes(graph) {
308
641
  function getCriticalPath(graph) {
309
642
  const root = graph.nodes.get(graph.rootNodeId);
310
643
  if (!root) return [];
311
- function nodeDuration2(node) {
644
+ function nodeDuration3(node) {
312
645
  const end = node.endTime ?? Date.now();
313
646
  return end - node.startTime;
314
647
  }
315
648
  function dfs(node) {
316
649
  if (node.children.length === 0) {
317
- return { duration: nodeDuration2(node), path: [node] };
650
+ return { duration: nodeDuration3(node), path: [node] };
318
651
  }
319
652
  let bestChild = { duration: -1, path: [] };
320
653
  for (const childId of node.children) {
@@ -326,7 +659,7 @@ function getCriticalPath(graph) {
326
659
  }
327
660
  }
328
661
  return {
329
- duration: nodeDuration2(node) + bestChild.duration,
662
+ duration: nodeDuration3(node) + bestChild.duration,
330
663
  path: [node, ...bestChild.path]
331
664
  };
332
665
  }
@@ -429,7 +762,7 @@ function groupByTraceId(graphs) {
429
762
  }
430
763
  function stitchTrace(graphs) {
431
764
  if (graphs.length === 0) throw new Error("No graphs to stitch");
432
- const traceId = graphs[0].traceId ?? "";
765
+ const traceId = graphs[0]?.traceId ?? "";
433
766
  const graphsBySpan = /* @__PURE__ */ new Map();
434
767
  const childMap = /* @__PURE__ */ new Map();
435
768
  let rootGraph = null;
@@ -481,6 +814,10 @@ function getTraceTree(trace) {
481
814
  }
482
815
 
483
816
  // src/guards.ts
817
+ function explainMessage(explanation) {
818
+ const sourceLabel = explanation.source === "static" ? "static config" : explanation.source === "soma-policy" ? `soma-policy${explanation.evidence ? ` (${explanation.evidence})` : ""}` : explanation.source === "assertion" ? "assertion" : "adaptive";
819
+ return `This run was stopped because ${explanation.rule} reached ${explanation.actual}, which exceeds the limit of ${explanation.threshold}. Source: ${sourceLabel}.`;
820
+ }
484
821
  var DEFAULT_TIMEOUTS = {
485
822
  tool: 3e4,
486
823
  // 30s
@@ -507,22 +844,36 @@ function checkGuards(graph, config) {
507
844
  const timeoutThreshold = timeouts[node.type];
508
845
  const elapsed = now - node.startTime;
509
846
  if (elapsed > timeoutThreshold) {
847
+ const explanation = {
848
+ rule: "timeout",
849
+ threshold: timeoutThreshold,
850
+ actual: elapsed,
851
+ source: "static"
852
+ };
510
853
  violations.push({
511
854
  type: "timeout",
512
855
  nodeId: node.id,
513
- message: `Node ${node.id} (${node.type}: ${node.name}) has been running for ${elapsed}ms, exceeding timeout of ${timeoutThreshold}ms`,
514
- timestamp: now
856
+ message: explainMessage(explanation),
857
+ timestamp: now,
858
+ explanation
515
859
  });
516
860
  }
517
861
  }
518
862
  }
519
863
  const depth = getDepth(graph);
520
864
  if (depth > maxDepth) {
865
+ const explanation = {
866
+ rule: "max-depth",
867
+ threshold: maxDepth,
868
+ actual: depth,
869
+ source: "static"
870
+ };
521
871
  violations.push({
522
872
  type: "spawn-explosion",
523
873
  nodeId: graph.rootNodeId,
524
- message: `Graph depth ${depth} exceeds maximum depth of ${maxDepth}`,
525
- timestamp: now
874
+ message: explainMessage(explanation),
875
+ timestamp: now,
876
+ explanation
526
877
  });
527
878
  }
528
879
  let agentCount = 0;
@@ -532,14 +883,26 @@ function checkGuards(graph, config) {
532
883
  }
533
884
  }
534
885
  if (agentCount > maxAgentSpawns) {
886
+ const explanation = {
887
+ rule: "max-agent-spawns",
888
+ threshold: maxAgentSpawns,
889
+ actual: agentCount,
890
+ source: "static"
891
+ };
535
892
  violations.push({
536
893
  type: "spawn-explosion",
537
894
  nodeId: graph.rootNodeId,
538
- message: `Total agent/subagent count ${agentCount} exceeds maximum of ${maxAgentSpawns}`,
539
- timestamp: now
895
+ message: explainMessage(explanation),
896
+ timestamp: now,
897
+ explanation
540
898
  });
541
899
  }
542
900
  violations.push(...detectReasoningLoops(graph, maxReasoningSteps, now));
901
+ if (config?.policySource) {
902
+ violations.push(
903
+ ...checkPolicyViolations(graph, config.policySource, config.policyThresholds, now)
904
+ );
905
+ }
543
906
  return violations;
544
907
  }
545
908
  function detectReasoningLoops(graph, maxSteps, timestamp) {
@@ -559,11 +922,18 @@ function detectReasoningLoops(graph, maxSteps, timestamp) {
559
922
  }
560
923
  if (newCount > maxSteps && !reported.has(newType)) {
561
924
  reported.add(newType);
925
+ const explanation = {
926
+ rule: "max-reasoning-steps",
927
+ threshold: maxSteps,
928
+ actual: newCount,
929
+ source: "static"
930
+ };
562
931
  violations.push({
563
932
  type: "reasoning-loop",
564
933
  nodeId: node.id,
565
- message: `Detected ${newCount} consecutive ${newType} nodes along path to ${node.name}`,
566
- timestamp
934
+ message: explainMessage(explanation),
935
+ timestamp,
936
+ explanation
567
937
  });
568
938
  }
569
939
  const children = getChildren(graph, nodeId);
@@ -574,6 +944,61 @@ function detectReasoningLoops(graph, maxSteps, timestamp) {
574
944
  walk(graph.rootNodeId, 0, null);
575
945
  return violations;
576
946
  }
947
+ function checkPolicyViolations(graph, policySource, thresholds, timestamp) {
948
+ const violations = [];
949
+ const maxFailureRate = thresholds?.maxFailureRate ?? 0.5;
950
+ const minConformance = thresholds?.minConformance ?? 0.7;
951
+ const failureRate = policySource.recentFailureRate(graph.agentId);
952
+ if (failureRate > maxFailureRate) {
953
+ const explanation = {
954
+ rule: "max-failure-rate",
955
+ threshold: maxFailureRate,
956
+ actual: failureRate,
957
+ source: "soma-policy"
958
+ };
959
+ violations.push({
960
+ type: "high-failure-rate",
961
+ nodeId: graph.rootNodeId,
962
+ message: explainMessage(explanation),
963
+ timestamp,
964
+ explanation
965
+ });
966
+ }
967
+ const conformanceScore = policySource.lastConformanceScore(graph.agentId);
968
+ if (conformanceScore !== null && conformanceScore < minConformance) {
969
+ const explanation = {
970
+ rule: "min-conformance",
971
+ threshold: minConformance,
972
+ actual: conformanceScore,
973
+ source: "soma-policy"
974
+ };
975
+ violations.push({
976
+ type: "conformance-drift",
977
+ nodeId: graph.rootNodeId,
978
+ message: explainMessage(explanation),
979
+ timestamp,
980
+ explanation
981
+ });
982
+ }
983
+ for (const node of graph.nodes.values()) {
984
+ if (node.status === "running" && policySource.isKnownBottleneck(node.name)) {
985
+ const explanation = {
986
+ rule: "known-bottleneck",
987
+ threshold: "flagged",
988
+ actual: "running",
989
+ source: "soma-policy"
990
+ };
991
+ violations.push({
992
+ type: "known-bottleneck",
993
+ nodeId: node.id,
994
+ message: explainMessage(explanation),
995
+ timestamp,
996
+ explanation
997
+ });
998
+ }
999
+ }
1000
+ return violations;
1001
+ }
577
1002
  function withGuards(builder, config) {
578
1003
  const logger = config?.logger ?? ((msg) => console.warn(`[AgentFlow Guard] ${msg}`));
579
1004
  const onViolation = config?.onViolation ?? "warn";
@@ -638,9 +1063,639 @@ function withGuards(builder, config) {
638
1063
  };
639
1064
  }
640
1065
 
641
- // src/live.ts
1066
+ // src/prompt-builder.ts
1067
+ var ROLE = "You are analyzing execution data for an AI agent system. Provide clear, actionable analysis based on the data below.";
1068
+ function fmtDuration(ms) {
1069
+ if (ms < 1e3) return `${ms}ms`;
1070
+ return `${(ms / 1e3).toFixed(1)}s`;
1071
+ }
1072
+ function fmtTime(ts) {
1073
+ return new Date(ts).toISOString();
1074
+ }
1075
+ function durationStats(durations) {
1076
+ if (durations.length === 0) return { avg: 0, p50: 0, p95: 0, min: 0, max: 0 };
1077
+ const sorted = [...durations].sort((a, b) => a - b);
1078
+ const sum = sorted.reduce((a, b) => a + b, 0);
1079
+ return {
1080
+ avg: Math.round(sum / sorted.length),
1081
+ p50: sorted[Math.floor(sorted.length * 0.5)] ?? 0,
1082
+ p95: sorted[Math.floor(sorted.length * 0.95)] ?? 0,
1083
+ min: sorted[0] ?? 0,
1084
+ max: sorted[sorted.length - 1] ?? 0
1085
+ };
1086
+ }
1087
+ function buildFailureAnalysisPrompt(events, profile) {
1088
+ const stats = durationStats(profile.recentDurations);
1089
+ const failureDetails = events.map((e, i) => {
1090
+ const lines = [
1091
+ `Failure ${i + 1}:`,
1092
+ ` Time: ${fmtTime(e.timestamp)}`,
1093
+ ` Duration: ${fmtDuration(e.duration)}`,
1094
+ ` Path: ${e.pathSignature}`
1095
+ ];
1096
+ if (e.failurePoint) {
1097
+ lines.push(` Failed at: ${e.failurePoint.nodeName} (${e.failurePoint.nodeType})`);
1098
+ if (e.failurePoint.error) lines.push(` Error: ${e.failurePoint.error}`);
1099
+ }
1100
+ if (e.violations.length > 0) {
1101
+ lines.push(` Violations: ${e.violations.map((v) => v.message).join("; ")}`);
1102
+ }
1103
+ return lines.join("\n");
1104
+ }).join("\n\n");
1105
+ return `${ROLE}
1106
+
1107
+ ## Agent Profile
1108
+ - Agent: ${profile.agentId}
1109
+ - Total runs: ${profile.totalRuns}
1110
+ - Failure rate: ${(profile.failureRate * 100).toFixed(1)}% (${profile.failureCount} failures / ${profile.totalRuns} total)
1111
+ - Avg duration: ${fmtDuration(stats.avg)} (p50: ${fmtDuration(stats.p50)}, p95: ${fmtDuration(stats.p95)})
1112
+ - Known bottlenecks: ${profile.knownBottlenecks.length > 0 ? profile.knownBottlenecks.join(", ") : "none"}
1113
+ - Last conformance score: ${profile.lastConformanceScore ?? "N/A"}
1114
+
1115
+ ## Recent Failures (${events.length})
1116
+
1117
+ ${failureDetails}
1118
+
1119
+ ## Question
1120
+ Analyze these failures. What patterns do you see? What is the most likely root cause? Are these related or independent failures?`;
1121
+ }
1122
+ function buildAnomalyExplanationPrompt(event, profile) {
1123
+ const stats = durationStats(profile.recentDurations);
1124
+ const eventDetails = [
1125
+ `Time: ${fmtTime(event.timestamp)}`,
1126
+ `Status: ${event.status}`,
1127
+ `Duration: ${fmtDuration(event.duration)}`,
1128
+ `Path: ${event.pathSignature}`,
1129
+ `Node count: ${event.nodeCount}`
1130
+ ];
1131
+ if (event.processContext) {
1132
+ eventDetails.push(`Conformance score: ${event.processContext.conformanceScore}`);
1133
+ eventDetails.push(`Is anomaly: ${event.processContext.isAnomaly}`);
1134
+ eventDetails.push(`Variant: ${event.processContext.variant}`);
1135
+ }
1136
+ if (event.failurePoint) {
1137
+ eventDetails.push(`Failed at: ${event.failurePoint.nodeName} (${event.failurePoint.nodeType})`);
1138
+ if (event.failurePoint.error) eventDetails.push(`Error: ${event.failurePoint.error}`);
1139
+ }
1140
+ if (event.violations.length > 0) {
1141
+ eventDetails.push(`Violations: ${event.violations.map((v) => v.message).join("; ")}`);
1142
+ }
1143
+ return `${ROLE}
1144
+
1145
+ ## Agent Baseline (from profile)
1146
+ - Agent: ${profile.agentId}
1147
+ - Total runs: ${profile.totalRuns}
1148
+ - Typical failure rate: ${(profile.failureRate * 100).toFixed(1)}%
1149
+ - Typical duration: avg ${fmtDuration(stats.avg)}, p50 ${fmtDuration(stats.p50)}, p95 ${fmtDuration(stats.p95)}
1150
+ - Last conformance score: ${profile.lastConformanceScore ?? "N/A"}
1151
+ - Known bottlenecks: ${profile.knownBottlenecks.length > 0 ? profile.knownBottlenecks.join(", ") : "none"}
1152
+
1153
+ ## Anomalous Execution
1154
+ ${eventDetails.join("\n")}
1155
+
1156
+ ## Question
1157
+ 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?`;
1158
+ }
1159
+ function buildAgentSummaryPrompt(profile, recentEvents, patterns) {
1160
+ const stats = durationStats(profile.recentDurations);
1161
+ const recentOutcomes = recentEvents.slice(0, 10).map((e) => ` ${fmtTime(e.timestamp)} \u2014 ${e.eventType} (${fmtDuration(e.duration)})`).join("\n");
1162
+ const patternSummary = patterns.length > 0 ? patterns.slice(0, 3).map((p) => {
1163
+ const lines = [
1164
+ ` Variants: ${p.pattern.variantCount} across ${p.pattern.totalGraphs} executions`,
1165
+ ` Top variant: ${p.pattern.topVariants[0]?.pathSignature ?? "N/A"} (${p.pattern.topVariants[0]?.percentage.toFixed(0) ?? 0}%)`
1166
+ ];
1167
+ if (p.pattern.topBottlenecks.length > 0) {
1168
+ const topB = p.pattern.topBottlenecks[0];
1169
+ if (topB)
1170
+ lines.push(` Top bottleneck: ${topB.nodeName} (p95: ${fmtDuration(topB.p95)})`);
1171
+ }
1172
+ return lines.join("\n");
1173
+ }).join("\n\n") : " No patterns discovered yet.";
1174
+ const dataNote = recentEvents.length === 0 && patterns.length === 0 ? "\nNote: Limited data available. Summary is based only on the profile statistics.\n" : "";
1175
+ return `${ROLE}
1176
+
1177
+ ## Agent Profile
1178
+ - Agent: ${profile.agentId}
1179
+ - Total runs: ${profile.totalRuns}
1180
+ - Success rate: ${((1 - profile.failureRate) * 100).toFixed(1)}% (${profile.successCount} successes, ${profile.failureCount} failures)
1181
+ - Duration: avg ${fmtDuration(stats.avg)}, p50 ${fmtDuration(stats.p50)}, p95 ${fmtDuration(stats.p95)}, range ${fmtDuration(stats.min)}\u2013${fmtDuration(stats.max)}
1182
+ - Known bottlenecks: ${profile.knownBottlenecks.length > 0 ? profile.knownBottlenecks.join(", ") : "none"}
1183
+ - Last conformance score: ${profile.lastConformanceScore ?? "N/A"}
1184
+ - Last pattern analysis: ${profile.lastPatternTimestamp ? fmtTime(profile.lastPatternTimestamp) : "never"}
1185
+ ${dataNote}
1186
+ ## Recent Executions (last ${recentEvents.slice(0, 10).length})
1187
+ ${recentOutcomes || " No recent events."}
1188
+
1189
+ ## Pattern Analysis
1190
+ ${patternSummary}
1191
+
1192
+ ## Question
1193
+ 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?`;
1194
+ }
1195
+ function buildFixSuggestionPrompt(events, profile, patterns) {
1196
+ const failureGroups = /* @__PURE__ */ new Map();
1197
+ for (const e of events) {
1198
+ const key = e.failurePoint?.error ?? e.pathSignature;
1199
+ const group = failureGroups.get(key) ?? [];
1200
+ group.push(e);
1201
+ failureGroups.set(key, group);
1202
+ }
1203
+ const failureGroupSummary = [...failureGroups.entries()].map(([key, group]) => {
1204
+ const latest = group[0];
1205
+ return ` "${key}" \u2014 ${group.length} occurrence(s), latest at ${latest ? fmtTime(latest.timestamp) : "unknown"}`;
1206
+ }).join("\n");
1207
+ const bottleneckDetails = patterns.flatMap((p) => p.pattern.topBottlenecks).map((b) => ` ${b.nodeName} (${b.nodeType}) \u2014 p95: ${fmtDuration(b.p95)}`);
1208
+ const uniqueBottlenecks = [...new Set(bottleneckDetails)].join("\n");
1209
+ const conformanceIssues = events.filter((e) => e.processContext && e.processContext.conformanceScore < 0.8).map(
1210
+ (e) => ` ${fmtTime(e.timestamp)}: conformance ${e.processContext?.conformanceScore}, variant "${e.processContext?.variant}"`
1211
+ ).join("\n");
1212
+ return `${ROLE}
1213
+
1214
+ ## Agent Profile
1215
+ - Agent: ${profile.agentId}
1216
+ - Failure rate: ${(profile.failureRate * 100).toFixed(1)}%
1217
+ - Known bottlenecks: ${profile.knownBottlenecks.length > 0 ? profile.knownBottlenecks.join(", ") : "none"}
1218
+
1219
+ ## Failure Patterns (${events.length} failures)
1220
+ ${failureGroupSummary || " No failures recorded."}
1221
+
1222
+ ## Bottlenecks
1223
+ ${uniqueBottlenecks || " No bottlenecks detected."}
1224
+
1225
+ ## Conformance Issues
1226
+ ${conformanceIssues || " No conformance issues."}
1227
+
1228
+ ## Question
1229
+ 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.`;
1230
+ }
1231
+
1232
+ // src/insight-engine.ts
1233
+ var DEFAULT_CACHE_TTL_MS = 36e5;
1234
+ var SCHEMA_VERSION2 = 1;
1235
+ function simpleHash(input) {
1236
+ let hash = 0;
1237
+ for (let i = 0; i < input.length; i++) {
1238
+ const char = input.charCodeAt(i);
1239
+ hash = (hash << 5) - hash + char | 0;
1240
+ }
1241
+ return (hash >>> 0).toString(36);
1242
+ }
1243
+ function createInsightEngine(store, analysisFn, config) {
1244
+ const cacheTtlMs = config?.cacheTtlMs ?? DEFAULT_CACHE_TTL_MS;
1245
+ function checkCache(agentId, insightType, dataHash) {
1246
+ const recent = store.getRecentInsights(agentId, { type: insightType, limit: 1 });
1247
+ if (recent.length === 0) return null;
1248
+ const cached = recent[0];
1249
+ if (!cached || cached.dataHash !== dataHash) return null;
1250
+ const age = Date.now() - cached.timestamp;
1251
+ if (age >= cacheTtlMs) return null;
1252
+ return cached;
1253
+ }
1254
+ function storeAndReturn(agentId, insightType, prompt, response, dataHash) {
1255
+ const event = {
1256
+ eventType: "insight.generated",
1257
+ agentId,
1258
+ timestamp: Date.now(),
1259
+ schemaVersion: SCHEMA_VERSION2,
1260
+ insightType,
1261
+ prompt,
1262
+ response,
1263
+ dataHash
1264
+ };
1265
+ store.appendInsight(event);
1266
+ return {
1267
+ agentId,
1268
+ insightType,
1269
+ content: response,
1270
+ cached: false,
1271
+ timestamp: event.timestamp
1272
+ };
1273
+ }
1274
+ function shortCircuit(agentId, insightType, content, cached, timestamp) {
1275
+ return { agentId, insightType, content, cached, timestamp: timestamp ?? Date.now() };
1276
+ }
1277
+ return {
1278
+ async explainFailures(agentId) {
1279
+ const profile = store.getAgentProfile(agentId);
1280
+ if (!profile) {
1281
+ return shortCircuit(
1282
+ agentId,
1283
+ "failure-analysis",
1284
+ "No data available for this agent.",
1285
+ false
1286
+ );
1287
+ }
1288
+ const events = store.getRecentEvents(agentId, { limit: 50 });
1289
+ const failures = events.filter((e) => e.eventType === "execution.failed");
1290
+ if (failures.length === 0) {
1291
+ return shortCircuit(
1292
+ agentId,
1293
+ "failure-analysis",
1294
+ "No recent failures found for this agent.",
1295
+ false
1296
+ );
1297
+ }
1298
+ const dataHash = simpleHash(JSON.stringify({ failures, profile }));
1299
+ const cached = checkCache(agentId, "failure-analysis", dataHash);
1300
+ if (cached) {
1301
+ return shortCircuit(agentId, "failure-analysis", cached.response, true, cached.timestamp);
1302
+ }
1303
+ const prompt = buildFailureAnalysisPrompt(failures, profile);
1304
+ try {
1305
+ const response = await analysisFn(prompt);
1306
+ return storeAndReturn(agentId, "failure-analysis", prompt, response, dataHash);
1307
+ } catch (err) {
1308
+ const message = err instanceof Error ? err.message : String(err);
1309
+ return shortCircuit(agentId, "failure-analysis", `Analysis failed: ${message}`, false);
1310
+ }
1311
+ },
1312
+ async explainAnomaly(agentId, event) {
1313
+ const profile = store.getAgentProfile(agentId);
1314
+ if (!profile) {
1315
+ return shortCircuit(
1316
+ agentId,
1317
+ "anomaly-explanation",
1318
+ "No data available for this agent.",
1319
+ false
1320
+ );
1321
+ }
1322
+ const dataHash = simpleHash(JSON.stringify({ event, profile }));
1323
+ const cached = checkCache(agentId, "anomaly-explanation", dataHash);
1324
+ if (cached) {
1325
+ return shortCircuit(
1326
+ agentId,
1327
+ "anomaly-explanation",
1328
+ cached.response,
1329
+ true,
1330
+ cached.timestamp
1331
+ );
1332
+ }
1333
+ const prompt = buildAnomalyExplanationPrompt(event, profile);
1334
+ try {
1335
+ const response = await analysisFn(prompt);
1336
+ return storeAndReturn(agentId, "anomaly-explanation", prompt, response, dataHash);
1337
+ } catch (err) {
1338
+ const message = err instanceof Error ? err.message : String(err);
1339
+ return shortCircuit(agentId, "anomaly-explanation", `Analysis failed: ${message}`, false);
1340
+ }
1341
+ },
1342
+ async summarizeAgent(agentId) {
1343
+ const profile = store.getAgentProfile(agentId);
1344
+ if (!profile) {
1345
+ return shortCircuit(agentId, "agent-summary", "No data available for this agent.", false);
1346
+ }
1347
+ const recentEvents = store.getRecentEvents(agentId, { limit: 20 });
1348
+ const patterns = store.getPatternHistory(agentId, { limit: 5 });
1349
+ const dataHash = simpleHash(JSON.stringify({ profile, recentEvents, patterns }));
1350
+ const cached = checkCache(agentId, "agent-summary", dataHash);
1351
+ if (cached) {
1352
+ return shortCircuit(agentId, "agent-summary", cached.response, true, cached.timestamp);
1353
+ }
1354
+ const prompt = buildAgentSummaryPrompt(profile, recentEvents, patterns);
1355
+ try {
1356
+ const response = await analysisFn(prompt);
1357
+ return storeAndReturn(agentId, "agent-summary", prompt, response, dataHash);
1358
+ } catch (err) {
1359
+ const message = err instanceof Error ? err.message : String(err);
1360
+ return shortCircuit(agentId, "agent-summary", `Analysis failed: ${message}`, false);
1361
+ }
1362
+ },
1363
+ async suggestFixes(agentId) {
1364
+ const profile = store.getAgentProfile(agentId);
1365
+ if (!profile) {
1366
+ return shortCircuit(agentId, "fix-suggestion", "No data available for this agent.", false);
1367
+ }
1368
+ const events = store.getRecentEvents(agentId, { limit: 50 });
1369
+ const failures = events.filter((e) => e.eventType === "execution.failed");
1370
+ const patterns = store.getPatternHistory(agentId, { limit: 5 });
1371
+ if (failures.length === 0 && profile.knownBottlenecks.length === 0) {
1372
+ return shortCircuit(
1373
+ agentId,
1374
+ "fix-suggestion",
1375
+ "Agent is healthy \u2014 no failures or bottlenecks detected.",
1376
+ false
1377
+ );
1378
+ }
1379
+ const dataHash = simpleHash(JSON.stringify({ failures, profile, patterns }));
1380
+ const cached = checkCache(agentId, "fix-suggestion", dataHash);
1381
+ if (cached) {
1382
+ return shortCircuit(agentId, "fix-suggestion", cached.response, true, cached.timestamp);
1383
+ }
1384
+ const prompt = buildFixSuggestionPrompt(failures, profile, patterns);
1385
+ try {
1386
+ const response = await analysisFn(prompt);
1387
+ return storeAndReturn(agentId, "fix-suggestion", prompt, response, dataHash);
1388
+ } catch (err) {
1389
+ const message = err instanceof Error ? err.message : String(err);
1390
+ return shortCircuit(agentId, "fix-suggestion", `Analysis failed: ${message}`, false);
1391
+ }
1392
+ }
1393
+ };
1394
+ }
1395
+
1396
+ // src/json-event-writer.ts
1397
+ var import_node_fs = require("fs");
1398
+ var import_node_path = require("path");
1399
+ function createJsonEventWriter(config) {
1400
+ const { outputDir } = config;
1401
+ function ensureDir() {
1402
+ (0, import_node_fs.mkdirSync)(outputDir, { recursive: true });
1403
+ }
1404
+ function eventFileName(event) {
1405
+ const typePart = event.eventType.replace(/\./g, "-");
1406
+ const agentId = "agentId" in event ? event.agentId : "unknown";
1407
+ return `${typePart}-${agentId}-${event.timestamp}.json`;
1408
+ }
1409
+ return {
1410
+ async write(_graph) {
1411
+ },
1412
+ async writeEvent(event) {
1413
+ ensureDir();
1414
+ const fileName = eventFileName(event);
1415
+ const filePath = (0, import_node_path.join)(outputDir, fileName);
1416
+ (0, import_node_fs.writeFileSync)(filePath, JSON.stringify(event, null, 2), "utf-8");
1417
+ }
1418
+ };
1419
+ }
1420
+
1421
+ // src/knowledge-store.ts
642
1422
  var import_node_fs2 = require("fs");
643
1423
  var import_node_path2 = require("path");
1424
+ var DEFAULT_BASE_DIR = ".agentflow/knowledge";
1425
+ var MAX_RECENT_DURATIONS = 100;
1426
+ var writeCounter = 0;
1427
+ function toDateDir(epochMs) {
1428
+ return new Date(epochMs).toISOString().slice(0, 10);
1429
+ }
1430
+ function readJson(filePath) {
1431
+ try {
1432
+ return JSON.parse((0, import_node_fs2.readFileSync)(filePath, "utf-8"));
1433
+ } catch {
1434
+ return null;
1435
+ }
1436
+ }
1437
+ function writeJsonAtomic(filePath, data) {
1438
+ const tmpPath = `${filePath}.tmp.${Date.now()}`;
1439
+ (0, import_node_fs2.writeFileSync)(tmpPath, JSON.stringify(data, null, 2), "utf-8");
1440
+ (0, import_node_fs2.renameSync)(tmpPath, filePath);
1441
+ }
1442
+ function emptyProfile(agentId) {
1443
+ return {
1444
+ agentId,
1445
+ totalRuns: 0,
1446
+ successCount: 0,
1447
+ failureCount: 0,
1448
+ failureRate: 0,
1449
+ recentDurations: [],
1450
+ lastConformanceScore: null,
1451
+ knownBottlenecks: [],
1452
+ lastPatternTimestamp: null,
1453
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
1454
+ };
1455
+ }
1456
+ function mergeExecutionEvent(profile, event) {
1457
+ const totalRuns = profile.totalRuns + 1;
1458
+ const isFailure = event.eventType === "execution.failed";
1459
+ const successCount = profile.successCount + (isFailure ? 0 : 1);
1460
+ const failureCount = profile.failureCount + (isFailure ? 1 : 0);
1461
+ const durations = [...profile.recentDurations, event.duration];
1462
+ if (durations.length > MAX_RECENT_DURATIONS) {
1463
+ durations.shift();
1464
+ }
1465
+ const conformanceScore = event.processContext?.conformanceScore ?? profile.lastConformanceScore;
1466
+ return {
1467
+ agentId: profile.agentId,
1468
+ totalRuns,
1469
+ successCount,
1470
+ failureCount,
1471
+ failureRate: failureCount / totalRuns,
1472
+ recentDurations: durations,
1473
+ lastConformanceScore: conformanceScore,
1474
+ knownBottlenecks: profile.knownBottlenecks,
1475
+ lastPatternTimestamp: profile.lastPatternTimestamp,
1476
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
1477
+ };
1478
+ }
1479
+ function mergePatternEvent(profile, event) {
1480
+ const existingBottlenecks = new Set(profile.knownBottlenecks);
1481
+ for (const b of event.pattern.topBottlenecks) {
1482
+ existingBottlenecks.add(b.nodeName);
1483
+ }
1484
+ return {
1485
+ ...profile,
1486
+ knownBottlenecks: [...existingBottlenecks],
1487
+ lastPatternTimestamp: event.timestamp,
1488
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
1489
+ };
1490
+ }
1491
+ function createKnowledgeStore(config) {
1492
+ const baseDir = config?.baseDir ?? DEFAULT_BASE_DIR;
1493
+ const eventsDir = (0, import_node_path2.join)(baseDir, "events");
1494
+ const patternsDir = (0, import_node_path2.join)(baseDir, "patterns");
1495
+ const profilesDir = (0, import_node_path2.join)(baseDir, "profiles");
1496
+ const insightsDir = (0, import_node_path2.join)(baseDir, "insights");
1497
+ function ensureDir(dir) {
1498
+ (0, import_node_fs2.mkdirSync)(dir, { recursive: true });
1499
+ }
1500
+ function profilePath(agentId) {
1501
+ const safe = agentId.replace(/[/\\]/g, "_").replace(/\.\./g, "_");
1502
+ return (0, import_node_path2.join)(profilesDir, `${safe}.json`);
1503
+ }
1504
+ function appendExecutionEvent(event) {
1505
+ const dateDir = (0, import_node_path2.join)(eventsDir, event.agentId, toDateDir(event.timestamp));
1506
+ ensureDir(dateDir);
1507
+ const typePart = event.eventType.replace(/\./g, "-");
1508
+ const seq = String(writeCounter++).padStart(4, "0");
1509
+ const fileName = `${typePart}-${event.timestamp}-${seq}.json`;
1510
+ (0, import_node_fs2.writeFileSync)((0, import_node_path2.join)(dateDir, fileName), JSON.stringify(event, null, 2), "utf-8");
1511
+ ensureDir(profilesDir);
1512
+ const existing = readJson(profilePath(event.agentId)) ?? emptyProfile(event.agentId);
1513
+ const updated = mergeExecutionEvent(existing, event);
1514
+ writeJsonAtomic(profilePath(event.agentId), updated);
1515
+ }
1516
+ function appendPatternEvent(event) {
1517
+ const agentPatternDir = (0, import_node_path2.join)(patternsDir, event.agentId);
1518
+ ensureDir(agentPatternDir);
1519
+ const seq = String(writeCounter++).padStart(4, "0");
1520
+ const fileName = `${event.timestamp}-${seq}.json`;
1521
+ (0, import_node_fs2.writeFileSync)((0, import_node_path2.join)(agentPatternDir, fileName), JSON.stringify(event, null, 2), "utf-8");
1522
+ ensureDir(profilesDir);
1523
+ const existing = readJson(profilePath(event.agentId)) ?? emptyProfile(event.agentId);
1524
+ const updated = mergePatternEvent(existing, event);
1525
+ writeJsonAtomic(profilePath(event.agentId), updated);
1526
+ }
1527
+ return {
1528
+ baseDir,
1529
+ append(event) {
1530
+ if (event.eventType === "pattern.discovered" || event.eventType === "pattern.updated") {
1531
+ appendPatternEvent(event);
1532
+ } else {
1533
+ appendExecutionEvent(event);
1534
+ }
1535
+ },
1536
+ getRecentEvents(agentId, options) {
1537
+ const limit = options?.limit ?? 50;
1538
+ const since = options?.since ?? 0;
1539
+ const agentDir = (0, import_node_path2.join)(eventsDir, agentId);
1540
+ if (!(0, import_node_fs2.existsSync)(agentDir)) return [];
1541
+ const events = [];
1542
+ const dateDirs = (0, import_node_fs2.readdirSync)(agentDir).sort().reverse();
1543
+ for (const dateDir of dateDirs) {
1544
+ const fullDateDir = (0, import_node_path2.join)(agentDir, dateDir);
1545
+ let files;
1546
+ try {
1547
+ files = (0, import_node_fs2.readdirSync)(fullDateDir).filter((f) => f.endsWith(".json"));
1548
+ } catch {
1549
+ continue;
1550
+ }
1551
+ for (const file of files) {
1552
+ const event = readJson((0, import_node_path2.join)(fullDateDir, file));
1553
+ if (event && event.timestamp > since) {
1554
+ events.push(event);
1555
+ }
1556
+ }
1557
+ if (events.length >= limit * 2) break;
1558
+ }
1559
+ events.sort((a, b) => b.timestamp - a.timestamp);
1560
+ return events.slice(0, limit);
1561
+ },
1562
+ getAgentProfile(agentId) {
1563
+ return readJson(profilePath(agentId));
1564
+ },
1565
+ getPatternHistory(agentId, options) {
1566
+ const limit = options?.limit ?? 20;
1567
+ const agentPatternDir = (0, import_node_path2.join)(patternsDir, agentId);
1568
+ if (!(0, import_node_fs2.existsSync)(agentPatternDir)) return [];
1569
+ const files = (0, import_node_fs2.readdirSync)(agentPatternDir).filter((f) => f.endsWith(".json")).sort().reverse();
1570
+ const events = [];
1571
+ for (const file of files.slice(0, limit)) {
1572
+ const event = readJson((0, import_node_path2.join)(agentPatternDir, file));
1573
+ if (event) events.push(event);
1574
+ }
1575
+ return events;
1576
+ },
1577
+ compact(options) {
1578
+ let removed = 0;
1579
+ if ((0, import_node_fs2.existsSync)(eventsDir)) {
1580
+ for (const agentId of (0, import_node_fs2.readdirSync)(eventsDir)) {
1581
+ const agentDir = (0, import_node_path2.join)(eventsDir, agentId);
1582
+ let dateDirs;
1583
+ try {
1584
+ dateDirs = (0, import_node_fs2.readdirSync)(agentDir);
1585
+ } catch {
1586
+ continue;
1587
+ }
1588
+ for (const dateDir of dateDirs) {
1589
+ const fullDateDir = (0, import_node_path2.join)(agentDir, dateDir);
1590
+ let files;
1591
+ try {
1592
+ files = (0, import_node_fs2.readdirSync)(fullDateDir).filter((f) => f.endsWith(".json"));
1593
+ } catch {
1594
+ continue;
1595
+ }
1596
+ for (const file of files) {
1597
+ const parts = file.replace(".json", "").split("-");
1598
+ const tsPart = parts[parts.length - 2];
1599
+ const ts = tsPart ? Number.parseInt(tsPart, 10) : 0;
1600
+ if (!Number.isNaN(ts) && ts < options.olderThan) {
1601
+ try {
1602
+ (0, import_node_fs2.rmSync)((0, import_node_path2.join)(fullDateDir, file));
1603
+ removed++;
1604
+ } catch {
1605
+ }
1606
+ }
1607
+ }
1608
+ try {
1609
+ if ((0, import_node_fs2.readdirSync)(fullDateDir).length === 0) {
1610
+ (0, import_node_fs2.rmSync)(fullDateDir, { recursive: true });
1611
+ }
1612
+ } catch {
1613
+ }
1614
+ }
1615
+ }
1616
+ }
1617
+ if ((0, import_node_fs2.existsSync)(patternsDir)) {
1618
+ for (const agentId of (0, import_node_fs2.readdirSync)(patternsDir)) {
1619
+ const agentPatternDir = (0, import_node_path2.join)(patternsDir, agentId);
1620
+ let files;
1621
+ try {
1622
+ files = (0, import_node_fs2.readdirSync)(agentPatternDir).filter((f) => f.endsWith(".json"));
1623
+ } catch {
1624
+ continue;
1625
+ }
1626
+ for (const file of files) {
1627
+ const ts = Number.parseInt(file.split("-")[0] ?? "", 10);
1628
+ if (!Number.isNaN(ts) && ts < options.olderThan) {
1629
+ try {
1630
+ (0, import_node_fs2.rmSync)((0, import_node_path2.join)(agentPatternDir, file));
1631
+ removed++;
1632
+ } catch {
1633
+ }
1634
+ }
1635
+ }
1636
+ }
1637
+ }
1638
+ if ((0, import_node_fs2.existsSync)(insightsDir)) {
1639
+ for (const agentId of (0, import_node_fs2.readdirSync)(insightsDir)) {
1640
+ const agentInsightDir = (0, import_node_path2.join)(insightsDir, agentId);
1641
+ let files;
1642
+ try {
1643
+ files = (0, import_node_fs2.readdirSync)(agentInsightDir).filter((f) => f.endsWith(".json"));
1644
+ } catch {
1645
+ continue;
1646
+ }
1647
+ for (const file of files) {
1648
+ const parts = file.replace(".json", "").split("-");
1649
+ const tsPart = parts[parts.length - 2];
1650
+ const ts = tsPart ? Number.parseInt(tsPart, 10) : 0;
1651
+ if (!Number.isNaN(ts) && ts < options.olderThan) {
1652
+ try {
1653
+ (0, import_node_fs2.rmSync)((0, import_node_path2.join)(agentInsightDir, file));
1654
+ removed++;
1655
+ } catch {
1656
+ }
1657
+ }
1658
+ }
1659
+ }
1660
+ }
1661
+ return { removed };
1662
+ },
1663
+ appendInsight(event) {
1664
+ const agentInsightDir = (0, import_node_path2.join)(insightsDir, event.agentId);
1665
+ ensureDir(agentInsightDir);
1666
+ const seq = String(writeCounter++).padStart(4, "0");
1667
+ const fileName = `${event.insightType}-${event.timestamp}-${seq}.json`;
1668
+ (0, import_node_fs2.writeFileSync)((0, import_node_path2.join)(agentInsightDir, fileName), JSON.stringify(event, null, 2), "utf-8");
1669
+ },
1670
+ getRecentInsights(agentId, options) {
1671
+ const limit = options?.limit ?? 10;
1672
+ const typeFilter = options?.type;
1673
+ const agentInsightDir = (0, import_node_path2.join)(insightsDir, agentId);
1674
+ if (!(0, import_node_fs2.existsSync)(agentInsightDir)) return [];
1675
+ const files = (0, import_node_fs2.readdirSync)(agentInsightDir).filter((f) => f.endsWith(".json"));
1676
+ const events = [];
1677
+ for (const file of files) {
1678
+ const event = readJson((0, import_node_path2.join)(agentInsightDir, file));
1679
+ if (event) {
1680
+ if (typeFilter && event.insightType !== typeFilter) continue;
1681
+ events.push(event);
1682
+ }
1683
+ }
1684
+ events.sort((a, b) => b.timestamp - a.timestamp);
1685
+ return events.slice(0, limit);
1686
+ },
1687
+ // EventWriter interface
1688
+ async write(_graph) {
1689
+ },
1690
+ async writeEvent(event) {
1691
+ this.append(event);
1692
+ }
1693
+ };
1694
+ }
1695
+
1696
+ // src/live.ts
1697
+ var import_node_fs4 = require("fs");
1698
+ var import_node_path4 = require("path");
644
1699
 
645
1700
  // src/loader.ts
646
1701
  function toNodesMap(raw) {
@@ -669,7 +1724,8 @@ function loadGraph(input) {
669
1724
  events: raw.events ?? [],
670
1725
  traceId: raw.traceId,
671
1726
  spanId: raw.spanId,
672
- parentSpanId: raw.parentSpanId
1727
+ parentSpanId: raw.parentSpanId,
1728
+ metadata: raw.metadata ?? {}
673
1729
  };
674
1730
  }
675
1731
  function graphToJson(graph) {
@@ -696,8 +1752,8 @@ function graphToJson(graph) {
696
1752
 
697
1753
  // src/process-audit.ts
698
1754
  var import_node_child_process = require("child_process");
699
- var import_node_fs = require("fs");
700
- var import_node_path = require("path");
1755
+ var import_node_fs3 = require("fs");
1756
+ var import_node_path3 = require("path");
701
1757
  function isPidAlive(pid) {
702
1758
  try {
703
1759
  process.kill(pid, 0);
@@ -708,7 +1764,7 @@ function isPidAlive(pid) {
708
1764
  }
709
1765
  function pidMatchesName(pid, name) {
710
1766
  try {
711
- const cmdline = (0, import_node_fs.readFileSync)(`/proc/${pid}/cmdline`, "utf8");
1767
+ const cmdline = (0, import_node_fs3.readFileSync)(`/proc/${pid}/cmdline`, "utf8");
712
1768
  return cmdline.includes(name);
713
1769
  } catch {
714
1770
  return false;
@@ -716,8 +1772,8 @@ function pidMatchesName(pid, name) {
716
1772
  }
717
1773
  function readPidFile(path) {
718
1774
  try {
719
- const pid = parseInt((0, import_node_fs.readFileSync)(path, "utf8").trim(), 10);
720
- return isNaN(pid) ? null : pid;
1775
+ const pid = parseInt((0, import_node_fs3.readFileSync)(path, "utf8").trim(), 10);
1776
+ return Number.isNaN(pid) ? null : pid;
721
1777
  } catch {
722
1778
  return null;
723
1779
  }
@@ -731,8 +1787,8 @@ function auditPidFile(config) {
731
1787
  pid: null,
732
1788
  alive: false,
733
1789
  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"
1790
+ stale: !(0, import_node_fs3.existsSync)(config.pidFile),
1791
+ reason: (0, import_node_fs3.existsSync)(config.pidFile) ? "PID file exists but content is invalid" : "No PID file found"
736
1792
  };
737
1793
  }
738
1794
  const alive = isPidAlive(pid);
@@ -752,8 +1808,15 @@ function auditSystemd(config) {
752
1808
  if (config.systemdUnit === null || config.systemdUnit === void 0) return null;
753
1809
  const unit = config.systemdUnit;
754
1810
  try {
755
- const raw = (0, import_node_child_process.execSync)(
756
- `systemctl --user show ${unit} --property=ActiveState,SubState,MainPID,NRestarts,Result --no-pager 2>/dev/null`,
1811
+ const raw = (0, import_node_child_process.execFileSync)(
1812
+ "systemctl",
1813
+ [
1814
+ "--user",
1815
+ "show",
1816
+ unit,
1817
+ "--property=ActiveState,SubState,MainPID,NRestarts,Result",
1818
+ "--no-pager"
1819
+ ],
757
1820
  { encoding: "utf8", timeout: 5e3 }
758
1821
  );
759
1822
  const props = {};
@@ -761,11 +1824,11 @@ function auditSystemd(config) {
761
1824
  const [k, ...v] = line.split("=");
762
1825
  if (k) props[k.trim()] = v.join("=").trim();
763
1826
  }
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";
1827
+ const activeState = props.ActiveState ?? "unknown";
1828
+ const subState = props.SubState ?? "unknown";
1829
+ const mainPid = parseInt(props.MainPID ?? "0", 10);
1830
+ const restarts = parseInt(props.NRestarts ?? "0", 10);
1831
+ const result = props.Result ?? "unknown";
769
1832
  return {
770
1833
  unit,
771
1834
  activeState,
@@ -781,9 +1844,9 @@ function auditSystemd(config) {
781
1844
  }
782
1845
  }
783
1846
  function auditWorkers(config) {
784
- if (!config.workersFile || !(0, import_node_fs.existsSync)(config.workersFile)) return null;
1847
+ if (!config.workersFile || !(0, import_node_fs3.existsSync)(config.workersFile)) return null;
785
1848
  try {
786
- const data = JSON.parse((0, import_node_fs.readFileSync)(config.workersFile, "utf8"));
1849
+ const data = JSON.parse((0, import_node_fs3.readFileSync)(config.workersFile, "utf8"));
787
1850
  const orchPid = data.pid ?? null;
788
1851
  const orchAlive = orchPid ? isPidAlive(orchPid) : false;
789
1852
  const workers = [];
@@ -811,17 +1874,17 @@ function auditWorkers(config) {
811
1874
  }
812
1875
  function readCmdline(pid) {
813
1876
  try {
814
- return (0, import_node_fs.readFileSync)(`/proc/${pid}/cmdline`, "utf8").replace(/\0/g, " ").trim();
1877
+ return (0, import_node_fs3.readFileSync)(`/proc/${pid}/cmdline`, "utf8").replace(/\0/g, " ").trim();
815
1878
  } catch {
816
1879
  return "";
817
1880
  }
818
1881
  }
819
1882
  function getOsProcesses(processName) {
820
1883
  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
- );
1884
+ const raw = (0, import_node_child_process.execSync)(`ps -eo pid,pcpu,pmem,etime,lstart,args --no-headers`, {
1885
+ encoding: "utf8",
1886
+ timeout: 5e3
1887
+ });
825
1888
  const results = [];
826
1889
  for (const line of raw.split("\n")) {
827
1890
  if (!line.includes(processName)) continue;
@@ -829,7 +1892,7 @@ function getOsProcesses(processName) {
829
1892
  const trimmed = line.trim();
830
1893
  const parts = trimmed.split(/\s+/);
831
1894
  const pid = parseInt(parts[0] ?? "0", 10);
832
- if (isNaN(pid) || pid <= 0) continue;
1895
+ if (Number.isNaN(pid) || pid <= 0) continue;
833
1896
  const cpu = parts[1] ?? "0";
834
1897
  const mem = parts[2] ?? "0";
835
1898
  const elapsed = parts[3] ?? "";
@@ -844,53 +1907,83 @@ function getOsProcesses(processName) {
844
1907
  }
845
1908
  }
846
1909
  function discoverProcessConfig(dirs) {
847
- let pidFile;
848
- let workersFile;
849
- let processName = "";
1910
+ const configs = discoverAllProcessConfigs(dirs);
1911
+ return configs.length > 0 ? configs[0] ?? null : null;
1912
+ }
1913
+ function discoverAllProcessConfigs(dirs) {
1914
+ const configs = /* @__PURE__ */ new Map();
850
1915
  for (const dir of dirs) {
851
- if (!(0, import_node_fs.existsSync)(dir)) continue;
1916
+ if (!(0, import_node_fs3.existsSync)(dir)) continue;
852
1917
  let entries;
853
1918
  try {
854
- entries = (0, import_node_fs.readdirSync)(dir);
1919
+ entries = (0, import_node_fs3.readdirSync)(dir);
855
1920
  } catch {
856
1921
  continue;
857
1922
  }
858
1923
  for (const f of entries) {
859
- const fp = (0, import_node_path.join)(dir, f);
1924
+ const fp = (0, import_node_path3.join)(dir, f);
860
1925
  try {
861
- if (!(0, import_node_fs.statSync)(fp).isFile()) continue;
1926
+ if (!(0, import_node_fs3.statSync)(fp).isFile()) continue;
862
1927
  } catch {
863
1928
  continue;
864
1929
  }
865
- if (f.endsWith(".pid") && !pidFile) {
866
- pidFile = fp;
867
- if (!processName) {
868
- processName = (0, import_node_path.basename)(f, ".pid");
1930
+ if (f.endsWith(".pid")) {
1931
+ const name = (0, import_node_path3.basename)(f, ".pid");
1932
+ if (!configs.has(name)) {
1933
+ configs.set(name, { processName: name });
869
1934
  }
1935
+ const cfg = configs.get(name) ?? { processName: name };
1936
+ if (!cfg.pidFile) cfg.pidFile = fp;
870
1937
  }
871
- if ((f === "workers.json" || f.endsWith("-workers.json")) && !workersFile) {
872
- workersFile = fp;
873
- if (!processName && f !== "workers.json") {
874
- processName = (0, import_node_path.basename)(f, "-workers.json");
1938
+ if (f === "workers.json" || f.endsWith("-workers.json")) {
1939
+ const name = f === "workers.json" ? "" : (0, import_node_path3.basename)(f, "-workers.json");
1940
+ if (name && !configs.has(name)) {
1941
+ configs.set(name, { processName: name });
1942
+ }
1943
+ if (name) {
1944
+ const cfg = configs.get(name) ?? { processName: name };
1945
+ if (!cfg.workersFile) cfg.workersFile = fp;
875
1946
  }
876
1947
  }
877
1948
  }
878
1949
  }
879
- if (!processName && !pidFile && !workersFile) return null;
880
- if (!processName) processName = "agent";
881
- let systemdUnit;
882
1950
  try {
883
- const unitName = `${processName}.service`;
884
- const result = (0, import_node_child_process.execSync)(
885
- `systemctl --user show ${unitName} --property=LoadState --no-pager 2>/dev/null`,
886
- { encoding: "utf8", timeout: 3e3 }
1951
+ const raw = (0, import_node_child_process.execSync)(
1952
+ "systemctl --user list-units --type=service --all --no-legend --no-pager 2>/dev/null",
1953
+ { encoding: "utf8", timeout: 5e3 }
887
1954
  );
888
- if (result.includes("LoadState=loaded")) {
889
- systemdUnit = unitName;
1955
+ for (const line of raw.trim().split("\n")) {
1956
+ if (!line.trim()) continue;
1957
+ const parts = line.trim().split(/\s+/);
1958
+ const unitName = parts[0] ?? "";
1959
+ const loadState = parts[1] ?? "";
1960
+ if (!unitName.endsWith(".service") || loadState !== "loaded") continue;
1961
+ const name = unitName.replace(".service", "");
1962
+ if (/^(dbus|gpg-agent|dirmngr|keyboxd|snapd\.|pk-|launchpadlib-)/.test(name)) continue;
1963
+ if (!configs.has(name)) {
1964
+ configs.set(name, { processName: name });
1965
+ }
1966
+ const cfg = configs.get(name) ?? { processName: name };
1967
+ cfg.systemdUnit = unitName;
890
1968
  }
891
1969
  } catch {
892
1970
  }
893
- return { processName, pidFile, workersFile, systemdUnit };
1971
+ for (const cfg of configs.values()) {
1972
+ if (cfg.systemdUnit !== void 0) continue;
1973
+ try {
1974
+ const unitName = `${cfg.processName}.service`;
1975
+ const result = (0, import_node_child_process.execFileSync)(
1976
+ "systemctl",
1977
+ ["--user", "show", unitName, "--property=LoadState", "--no-pager"],
1978
+ { encoding: "utf8", timeout: 3e3 }
1979
+ );
1980
+ if (result.includes("LoadState=loaded")) {
1981
+ cfg.systemdUnit = unitName;
1982
+ }
1983
+ } catch {
1984
+ }
1985
+ }
1986
+ return [...configs.values()];
894
1987
  }
895
1988
  function auditProcesses(config) {
896
1989
  const pidFile = auditPidFile(config);
@@ -906,16 +1999,45 @@ function auditProcesses(config) {
906
1999
  }
907
2000
  }
908
2001
  if (systemd?.mainPid) knownPids.add(systemd.mainPid);
2002
+ const childPids = /* @__PURE__ */ new Set();
2003
+ for (const knownPid of knownPids) {
2004
+ try {
2005
+ const childrenRaw = (0, import_node_fs3.readFileSync)(
2006
+ `/proc/${knownPid}/task/${knownPid}/children`,
2007
+ "utf8"
2008
+ ).trim();
2009
+ if (childrenRaw) {
2010
+ for (const c of childrenRaw.split(/\s+/)) {
2011
+ const cp = parseInt(c, 10);
2012
+ if (!Number.isNaN(cp)) childPids.add(cp);
2013
+ }
2014
+ }
2015
+ } catch {
2016
+ }
2017
+ }
2018
+ for (const p of osProcesses) {
2019
+ if (knownPids.has(p.pid)) continue;
2020
+ try {
2021
+ const statusContent = (0, import_node_fs3.readFileSync)(`/proc/${p.pid}/status`, "utf8");
2022
+ const ppidMatch = statusContent.match(/^PPid:\s+(\d+)/m);
2023
+ if (ppidMatch) {
2024
+ const ppid = parseInt(ppidMatch[1] ?? "0", 10);
2025
+ if (knownPids.has(ppid)) childPids.add(p.pid);
2026
+ }
2027
+ } catch {
2028
+ }
2029
+ }
909
2030
  const selfPid = process.pid;
910
2031
  const selfPpid = process.ppid;
911
2032
  const orphans = osProcesses.filter(
912
- (p) => !knownPids.has(p.pid) && p.pid !== selfPid && p.pid !== selfPpid
2033
+ (p) => !knownPids.has(p.pid) && !childPids.has(p.pid) && p.pid !== selfPid && p.pid !== selfPpid
913
2034
  );
914
2035
  const problems = [];
915
2036
  if (pidFile?.stale) problems.push(`Stale PID file: ${pidFile.reason}`);
916
2037
  if (systemd?.crashLooping) problems.push("Systemd unit is crash-looping (auto-restart)");
917
2038
  if (systemd?.failed) problems.push("Systemd unit has failed");
918
- if (systemd && systemd.restarts > 10) problems.push(`High systemd restart count: ${systemd.restarts}`);
2039
+ if (systemd && systemd.restarts > 10)
2040
+ problems.push(`High systemd restart count: ${systemd.restarts}`);
919
2041
  if (pidFile?.pid && systemd?.mainPid && pidFile.pid !== systemd.mainPid) {
920
2042
  problems.push(`PID mismatch: file says ${pidFile.pid}, systemd says ${systemd.mainPid}`);
921
2043
  }
@@ -924,15 +2046,24 @@ function auditProcesses(config) {
924
2046
  if (w.stale) problems.push(`Worker "${w.name}" (pid ${w.pid}) declares running but is dead`);
925
2047
  }
926
2048
  }
927
- if (orphans.length > 0) problems.push(`${orphans.length} orphan process(es) not tracked by PID file or workers registry`);
2049
+ if (orphans.length > 0)
2050
+ problems.push(
2051
+ `${orphans.length} orphan process(es) not tracked by PID file or workers registry`
2052
+ );
928
2053
  return { pidFile, systemd, workers, osProcesses, orphans, problems };
929
2054
  }
930
2055
  function formatAuditReport(result) {
931
2056
  const lines = [];
932
2057
  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");
2058
+ lines.push(
2059
+ "\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"
2060
+ );
2061
+ lines.push(
2062
+ "\u2551 \u{1F50D} P R O C E S S A U D I T \u2551"
2063
+ );
2064
+ lines.push(
2065
+ "\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"
2066
+ );
936
2067
  if (result.pidFile) {
937
2068
  const pf = result.pidFile;
938
2069
  const icon = pf.pid && pf.alive && pf.matchesProcess ? "\u2705" : pf.stale ? "\u26A0\uFE0F " : "\u2139\uFE0F ";
@@ -950,25 +2081,33 @@ function formatAuditReport(result) {
950
2081
  }
951
2082
  if (result.workers) {
952
2083
  const w = result.workers;
953
- lines.push(`
954
- Workers (orchestrator pid ${w.orchestratorPid ?? "unknown"} ${w.orchestratorAlive ? "\u2705" : "\u274C"})`);
2084
+ lines.push(
2085
+ `
2086
+ Workers (orchestrator pid ${w.orchestratorPid ?? "unknown"} ${w.orchestratorAlive ? "\u2705" : "\u274C"})`
2087
+ );
955
2088
  for (const worker of w.workers) {
956
2089
  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}`);
2090
+ lines.push(
2091
+ ` ${icon} ${worker.name.padEnd(14)} pid=${String(worker.pid ?? "-").padEnd(8)} status=${worker.declaredStatus}`
2092
+ );
958
2093
  }
959
2094
  }
960
2095
  if (result.osProcesses.length > 0) {
961
2096
  lines.push(`
962
2097
  OS Processes (${result.osProcesses.length} total)`);
963
2098
  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)}`);
2099
+ lines.push(
2100
+ ` 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)}`
2101
+ );
965
2102
  }
966
2103
  }
967
2104
  if (result.orphans.length > 0) {
968
2105
  lines.push(`
969
2106
  \u26A0\uFE0F ${result.orphans.length} ORPHAN PROCESS(ES):`);
970
2107
  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}`);
2108
+ lines.push(
2109
+ ` PID ${String(p.pid).padEnd(8)} CPU=${p.cpu.padEnd(6)} MEM=${p.mem.padEnd(6)} Up=${p.elapsed}`
2110
+ );
972
2111
  lines.push(` Started: ${p.started}`);
973
2112
  lines.push(` Command: ${p.cmdline || p.command}`);
974
2113
  }
@@ -1006,26 +2145,26 @@ function parseArgs(argv) {
1006
2145
  if (args[0] === "live") args.shift();
1007
2146
  let i = 0;
1008
2147
  while (i < args.length) {
1009
- const arg = args[i];
2148
+ const arg = args[i] ?? "";
1010
2149
  if (arg === "--help" || arg === "-h") {
1011
2150
  printUsage();
1012
2151
  process.exit(0);
1013
2152
  } else if (arg === "--refresh" || arg === "-r") {
1014
2153
  i++;
1015
2154
  const v = parseInt(args[i] ?? "", 10);
1016
- if (!isNaN(v) && v > 0) config.refreshMs = v * 1e3;
2155
+ if (!Number.isNaN(v) && v > 0) config.refreshMs = v * 1e3;
1017
2156
  i++;
1018
2157
  } else if (arg === "--recursive" || arg === "-R") {
1019
2158
  config.recursive = true;
1020
2159
  i++;
1021
2160
  } else if (!arg.startsWith("-")) {
1022
- config.dirs.push((0, import_node_path2.resolve)(arg));
2161
+ config.dirs.push((0, import_node_path4.resolve)(arg));
1023
2162
  i++;
1024
2163
  } else {
1025
2164
  i++;
1026
2165
  }
1027
2166
  }
1028
- if (config.dirs.length === 0) config.dirs.push((0, import_node_path2.resolve)("."));
2167
+ if (config.dirs.length === 0) config.dirs.push((0, import_node_path4.resolve)("."));
1029
2168
  return config;
1030
2169
  }
1031
2170
  function printUsage() {
@@ -1061,7 +2200,7 @@ function scanFiles(dirs, recursive) {
1061
2200
  const seen = /* @__PURE__ */ new Set();
1062
2201
  function scanDir(d, topLevel) {
1063
2202
  try {
1064
- const dirStat = (0, import_node_fs2.statSync)(d);
2203
+ const dirStat = (0, import_node_fs4.statSync)(d);
1065
2204
  const dirMtime = dirStat.mtime.getTime();
1066
2205
  const cachedMtime = dirMtimeCache.get(d);
1067
2206
  if (cachedMtime === dirMtime) {
@@ -1077,13 +2216,13 @@ function scanFiles(dirs, recursive) {
1077
2216
  }
1078
2217
  }
1079
2218
  const dirResults = [];
1080
- for (const f of (0, import_node_fs2.readdirSync)(d)) {
2219
+ for (const f of (0, import_node_fs4.readdirSync)(d)) {
1081
2220
  if (f.startsWith(".")) continue;
1082
- const fp = (0, import_node_path2.join)(d, f);
2221
+ const fp = (0, import_node_path4.join)(d, f);
1083
2222
  if (seen.has(fp)) continue;
1084
2223
  let stat;
1085
2224
  try {
1086
- stat = (0, import_node_fs2.statSync)(fp);
2225
+ stat = (0, import_node_fs4.statSync)(fp);
1087
2226
  } catch {
1088
2227
  continue;
1089
2228
  }
@@ -1094,12 +2233,22 @@ function scanFiles(dirs, recursive) {
1094
2233
  if (!stat.isFile()) continue;
1095
2234
  if (f.endsWith(".json")) {
1096
2235
  seen.add(fp);
1097
- const entry = { filename: f, path: fp, mtime: stat.mtime.getTime(), ext: ".json" };
2236
+ const entry = {
2237
+ filename: f,
2238
+ path: fp,
2239
+ mtime: stat.mtime.getTime(),
2240
+ ext: ".json"
2241
+ };
1098
2242
  results.push(entry);
1099
2243
  dirResults.push(entry);
1100
2244
  } else if (f.endsWith(".jsonl")) {
1101
2245
  seen.add(fp);
1102
- const entry = { filename: f, path: fp, mtime: stat.mtime.getTime(), ext: ".jsonl" };
2246
+ const entry = {
2247
+ filename: f,
2248
+ path: fp,
2249
+ mtime: stat.mtime.getTime(),
2250
+ ext: ".jsonl"
2251
+ };
1103
2252
  results.push(entry);
1104
2253
  dirResults.push(entry);
1105
2254
  }
@@ -1115,13 +2264,13 @@ function scanFiles(dirs, recursive) {
1115
2264
  }
1116
2265
  function safeReadJson(fp) {
1117
2266
  try {
1118
- return JSON.parse((0, import_node_fs2.readFileSync)(fp, "utf8"));
2267
+ return JSON.parse((0, import_node_fs4.readFileSync)(fp, "utf8"));
1119
2268
  } catch {
1120
2269
  return null;
1121
2270
  }
1122
2271
  }
1123
2272
  function nameFromFile(filename) {
1124
- return (0, import_node_path2.basename)(filename).replace(/\.(json|jsonl)$/, "").replace(/-state$/, "");
2273
+ return (0, import_node_path4.basename)(filename).replace(/\.(json|jsonl)$/, "").replace(/-state$/, "");
1125
2274
  }
1126
2275
  function normalizeStatus(val) {
1127
2276
  if (typeof val !== "string") return "unknown";
@@ -1161,7 +2310,7 @@ function findTimestamp(obj) {
1161
2310
  if (typeof val === "number") return val > 1e12 ? val : val * 1e3;
1162
2311
  if (typeof val === "string") {
1163
2312
  const d = Date.parse(val);
1164
- if (!isNaN(d)) return d;
2313
+ if (!Number.isNaN(d)) return d;
1165
2314
  }
1166
2315
  }
1167
2316
  return 0;
@@ -1193,7 +2342,7 @@ function extractDetail(obj) {
1193
2342
  }
1194
2343
  return parts.join(" | ") || "";
1195
2344
  }
1196
- function tryLoadTrace(fp, raw) {
2345
+ function tryLoadTrace(_fp, raw) {
1197
2346
  if (typeof raw !== "object" || raw === null) return null;
1198
2347
  const obj = raw;
1199
2348
  if (!("nodes" in obj)) return null;
@@ -1299,11 +2448,11 @@ function processJsonFile(file) {
1299
2448
  }
1300
2449
  function processJsonlFile(file) {
1301
2450
  try {
1302
- const content = (0, import_node_fs2.readFileSync)(file.path, "utf8").trim();
2451
+ const content = (0, import_node_fs4.readFileSync)(file.path, "utf8").trim();
1303
2452
  if (!content) return [];
1304
2453
  const lines = content.split("\n");
1305
2454
  const lineCount = lines.length;
1306
- const lastObj = JSON.parse(lines[lines.length - 1]);
2455
+ const lastObj = JSON.parse(lines[lines.length - 1] ?? "{}");
1307
2456
  const name = lastObj.jobId ?? lastObj.agentId ?? lastObj.name ?? lastObj.id ?? nameFromFile(file.filename);
1308
2457
  if (lastObj.action !== void 0 || lastObj.jobId !== void 0) {
1309
2458
  const status2 = findStatus(lastObj);
@@ -1399,7 +2548,7 @@ function processJsonlFile(file) {
1399
2548
  }
1400
2549
  const parts = [];
1401
2550
  if (model) {
1402
- const shortModel = model.includes("/") ? model.split("/").pop() : model;
2551
+ const shortModel = model.includes("/") ? model.split("/").pop() ?? model : model;
1403
2552
  parts.push(shortModel.slice(0, 20));
1404
2553
  }
1405
2554
  if (toolCalls.length > 0) {
@@ -1444,7 +2593,8 @@ function writeLine(lines, text) {
1444
2593
  }
1445
2594
  function flushLines(lines) {
1446
2595
  process.stdout.write("\x1B[H");
1447
- process.stdout.write(lines.join("\n") + "\n");
2596
+ process.stdout.write(`${lines.join("\n")}
2597
+ `);
1448
2598
  process.stdout.write("\x1B[J");
1449
2599
  }
1450
2600
  var prevFileCount = 0;
@@ -1538,7 +2688,7 @@ function render(config) {
1538
2688
  const status = fail > 0 ? "error" : running > 0 ? "running" : ok > 0 ? "ok" : "unknown";
1539
2689
  groups.push({
1540
2690
  name: groupName,
1541
- source: records[0].source,
2691
+ source: records[0]?.source ?? "trace",
1542
2692
  status,
1543
2693
  lastTs,
1544
2694
  detail: `${records.length} agents`,
@@ -1563,15 +2713,15 @@ function render(config) {
1563
2713
  if (age > 36e5 || age < 0) continue;
1564
2714
  const idx = 11 - Math.floor(age / 3e5);
1565
2715
  if (idx >= 0 && idx < 12) {
1566
- buckets[idx]++;
1567
- if (r.status === "error") failBuckets[idx]++;
2716
+ buckets[idx] = (buckets[idx] ?? 0) + 1;
2717
+ if (r.status === "error") failBuckets[idx] = (failBuckets[idx] ?? 0) + 1;
1568
2718
  }
1569
2719
  }
1570
2720
  const maxBucket = Math.max(...buckets, 1);
1571
2721
  const sparkChars = " \u2581\u2582\u2583\u2584\u2585\u2586\u2587\u2588";
1572
2722
  const spark = buckets.map((v, i) => {
1573
2723
  const level = Math.round(v / maxBucket * 8);
1574
- return (failBuckets[i] > 0 ? C.red : C.green) + sparkChars[level] + C.reset;
2724
+ return ((failBuckets[i] ?? 0) > 0 ? C.red : C.green) + sparkChars[level] + C.reset;
1575
2725
  }).join("");
1576
2726
  let auditResult = null;
1577
2727
  if (now - lastAuditTime > 1e4) {
@@ -1584,8 +2734,10 @@ function render(config) {
1584
2734
  cachedAuditResult = auditResult;
1585
2735
  lastAuditTime = now;
1586
2736
  } catch (err) {
1587
- process.stderr.write(`[agentflow] process audit error: ${err instanceof Error ? err.message : err}
1588
- `);
2737
+ process.stderr.write(
2738
+ `[agentflow] process audit error: ${err instanceof Error ? err.message : err}
2739
+ `
2740
+ );
1589
2741
  }
1590
2742
  }
1591
2743
  } else {
@@ -1642,7 +2794,7 @@ function render(config) {
1642
2794
  return new Date(ts).toLocaleTimeString();
1643
2795
  }
1644
2796
  function truncate(s, max) {
1645
- return s.length > max ? s.slice(0, max - 1) + "\u2026" : s;
2797
+ return s.length > max ? `${s.slice(0, max - 1)}\u2026` : s;
1646
2798
  }
1647
2799
  const termWidth = process.stdout.columns || 120;
1648
2800
  const detailWidth = Math.max(20, termWidth - 60);
@@ -1683,7 +2835,8 @@ function render(config) {
1683
2835
  if (ar.systemd) {
1684
2836
  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
2837
  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}`;
2838
+ if (ar.systemd.restarts > 0)
2839
+ sysdLabel += ` ${C.dim}(${ar.systemd.restarts} restarts)${C.reset}`;
1687
2840
  }
1688
2841
  let pidLabel = "";
1689
2842
  if (ar.pidFile?.pid) {
@@ -1692,7 +2845,10 @@ function render(config) {
1692
2845
  }
1693
2846
  writeLine(L, "");
1694
2847
  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}`}`);
2848
+ writeLine(
2849
+ L,
2850
+ ` ${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}`}`
2851
+ );
1696
2852
  if (workerParts.length > 0) {
1697
2853
  writeLine(L, ` ${C.dim}Workers${C.reset} ${workerParts.join(" ")}`);
1698
2854
  }
@@ -1704,7 +2860,10 @@ function render(config) {
1704
2860
  if (ar.orphans.length > 0) {
1705
2861
  for (const o of ar.orphans.slice(0, 5)) {
1706
2862
  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}`);
2863
+ writeLine(
2864
+ L,
2865
+ ` ${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}`
2866
+ );
1708
2867
  }
1709
2868
  if (ar.orphans.length > 5) {
1710
2869
  writeLine(L, ` ${C.dim}... +${ar.orphans.length - 5} more orphans${C.reset}`);
@@ -1778,7 +2937,7 @@ function render(config) {
1778
2937
  for (let i = 0; i < Math.min(tree.length, 6); i++) {
1779
2938
  const tg = tree[i];
1780
2939
  const depth = getDistDepth(dt, tg.spanId);
1781
- const indent = " " + "\u2502 ".repeat(Math.max(0, depth - 1));
2940
+ const indent = ` ${"\u2502 ".repeat(Math.max(0, depth - 1))}`;
1782
2941
  const isLast = i === tree.length - 1 || getDistDepth(dt, tree[i + 1]?.spanId) <= depth;
1783
2942
  const conn = depth === 0 ? " " : isLast ? "\u2514\u2500 " : "\u251C\u2500 ";
1784
2943
  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 +2958,7 @@ function render(config) {
1799
2958
  const t = new Date(r.lastActive).toLocaleTimeString();
1800
2959
  const agent = truncate(r.id, 26).padEnd(26);
1801
2960
  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";
2961
+ const ageStr = age < 60 ? `${age}s ago` : age < 3600 ? `${Math.floor(age / 60)}m ago` : `${Math.floor(age / 3600)}h ago`;
1803
2962
  const det = truncate(r.detail, detailWidth);
1804
2963
  writeLine(
1805
2964
  L,
@@ -1813,7 +2972,7 @@ function render(config) {
1813
2972
  for (const d of config.dirs) writeLine(L, ` ${C.dim} ${d}${C.reset}`);
1814
2973
  }
1815
2974
  writeLine(L, "");
1816
- const dirLabel = config.dirs.length === 1 ? config.dirs[0] : `${config.dirs.length} directories`;
2975
+ const dirLabel = config.dirs.length === 1 ? config.dirs[0] ?? "" : `${config.dirs.length} directories`;
1817
2976
  writeLine(L, ` ${C.dim}Watching: ${dirLabel}${C.reset}`);
1818
2977
  writeLine(L, ` ${C.dim}Press Ctrl+C to exit${C.reset}`);
1819
2978
  flushLines(L);
@@ -1829,13 +2988,13 @@ function getDistDepth(dt, spanId, visited) {
1829
2988
  }
1830
2989
  function startLive(argv) {
1831
2990
  const config = parseArgs(argv);
1832
- const valid = config.dirs.filter((d) => (0, import_node_fs2.existsSync)(d));
2991
+ const valid = config.dirs.filter((d) => (0, import_node_fs4.existsSync)(d));
1833
2992
  if (valid.length === 0) {
1834
2993
  console.error(`No valid directories found: ${config.dirs.join(", ")}`);
1835
2994
  console.error("Specify directories containing JSON/JSONL files: agentflow live <dir> [dir...]");
1836
2995
  process.exit(1);
1837
2996
  }
1838
- const invalid = config.dirs.filter((d) => !(0, import_node_fs2.existsSync)(d));
2997
+ const invalid = config.dirs.filter((d) => !(0, import_node_fs4.existsSync)(d));
1839
2998
  if (invalid.length > 0) {
1840
2999
  console.warn(`Skipping non-existent: ${invalid.join(", ")}`);
1841
3000
  }
@@ -1847,7 +3006,7 @@ function startLive(argv) {
1847
3006
  let debounce = null;
1848
3007
  for (const dir of config.dirs) {
1849
3008
  try {
1850
- (0, import_node_fs2.watch)(dir, { recursive: config.recursive }, () => {
3009
+ (0, import_node_fs4.watch)(dir, { recursive: config.recursive }, () => {
1851
3010
  if (debounce) clearTimeout(debounce);
1852
3011
  debounce = setTimeout(() => render(config), 500);
1853
3012
  });
@@ -1864,7 +3023,7 @@ function startLive(argv) {
1864
3023
  };
1865
3024
  process.on("SIGINT", () => {
1866
3025
  cleanup();
1867
- console.log(C.dim + "Monitor stopped." + C.reset);
3026
+ console.log(`${C.dim}Monitor stopped.${C.reset}`);
1868
3027
  process.exit(0);
1869
3028
  });
1870
3029
  process.on("SIGTERM", () => {
@@ -1873,22 +3032,169 @@ function startLive(argv) {
1873
3032
  });
1874
3033
  }
1875
3034
 
3035
+ // src/policy-source.ts
3036
+ var import_node_fs5 = require("fs");
3037
+ var import_node_path5 = require("path");
3038
+ function createPolicySource(store) {
3039
+ return {
3040
+ recentFailureRate(agentId) {
3041
+ const profile = store.getAgentProfile(agentId);
3042
+ return profile?.failureRate ?? 0;
3043
+ },
3044
+ isKnownBottleneck(nodeName) {
3045
+ const profilesDir = (0, import_node_path5.join)(store.baseDir, "profiles");
3046
+ let agentIds;
3047
+ try {
3048
+ agentIds = (0, import_node_fs5.readdirSync)(profilesDir).filter((f) => f.endsWith(".json")).map((f) => f.replace(".json", ""));
3049
+ } catch {
3050
+ return false;
3051
+ }
3052
+ for (const agentId of agentIds) {
3053
+ const profile = store.getAgentProfile(agentId);
3054
+ if (profile?.knownBottlenecks.includes(nodeName)) {
3055
+ return true;
3056
+ }
3057
+ }
3058
+ return false;
3059
+ },
3060
+ lastConformanceScore(agentId) {
3061
+ const profile = store.getAgentProfile(agentId);
3062
+ return profile?.lastConformanceScore ?? null;
3063
+ },
3064
+ getAgentProfile(agentId) {
3065
+ return store.getAgentProfile(agentId);
3066
+ }
3067
+ };
3068
+ }
3069
+
3070
+ // src/receipts.ts
3071
+ function extractTokenCost(node) {
3072
+ const semantic = node.metadata?.semantic;
3073
+ if (semantic?.tokenCost !== void 0 && semantic.tokenCost !== null) {
3074
+ return semantic.tokenCost;
3075
+ }
3076
+ if (node.state?.tokenCost !== void 0 && node.state.tokenCost !== null) {
3077
+ return node.state.tokenCost;
3078
+ }
3079
+ return null;
3080
+ }
3081
+ function extractError(node) {
3082
+ if (node.state?.error !== void 0 && node.state.error !== null) {
3083
+ return String(node.state.error);
3084
+ }
3085
+ if (node.metadata?.error !== void 0 && node.metadata.error !== null) {
3086
+ return String(node.metadata.error);
3087
+ }
3088
+ return null;
3089
+ }
3090
+ function nodeDuration(node) {
3091
+ if (node.endTime === null) return null;
3092
+ return node.endTime - node.startTime;
3093
+ }
3094
+ function toReceipt(graph) {
3095
+ const nodes = [...graph.nodes.values()];
3096
+ nodes.sort((a, b) => a.startTime - b.startTime);
3097
+ const steps = nodes.map((node) => ({
3098
+ nodeId: node.id,
3099
+ name: node.name,
3100
+ type: node.type,
3101
+ status: node.status,
3102
+ durationMs: nodeDuration(node),
3103
+ tokenCost: extractTokenCost(node),
3104
+ error: extractError(node)
3105
+ }));
3106
+ let succeeded = 0;
3107
+ let failed = 0;
3108
+ const skipped = 0;
3109
+ for (const node of nodes) {
3110
+ if (node.status === "completed") {
3111
+ succeeded++;
3112
+ } else if (node.status === "failed" || node.status === "hung" || node.status === "timeout") {
3113
+ failed++;
3114
+ }
3115
+ }
3116
+ const attempted = nodes.length;
3117
+ let totalTokenCost = null;
3118
+ for (const step of steps) {
3119
+ if (step.tokenCost !== null) {
3120
+ totalTokenCost = (totalTokenCost ?? 0) + step.tokenCost;
3121
+ }
3122
+ }
3123
+ const totalDurationMs = graph.endTime !== null ? graph.endTime - graph.startTime : null;
3124
+ return {
3125
+ runId: graph.id,
3126
+ agentId: graph.agentId,
3127
+ status: graph.status,
3128
+ startTime: graph.startTime,
3129
+ endTime: graph.endTime,
3130
+ totalDurationMs,
3131
+ totalTokenCost,
3132
+ steps,
3133
+ summary: { attempted, succeeded, failed, skipped }
3134
+ };
3135
+ }
3136
+ function formatReceipt(receipt) {
3137
+ const lines = [];
3138
+ lines.push("=== Run Receipt ===");
3139
+ lines.push(`Run: ${receipt.runId}`);
3140
+ lines.push(`Agent: ${receipt.agentId}`);
3141
+ lines.push(`Status: ${receipt.status}`);
3142
+ lines.push(
3143
+ `Duration: ${receipt.totalDurationMs !== null ? `${receipt.totalDurationMs}ms` : "\u2014"}`
3144
+ );
3145
+ lines.push("");
3146
+ const s = receipt.summary;
3147
+ lines.push(
3148
+ `Summary: ${s.attempted} attempted, ${s.succeeded} succeeded, ${s.failed} failed, ${s.skipped} skipped`
3149
+ );
3150
+ lines.push("");
3151
+ const nameWidth = Math.max(4, ...receipt.steps.map((st) => st.name.length));
3152
+ const typeWidth = Math.max(4, ...receipt.steps.map((st) => st.type.length));
3153
+ const pad = (str, width) => str.padEnd(width);
3154
+ const padNum = (str, width) => str.padStart(width);
3155
+ const idxWidth = Math.max(2, String(receipt.steps.length).length);
3156
+ const statusWidth = 9;
3157
+ const durWidth = 10;
3158
+ const tokWidth = 8;
3159
+ lines.push(
3160
+ ` ${padNum("#", idxWidth)} | ${pad("Step", nameWidth)} | ${pad("Type", typeWidth)} | ${pad("Status", statusWidth)} | ${padNum("Duration", durWidth)} | ${padNum("Tokens", tokWidth)}`
3161
+ );
3162
+ lines.push(
3163
+ `${"-".repeat(idxWidth + 1)}|${"-".repeat(nameWidth + 2)}|${"-".repeat(typeWidth + 2)}|${"-".repeat(statusWidth + 2)}|${"-".repeat(durWidth + 2)}|${"-".repeat(tokWidth + 2)}`
3164
+ );
3165
+ for (let i = 0; i < receipt.steps.length; i++) {
3166
+ const step = receipt.steps[i];
3167
+ const durStr = step.durationMs !== null ? `${step.durationMs}ms` : "\u2014";
3168
+ const tokStr = step.tokenCost !== null ? String(step.tokenCost) : "\u2014";
3169
+ lines.push(
3170
+ ` ${padNum(String(i + 1), idxWidth)} | ${pad(step.name, nameWidth)} | ${pad(step.type, typeWidth)} | ${pad(step.status, statusWidth)} | ${padNum(durStr, durWidth)} | ${padNum(tokStr, tokWidth)}`
3171
+ );
3172
+ }
3173
+ lines.push("");
3174
+ if (receipt.totalTokenCost !== null) {
3175
+ lines.push(`Total token cost: ${receipt.totalTokenCost}`);
3176
+ } else {
3177
+ lines.push("Total token cost: no cost data");
3178
+ }
3179
+ return lines.join("\n");
3180
+ }
3181
+
1876
3182
  // src/runner.ts
1877
3183
  var import_node_child_process2 = require("child_process");
1878
- var import_node_fs3 = require("fs");
1879
- var import_node_path3 = require("path");
3184
+ var import_node_fs6 = require("fs");
3185
+ var import_node_path6 = require("path");
1880
3186
  function globToRegex(pattern) {
1881
3187
  const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
1882
3188
  return new RegExp(`^${escaped}$`);
1883
3189
  }
1884
3190
  function snapshotDir(dir, patterns) {
1885
3191
  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)) {
3192
+ if (!(0, import_node_fs6.existsSync)(dir)) return result;
3193
+ for (const entry of (0, import_node_fs6.readdirSync)(dir)) {
1888
3194
  if (!patterns.some((re) => re.test(entry))) continue;
1889
- const full = (0, import_node_path3.join)(dir, entry);
3195
+ const full = (0, import_node_path6.join)(dir, entry);
1890
3196
  try {
1891
- const stat = (0, import_node_fs3.statSync)(full);
3197
+ const stat = (0, import_node_fs6.statSync)(full);
1892
3198
  if (stat.isFile()) {
1893
3199
  result.set(full, stat.mtimeMs);
1894
3200
  }
@@ -1898,11 +3204,11 @@ function snapshotDir(dir, patterns) {
1898
3204
  return result;
1899
3205
  }
1900
3206
  function agentIdFromFilename(filePath) {
1901
- const base = (0, import_node_path3.basename)(filePath, ".json");
3207
+ const base = (0, import_node_path6.basename)(filePath, ".json");
1902
3208
  const cleaned = base.replace(/-state$/, "");
1903
3209
  return `alfred-${cleaned}`;
1904
3210
  }
1905
- function deriveAgentId(command) {
3211
+ function deriveAgentId(_command) {
1906
3212
  return "orchestrator";
1907
3213
  }
1908
3214
  function fileTimestamp() {
@@ -1920,7 +3226,7 @@ async function runTraced(config) {
1920
3226
  if (command.length === 0) {
1921
3227
  throw new Error("runTraced: command must not be empty");
1922
3228
  }
1923
- const resolvedTracesDir = (0, import_node_path3.resolve)(tracesDir);
3229
+ const resolvedTracesDir = (0, import_node_path6.resolve)(tracesDir);
1924
3230
  const patterns = watchPatterns.map(globToRegex);
1925
3231
  const orchestrator = createGraphBuilder({ agentId, trigger });
1926
3232
  const { traceId, spanId } = orchestrator.traceContext;
@@ -2003,23 +3309,27 @@ async function runTraced(config) {
2003
3309
  childBuilder.endNode(childRootId);
2004
3310
  allGraphs.push(childBuilder.build());
2005
3311
  }
2006
- if (!(0, import_node_fs3.existsSync)(resolvedTracesDir)) {
2007
- (0, import_node_fs3.mkdirSync)(resolvedTracesDir, { recursive: true });
3312
+ if (!(0, import_node_fs6.existsSync)(resolvedTracesDir)) {
3313
+ (0, import_node_fs6.mkdirSync)(resolvedTracesDir, { recursive: true });
2008
3314
  }
2009
3315
  const ts = fileTimestamp();
2010
3316
  const tracePaths = [];
2011
3317
  for (const graph of allGraphs) {
2012
3318
  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`);
3319
+ const outPath = (0, import_node_path6.join)(resolvedTracesDir, filename);
3320
+ const resolvedOut = (0, import_node_path6.resolve)(outPath);
3321
+ if (!resolvedOut.startsWith(`${resolvedTracesDir}/`) && resolvedOut !== resolvedTracesDir) {
3322
+ throw new Error(
3323
+ `Path traversal detected: agentId "${graph.agentId}" escapes traces directory`
3324
+ );
2017
3325
  }
2018
- (0, import_node_fs3.writeFileSync)(outPath, JSON.stringify(graphToJson(graph), null, 2), "utf-8");
3326
+ (0, import_node_fs6.writeFileSync)(outPath, JSON.stringify(graphToJson(graph), null, 2), "utf-8");
2019
3327
  tracePaths.push(outPath);
2020
3328
  }
2021
3329
  if (tracePaths.length > 0) {
2022
- console.log(`\u{1F50D} Run "agentflow trace show ${orchestratorGraph.id} --traces-dir ${resolvedTracesDir}" to inspect`);
3330
+ console.log(
3331
+ `\u{1F50D} Run "agentflow trace show ${orchestratorGraph.id} --traces-dir ${resolvedTracesDir}" to inspect`
3332
+ );
2023
3333
  }
2024
3334
  return {
2025
3335
  exitCode,
@@ -2031,9 +3341,169 @@ async function runTraced(config) {
2031
3341
  };
2032
3342
  }
2033
3343
 
3344
+ // src/soma-event-writer.ts
3345
+ var import_node_fs7 = require("fs");
3346
+ var import_node_path7 = require("path");
3347
+ function compactIso(epochMs) {
3348
+ return new Date(epochMs).toISOString().slice(0, 19).replace(/:/g, "");
3349
+ }
3350
+ function isoDate(epochMs) {
3351
+ return new Date(epochMs).toISOString().slice(0, 10);
3352
+ }
3353
+ function renderFrontmatter(fields) {
3354
+ const lines = ["---"];
3355
+ for (const [key, value] of Object.entries(fields)) {
3356
+ if (value === void 0) continue;
3357
+ if (Array.isArray(value)) {
3358
+ lines.push(`${key}: [${value.map((v) => `'${v}'`).join(", ")}]`);
3359
+ } else if (typeof value === "string") {
3360
+ lines.push(`${key}: '${value}'`);
3361
+ } else {
3362
+ lines.push(`${key}: ${String(value)}`);
3363
+ }
3364
+ }
3365
+ lines.push("---");
3366
+ return lines.join("\n");
3367
+ }
3368
+ function formatDuration(ms) {
3369
+ if (ms < 1e3) return `${ms.toFixed(0)}ms`;
3370
+ if (ms < 6e4) return `${(ms / 1e3).toFixed(1)}s`;
3371
+ return `${(ms / 6e4).toFixed(1)}min`;
3372
+ }
3373
+ function executionEventToMarkdown(event) {
3374
+ const isCompleted = event.eventType === "execution.completed";
3375
+ const subtype = isCompleted ? "completed" : "failed";
3376
+ const tags = ["agentflow/execution", `agent/${event.agentId}`, `status/${subtype}`];
3377
+ if (event.processContext?.isAnomaly) {
3378
+ tags.push("agentflow/anomaly");
3379
+ }
3380
+ const frontmatter = {
3381
+ type: "execution",
3382
+ subtype,
3383
+ name: `Execution: ${event.agentId} \u2014 ${subtype}`,
3384
+ source: "agentflow",
3385
+ created: isoDate(event.timestamp),
3386
+ alfred_tags: tags,
3387
+ agentflow_graph_id: event.graphId,
3388
+ duration_ms: event.duration,
3389
+ node_count: event.nodeCount
3390
+ };
3391
+ if (event.processContext) {
3392
+ frontmatter.conformance_score = event.processContext.conformanceScore;
3393
+ frontmatter.is_anomaly = event.processContext.isAnomaly;
3394
+ }
3395
+ const body = [];
3396
+ body.push(`# Execution: ${event.agentId} \u2014 ${subtype}
3397
+ `);
3398
+ body.push(`**Duration:** ${formatDuration(event.duration)} `);
3399
+ body.push(`**Nodes:** ${event.nodeCount} `);
3400
+ body.push(`**Status:** ${event.status}
3401
+ `);
3402
+ if (event.pathSignature) {
3403
+ body.push(`## Path
3404
+ `);
3405
+ body.push(`\`${event.pathSignature}\`
3406
+ `);
3407
+ }
3408
+ if (!isCompleted && event.failurePoint) {
3409
+ const fp = event.failurePoint;
3410
+ body.push(`## Failure Point
3411
+ `);
3412
+ body.push(`**Node:** ${fp.nodeType}:${fp.nodeName} (\`${fp.nodeId}\`) `);
3413
+ if (fp.error) {
3414
+ body.push(`**Error:** ${fp.error}
3415
+ `);
3416
+ }
3417
+ }
3418
+ if (event.processContext) {
3419
+ body.push(`## Process Context
3420
+ `);
3421
+ body.push(`**Conformance:** ${(event.processContext.conformanceScore * 100).toFixed(0)}% `);
3422
+ body.push(`**Anomaly:** ${event.processContext.isAnomaly ? "yes" : "no"}
3423
+ `);
3424
+ }
3425
+ body.push(`## Related
3426
+ `);
3427
+ body.push(`- [[agent/${event.agentId}]]`);
3428
+ return `${renderFrontmatter(frontmatter)}
3429
+
3430
+ ${body.join("\n")}`;
3431
+ }
3432
+ function patternEventToMarkdown(event) {
3433
+ const { pattern } = event;
3434
+ const tags = ["agentflow/pattern", `agent/${event.agentId}`];
3435
+ const frontmatter = {
3436
+ type: "synthesis",
3437
+ subtype: "pattern-discovery",
3438
+ name: `Pattern: ${event.agentId} \u2014 ${pattern.variantCount} variants across ${pattern.totalGraphs} runs`,
3439
+ source: "agentflow",
3440
+ created: isoDate(event.timestamp),
3441
+ alfred_tags: tags,
3442
+ variant_count: pattern.variantCount,
3443
+ total_graphs: pattern.totalGraphs
3444
+ };
3445
+ const body = [];
3446
+ body.push(`# Pattern: ${event.agentId}
3447
+ `);
3448
+ body.push(`**Variants:** ${pattern.variantCount} `);
3449
+ body.push(`**Total Runs:** ${pattern.totalGraphs}
3450
+ `);
3451
+ if (pattern.topVariants.length > 0) {
3452
+ body.push(`## Top Variants
3453
+ `);
3454
+ body.push(`| Path | Count | % |`);
3455
+ body.push(`|------|-------|---|`);
3456
+ for (const v of pattern.topVariants) {
3457
+ const sig = v.pathSignature.length > 60 ? `${v.pathSignature.slice(0, 57)}...` : v.pathSignature;
3458
+ body.push(`| \`${sig}\` | ${v.count} | ${v.percentage.toFixed(1)}% |`);
3459
+ }
3460
+ body.push("");
3461
+ }
3462
+ if (pattern.topBottlenecks.length > 0) {
3463
+ body.push(`## Top Bottlenecks
3464
+ `);
3465
+ body.push(`| Node | Type | p95 |`);
3466
+ body.push(`|------|------|-----|`);
3467
+ for (const b of pattern.topBottlenecks) {
3468
+ body.push(`| ${b.nodeName} | ${b.nodeType} | ${formatDuration(b.p95)} |`);
3469
+ }
3470
+ body.push("");
3471
+ }
3472
+ body.push(`## Related
3473
+ `);
3474
+ body.push(`- [[agent/${event.agentId}]]`);
3475
+ return `${renderFrontmatter(frontmatter)}
3476
+
3477
+ ${body.join("\n")}`;
3478
+ }
3479
+ function createSomaEventWriter(config) {
3480
+ const { inboxDir } = config;
3481
+ function ensureDir() {
3482
+ (0, import_node_fs7.mkdirSync)(inboxDir, { recursive: true });
3483
+ }
3484
+ function eventFileName(event) {
3485
+ const agentId = event.agentId;
3486
+ const ts = compactIso(event.timestamp);
3487
+ if (event.eventType === "pattern.discovered" || event.eventType === "pattern.updated") {
3488
+ return `synthesis-${agentId}-${ts}.md`;
3489
+ }
3490
+ return `execution-${agentId}-${ts}.md`;
3491
+ }
3492
+ return {
3493
+ async write(_graph) {
3494
+ },
3495
+ async writeEvent(event) {
3496
+ ensureDir();
3497
+ const markdown = event.eventType === "pattern.discovered" || event.eventType === "pattern.updated" ? patternEventToMarkdown(event) : executionEventToMarkdown(event);
3498
+ const fileName = eventFileName(event);
3499
+ (0, import_node_fs7.writeFileSync)((0, import_node_path7.join)(inboxDir, fileName), markdown, "utf-8");
3500
+ }
3501
+ };
3502
+ }
3503
+
2034
3504
  // src/trace-store.ts
2035
3505
  var import_promises = require("fs/promises");
2036
- var import_path = require("path");
3506
+ var import_node_path8 = require("path");
2037
3507
  function createTraceStore(dir) {
2038
3508
  async function ensureDir() {
2039
3509
  await (0, import_promises.mkdir)(dir, { recursive: true });
@@ -2050,7 +3520,7 @@ function createTraceStore(dir) {
2050
3520
  for (const file of files) {
2051
3521
  if (!file.endsWith(".json")) continue;
2052
3522
  try {
2053
- const content = await (0, import_promises.readFile)((0, import_path.join)(dir, file), "utf-8");
3523
+ const content = await (0, import_promises.readFile)((0, import_node_path8.join)(dir, file), "utf-8");
2054
3524
  const graph = loadGraph(content);
2055
3525
  graphs.push(graph);
2056
3526
  } catch {
@@ -2062,10 +3532,10 @@ function createTraceStore(dir) {
2062
3532
  async save(graph) {
2063
3533
  await ensureDir();
2064
3534
  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) {
3535
+ const filePath = (0, import_node_path8.join)(dir, `${graph.id}.json`);
3536
+ const resolvedBase = (0, import_node_path8.resolve)(dir);
3537
+ const resolvedPath = (0, import_node_path8.resolve)(filePath);
3538
+ if (!resolvedPath.startsWith(`${resolvedBase}/`) && resolvedPath !== resolvedBase) {
2069
3539
  throw new Error(`Path traversal detected: "${graph.id}" escapes base directory`);
2070
3540
  }
2071
3541
  await (0, import_promises.writeFile)(filePath, JSON.stringify(json, null, 2), "utf-8");
@@ -2073,7 +3543,7 @@ function createTraceStore(dir) {
2073
3543
  },
2074
3544
  async get(graphId) {
2075
3545
  await ensureDir();
2076
- const filePath = (0, import_path.join)(dir, `${graphId}.json`);
3546
+ const filePath = (0, import_node_path8.join)(dir, `${graphId}.json`);
2077
3547
  try {
2078
3548
  const content = await (0, import_promises.readFile)(filePath, "utf-8");
2079
3549
  return loadGraph(content);
@@ -2143,15 +3613,15 @@ var STATUS_ICONS = {
2143
3613
  hung: "\u231B",
2144
3614
  timeout: "\u231B"
2145
3615
  };
2146
- function formatDuration(ms) {
3616
+ function formatDuration2(ms) {
2147
3617
  if (ms < 1e3) return `${ms}ms`;
2148
3618
  if (ms < 6e4) return `${(ms / 1e3).toFixed(1)}s`;
2149
3619
  if (ms < 36e5) return `${(ms / 6e4).toFixed(1)}m`;
2150
3620
  return `${(ms / 36e5).toFixed(1)}h`;
2151
3621
  }
2152
- function nodeDuration(node, graphEndTime) {
3622
+ function nodeDuration2(node, graphEndTime) {
2153
3623
  const end = node.endTime ?? graphEndTime;
2154
- return formatDuration(end - node.startTime);
3624
+ return formatDuration2(end - node.startTime);
2155
3625
  }
2156
3626
  function getGenAiInfo(node) {
2157
3627
  const parts = [];
@@ -2183,7 +3653,7 @@ function toAsciiTree(graph) {
2183
3653
  const node = graph.nodes.get(nodeId);
2184
3654
  if (!node) return;
2185
3655
  const icon = STATUS_ICONS[node.status];
2186
- const duration = nodeDuration(node, endTime);
3656
+ const duration = nodeDuration2(node, endTime);
2187
3657
  const genAi = getGenAiInfo(node);
2188
3658
  const violation = hasViolation(node, graph) ? " \u26A0" : "";
2189
3659
  const errorInfo = node.status === "failed" && node.metadata.error ? ` \u2014 ${node.metadata.error}` : "";
@@ -2195,7 +3665,8 @@ function toAsciiTree(graph) {
2195
3665
  const children = getChildren(graph, nodeId);
2196
3666
  const childPrefix = isRoot ? "" : prefix + (isLast ? " " : "\u2502 ");
2197
3667
  for (let i = 0; i < children.length; i++) {
2198
- renderNode(children[i].id, childPrefix, i === children.length - 1, false);
3668
+ const childId = children[i]?.id;
3669
+ if (childId) renderNode(childId, childPrefix, i === children.length - 1, false);
2199
3670
  }
2200
3671
  }
2201
3672
  renderNode(graph.rootNodeId, "", true, true);
@@ -2214,7 +3685,7 @@ function toTimeline(graph) {
2214
3685
  const tickCount = Math.min(5, Math.max(2, Math.floor(barWidth / 10)));
2215
3686
  for (let i = 0; i <= tickCount; i++) {
2216
3687
  const t = totalDuration * i / tickCount;
2217
- scaleLabels.push(formatDuration(t));
3688
+ scaleLabels.push(formatDuration2(t));
2218
3689
  }
2219
3690
  let header = "";
2220
3691
  for (let i = 0; i < scaleLabels.length; i++) {
@@ -2258,7 +3729,7 @@ function toTimeline(graph) {
2258
3729
  }
2259
3730
  }
2260
3731
  const icon = STATUS_ICONS[node.status];
2261
- const duration = nodeDuration(node, graphEnd);
3732
+ const duration = nodeDuration2(node, graphEnd);
2262
3733
  const violation = hasViolation(node, graph) ? " \u26A0" : "";
2263
3734
  lines.push(`${bar} ${icon} ${node.name} (${duration})${violation}`);
2264
3735
  }
@@ -2266,9 +3737,9 @@ function toTimeline(graph) {
2266
3737
  }
2267
3738
 
2268
3739
  // src/watch.ts
2269
- var import_node_fs5 = require("fs");
3740
+ var import_node_fs9 = require("fs");
2270
3741
  var import_node_os = require("os");
2271
- var import_node_path4 = require("path");
3742
+ var import_node_path9 = require("path");
2272
3743
 
2273
3744
  // src/watch-alerts.ts
2274
3745
  var import_node_child_process3 = require("child_process");
@@ -2389,15 +3860,15 @@ function sendCommand(payload, cmd) {
2389
3860
  }
2390
3861
 
2391
3862
  // src/watch-state.ts
2392
- var import_node_fs4 = require("fs");
3863
+ var import_node_fs8 = require("fs");
2393
3864
  function parseDuration(input) {
2394
3865
  const match = input.match(/^(\d+(?:\.\d+)?)\s*(s|m|h|d)$/i);
2395
3866
  if (!match) {
2396
3867
  const n = parseInt(input, 10);
2397
- return isNaN(n) ? 0 : n * 1e3;
3868
+ return Number.isNaN(n) ? 0 : n * 1e3;
2398
3869
  }
2399
- const value = parseFloat(match[1]);
2400
- switch (match[2].toLowerCase()) {
3870
+ const value = parseFloat(match[1] ?? "0");
3871
+ switch (match[2]?.toLowerCase()) {
2401
3872
  case "s":
2402
3873
  return value * 1e3;
2403
3874
  case "m":
@@ -2414,9 +3885,9 @@ function emptyState() {
2414
3885
  return { version: 1, agents: {}, lastPollTime: 0 };
2415
3886
  }
2416
3887
  function loadWatchState(filePath) {
2417
- if (!(0, import_node_fs4.existsSync)(filePath)) return emptyState();
3888
+ if (!(0, import_node_fs8.existsSync)(filePath)) return emptyState();
2418
3889
  try {
2419
- const raw = JSON.parse((0, import_node_fs4.readFileSync)(filePath, "utf8"));
3890
+ const raw = JSON.parse((0, import_node_fs8.readFileSync)(filePath, "utf8"));
2420
3891
  if (raw.version !== 1 || typeof raw.agents !== "object") return emptyState();
2421
3892
  return raw;
2422
3893
  } catch {
@@ -2424,13 +3895,13 @@ function loadWatchState(filePath) {
2424
3895
  }
2425
3896
  }
2426
3897
  function saveWatchState(filePath, state) {
2427
- const tmp = filePath + ".tmp";
3898
+ const tmp = `${filePath}.tmp`;
2428
3899
  try {
2429
- (0, import_node_fs4.writeFileSync)(tmp, JSON.stringify(state, null, 2), "utf8");
2430
- (0, import_node_fs4.renameSync)(tmp, filePath);
3900
+ (0, import_node_fs8.writeFileSync)(tmp, JSON.stringify(state, null, 2), "utf8");
3901
+ (0, import_node_fs8.renameSync)(tmp, filePath);
2431
3902
  } catch {
2432
3903
  try {
2433
- (0, import_node_fs4.writeFileSync)(filePath, JSON.stringify(state, null, 2), "utf8");
3904
+ (0, import_node_fs8.writeFileSync)(filePath, JSON.stringify(state, null, 2), "utf8");
2434
3905
  } catch {
2435
3906
  }
2436
3907
  }
@@ -2440,12 +3911,12 @@ function estimateInterval(history) {
2440
3911
  const sorted = [...history].sort((a, b) => a - b);
2441
3912
  const deltas = [];
2442
3913
  for (let i = 1; i < sorted.length; i++) {
2443
- const d = sorted[i] - sorted[i - 1];
3914
+ const d = (sorted[i] ?? 0) - (sorted[i - 1] ?? 0);
2444
3915
  if (d > 0) deltas.push(d);
2445
3916
  }
2446
3917
  if (deltas.length === 0) return 0;
2447
3918
  deltas.sort((a, b) => a - b);
2448
- return deltas[Math.floor(deltas.length / 2)];
3919
+ return deltas[Math.floor(deltas.length / 2)] ?? 0;
2449
3920
  }
2450
3921
  function detectTransitions(previous, currentRecords, config, now) {
2451
3922
  const alerts = [];
@@ -2602,7 +4073,7 @@ function parseWatchArgs(argv) {
2602
4073
  if (args[0] === "watch") args.shift();
2603
4074
  let i = 0;
2604
4075
  while (i < args.length) {
2605
- const arg = args[i];
4076
+ const arg = args[i] ?? "";
2606
4077
  if (arg === "--help" || arg === "-h") {
2607
4078
  printWatchUsage();
2608
4079
  process.exit(0);
@@ -2625,8 +4096,8 @@ function parseWatchArgs(argv) {
2625
4096
  i++;
2626
4097
  const val = args[i] ?? "";
2627
4098
  if (val === "telegram") {
2628
- const botToken = process.env["AGENTFLOW_TELEGRAM_BOT_TOKEN"] ?? "";
2629
- const chatId = process.env["AGENTFLOW_TELEGRAM_CHAT_ID"] ?? "";
4099
+ const botToken = process.env.AGENTFLOW_TELEGRAM_BOT_TOKEN ?? "";
4100
+ const chatId = process.env.AGENTFLOW_TELEGRAM_CHAT_ID ?? "";
2630
4101
  if (botToken && chatId) {
2631
4102
  notifyChannels.push({ type: "telegram", botToken, chatId });
2632
4103
  } else {
@@ -2643,7 +4114,7 @@ function parseWatchArgs(argv) {
2643
4114
  } else if (arg === "--poll") {
2644
4115
  i++;
2645
4116
  const v = parseInt(args[i] ?? "", 10);
2646
- if (!isNaN(v) && v > 0) pollIntervalMs = v * 1e3;
4117
+ if (!Number.isNaN(v) && v > 0) pollIntervalMs = v * 1e3;
2647
4118
  i++;
2648
4119
  } else if (arg === "--cooldown") {
2649
4120
  i++;
@@ -2658,20 +4129,20 @@ function parseWatchArgs(argv) {
2658
4129
  recursive = true;
2659
4130
  i++;
2660
4131
  } else if (!arg.startsWith("-")) {
2661
- dirs.push((0, import_node_path4.resolve)(arg));
4132
+ dirs.push((0, import_node_path9.resolve)(arg));
2662
4133
  i++;
2663
4134
  } else {
2664
4135
  i++;
2665
4136
  }
2666
4137
  }
2667
- if (dirs.length === 0) dirs.push((0, import_node_path4.resolve)("."));
4138
+ if (dirs.length === 0) dirs.push((0, import_node_path9.resolve)("."));
2668
4139
  if (alertConditions.length === 0) {
2669
4140
  alertConditions.push({ type: "error" });
2670
4141
  alertConditions.push({ type: "recovery" });
2671
4142
  }
2672
4143
  notifyChannels.unshift({ type: "stdout" });
2673
4144
  if (!stateFilePath) {
2674
- stateFilePath = (0, import_node_path4.join)(dirs[0], ".agentflow-watch-state.json");
4145
+ stateFilePath = (0, import_node_path9.join)(dirs[0] ?? ".", ".agentflow-watch-state.json");
2675
4146
  }
2676
4147
  return {
2677
4148
  dirs,
@@ -2679,7 +4150,7 @@ function parseWatchArgs(argv) {
2679
4150
  pollIntervalMs,
2680
4151
  alertConditions,
2681
4152
  notifyChannels,
2682
- stateFilePath: (0, import_node_path4.resolve)(stateFilePath),
4153
+ stateFilePath: (0, import_node_path9.resolve)(stateFilePath),
2683
4154
  cooldownMs
2684
4155
  };
2685
4156
  }
@@ -2733,12 +4204,12 @@ Examples:
2733
4204
  }
2734
4205
  function startWatch(argv) {
2735
4206
  const config = parseWatchArgs(argv);
2736
- const valid = config.dirs.filter((d) => (0, import_node_fs5.existsSync)(d));
4207
+ const valid = config.dirs.filter((d) => (0, import_node_fs9.existsSync)(d));
2737
4208
  if (valid.length === 0) {
2738
4209
  console.error(`No valid directories found: ${config.dirs.join(", ")}`);
2739
4210
  process.exit(1);
2740
4211
  }
2741
- const invalid = config.dirs.filter((d) => !(0, import_node_fs5.existsSync)(d));
4212
+ const invalid = config.dirs.filter((d) => !(0, import_node_fs9.existsSync)(d));
2742
4213
  if (invalid.length > 0) {
2743
4214
  console.warn(`Skipping non-existent: ${invalid.join(", ")}`);
2744
4215
  }
@@ -2759,7 +4230,7 @@ agentflow watch started`);
2759
4230
  console.log(` Poll: ${config.pollIntervalMs / 1e3}s`);
2760
4231
  console.log(` Alert on: ${condLabels.join(", ")}`);
2761
4232
  console.log(
2762
- ` Notify: stdout${channelLabels.length > 0 ? ", " + channelLabels.join(", ") : ""}`
4233
+ ` Notify: stdout${channelLabels.length > 0 ? `, ${channelLabels.join(", ")}` : ""}`
2763
4234
  );
2764
4235
  console.log(` Cooldown: ${Math.floor(config.cooldownMs / 6e4)}m`);
2765
4236
  console.log(` State: ${config.stateFilePath}`);
@@ -2818,12 +4289,30 @@ agentflow watch started`);
2818
4289
  // Annotate the CommonJS export names for ESM import in node:
2819
4290
  0 && (module.exports = {
2820
4291
  auditProcesses,
4292
+ buildAgentSummaryPrompt,
4293
+ buildAnomalyExplanationPrompt,
4294
+ buildFailureAnalysisPrompt,
4295
+ buildFixSuggestionPrompt,
4296
+ checkConformance,
2821
4297
  checkGuards,
4298
+ createEventEmitter,
4299
+ createExecutionEvent,
2822
4300
  createGraphBuilder,
4301
+ createInsightEngine,
4302
+ createJsonEventWriter,
4303
+ createKnowledgeStore,
4304
+ createPatternEvent,
4305
+ createPolicySource,
4306
+ createSomaEventWriter,
2823
4307
  createTraceStore,
4308
+ discoverAllProcessConfigs,
4309
+ discoverProcess,
2824
4310
  discoverProcessConfig,
4311
+ findVariants,
2825
4312
  findWaitingOn,
2826
4313
  formatAuditReport,
4314
+ formatReceipt,
4315
+ getBottlenecks,
2827
4316
  getChildren,
2828
4317
  getCriticalPath,
2829
4318
  getDepth,
@@ -2832,6 +4321,7 @@ agentflow watch started`);
2832
4321
  getHungNodes,
2833
4322
  getNode,
2834
4323
  getParent,
4324
+ getPathSignature,
2835
4325
  getStats,
2836
4326
  getSubtree,
2837
4327
  getTraceTree,
@@ -2843,6 +4333,7 @@ agentflow watch started`);
2843
4333
  startWatch,
2844
4334
  stitchTrace,
2845
4335
  toAsciiTree,
4336
+ toReceipt,
2846
4337
  toTimeline,
2847
4338
  withGuards
2848
4339
  });