swarm-engine 1.3.0 → 1.41.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. package/README.md +45 -7
  2. package/dist/cli/commands/memory.js +117 -0
  3. package/dist/cli/commands/memory.js.map +1 -1
  4. package/dist/core/patterns.d.ts +7 -1
  5. package/dist/core/patterns.d.ts.map +1 -1
  6. package/dist/core/patterns.js +23 -0
  7. package/dist/core/patterns.js.map +1 -1
  8. package/dist/core/types.d.ts +39 -0
  9. package/dist/core/types.d.ts.map +1 -1
  10. package/dist/core/types.js.map +1 -1
  11. package/dist/index.d.ts +55 -0
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +40 -0
  14. package/dist/index.js.map +1 -1
  15. package/dist/memory/index.d.ts +37 -0
  16. package/dist/memory/index.d.ts.map +1 -1
  17. package/dist/memory/index.js +91 -0
  18. package/dist/memory/index.js.map +1 -1
  19. package/dist/runtime/adaptive.d.ts +1 -0
  20. package/dist/runtime/adaptive.d.ts.map +1 -1
  21. package/dist/runtime/adaptive.js +18 -4
  22. package/dist/runtime/adaptive.js.map +1 -1
  23. package/dist/runtime/agent-runner.d.ts +18 -0
  24. package/dist/runtime/agent-runner.d.ts.map +1 -1
  25. package/dist/runtime/agent-runner.js +147 -4
  26. package/dist/runtime/agent-runner.js.map +1 -1
  27. package/dist/runtime/backends/claude-session.d.ts +49 -0
  28. package/dist/runtime/backends/claude-session.d.ts.map +1 -0
  29. package/dist/runtime/backends/claude-session.js +237 -0
  30. package/dist/runtime/backends/claude-session.js.map +1 -0
  31. package/dist/runtime/backends/claude.d.ts.map +1 -1
  32. package/dist/runtime/backends/claude.js +90 -4
  33. package/dist/runtime/backends/claude.js.map +1 -1
  34. package/dist/runtime/backends/types.d.ts +31 -1
  35. package/dist/runtime/backends/types.d.ts.map +1 -1
  36. package/dist/runtime/compaction.d.ts +7 -1
  37. package/dist/runtime/compaction.d.ts.map +1 -1
  38. package/dist/runtime/compaction.js +12 -2
  39. package/dist/runtime/compaction.js.map +1 -1
  40. package/dist/runtime/distiller.d.ts +1 -0
  41. package/dist/runtime/distiller.d.ts.map +1 -1
  42. package/dist/runtime/distiller.js +8 -2
  43. package/dist/runtime/distiller.js.map +1 -1
  44. package/dist/runtime/engine.d.ts +48 -2
  45. package/dist/runtime/engine.d.ts.map +1 -1
  46. package/dist/runtime/engine.js +600 -8
  47. package/dist/runtime/engine.js.map +1 -1
  48. package/dist/runtime/execution-graph.d.ts +86 -0
  49. package/dist/runtime/execution-graph.d.ts.map +1 -0
  50. package/dist/runtime/execution-graph.js +441 -0
  51. package/dist/runtime/execution-graph.js.map +1 -0
  52. package/dist/runtime/executor.d.ts +7 -1
  53. package/dist/runtime/executor.d.ts.map +1 -1
  54. package/dist/runtime/executor.js +20 -0
  55. package/dist/runtime/executor.js.map +1 -1
  56. package/dist/runtime/graph-adversarial.d.ts +88 -0
  57. package/dist/runtime/graph-adversarial.d.ts.map +1 -0
  58. package/dist/runtime/graph-adversarial.js +378 -0
  59. package/dist/runtime/graph-adversarial.js.map +1 -0
  60. package/dist/runtime/graph-analyzer.d.ts +106 -0
  61. package/dist/runtime/graph-analyzer.d.ts.map +1 -0
  62. package/dist/runtime/graph-analyzer.js +321 -0
  63. package/dist/runtime/graph-analyzer.js.map +1 -0
  64. package/dist/runtime/graph-causal.d.ts +91 -0
  65. package/dist/runtime/graph-causal.d.ts.map +1 -0
  66. package/dist/runtime/graph-causal.js +292 -0
  67. package/dist/runtime/graph-causal.js.map +1 -0
  68. package/dist/runtime/graph-context-router.d.ts +73 -0
  69. package/dist/runtime/graph-context-router.d.ts.map +1 -0
  70. package/dist/runtime/graph-context-router.js +162 -0
  71. package/dist/runtime/graph-context-router.js.map +1 -0
  72. package/dist/runtime/graph-discovery.d.ts +71 -0
  73. package/dist/runtime/graph-discovery.d.ts.map +1 -0
  74. package/dist/runtime/graph-discovery.js +364 -0
  75. package/dist/runtime/graph-discovery.js.map +1 -0
  76. package/dist/runtime/graph-dropout.d.ts +59 -0
  77. package/dist/runtime/graph-dropout.d.ts.map +1 -0
  78. package/dist/runtime/graph-dropout.js +204 -0
  79. package/dist/runtime/graph-dropout.js.map +1 -0
  80. package/dist/runtime/graph-embeddings.d.ts +58 -0
  81. package/dist/runtime/graph-embeddings.d.ts.map +1 -0
  82. package/dist/runtime/graph-embeddings.js +299 -0
  83. package/dist/runtime/graph-embeddings.js.map +1 -0
  84. package/dist/runtime/graph-feedback.d.ts +30 -0
  85. package/dist/runtime/graph-feedback.d.ts.map +1 -0
  86. package/dist/runtime/graph-feedback.js +80 -0
  87. package/dist/runtime/graph-feedback.js.map +1 -0
  88. package/dist/runtime/graph-gnn.d.ts +120 -0
  89. package/dist/runtime/graph-gnn.d.ts.map +1 -0
  90. package/dist/runtime/graph-gnn.js +524 -0
  91. package/dist/runtime/graph-gnn.js.map +1 -0
  92. package/dist/runtime/graph-learner.d.ts +70 -0
  93. package/dist/runtime/graph-learner.d.ts.map +1 -0
  94. package/dist/runtime/graph-learner.js +265 -0
  95. package/dist/runtime/graph-learner.js.map +1 -0
  96. package/dist/runtime/graph-meta-adversarial.d.ts +113 -0
  97. package/dist/runtime/graph-meta-adversarial.d.ts.map +1 -0
  98. package/dist/runtime/graph-meta-adversarial.js +366 -0
  99. package/dist/runtime/graph-meta-adversarial.js.map +1 -0
  100. package/dist/runtime/graph-meta.d.ts +115 -0
  101. package/dist/runtime/graph-meta.d.ts.map +1 -0
  102. package/dist/runtime/graph-meta.js +465 -0
  103. package/dist/runtime/graph-meta.js.map +1 -0
  104. package/dist/runtime/graph-self-evolve.d.ts +92 -0
  105. package/dist/runtime/graph-self-evolve.d.ts.map +1 -0
  106. package/dist/runtime/graph-self-evolve.js +422 -0
  107. package/dist/runtime/graph-self-evolve.js.map +1 -0
  108. package/dist/runtime/graph-synthesis.d.ts +47 -0
  109. package/dist/runtime/graph-synthesis.d.ts.map +1 -0
  110. package/dist/runtime/graph-synthesis.js +232 -0
  111. package/dist/runtime/graph-synthesis.js.map +1 -0
  112. package/dist/runtime/graph-trajectory.d.ts +88 -0
  113. package/dist/runtime/graph-trajectory.d.ts.map +1 -0
  114. package/dist/runtime/graph-trajectory.js +334 -0
  115. package/dist/runtime/graph-trajectory.js.map +1 -0
  116. package/dist/runtime/learning-engine.d.ts +12 -0
  117. package/dist/runtime/learning-engine.d.ts.map +1 -1
  118. package/dist/runtime/learning-engine.js +70 -0
  119. package/dist/runtime/learning-engine.js.map +1 -1
  120. package/dist/runtime/prompt-compressor.d.ts +16 -0
  121. package/dist/runtime/prompt-compressor.d.ts.map +1 -0
  122. package/dist/runtime/prompt-compressor.js +68 -0
  123. package/dist/runtime/prompt-compressor.js.map +1 -0
  124. package/dist/runtime/repo-map.d.ts +40 -0
  125. package/dist/runtime/repo-map.d.ts.map +1 -0
  126. package/dist/runtime/repo-map.js +358 -0
  127. package/dist/runtime/repo-map.js.map +1 -0
  128. package/dist/runtime/sdk-mcp-server.d.ts +44 -0
  129. package/dist/runtime/sdk-mcp-server.d.ts.map +1 -0
  130. package/dist/runtime/sdk-mcp-server.js +133 -0
  131. package/dist/runtime/sdk-mcp-server.js.map +1 -0
  132. package/dist/runtime/structured-handoff.d.ts +41 -0
  133. package/dist/runtime/structured-handoff.d.ts.map +1 -0
  134. package/dist/runtime/structured-handoff.js +279 -0
  135. package/dist/runtime/structured-handoff.js.map +1 -0
  136. package/dist/runtime/token-analytics.d.ts +38 -0
  137. package/dist/runtime/token-analytics.d.ts.map +1 -0
  138. package/dist/runtime/token-analytics.js +59 -0
  139. package/dist/runtime/token-analytics.js.map +1 -0
  140. package/dist/runtime/verifier.d.ts +10 -0
  141. package/dist/runtime/verifier.d.ts.map +1 -1
  142. package/dist/runtime/verifier.js +97 -1
  143. package/dist/runtime/verifier.js.map +1 -1
  144. package/package.json +2 -2
  145. package/skills/swarm-output-style/SKILL.md +71 -33
@@ -23,9 +23,32 @@ import { estimateCacheSavings } from './cache-optimizer.js';
23
23
  import { createLivingSpec, updateSpecFromPhase } from './living-spec.js';
24
24
  import { estimateTokens } from '../utils/tokens.js';
25
25
  import { unlinkSync } from 'fs';
26
+ import { execFile } from 'child_process';
27
+ import { promisify } from 'util';
28
+ const execFileAsync = promisify(execFile);
26
29
  import { AgentRunner } from './agent-runner.js';
27
30
  import { LearningEngine } from './learning-engine.js';
31
+ import { ExecutionGraph } from './execution-graph.js';
32
+ import { GraphLearner } from './graph-learner.js';
33
+ import { GraphAnalyzer } from './graph-analyzer.js';
34
+ import { GraphContextRouter } from './graph-context-router.js';
35
+ import { ReviewFeedbackRecorder } from './graph-feedback.js';
36
+ import { parseReviewOutput } from './review-schema.js';
28
37
  import { closeDatabase } from './database.js';
38
+ import { CausalGraphEngine } from './graph-causal.js';
39
+ import { FailurePropagationPredictor } from './graph-gnn.js';
40
+ import { AdversarialEvolver } from './graph-adversarial.js';
41
+ import { MetaPatternSelector } from './graph-meta.js';
42
+ import { PredictiveDropout } from './graph-dropout.js';
43
+ import { PatternSynthesizer } from './graph-synthesis.js';
44
+ import { TrajectoryPredictor } from './graph-trajectory.js';
45
+ import { MetaAdversarialTester } from './graph-meta-adversarial.js';
46
+ import { RuleEvolver } from './graph-self-evolve.js';
47
+ import { TaskDiscovery } from './graph-discovery.js';
48
+ import { OrchestrationEmbedder } from './graph-embeddings.js';
49
+ import { HandoffParser } from './structured-handoff.js';
50
+ import { classifyTaskComplexity } from './cascade.js';
51
+ import { SwarmMcpServer } from './sdk-mcp-server.js';
29
52
  /**
30
53
  * The Swarm Orchestration Engine.
31
54
  *
@@ -61,6 +84,137 @@ export class SwarmEngine {
61
84
  livingSpec = null;
62
85
  agentRunner;
63
86
  learningEngine;
87
+ executionGraph = null;
88
+ graphAnalyzer = null;
89
+ graphLearner = null;
90
+ // Lazy-initialized ML modules (only constructed on first access)
91
+ _reviewFeedback;
92
+ _causalEngine;
93
+ _gnnPredictor;
94
+ _adversarialEvolver;
95
+ _metaSelector;
96
+ _predictiveDropout;
97
+ _gnnWeights;
98
+ _patternSynthesizer;
99
+ _trajectoryPredictor;
100
+ _metaAdversarial;
101
+ _ruleEvolver;
102
+ _taskDiscovery;
103
+ _orchestrationEmbedder;
104
+ swarmMcpServer = null;
105
+ decisions = new Map();
106
+ get reviewFeedback() {
107
+ if (this._reviewFeedback === undefined) {
108
+ this._reviewFeedback = this.executionGraph
109
+ ? this.tryInit(() => new ReviewFeedbackRecorder(this.executionGraph), 'ReviewFeedbackRecorder')
110
+ : null;
111
+ }
112
+ return this._reviewFeedback;
113
+ }
114
+ get causalEngine() {
115
+ if (this._causalEngine === undefined) {
116
+ this._causalEngine = this.executionGraph
117
+ ? this.tryInit(() => new CausalGraphEngine(this.executionGraph), 'CausalGraphEngine')
118
+ : null;
119
+ }
120
+ return this._causalEngine;
121
+ }
122
+ get gnnPredictor() {
123
+ if (this._gnnPredictor === undefined) {
124
+ this._gnnPredictor = this.executionGraph
125
+ ? this.tryInit(() => new FailurePropagationPredictor(this.executionGraph), 'FailurePropagationPredictor')
126
+ : null;
127
+ }
128
+ return this._gnnPredictor;
129
+ }
130
+ get adversarialEvolver() {
131
+ if (this._adversarialEvolver === undefined) {
132
+ this._adversarialEvolver = this.executionGraph
133
+ ? this.tryInit(() => new AdversarialEvolver(this.executionGraph), 'AdversarialEvolver')
134
+ : null;
135
+ }
136
+ return this._adversarialEvolver;
137
+ }
138
+ get metaSelector() {
139
+ if (this._metaSelector === undefined) {
140
+ this._metaSelector = this.executionGraph
141
+ ? this.tryInit(() => new MetaPatternSelector(this.executionGraph), 'MetaPatternSelector')
142
+ : null;
143
+ }
144
+ return this._metaSelector;
145
+ }
146
+ get predictiveDropout() {
147
+ if (this._predictiveDropout === undefined) {
148
+ this._predictiveDropout = this.executionGraph
149
+ ? this.tryInit(() => new PredictiveDropout(this.executionGraph), 'PredictiveDropout')
150
+ : null;
151
+ }
152
+ return this._predictiveDropout;
153
+ }
154
+ get gnnWeights() {
155
+ if (this._gnnWeights === undefined) {
156
+ if (this.gnnPredictor) {
157
+ try {
158
+ this._gnnWeights = this.gnnPredictor.initWeights();
159
+ }
160
+ catch {
161
+ this._gnnWeights = null;
162
+ }
163
+ }
164
+ else {
165
+ this._gnnWeights = null;
166
+ }
167
+ }
168
+ return this._gnnWeights;
169
+ }
170
+ get patternSynthesizer() {
171
+ if (this._patternSynthesizer === undefined) {
172
+ this._patternSynthesizer = this.executionGraph
173
+ ? this.tryInit(() => new PatternSynthesizer(this.executionGraph), 'PatternSynthesizer')
174
+ : null;
175
+ }
176
+ return this._patternSynthesizer;
177
+ }
178
+ get trajectoryPredictor() {
179
+ if (this._trajectoryPredictor === undefined) {
180
+ this._trajectoryPredictor = this.executionGraph
181
+ ? this.tryInit(() => new TrajectoryPredictor(this.executionGraph), 'TrajectoryPredictor')
182
+ : null;
183
+ }
184
+ return this._trajectoryPredictor;
185
+ }
186
+ get metaAdversarial() {
187
+ if (this._metaAdversarial === undefined) {
188
+ this._metaAdversarial = this.executionGraph
189
+ ? this.tryInit(() => new MetaAdversarialTester(this.executionGraph), 'MetaAdversarialTester')
190
+ : null;
191
+ }
192
+ return this._metaAdversarial;
193
+ }
194
+ get ruleEvolver() {
195
+ if (this._ruleEvolver === undefined) {
196
+ this._ruleEvolver = this.executionGraph
197
+ ? this.tryInit(() => new RuleEvolver(this.executionGraph), 'RuleEvolver')
198
+ : null;
199
+ }
200
+ return this._ruleEvolver;
201
+ }
202
+ get taskDiscovery() {
203
+ if (this._taskDiscovery === undefined) {
204
+ this._taskDiscovery = this.executionGraph
205
+ ? this.tryInit(() => new TaskDiscovery(this.executionGraph), 'TaskDiscovery')
206
+ : null;
207
+ }
208
+ return this._taskDiscovery;
209
+ }
210
+ get orchestrationEmbedder() {
211
+ if (this._orchestrationEmbedder === undefined) {
212
+ this._orchestrationEmbedder = this.executionGraph
213
+ ? this.tryInit(() => new OrchestrationEmbedder(this.executionGraph), 'OrchestrationEmbedder')
214
+ : null;
215
+ }
216
+ return this._orchestrationEmbedder;
217
+ }
64
218
  constructor(options) {
65
219
  this.options = options;
66
220
  this.registry = options.registry;
@@ -92,6 +246,51 @@ export class SwarmEngine {
92
246
  this.modelRouter = this.tryInit(() => new ModelRouter(), 'ModelRouter');
93
247
  this.autonomyTracker = this.tryInit(() => new AutonomyTracker(), 'AutonomyTracker');
94
248
  this.compounder = this.tryInit(() => new KnowledgeCompounder(), 'KnowledgeCompounder');
249
+ this.executionGraph = this.tryInit(() => new ExecutionGraph(), 'ExecutionGraph');
250
+ this.executionGraph?.attachToEventBus(this.bus);
251
+ // Create SwarmMcpServer for SDK tool injection
252
+ try {
253
+ this.swarmMcpServer = new SwarmMcpServer({
254
+ phaseOutputs: this.phaseOutputs,
255
+ graph: this.executionGraph,
256
+ decisions: this.decisions,
257
+ });
258
+ }
259
+ catch {
260
+ this.swarmMcpServer = null;
261
+ }
262
+ // Create graph analysis deps (reused by AgentRunner + LearningEngine)
263
+ this.graphLearner = this.executionGraph
264
+ ? this.tryInit(() => new GraphLearner(this.executionGraph), 'GraphLearner')
265
+ : null;
266
+ this.graphAnalyzer = this.executionGraph
267
+ ? this.tryInit(() => new GraphAnalyzer(this.executionGraph), 'GraphAnalyzer')
268
+ : null;
269
+ // ML modules (reviewFeedback, causalEngine, gnnPredictor, adversarialEvolver,
270
+ // metaSelector, predictiveDropout, gnnWeights, patternSynthesizer, trajectoryPredictor,
271
+ // metaAdversarial, ruleEvolver, taskDiscovery, orchestrationEmbedder) are lazy-initialized
272
+ // via property getters — they are only constructed on first access.
273
+ // Prune stale graph data on init (Feature 6: graph decay)
274
+ if (this.executionGraph) {
275
+ try {
276
+ this.executionGraph.pruneOlderThan(90);
277
+ }
278
+ catch {
279
+ /* silent */
280
+ }
281
+ }
282
+ // Rule evolution proposal on startup (skip in mock mode and when no historical data)
283
+ if (this.ruleEvolver && !this.options.mock) {
284
+ try {
285
+ const proposals = this.ruleEvolver.evolveRules(50);
286
+ if (proposals.length > 0) {
287
+ this.log.info(`Rule evolver proposes ${proposals.length} rule updates (top: ${proposals[0].rule.name}, +${Math.round(proposals[0].backtestedImprovement * 100)}%)`);
288
+ }
289
+ }
290
+ catch (e) {
291
+ this.log.debug(`Rule evolution failed: ${e}`);
292
+ }
293
+ }
95
294
  if (options.worktreeEnabled) {
96
295
  try {
97
296
  this.worktreeManager = new WorktreeManager(options.cwd);
@@ -107,6 +306,9 @@ export class SwarmEngine {
107
306
  logger: options.logger,
108
307
  compaction: this.compaction,
109
308
  worktreeManager: this.worktreeManager,
309
+ contextRouter: this.executionGraph ? new GraphContextRouter(this.executionGraph) : undefined,
310
+ graphLearner: this.graphLearner,
311
+ graphAnalyzer: this.graphAnalyzer,
110
312
  });
111
313
  this.learningEngine = new LearningEngine({
112
314
  traceStore: this.traceStore,
@@ -114,6 +316,10 @@ export class SwarmEngine {
114
316
  modelRouter: this.modelRouter,
115
317
  autonomyTracker: this.autonomyTracker,
116
318
  compounder: this.compounder,
319
+ graphLearner: this.graphLearner,
320
+ metaSelector: this.metaSelector,
321
+ predictiveDropout: this.predictiveDropout,
322
+ orchestrationEmbedder: this.orchestrationEmbedder,
117
323
  log: options.logger,
118
324
  });
119
325
  if (options.pluginLoader) {
@@ -124,10 +330,35 @@ export class SwarmEngine {
124
330
  /**
125
331
  * Execute an orchestration from config.
126
332
  */
127
- async execute(config) {
333
+ async execute(config, executeOptions) {
128
334
  // Re-use existing instance if present (e.g., when resuming from checkpoint)
129
335
  const orchestration = this.orchestrations.get(config.id) ?? this.createInstance(config);
130
336
  this.orchestrations.set(config.id, orchestration);
337
+ // Dry run mode: execute only the first phase in 'plan' mode, then return
338
+ if (executeOptions?.dryRun) {
339
+ this.log.info('Dry run mode: running first phase in plan mode');
340
+ const firstPhase = orchestration.phases[0];
341
+ if (firstPhase) {
342
+ this.executor.setPermissionMode('plan');
343
+ try {
344
+ this.phaseOutputs.clear();
345
+ orchestration.status = 'running';
346
+ orchestration.startedAt = new Date();
347
+ const outputs = await this.executePhase(orchestration, firstPhase);
348
+ this.phaseOutputs.set(firstPhase.config.name, outputs);
349
+ orchestration.status = 'completed';
350
+ }
351
+ catch (error) {
352
+ orchestration.status = 'failed';
353
+ this.log.error('Dry run failed', { error: error instanceof Error ? error.message : String(error) });
354
+ }
355
+ finally {
356
+ this.executor.setPermissionMode(this.options.permissionMode ?? 'default');
357
+ orchestration.completedAt = new Date();
358
+ }
359
+ return orchestration;
360
+ }
361
+ }
131
362
  this.log.info(`Starting orchestration: ${config.name}`, {
132
363
  pattern: config.pattern,
133
364
  phases: config.phases.length,
@@ -138,9 +369,45 @@ export class SwarmEngine {
138
369
  pattern: config.pattern,
139
370
  phaseCount: config.phases.length,
140
371
  }, 'engine');
372
+ // Single-agent fast path for simple tasks
373
+ if (config.phases?.length === 1 && config.phases[0].agents?.length === 1) {
374
+ try {
375
+ const complexity = classifyTaskComplexity(config.description || '');
376
+ if (complexity === 'simple') {
377
+ this.log.info('Simple task detected — using single-agent fast path');
378
+ this.bus.emit('system:warning', { type: 'single-agent-bypass', task: config.description }, 'engine');
379
+ try {
380
+ const phase = config.phases[0];
381
+ const agent = phase.agents[0];
382
+ const runContext = {
383
+ phaseOutputs: new Map(),
384
+ intraPhaseOutputs: new Map(),
385
+ livingSpec: null,
386
+ sharedContextFiles: [],
387
+ };
388
+ const instance = await this.agentRunner.executeAgentWithRetry(agent, orchestration.phases[0], orchestration, 0, runContext);
389
+ orchestration.status = instance.result?.output ? 'completed' : 'failed';
390
+ orchestration.startedAt = new Date();
391
+ orchestration.completedAt = new Date();
392
+ orchestration.phases[0].status = orchestration.status;
393
+ if (instance.result)
394
+ orchestration.phases[0].agents = [instance];
395
+ orchestration.usage = instance.usage;
396
+ return orchestration;
397
+ }
398
+ catch (e) {
399
+ this.log.debug(`Single-agent bypass failed, falling through to normal execution: ${e}`);
400
+ // Fall through to normal execution
401
+ }
402
+ }
403
+ }
404
+ catch {
405
+ /* classification failed — fall through */
406
+ }
407
+ }
141
408
  orchestration.status = 'running';
142
409
  orchestration.startedAt = new Date();
143
- this.phaseOutputs = new Map();
410
+ this.phaseOutputs.clear();
144
411
  // TODO: Checkpoint doesn't store phase outputs — resumed phases lose inter-phase context
145
412
  this.reflections = [];
146
413
  this.sharedContextFiles = [];
@@ -158,6 +425,20 @@ export class SwarmEngine {
158
425
  completed.add(phase.config.name);
159
426
  }
160
427
  }
428
+ // GNN pre-execution risk prediction (Feature 2: GNN Failure Propagation)
429
+ if (this.gnnPredictor && this.gnnWeights) {
430
+ try {
431
+ const risks = this.gnnPredictor.predict(orchestration.config.id, this.gnnWeights);
432
+ const highRisk = risks.filter((r) => r.riskScore > 0.7);
433
+ if (highRisk.length > 0) {
434
+ this.log.warn(`GNN predicts high failure risk for ${highRisk.length} nodes`);
435
+ this.bus.emit('system:warning', { type: 'gnn-risk', risks: highRisk }, 'engine');
436
+ }
437
+ }
438
+ catch (e) {
439
+ this.log.debug(`GNN prediction failed: ${e}`);
440
+ }
441
+ }
161
442
  while (completed.size < orchestration.phases.length) {
162
443
  // Check if abort was requested
163
444
  if (this.abortController.signal.aborted) {
@@ -226,11 +507,84 @@ export class SwarmEngine {
226
507
  }
227
508
  if (phase.status === 'completed') {
228
509
  completed.add(phase.config.name);
510
+ // Confidence-gated phase transition (Feature 3)
511
+ if (this.graphAnalyzer) {
512
+ try {
513
+ const reflection = this.reflectionEngine.reflect(phase);
514
+ const patternStats = this.graphLearner?.getPatternStats(orchestration.config.pattern)?.[0];
515
+ const gateOutputs = this.phaseOutputs.get(phase.config.name) || [];
516
+ const gate = this.graphAnalyzer.evaluatePhaseConfidence({
517
+ kind: phase.config.kind ?? 'implement',
518
+ agentCount: phase.agents.length,
519
+ outputs: gateOutputs,
520
+ usage: phase.usage,
521
+ }, { confidence: reflection.confidence }, patternStats
522
+ ? { successRate: patternStats.successRate, avgDurationMs: patternStats.avgDurationMs }
523
+ : undefined);
524
+ if (gate.result === 'halt') {
525
+ this.log.warn(`Phase gate HALT: ${gate.reason}`);
526
+ orchestration.status = 'failed';
527
+ }
528
+ if (gate.result === 'warn') {
529
+ this.log.warn(`Phase gate WARNING: ${gate.reason}`);
530
+ this.bus.emit('system:warning', {
531
+ type: 'gate-warning',
532
+ orchestrationId: orchestration.config.id,
533
+ phase: phase.config.name,
534
+ reason: gate.reason,
535
+ }, 'engine');
536
+ }
537
+ }
538
+ catch {
539
+ /* Graph gate evaluation failed — continue */
540
+ }
541
+ }
542
+ // Trajectory prediction after each completed phase
543
+ if (this.trajectoryPredictor) {
544
+ try {
545
+ const features = this.trajectoryPredictor.extractFeatures(orchestration, phase);
546
+ const pred = this.trajectoryPredictor.predict(features);
547
+ if (pred.successProbability < 0.3 && pred.confidence !== 'low') {
548
+ this.log.warn(`Trajectory predictor: ${(pred.successProbability * 100).toFixed(0)}% success probability (${pred.trajectory})`);
549
+ this.bus.emit('system:warning', { type: 'trajectory-risk', ...pred }, 'engine');
550
+ }
551
+ }
552
+ catch (e) {
553
+ this.log.debug(`Trajectory prediction failed: ${e}`);
554
+ }
555
+ }
229
556
  }
230
557
  else if (phase.status === 'failed') {
231
558
  orchestration.status = 'failed';
232
559
  }
233
560
  }
561
+ // Trajectory forecast over remaining phases
562
+ if (this.trajectoryPredictor) {
563
+ try {
564
+ const forecastCompleted = orchestration.phases
565
+ .filter((p) => p.status === 'completed')
566
+ .map((p) => ({
567
+ name: p.config.name,
568
+ status: p.status,
569
+ tokens: p.usage?.totalTokens || 0,
570
+ durationMs: p.usage?.durationMs || 0,
571
+ confidence: 'medium',
572
+ }));
573
+ const forecastRemaining = orchestration.phases
574
+ .filter((p) => p.status === 'pending')
575
+ .map((p) => p.config.name);
576
+ if (forecastRemaining.length > 0) {
577
+ const forecast = this.trajectoryPredictor.predictTrajectory(orchestration.config.id, forecastCompleted, forecastRemaining, orchestration.config.pattern);
578
+ if (forecast.overallSuccessProb < 0.3) {
579
+ this.log.warn(`Trajectory forecast: ${Math.round(forecast.overallSuccessProb * 100)}% success probability`);
580
+ this.bus.emit('system:warning', { type: 'trajectory-forecast', forecast }, 'engine');
581
+ }
582
+ }
583
+ }
584
+ catch (e) {
585
+ this.log.debug(`Trajectory forecast failed: ${e}`);
586
+ }
587
+ }
234
588
  if (orchestration.status === 'failed')
235
589
  break;
236
590
  // Check cost budget after each batch of phases
@@ -264,6 +618,50 @@ export class SwarmEngine {
264
618
  error: error instanceof Error ? error.message : String(error),
265
619
  });
266
620
  }
621
+ // Post-failure causal analysis (Feature 1: Causal Inference)
622
+ // Note: suggestIntervention scans all historical orchestrations — skip when graph is large
623
+ if (orchestration.status === 'failed' && this.causalEngine && this.executionGraph) {
624
+ try {
625
+ const orchCount = this.executionGraph.getNodesByType('orchestration').length;
626
+ if (orchCount <= 200) {
627
+ const suggestions = this.causalEngine.suggestIntervention(orchestration.config.id);
628
+ if (suggestions.length > 0) {
629
+ this.log.info(`Causal analysis suggests: ${suggestions[0].reasoning}`);
630
+ this.bus.emit('system:warning', { type: 'causal-suggestion', suggestions }, 'engine');
631
+ }
632
+ }
633
+ }
634
+ catch (e) {
635
+ this.log.debug(`Causal suggestion failed: ${e}`);
636
+ }
637
+ }
638
+ // Self-audit on failure: red-team the engine's own ML subsystems
639
+ // Skipped in mock mode and when insufficient historical data to avoid test overhead
640
+ if (orchestration.status === 'failed' && this.metaAdversarial && !this.options.mock) {
641
+ try {
642
+ const orchCount = this.executionGraph?.getNodesByType('orchestration').length ?? 0;
643
+ if (orchCount >= 5) {
644
+ const report = this.metaAdversarial.runFullAudit({
645
+ patternSelector: this.metaSelector ?? undefined,
646
+ dropout: this.predictiveDropout ?? undefined,
647
+ causal: this.causalEngine
648
+ ? {
649
+ estimateCausalEffect: (t, tv, cv) =>
650
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
651
+ this.causalEngine.estimateCausalEffect(t, tv, cv),
652
+ }
653
+ : undefined,
654
+ });
655
+ if (report.overallRisk !== 'low') {
656
+ this.log.warn(`Self-audit found ${report.vulnerabilities.length} ML vulnerabilities (risk: ${report.overallRisk})`);
657
+ this.bus.emit('system:warning', { type: 'self-audit', overallRisk: report.overallRisk, recommendations: report.recommendations }, 'engine');
658
+ }
659
+ }
660
+ }
661
+ catch (e) {
662
+ this.log.debug(`Meta-adversarial audit failed: ${e}`);
663
+ }
664
+ }
267
665
  orchestration.completedAt = new Date();
268
666
  continuousVerifier.stop();
269
667
  // Clean up shared context files
@@ -276,6 +674,15 @@ export class SwarmEngine {
276
674
  this.sharedContextFiles = [];
277
675
  // Record learning outcomes (traces, heuristics, model routing, autonomy)
278
676
  await this.learningEngine.recordOutcome(orchestration, config, this.reflections);
677
+ // Train trajectory predictor on final outcome
678
+ if (this.trajectoryPredictor) {
679
+ try {
680
+ this.trajectoryPredictor.recordOutcome(orchestration, orchestration.status === 'completed');
681
+ }
682
+ catch (e) {
683
+ this.log.debug(`Trajectory training failed: ${e}`);
684
+ }
685
+ }
279
686
  // Compute total usage
280
687
  orchestration.usage = orchestration.phases.reduce((total, phase) => mergeUsageStats(total, phase.usage), emptyUsageStats());
281
688
  this.bus.emit(orchestration.status === 'completed' ? 'orchestration:complete' : 'orchestration:failed', {
@@ -382,6 +789,56 @@ export class SwarmEngine {
382
789
  // Inject learned heuristics and past solutions into the plan
383
790
  this.learningEngine.injectHeuristics(rewrittenPlan);
384
791
  this.learningEngine.injectSolutions(rewrittenPlan);
792
+ // Meta-pattern recommendation (Feature 4: Meta-Pattern Selector)
793
+ if (this.metaSelector) {
794
+ try {
795
+ const recommendation = this.metaSelector.selectPattern(task);
796
+ if (recommendation.confidence > 0.6) {
797
+ this.log.info(`Meta-selector recommends: ${recommendation.pattern} (${Math.round(recommendation.confidence * 100)}% confidence)`);
798
+ }
799
+ }
800
+ catch (e) {
801
+ this.log.debug(`Meta-pattern selection failed: ${e}`);
802
+ }
803
+ }
804
+ // Task discovery: log discovered backlog items from historical failure patterns (skip in mock mode)
805
+ if (this.taskDiscovery && !this.options.mock) {
806
+ try {
807
+ const backlog = this.taskDiscovery.generateBacklog(3);
808
+ if (backlog.length > 0) {
809
+ this.log.info(`Task discovery: ${backlog.length} actionable items found (top: ${backlog[0]?.title})`);
810
+ }
811
+ }
812
+ catch (e) {
813
+ this.log.debug(`Task discovery failed: ${e}`);
814
+ }
815
+ }
816
+ // Pattern synthesis: log insights from topology analysis (skip in mock mode)
817
+ if (this.patternSynthesizer && !this.options.mock) {
818
+ try {
819
+ const insights = this.patternSynthesizer.analyzeTopologyDiffs(3);
820
+ if (insights.length > 0) {
821
+ const top = insights[0];
822
+ this.log.info(`Pattern synthesis top insight: "${top.feature}" (correlation: ${top.successCorrelation.toFixed(2)}, n=${top.sampleSize})`);
823
+ }
824
+ }
825
+ catch (e) {
826
+ this.log.debug(`Pattern synthesis failed: ${e}`);
827
+ }
828
+ }
829
+ // Pattern synthesis: suggest novel pattern from historical success topology
830
+ if (this.patternSynthesizer && !this.options.mock) {
831
+ try {
832
+ const synthesized = this.patternSynthesizer.synthesizePattern(task);
833
+ if (synthesized && synthesized.confidence > 0.5) {
834
+ this.log.info(`Pattern synthesis suggests: ${synthesized.name} (${Math.round(synthesized.confidence * 100)}% confidence)`);
835
+ this.bus.emit('system:warning', { type: 'pattern-synthesis', pattern: synthesized }, 'engine');
836
+ }
837
+ }
838
+ catch (e) {
839
+ this.log.debug(`Pattern synthesis suggestion failed: ${e}`);
840
+ }
841
+ }
385
842
  // Search over candidate plans to find the best one
386
843
  const searcher = new PlanSearcher(this.costModel, this.traceStore ?? undefined);
387
844
  const candidates = searcher.search(rewrittenPlan, {
@@ -400,6 +857,24 @@ export class SwarmEngine {
400
857
  quality: c.cost.qualityScore,
401
858
  }));
402
859
  }
860
+ // Emit plan preview event so listeners can inspect before execution
861
+ try {
862
+ this.bus.emit('system:warning', {
863
+ type: 'plan-preview',
864
+ pattern: patternName,
865
+ phases: bestPlan.phases.map((p) => ({
866
+ name: p.name,
867
+ kind: p.kind,
868
+ agentCount: p.agents?.length || 0,
869
+ agents: p.agents?.map((a) => ({ type: a.type, model: a.model })) || [],
870
+ })),
871
+ estimatedCost: bestPlan.estimates,
872
+ task: task.slice(0, 200),
873
+ }, 'engine');
874
+ }
875
+ catch {
876
+ /* plan preview emit failed — non-critical */
877
+ }
403
878
  return bestPlan;
404
879
  }
405
880
  /**
@@ -417,9 +892,89 @@ export class SwarmEngine {
417
892
  this.bus.emit('system:warning', { type: 'agent-dropout', ...dropout }, 'adaptive');
418
893
  }
419
894
  }
895
+ // Predictive dropout from ML model (Feature 5: Predictive Dropout)
896
+ if (this.predictiveDropout && phase.config.agents.length > 1) {
897
+ try {
898
+ const recommendations = this.predictiveDropout.predictRedundant(phase.config.agents.map((a) => ({ id: a.name || a.type, agentType: a.type })), phase.config.kind ?? 'implement', 0, orchestration.config.pattern || '');
899
+ const toDrop = recommendations.filter((r) => r.shouldDropOut);
900
+ if (toDrop.length > 0) {
901
+ this.log.info(`Predictive dropout: skipping ${toDrop.map((d) => d.agentType).join(', ')}`);
902
+ phase.config.agents = phase.config.agents.filter((a) => !toDrop.some((d) => d.agentType === a.type));
903
+ }
904
+ }
905
+ catch (e) {
906
+ this.log.debug(`Predictive dropout failed: ${e}`);
907
+ }
908
+ }
909
+ // Feature 5: Diff-based review context — inject git diff before review phases
910
+ if (phase.config.kind === 'review' && !this.options.mock) {
911
+ try {
912
+ const cwd = this.options.cwd || process.cwd();
913
+ const { stdout: diff } = await execFileAsync('git', ['diff', '--stat', 'HEAD'], {
914
+ cwd,
915
+ timeout: 10000,
916
+ encoding: 'utf-8',
917
+ });
918
+ const trimmedDiff = diff.slice(0, 3000);
919
+ if (trimmedDiff.trim()) {
920
+ const { stdout: diffDetail } = await execFileAsync('git', ['diff', 'HEAD'], {
921
+ cwd,
922
+ timeout: 10000,
923
+ encoding: 'utf-8',
924
+ });
925
+ const existing = this.phaseOutputs.get(phase.config.name) || [];
926
+ existing.unshift(`## Git Diff Summary\n\`\`\`\n${trimmedDiff}\n\`\`\`\n\n## Detailed Changes\n\`\`\`diff\n${diffDetail.slice(0, 5000)}\n\`\`\``);
927
+ this.phaseOutputs.set(phase.config.name, existing);
928
+ }
929
+ }
930
+ catch (e) {
931
+ this.log.debug(`Diff context failed: ${e}`);
932
+ }
933
+ }
934
+ // Feature 6: Pre-review lint wiring — run lint/typecheck before review phases
935
+ if (phase.config.kind === 'review' && this.verifier && !this.options.mock) {
936
+ try {
937
+ const lintResults = await this.verifier.verify();
938
+ const failures = lintResults.filter((r) => !r.passed);
939
+ if (failures.length > 0) {
940
+ const lintOutput = failures.map((f) => `${f.step}: ${f.output?.slice(0, 500)}`).join('\n');
941
+ const existing = this.phaseOutputs.get(phase.config.name) || [];
942
+ existing.unshift(`## Pre-Review Lint/Typecheck Results\n${lintOutput}`);
943
+ this.phaseOutputs.set(phase.config.name, existing);
944
+ }
945
+ }
946
+ catch (e) {
947
+ this.log.debug(`Pre-review checks failed: ${e}`);
948
+ }
949
+ }
420
950
  const maxRetries = phase.config.maxRetries ?? 2;
951
+ // Smart phase output routing: filter prior outputs by relevance instead of passing everything
952
+ let filteredOutputs = this.phaseOutputs;
953
+ if (phase.config.kind === 'review') {
954
+ // Review phase only needs implement/integrate/test outputs, not research
955
+ const reviewFiltered = new Map();
956
+ for (const [phaseName, outputs] of this.phaseOutputs) {
957
+ if (phaseName.includes('implement') || phaseName.includes('integrate') || phaseName.includes('test')) {
958
+ reviewFiltered.set(phaseName, outputs);
959
+ }
960
+ }
961
+ // Fall back to all outputs if nothing matched
962
+ if (reviewFiltered.size > 0)
963
+ filteredOutputs = reviewFiltered;
964
+ }
965
+ if (phase.config.kind === 'test') {
966
+ // Test phase only needs implement outputs
967
+ const testFiltered = new Map();
968
+ for (const [phaseName, outputs] of this.phaseOutputs) {
969
+ if (phaseName.includes('implement')) {
970
+ testFiltered.set(phaseName, outputs);
971
+ }
972
+ }
973
+ if (testFiltered.size > 0)
974
+ filteredOutputs = testFiltered;
975
+ }
421
976
  const runContext = {
422
- phaseOutputs: this.phaseOutputs,
977
+ phaseOutputs: filteredOutputs,
423
978
  livingSpec: this.livingSpec,
424
979
  sharedContextFiles: this.sharedContextFiles,
425
980
  cwd: this.options.cwd,
@@ -467,9 +1022,16 @@ export class SwarmEngine {
467
1022
  }
468
1023
  }
469
1024
  else {
470
- // Run agents sequentially (with retry)
1025
+ // Run agents sequentially (with retry) — accumulate intra-phase outputs for live re-routing (Feature 1)
1026
+ const intraPhaseOutputs = new Map();
471
1027
  for (const assignment of phase.config.agents) {
472
- await this.agentRunner.executeAgentWithRetry(assignment, phase, orchestration, maxRetries, runContext);
1028
+ const agentInstance = await this.agentRunner.executeAgentWithRetry(assignment, phase, orchestration, maxRetries, { ...runContext, intraPhaseOutputs });
1029
+ // After agent completes, add its output to intra-phase buffer for next sequential agent
1030
+ if (agentInstance?.result?.output) {
1031
+ const existing = intraPhaseOutputs.get(phase.config.name) || [];
1032
+ existing.push(agentInstance.result.output);
1033
+ intraPhaseOutputs.set(phase.config.name, existing);
1034
+ }
473
1035
  // Check phase token budget after each agent
474
1036
  if (phase.config.tokenBudget) {
475
1037
  const phaseTokens = phase.agents.reduce((sum, a) => sum + a.usage.totalTokens, 0);
@@ -490,8 +1052,23 @@ export class SwarmEngine {
490
1052
  }
491
1053
  phase.completedAt = new Date();
492
1054
  phase.usage = phase.agents.reduce((total, agent) => mergeUsageStats(total, agent.usage), emptyUsageStats());
1055
+ // Record review feedback into the knowledge graph (Feature 7)
1056
+ if (phase.config.kind === 'review' && this.reviewFeedback) {
1057
+ for (const agent of phase.agents) {
1058
+ if (agent.result?.output) {
1059
+ try {
1060
+ this.reviewFeedback.recordFindings(orchestration.config.id, phase.config.name, agent.result.output, agent.config.name);
1061
+ const findings = parseReviewOutput(agent.result.output, agent.config.name);
1062
+ this.reviewFeedback.adjustContextWeights(orchestration.config.id, findings);
1063
+ }
1064
+ catch (e) {
1065
+ this.log.debug(`Review feedback recording failed: ${e}`);
1066
+ }
1067
+ }
1068
+ }
1069
+ }
493
1070
  // Run verification after implementation phases
494
- if (phase.config.kind === 'implement' || phase.config.kind === 'integrate') {
1071
+ if ((phase.config.kind === 'implement' || phase.config.kind === 'integrate') && !this.options.mock) {
495
1072
  const results = await this.verifier.verify();
496
1073
  const summary = Verifier.summarize(results);
497
1074
  this.log.info(`Verification: ${summary}`, { phase: phase.config.name });
@@ -520,10 +1097,18 @@ export class SwarmEngine {
520
1097
  agents: phase.agents.length,
521
1098
  usage: phase.usage,
522
1099
  });
523
- // Collect agent outputs for inter-phase context
1100
+ // Collect agent outputs for inter-phase context (structured handoff)
524
1101
  const outputs = phase.agents
525
1102
  .filter((a) => a.result?.output)
526
- .map((a) => `[${a.config.name}]: ${a.result.output.slice(0, 2000)}`);
1103
+ .map((a) => {
1104
+ try {
1105
+ const handoff = HandoffParser.parseAgentOutput(a.result.output);
1106
+ return HandoffParser.serializeForNextPhase([{ agentName: a.config.name, handoff }], 4000);
1107
+ }
1108
+ catch {
1109
+ return `[${a.config.name}]: ${a.result.output.slice(0, 2000)}`;
1110
+ }
1111
+ });
527
1112
  // Update living spec with phase outputs
528
1113
  if (this.livingSpec) {
529
1114
  this.livingSpec = updateSpecFromPhase(this.livingSpec, phase.config.name, outputs);
@@ -635,6 +1220,10 @@ export class SwarmEngine {
635
1220
  }
636
1221
  catch { }
637
1222
  this.learningEngine.close();
1223
+ try {
1224
+ this.executionGraph?.close();
1225
+ }
1226
+ catch { }
638
1227
  closeDatabase();
639
1228
  this.removeSignalHandlers();
640
1229
  }
@@ -660,6 +1249,9 @@ export class SwarmEngine {
660
1249
  get patternRegistry() {
661
1250
  return this.patterns;
662
1251
  }
1252
+ get graph() {
1253
+ return this.executionGraph;
1254
+ }
663
1255
  // ─── Helpers ───────────────────────────────────────────────────
664
1256
  tryInit(factory, name) {
665
1257
  try {