swarm-engine 1.1.1 → 1.38.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +1 -1
- package/README.md +145 -30
- package/commands/diff-review.md +27 -15
- package/commands/discover.md +102 -0
- package/commands/dynamic.md +136 -0
- package/commands/fix-pr.md +30 -24
- package/commands/postmortem.md +106 -0
- package/commands/red-team.md +41 -26
- package/commands/research.md +22 -1
- package/commands/review-cycle.md +38 -20
- package/commands/spike.md +108 -0
- package/commands/swarm.md +68 -60
- package/commands/tdd.md +44 -24
- package/dist/cli/commands/acp.d.ts.map +1 -1
- package/dist/cli/commands/acp.js +12 -2
- package/dist/cli/commands/acp.js.map +1 -1
- package/dist/cli/commands/agents.d.ts.map +1 -1
- package/dist/cli/commands/agents.js +16 -13
- package/dist/cli/commands/agents.js.map +1 -1
- package/dist/cli/commands/completions.d.ts.map +1 -1
- package/dist/cli/commands/completions.js +21 -9
- package/dist/cli/commands/completions.js.map +1 -1
- package/dist/cli/commands/compound.d.ts.map +1 -1
- package/dist/cli/commands/compound.js +1 -2
- package/dist/cli/commands/compound.js.map +1 -1
- package/dist/cli/commands/configure.d.ts.map +1 -1
- package/dist/cli/commands/configure.js +24 -8
- package/dist/cli/commands/configure.js.map +1 -1
- package/dist/cli/commands/convert.d.ts +1 -1
- package/dist/cli/commands/convert.d.ts.map +1 -1
- package/dist/cli/commands/convert.js +22 -48
- package/dist/cli/commands/convert.js.map +1 -1
- package/dist/cli/commands/doctor.d.ts.map +1 -1
- package/dist/cli/commands/doctor.js +1 -3
- package/dist/cli/commands/doctor.js.map +1 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +17 -7
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/install.d.ts.map +1 -1
- package/dist/cli/commands/install.js +1 -1
- package/dist/cli/commands/install.js.map +1 -1
- package/dist/cli/commands/learn.js +6 -6
- package/dist/cli/commands/learn.js.map +1 -1
- package/dist/cli/commands/mcp.d.ts.map +1 -1
- package/dist/cli/commands/mcp.js +1 -2
- package/dist/cli/commands/mcp.js.map +1 -1
- package/dist/cli/commands/memory.d.ts.map +1 -1
- package/dist/cli/commands/memory.js +118 -2
- package/dist/cli/commands/memory.js.map +1 -1
- package/dist/cli/commands/orchestrate.d.ts.map +1 -1
- package/dist/cli/commands/orchestrate.js +20 -7
- package/dist/cli/commands/orchestrate.js.map +1 -1
- package/dist/cli/commands/plan.d.ts.map +1 -1
- package/dist/cli/commands/plan.js.map +1 -1
- package/dist/cli/commands/plugin.d.ts.map +1 -1
- package/dist/cli/commands/plugin.js +8 -5
- package/dist/cli/commands/plugin.js.map +1 -1
- package/dist/cli/commands/resume.js +1 -1
- package/dist/cli/commands/resume.js.map +1 -1
- package/dist/cli/commands/run.d.ts.map +1 -1
- package/dist/cli/commands/run.js +20 -6
- package/dist/cli/commands/run.js.map +1 -1
- package/dist/cli/commands/share.d.ts.map +1 -1
- package/dist/cli/commands/share.js +6 -1
- package/dist/cli/commands/share.js.map +1 -1
- package/dist/cli/commands/status.d.ts.map +1 -1
- package/dist/cli/commands/status.js +15 -7
- package/dist/cli/commands/status.js.map +1 -1
- package/dist/cli/commands/template.d.ts.map +1 -1
- package/dist/cli/commands/template.js +14 -6
- package/dist/cli/commands/template.js.map +1 -1
- package/dist/cli/commands/vault.d.ts.map +1 -1
- package/dist/cli/commands/vault.js +14 -9
- package/dist/cli/commands/vault.js.map +1 -1
- package/dist/cli/commands/verify.d.ts.map +1 -1
- package/dist/cli/commands/verify.js +2 -2
- package/dist/cli/commands/verify.js.map +1 -1
- package/dist/cli/commands/watch.js +1 -1
- package/dist/cli/commands/watch.js.map +1 -1
- package/dist/cli/index.js +14 -4
- package/dist/cli/index.js.map +1 -1
- package/dist/core/checkpoint.js +1 -1
- package/dist/core/checkpoint.js.map +1 -1
- package/dist/core/event-bus.d.ts.map +1 -1
- package/dist/core/event-bus.js +9 -3
- package/dist/core/event-bus.js.map +1 -1
- package/dist/core/lifecycle.js.map +1 -1
- package/dist/core/patterns.d.ts.map +1 -1
- package/dist/core/patterns.js +31 -8
- package/dist/core/patterns.js.map +1 -1
- package/dist/core/permissions.d.ts.map +1 -1
- package/dist/core/permissions.js +21 -10
- package/dist/core/permissions.js.map +1 -1
- package/dist/core/registry.d.ts.map +1 -1
- package/dist/core/registry.js +10 -6
- package/dist/core/registry.js.map +1 -1
- package/dist/core/snapshots.d.ts.map +1 -1
- package/dist/core/snapshots.js +17 -5
- package/dist/core/snapshots.js.map +1 -1
- package/dist/core/types.d.ts +33 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/hooks/index.js.map +1 -1
- package/dist/index.d.ts +114 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +92 -4
- package/dist/index.js.map +1 -1
- package/dist/memory/index.d.ts +38 -0
- package/dist/memory/index.d.ts.map +1 -1
- package/dist/memory/index.js +136 -24
- package/dist/memory/index.js.map +1 -1
- package/dist/memory/schema.d.ts +1 -0
- package/dist/memory/schema.d.ts.map +1 -1
- package/dist/memory/schema.js +20 -19
- package/dist/memory/schema.js.map +1 -1
- package/dist/plugin/index.d.ts.map +1 -1
- package/dist/plugin/index.js.map +1 -1
- package/dist/runtime/acp.d.ts.map +1 -1
- package/dist/runtime/acp.js +71 -41
- package/dist/runtime/acp.js.map +1 -1
- package/dist/runtime/adaptive.d.ts.map +1 -1
- package/dist/runtime/adaptive.js +30 -31
- package/dist/runtime/adaptive.js.map +1 -1
- package/dist/runtime/agent-runner.d.ts +62 -0
- package/dist/runtime/agent-runner.d.ts.map +1 -0
- package/dist/runtime/agent-runner.js +224 -0
- package/dist/runtime/agent-runner.js.map +1 -0
- package/dist/runtime/autonomy.d.ts +1 -0
- package/dist/runtime/autonomy.d.ts.map +1 -1
- package/dist/runtime/autonomy.js +37 -19
- package/dist/runtime/autonomy.js.map +1 -1
- package/dist/runtime/backends/claude.d.ts.map +1 -1
- package/dist/runtime/backends/claude.js +2 -2
- package/dist/runtime/backends/claude.js.map +1 -1
- package/dist/runtime/backends/codex.d.ts.map +1 -1
- package/dist/runtime/backends/codex.js +8 -11
- package/dist/runtime/backends/codex.js.map +1 -1
- package/dist/runtime/backends/gemini.d.ts.map +1 -1
- package/dist/runtime/backends/gemini.js +11 -7
- package/dist/runtime/backends/gemini.js.map +1 -1
- package/dist/runtime/backends/index.js +1 -1
- package/dist/runtime/backends/index.js.map +1 -1
- package/dist/runtime/backends/mock.d.ts.map +1 -1
- package/dist/runtime/backends/mock.js +1 -1
- package/dist/runtime/backends/mock.js.map +1 -1
- package/dist/runtime/backends/vercel-ai.d.ts.map +1 -1
- package/dist/runtime/backends/vercel-ai.js +41 -9
- package/dist/runtime/backends/vercel-ai.js.map +1 -1
- package/dist/runtime/cache-optimizer.d.ts.map +1 -1
- package/dist/runtime/cache-optimizer.js +3 -9
- package/dist/runtime/cache-optimizer.js.map +1 -1
- package/dist/runtime/cascade.d.ts.map +1 -1
- package/dist/runtime/cascade.js +34 -7
- package/dist/runtime/cascade.js.map +1 -1
- package/dist/runtime/chunker.d.ts.map +1 -1
- package/dist/runtime/chunker.js +12 -6
- package/dist/runtime/chunker.js.map +1 -1
- package/dist/runtime/compounder.d.ts +1 -1
- package/dist/runtime/compounder.d.ts.map +1 -1
- package/dist/runtime/compounder.js +30 -11
- package/dist/runtime/compounder.js.map +1 -1
- package/dist/runtime/cost-model.d.ts.map +1 -1
- package/dist/runtime/cost-model.js +1 -1
- package/dist/runtime/cost-model.js.map +1 -1
- package/dist/runtime/database.d.ts +16 -0
- package/dist/runtime/database.d.ts.map +1 -0
- package/dist/runtime/database.js +39 -0
- package/dist/runtime/database.js.map +1 -0
- package/dist/runtime/distiller.d.ts.map +1 -1
- package/dist/runtime/distiller.js +6 -3
- package/dist/runtime/distiller.js.map +1 -1
- package/dist/runtime/engine.d.ts +25 -9
- package/dist/runtime/engine.d.ts.map +1 -1
- package/dist/runtime/engine.js +417 -378
- package/dist/runtime/engine.js.map +1 -1
- package/dist/runtime/execution-graph.d.ts +86 -0
- package/dist/runtime/execution-graph.d.ts.map +1 -0
- package/dist/runtime/execution-graph.js +441 -0
- package/dist/runtime/execution-graph.js.map +1 -0
- package/dist/runtime/executor.d.ts +1 -2
- package/dist/runtime/executor.d.ts.map +1 -1
- package/dist/runtime/executor.js +45 -14
- package/dist/runtime/executor.js.map +1 -1
- package/dist/runtime/graph-adversarial.d.ts +88 -0
- package/dist/runtime/graph-adversarial.d.ts.map +1 -0
- package/dist/runtime/graph-adversarial.js +378 -0
- package/dist/runtime/graph-adversarial.js.map +1 -0
- package/dist/runtime/graph-analyzer.d.ts +106 -0
- package/dist/runtime/graph-analyzer.d.ts.map +1 -0
- package/dist/runtime/graph-analyzer.js +321 -0
- package/dist/runtime/graph-analyzer.js.map +1 -0
- package/dist/runtime/graph-causal.d.ts +91 -0
- package/dist/runtime/graph-causal.d.ts.map +1 -0
- package/dist/runtime/graph-causal.js +292 -0
- package/dist/runtime/graph-causal.js.map +1 -0
- package/dist/runtime/graph-context-router.d.ts +73 -0
- package/dist/runtime/graph-context-router.d.ts.map +1 -0
- package/dist/runtime/graph-context-router.js +162 -0
- package/dist/runtime/graph-context-router.js.map +1 -0
- package/dist/runtime/graph-discovery.d.ts +71 -0
- package/dist/runtime/graph-discovery.d.ts.map +1 -0
- package/dist/runtime/graph-discovery.js +367 -0
- package/dist/runtime/graph-discovery.js.map +1 -0
- package/dist/runtime/graph-dropout.d.ts +59 -0
- package/dist/runtime/graph-dropout.d.ts.map +1 -0
- package/dist/runtime/graph-dropout.js +196 -0
- package/dist/runtime/graph-dropout.js.map +1 -0
- package/dist/runtime/graph-embeddings.d.ts +58 -0
- package/dist/runtime/graph-embeddings.d.ts.map +1 -0
- package/dist/runtime/graph-embeddings.js +301 -0
- package/dist/runtime/graph-embeddings.js.map +1 -0
- package/dist/runtime/graph-feedback.d.ts +30 -0
- package/dist/runtime/graph-feedback.d.ts.map +1 -0
- package/dist/runtime/graph-feedback.js +82 -0
- package/dist/runtime/graph-feedback.js.map +1 -0
- package/dist/runtime/graph-gnn.d.ts +120 -0
- package/dist/runtime/graph-gnn.d.ts.map +1 -0
- package/dist/runtime/graph-gnn.js +524 -0
- package/dist/runtime/graph-gnn.js.map +1 -0
- package/dist/runtime/graph-learner.d.ts +70 -0
- package/dist/runtime/graph-learner.d.ts.map +1 -0
- package/dist/runtime/graph-learner.js +265 -0
- package/dist/runtime/graph-learner.js.map +1 -0
- package/dist/runtime/graph-meta-adversarial.d.ts +113 -0
- package/dist/runtime/graph-meta-adversarial.d.ts.map +1 -0
- package/dist/runtime/graph-meta-adversarial.js +366 -0
- package/dist/runtime/graph-meta-adversarial.js.map +1 -0
- package/dist/runtime/graph-meta.d.ts +115 -0
- package/dist/runtime/graph-meta.d.ts.map +1 -0
- package/dist/runtime/graph-meta.js +437 -0
- package/dist/runtime/graph-meta.js.map +1 -0
- package/dist/runtime/graph-self-evolve.d.ts +92 -0
- package/dist/runtime/graph-self-evolve.d.ts.map +1 -0
- package/dist/runtime/graph-self-evolve.js +422 -0
- package/dist/runtime/graph-self-evolve.js.map +1 -0
- package/dist/runtime/graph-synthesis.d.ts +47 -0
- package/dist/runtime/graph-synthesis.d.ts.map +1 -0
- package/dist/runtime/graph-synthesis.js +232 -0
- package/dist/runtime/graph-synthesis.js.map +1 -0
- package/dist/runtime/graph-trajectory.d.ts +88 -0
- package/dist/runtime/graph-trajectory.d.ts.map +1 -0
- package/dist/runtime/graph-trajectory.js +339 -0
- package/dist/runtime/graph-trajectory.js.map +1 -0
- package/dist/runtime/heuristics.d.ts +1 -0
- package/dist/runtime/heuristics.d.ts.map +1 -1
- package/dist/runtime/heuristics.js +44 -22
- package/dist/runtime/heuristics.js.map +1 -1
- package/dist/runtime/learning-engine.d.ts +63 -0
- package/dist/runtime/learning-engine.d.ts.map +1 -0
- package/dist/runtime/learning-engine.js +273 -0
- package/dist/runtime/learning-engine.js.map +1 -0
- package/dist/runtime/living-spec.js +3 -3
- package/dist/runtime/living-spec.js.map +1 -1
- package/dist/runtime/lsp.d.ts.map +1 -1
- package/dist/runtime/lsp.js +41 -14
- package/dist/runtime/lsp.js.map +1 -1
- package/dist/runtime/mcp.d.ts.map +1 -1
- package/dist/runtime/mcp.js +56 -19
- package/dist/runtime/mcp.js.map +1 -1
- package/dist/runtime/model-router.d.ts +1 -0
- package/dist/runtime/model-router.d.ts.map +1 -1
- package/dist/runtime/model-router.js +37 -21
- package/dist/runtime/model-router.js.map +1 -1
- package/dist/runtime/panes.d.ts.map +1 -1
- package/dist/runtime/panes.js +50 -49
- package/dist/runtime/panes.js.map +1 -1
- package/dist/runtime/plan-search.js +2 -2
- package/dist/runtime/plan-search.js.map +1 -1
- package/dist/runtime/plugins.d.ts +1 -1
- package/dist/runtime/plugins.d.ts.map +1 -1
- package/dist/runtime/plugins.js +63 -47
- package/dist/runtime/plugins.js.map +1 -1
- package/dist/runtime/reflexion.d.ts.map +1 -1
- package/dist/runtime/reflexion.js +4 -8
- package/dist/runtime/reflexion.js.map +1 -1
- package/dist/runtime/review-schema.d.ts.map +1 -1
- package/dist/runtime/review-schema.js +12 -12
- package/dist/runtime/review-schema.js.map +1 -1
- package/dist/runtime/rewriter.d.ts.map +1 -1
- package/dist/runtime/rewriter.js +29 -9
- package/dist/runtime/rewriter.js.map +1 -1
- package/dist/runtime/sharing.d.ts +1 -1
- package/dist/runtime/sharing.d.ts.map +1 -1
- package/dist/runtime/sharing.js +55 -27
- package/dist/runtime/sharing.js.map +1 -1
- package/dist/runtime/stats.d.ts +1 -0
- package/dist/runtime/stats.d.ts.map +1 -1
- package/dist/runtime/stats.js +40 -24
- package/dist/runtime/stats.js.map +1 -1
- package/dist/runtime/templates.d.ts.map +1 -1
- package/dist/runtime/templates.js +2 -2
- package/dist/runtime/templates.js.map +1 -1
- package/dist/runtime/traces.d.ts +1 -0
- package/dist/runtime/traces.d.ts.map +1 -1
- package/dist/runtime/traces.js +50 -28
- package/dist/runtime/traces.js.map +1 -1
- package/dist/runtime/verifier.d.ts.map +1 -1
- package/dist/runtime/verifier.js +12 -6
- package/dist/runtime/verifier.js.map +1 -1
- package/dist/runtime/worktree.d.ts.map +1 -1
- package/dist/runtime/worktree.js +35 -18
- package/dist/runtime/worktree.js.map +1 -1
- package/dist/tui/dashboard.d.ts.map +1 -1
- package/dist/tui/dashboard.js +20 -16
- package/dist/tui/dashboard.js.map +1 -1
- package/dist/tui/progress.d.ts +2 -0
- package/dist/tui/progress.d.ts.map +1 -1
- package/dist/tui/progress.js +105 -33
- package/dist/tui/progress.js.map +1 -1
- package/dist/tui/renderer.d.ts.map +1 -1
- package/dist/tui/renderer.js.map +1 -1
- package/dist/utils/compact-format.js +1 -1
- package/dist/utils/compact-format.js.map +1 -1
- package/dist/utils/config.d.ts.map +1 -1
- package/dist/utils/config.js.map +1 -1
- package/dist/utils/env.d.ts.map +1 -1
- package/dist/utils/env.js +19 -5
- package/dist/utils/env.js.map +1 -1
- package/dist/utils/errors.d.ts.map +1 -1
- package/dist/utils/errors.js +3 -7
- package/dist/utils/errors.js.map +1 -1
- package/dist/utils/output.d.ts.map +1 -1
- package/dist/utils/output.js +6 -2
- package/dist/utils/output.js.map +1 -1
- package/dist/utils/project-config.d.ts +18 -0
- package/dist/utils/project-config.d.ts.map +1 -1
- package/dist/utils/project-config.js +14 -6
- package/dist/utils/project-config.js.map +1 -1
- package/dist/utils/schemas.d.ts.map +1 -1
- package/dist/utils/schemas.js +12 -12
- package/dist/utils/schemas.js.map +1 -1
- package/dist/utils/terminal.d.ts.map +1 -1
- package/dist/utils/terminal.js +18 -7
- package/dist/utils/terminal.js.map +1 -1
- package/dist/utils/tiers.d.ts.map +1 -1
- package/dist/utils/tiers.js +14 -6
- package/dist/utils/tiers.js.map +1 -1
- package/package.json +15 -4
- package/skills/swarm-output-style/SKILL.md +154 -48
package/dist/runtime/engine.js
CHANGED
|
@@ -19,14 +19,30 @@ import { KnowledgeCompounder } from './compounder.js';
|
|
|
19
19
|
import { ModelRouter } from './model-router.js';
|
|
20
20
|
import { AutonomyTracker } from './autonomy.js';
|
|
21
21
|
import { ContinuousVerifier } from './verifier.js';
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
24
|
-
import { createLivingSpec, updateSpecFromPhase, formatLivingSpec } from './living-spec.js';
|
|
22
|
+
import { estimateCacheSavings } from './cache-optimizer.js';
|
|
23
|
+
import { createLivingSpec, updateSpecFromPhase } from './living-spec.js';
|
|
25
24
|
import { estimateTokens } from '../utils/tokens.js';
|
|
26
|
-
import {
|
|
27
|
-
import {
|
|
28
|
-
import {
|
|
29
|
-
import {
|
|
25
|
+
import { unlinkSync } from 'fs';
|
|
26
|
+
import { AgentRunner } from './agent-runner.js';
|
|
27
|
+
import { LearningEngine } from './learning-engine.js';
|
|
28
|
+
import { ExecutionGraph } from './execution-graph.js';
|
|
29
|
+
import { GraphLearner } from './graph-learner.js';
|
|
30
|
+
import { GraphAnalyzer } from './graph-analyzer.js';
|
|
31
|
+
import { GraphContextRouter } from './graph-context-router.js';
|
|
32
|
+
import { ReviewFeedbackRecorder } from './graph-feedback.js';
|
|
33
|
+
import { parseReviewOutput } from './review-schema.js';
|
|
34
|
+
import { closeDatabase } from './database.js';
|
|
35
|
+
import { CausalGraphEngine } from './graph-causal.js';
|
|
36
|
+
import { FailurePropagationPredictor } from './graph-gnn.js';
|
|
37
|
+
import { AdversarialEvolver } from './graph-adversarial.js';
|
|
38
|
+
import { MetaPatternSelector } from './graph-meta.js';
|
|
39
|
+
import { PredictiveDropout } from './graph-dropout.js';
|
|
40
|
+
import { PatternSynthesizer } from './graph-synthesis.js';
|
|
41
|
+
import { TrajectoryPredictor } from './graph-trajectory.js';
|
|
42
|
+
import { MetaAdversarialTester } from './graph-meta-adversarial.js';
|
|
43
|
+
import { RuleEvolver } from './graph-self-evolve.js';
|
|
44
|
+
import { TaskDiscovery } from './graph-discovery.js';
|
|
45
|
+
import { OrchestrationEmbedder } from './graph-embeddings.js';
|
|
30
46
|
/**
|
|
31
47
|
* The Swarm Orchestration Engine.
|
|
32
48
|
*
|
|
@@ -60,6 +76,24 @@ export class SwarmEngine {
|
|
|
60
76
|
signalHandlers = [];
|
|
61
77
|
sharedContextFiles = [];
|
|
62
78
|
livingSpec = null;
|
|
79
|
+
agentRunner;
|
|
80
|
+
learningEngine;
|
|
81
|
+
executionGraph = null;
|
|
82
|
+
graphAnalyzer = null;
|
|
83
|
+
graphLearner = null;
|
|
84
|
+
reviewFeedback = null;
|
|
85
|
+
causalEngine = null;
|
|
86
|
+
gnnPredictor = null;
|
|
87
|
+
adversarialEvolver = null;
|
|
88
|
+
metaSelector = null;
|
|
89
|
+
predictiveDropout = null;
|
|
90
|
+
gnnWeights = null;
|
|
91
|
+
patternSynthesizer = null;
|
|
92
|
+
trajectoryPredictor = null;
|
|
93
|
+
metaAdversarial = null;
|
|
94
|
+
ruleEvolver = null;
|
|
95
|
+
taskDiscovery = null;
|
|
96
|
+
orchestrationEmbedder = null;
|
|
63
97
|
constructor(options) {
|
|
64
98
|
this.options = options;
|
|
65
99
|
this.registry = options.registry;
|
|
@@ -85,43 +119,98 @@ export class SwarmEngine {
|
|
|
85
119
|
this.stats.attachToEventBus(this.bus, undefined);
|
|
86
120
|
this.costModel = new CostModel(this.stats);
|
|
87
121
|
this.reflectionEngine = new ReflectionEngine();
|
|
88
|
-
|
|
89
|
-
this.traceStore = new TraceStore();
|
|
90
|
-
}
|
|
91
|
-
catch (e) {
|
|
92
|
-
this.log.warn('TraceStore init failed', { error: e instanceof Error ? e.message : e });
|
|
93
|
-
}
|
|
122
|
+
this.traceStore = this.tryInit(() => new TraceStore(), 'TraceStore');
|
|
94
123
|
this.replanner = new AdaptiveReplanner(options.logger, this.traceStore ?? undefined);
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
124
|
+
this.heuristicStore = this.tryInit(() => new HeuristicStore(), 'HeuristicStore');
|
|
125
|
+
this.modelRouter = this.tryInit(() => new ModelRouter(), 'ModelRouter');
|
|
126
|
+
this.autonomyTracker = this.tryInit(() => new AutonomyTracker(), 'AutonomyTracker');
|
|
127
|
+
this.compounder = this.tryInit(() => new KnowledgeCompounder(), 'KnowledgeCompounder');
|
|
128
|
+
this.executionGraph = this.tryInit(() => new ExecutionGraph(), 'ExecutionGraph');
|
|
129
|
+
this.executionGraph?.attachToEventBus(this.bus);
|
|
130
|
+
// Create graph analysis deps (reused by AgentRunner + LearningEngine)
|
|
131
|
+
this.graphLearner = this.executionGraph ? this.tryInit(() => new GraphLearner(this.executionGraph), 'GraphLearner') : null;
|
|
132
|
+
this.graphAnalyzer = this.executionGraph ? this.tryInit(() => new GraphAnalyzer(this.executionGraph), 'GraphAnalyzer') : null;
|
|
133
|
+
this.reviewFeedback = this.executionGraph ? this.tryInit(() => new ReviewFeedbackRecorder(this.executionGraph), 'ReviewFeedbackRecorder') : null;
|
|
134
|
+
// Cutting-edge ML features
|
|
135
|
+
this.causalEngine = this.executionGraph
|
|
136
|
+
? this.tryInit(() => new CausalGraphEngine(this.executionGraph), 'CausalGraphEngine')
|
|
137
|
+
: null;
|
|
138
|
+
this.gnnPredictor = this.executionGraph
|
|
139
|
+
? this.tryInit(() => new FailurePropagationPredictor(this.executionGraph), 'FailurePropagationPredictor')
|
|
140
|
+
: null;
|
|
141
|
+
this.adversarialEvolver = this.executionGraph
|
|
142
|
+
? this.tryInit(() => new AdversarialEvolver(this.executionGraph), 'AdversarialEvolver')
|
|
143
|
+
: null;
|
|
144
|
+
this.metaSelector = this.executionGraph
|
|
145
|
+
? this.tryInit(() => new MetaPatternSelector(this.executionGraph), 'MetaPatternSelector')
|
|
146
|
+
: null;
|
|
147
|
+
this.predictiveDropout = this.executionGraph
|
|
148
|
+
? this.tryInit(() => new PredictiveDropout(this.executionGraph), 'PredictiveDropout')
|
|
149
|
+
: null;
|
|
150
|
+
// Initialize GNN weights (training happens lazily via background scheduling)
|
|
151
|
+
if (this.gnnPredictor) {
|
|
152
|
+
try {
|
|
153
|
+
this.gnnWeights = this.gnnPredictor.initWeights();
|
|
154
|
+
}
|
|
155
|
+
catch { /* silent */ }
|
|
156
|
+
}
|
|
157
|
+
// Self-aware engine features
|
|
158
|
+
this.patternSynthesizer = this.executionGraph ? this.tryInit(() => new PatternSynthesizer(this.executionGraph), 'PatternSynthesizer') : null;
|
|
159
|
+
this.trajectoryPredictor = this.executionGraph ? this.tryInit(() => new TrajectoryPredictor(this.executionGraph), 'TrajectoryPredictor') : null;
|
|
160
|
+
this.metaAdversarial = this.executionGraph ? this.tryInit(() => new MetaAdversarialTester(this.executionGraph), 'MetaAdversarialTester') : null;
|
|
161
|
+
this.ruleEvolver = this.executionGraph ? this.tryInit(() => new RuleEvolver(this.executionGraph), 'RuleEvolver') : null;
|
|
162
|
+
this.taskDiscovery = this.executionGraph ? this.tryInit(() => new TaskDiscovery(this.executionGraph), 'TaskDiscovery') : null;
|
|
163
|
+
this.orchestrationEmbedder = this.executionGraph ? this.tryInit(() => new OrchestrationEmbedder(this.executionGraph), 'OrchestrationEmbedder') : null;
|
|
164
|
+
// Prune stale graph data on init (Feature 6: graph decay)
|
|
165
|
+
if (this.executionGraph) {
|
|
166
|
+
try {
|
|
167
|
+
this.executionGraph.pruneOlderThan(90);
|
|
168
|
+
}
|
|
169
|
+
catch { /* silent */ }
|
|
115
170
|
}
|
|
116
|
-
|
|
117
|
-
|
|
171
|
+
// Rule evolution proposal on startup (skip in mock mode and when no historical data)
|
|
172
|
+
if (this.ruleEvolver && !this.options.mock) {
|
|
173
|
+
try {
|
|
174
|
+
const proposals = this.ruleEvolver.evolveRules(50);
|
|
175
|
+
if (proposals.length > 0) {
|
|
176
|
+
this.log.info(`Rule evolver proposes ${proposals.length} rule updates (top: ${proposals[0].rule.name}, +${Math.round(proposals[0].backtestedImprovement * 100)}%)`);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
catch (e) {
|
|
180
|
+
this.log.debug(`Rule evolution failed: ${e}`);
|
|
181
|
+
}
|
|
118
182
|
}
|
|
119
183
|
if (options.worktreeEnabled) {
|
|
120
184
|
try {
|
|
121
185
|
this.worktreeManager = new WorktreeManager(options.cwd);
|
|
122
186
|
}
|
|
123
|
-
catch {
|
|
187
|
+
catch {
|
|
188
|
+
/* not a git repo */
|
|
189
|
+
}
|
|
124
190
|
}
|
|
191
|
+
this.agentRunner = new AgentRunner({
|
|
192
|
+
registry: this.registry,
|
|
193
|
+
executor: this.executor,
|
|
194
|
+
bus: this.bus,
|
|
195
|
+
logger: options.logger,
|
|
196
|
+
compaction: this.compaction,
|
|
197
|
+
worktreeManager: this.worktreeManager,
|
|
198
|
+
contextRouter: this.executionGraph ? new GraphContextRouter(this.executionGraph) : undefined,
|
|
199
|
+
graphLearner: this.graphLearner,
|
|
200
|
+
graphAnalyzer: this.graphAnalyzer,
|
|
201
|
+
});
|
|
202
|
+
this.learningEngine = new LearningEngine({
|
|
203
|
+
traceStore: this.traceStore,
|
|
204
|
+
heuristicStore: this.heuristicStore,
|
|
205
|
+
modelRouter: this.modelRouter,
|
|
206
|
+
autonomyTracker: this.autonomyTracker,
|
|
207
|
+
compounder: this.compounder,
|
|
208
|
+
graphLearner: this.graphLearner,
|
|
209
|
+
metaSelector: this.metaSelector,
|
|
210
|
+
predictiveDropout: this.predictiveDropout,
|
|
211
|
+
orchestrationEmbedder: this.orchestrationEmbedder,
|
|
212
|
+
log: options.logger,
|
|
213
|
+
});
|
|
125
214
|
if (options.pluginLoader) {
|
|
126
215
|
options.pluginLoader.wireHooks(this.bus);
|
|
127
216
|
}
|
|
@@ -147,16 +236,14 @@ export class SwarmEngine {
|
|
|
147
236
|
orchestration.status = 'running';
|
|
148
237
|
orchestration.startedAt = new Date();
|
|
149
238
|
this.phaseOutputs = new Map();
|
|
239
|
+
// TODO: Checkpoint doesn't store phase outputs — resumed phases lose inter-phase context
|
|
150
240
|
this.reflections = [];
|
|
151
241
|
this.sharedContextFiles = [];
|
|
152
242
|
this.abortController = new AbortController();
|
|
153
243
|
this.livingSpec = createLivingSpec(config.description);
|
|
154
244
|
// Start continuous verification
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
continuousVerifier = new ContinuousVerifier(this.verifier, this.bus);
|
|
158
|
-
continuousVerifier.start();
|
|
159
|
-
}
|
|
245
|
+
const continuousVerifier = new ContinuousVerifier(this.verifier, this.bus);
|
|
246
|
+
continuousVerifier.start();
|
|
160
247
|
try {
|
|
161
248
|
// Execute phases using work-queue approach (handles DAGs correctly)
|
|
162
249
|
// If resuming, seed completed phases so the DAG executor skips them
|
|
@@ -166,6 +253,20 @@ export class SwarmEngine {
|
|
|
166
253
|
completed.add(phase.config.name);
|
|
167
254
|
}
|
|
168
255
|
}
|
|
256
|
+
// GNN pre-execution risk prediction (Feature 2: GNN Failure Propagation)
|
|
257
|
+
if (this.gnnPredictor && this.gnnWeights) {
|
|
258
|
+
try {
|
|
259
|
+
const risks = this.gnnPredictor.predict(orchestration.config.id, this.gnnWeights);
|
|
260
|
+
const highRisk = risks.filter(r => r.riskScore > 0.7);
|
|
261
|
+
if (highRisk.length > 0) {
|
|
262
|
+
this.log.warn(`GNN predicts high failure risk for ${highRisk.length} nodes`);
|
|
263
|
+
this.bus.emit('system:warning', { type: 'gnn-risk', risks: highRisk }, 'engine');
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
catch (e) {
|
|
267
|
+
this.log.debug(`GNN prediction failed: ${e}`);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
169
270
|
while (completed.size < orchestration.phases.length) {
|
|
170
271
|
// Check if abort was requested
|
|
171
272
|
if (this.abortController.signal.aborted) {
|
|
@@ -173,17 +274,19 @@ export class SwarmEngine {
|
|
|
173
274
|
break;
|
|
174
275
|
}
|
|
175
276
|
// Find all runnable phases (dependencies met, not yet completed)
|
|
176
|
-
const runnable = orchestration.phases.filter(p => {
|
|
277
|
+
const runnable = orchestration.phases.filter((p) => {
|
|
177
278
|
if (completed.has(p.config.name))
|
|
178
279
|
return false;
|
|
179
280
|
if (p.status === 'failed')
|
|
180
281
|
return false;
|
|
282
|
+
if (p.status === 'skipped')
|
|
283
|
+
return false;
|
|
181
284
|
const deps = p.config.dependsOn ?? [];
|
|
182
|
-
return deps.every(d => completed.has(d));
|
|
285
|
+
return deps.every((d) => completed.has(d));
|
|
183
286
|
});
|
|
184
287
|
if (runnable.length === 0) {
|
|
185
288
|
// No runnable phases — either all done or deadlocked
|
|
186
|
-
const incomplete = orchestration.phases.filter(p => !completed.has(p.config.name) && p.status !== 'failed');
|
|
289
|
+
const incomplete = orchestration.phases.filter((p) => !completed.has(p.config.name) && p.status !== 'failed');
|
|
187
290
|
if (incomplete.length > 0) {
|
|
188
291
|
// Deadlock — blocked phases with unmet dependencies
|
|
189
292
|
for (const p of incomplete) {
|
|
@@ -191,15 +294,14 @@ export class SwarmEngine {
|
|
|
191
294
|
}
|
|
192
295
|
orchestration.status = 'failed';
|
|
193
296
|
this.log.error('Orchestration deadlocked: blocked phases with unmet dependencies', {
|
|
194
|
-
blocked: incomplete.map(p => p.config.name),
|
|
297
|
+
blocked: incomplete.map((p) => p.config.name),
|
|
195
298
|
});
|
|
196
299
|
}
|
|
197
300
|
break;
|
|
198
301
|
}
|
|
199
|
-
//
|
|
302
|
+
// Handle approval gates sequentially (can't prompt in parallel)
|
|
303
|
+
const approved = [];
|
|
200
304
|
for (const phase of runnable) {
|
|
201
|
-
orchestration.currentPhase = orchestration.phases.indexOf(phase);
|
|
202
|
-
// Check for approval gate
|
|
203
305
|
if (phase.config.requiresApproval) {
|
|
204
306
|
this.log.info(`Phase requires approval: ${phase.config.name}`);
|
|
205
307
|
this.bus.emit('orchestration:paused', {
|
|
@@ -207,10 +309,9 @@ export class SwarmEngine {
|
|
|
207
309
|
phase: phase.config.name,
|
|
208
310
|
agentCount: phase.config.agents.length,
|
|
209
311
|
}, 'engine');
|
|
210
|
-
// In interactive mode, wait for approval
|
|
211
312
|
if (!this.options.nonInteractive) {
|
|
212
|
-
const
|
|
213
|
-
if (!
|
|
313
|
+
const ok = await this.requestApproval(phase.config.name, phase.config.agents);
|
|
314
|
+
if (!ok) {
|
|
214
315
|
phase.status = 'skipped';
|
|
215
316
|
this.log.info(`Phase skipped (not approved): ${phase.config.name}`);
|
|
216
317
|
completed.add(phase.config.name);
|
|
@@ -218,14 +319,76 @@ export class SwarmEngine {
|
|
|
218
319
|
}
|
|
219
320
|
}
|
|
220
321
|
}
|
|
322
|
+
approved.push(phase);
|
|
323
|
+
}
|
|
324
|
+
// Execute approved phases in parallel
|
|
325
|
+
orchestration.currentPhase = orchestration.phases.indexOf(approved[approved.length - 1] ?? runnable[0]);
|
|
326
|
+
const results = await Promise.allSettled(approved.map(async (phase) => {
|
|
221
327
|
const outputs = await this.executePhase(orchestration, phase);
|
|
222
328
|
this.phaseOutputs.set(phase.config.name, outputs);
|
|
329
|
+
}));
|
|
330
|
+
// Process results — mark completed or failed
|
|
331
|
+
for (let i = 0; i < results.length; i++) {
|
|
332
|
+
const phase = approved[i];
|
|
333
|
+
if (results[i].status === 'rejected') {
|
|
334
|
+
phase.status = 'failed';
|
|
335
|
+
}
|
|
223
336
|
if (phase.status === 'completed') {
|
|
224
337
|
completed.add(phase.config.name);
|
|
338
|
+
// Confidence-gated phase transition (Feature 3)
|
|
339
|
+
if (this.graphAnalyzer) {
|
|
340
|
+
try {
|
|
341
|
+
const reflection = this.reflectionEngine.reflect(phase);
|
|
342
|
+
const patternStats = this.graphLearner?.getPatternStats(orchestration.config.pattern)?.[0];
|
|
343
|
+
const gateOutputs = this.phaseOutputs.get(phase.config.name) || [];
|
|
344
|
+
const gate = this.graphAnalyzer.evaluatePhaseConfidence({ kind: phase.config.kind ?? 'implement', agentCount: phase.agents.length, outputs: gateOutputs, usage: phase.usage }, { confidence: reflection.confidence }, patternStats ? { successRate: patternStats.successRate, avgDurationMs: patternStats.avgDurationMs } : undefined);
|
|
345
|
+
if (gate.result === 'halt') {
|
|
346
|
+
this.log.warn(`Phase gate HALT: ${gate.reason}`);
|
|
347
|
+
orchestration.status = 'failed';
|
|
348
|
+
}
|
|
349
|
+
if (gate.result === 'warn') {
|
|
350
|
+
this.log.warn(`Phase gate WARNING: ${gate.reason}`);
|
|
351
|
+
this.bus.emit('system:warning', { type: 'gate-warning', orchestrationId: orchestration.config.id, phase: phase.config.name, reason: gate.reason }, 'engine');
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
catch { /* Graph gate evaluation failed — continue */ }
|
|
355
|
+
}
|
|
356
|
+
// Trajectory prediction after each completed phase
|
|
357
|
+
if (this.trajectoryPredictor) {
|
|
358
|
+
try {
|
|
359
|
+
const features = this.trajectoryPredictor.extractFeatures(orchestration, phase);
|
|
360
|
+
const pred = this.trajectoryPredictor.predict(features);
|
|
361
|
+
if (pred.successProbability < 0.3 && pred.confidence !== 'low') {
|
|
362
|
+
this.log.warn(`Trajectory predictor: ${(pred.successProbability * 100).toFixed(0)}% success probability (${pred.trajectory})`);
|
|
363
|
+
this.bus.emit('system:warning', { type: 'trajectory-risk', ...pred }, 'engine');
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
catch (e) {
|
|
367
|
+
this.log.debug(`Trajectory prediction failed: ${e}`);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
225
370
|
}
|
|
226
371
|
else if (phase.status === 'failed') {
|
|
227
372
|
orchestration.status = 'failed';
|
|
228
|
-
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
// Trajectory forecast over remaining phases
|
|
376
|
+
if (this.trajectoryPredictor) {
|
|
377
|
+
try {
|
|
378
|
+
const forecastCompleted = orchestration.phases
|
|
379
|
+
.filter(p => p.status === 'completed')
|
|
380
|
+
.map(p => ({ name: p.config.name, status: p.status, tokens: p.usage?.totalTokens || 0, durationMs: p.usage?.durationMs || 0, confidence: 'medium' }));
|
|
381
|
+
const forecastRemaining = orchestration.phases.filter(p => p.status === 'pending').map(p => p.config.name);
|
|
382
|
+
if (forecastRemaining.length > 0) {
|
|
383
|
+
const forecast = this.trajectoryPredictor.predictTrajectory(orchestration.config.id, forecastCompleted, forecastRemaining, orchestration.config.pattern);
|
|
384
|
+
if (forecast.overallSuccessProb < 0.3) {
|
|
385
|
+
this.log.warn(`Trajectory forecast: ${Math.round(forecast.overallSuccessProb * 100)}% success probability`);
|
|
386
|
+
this.bus.emit('system:warning', { type: 'trajectory-forecast', forecast }, 'engine');
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
catch (e) {
|
|
391
|
+
this.log.debug(`Trajectory forecast failed: ${e}`);
|
|
229
392
|
}
|
|
230
393
|
}
|
|
231
394
|
if (orchestration.status === 'failed')
|
|
@@ -233,12 +396,13 @@ export class SwarmEngine {
|
|
|
233
396
|
// Check cost budget after each batch of phases
|
|
234
397
|
if (orchestration.config.costBudget) {
|
|
235
398
|
const spent = orchestration.phases
|
|
236
|
-
.filter(p => p.status === 'completed')
|
|
399
|
+
.filter((p) => p.status === 'completed')
|
|
237
400
|
.reduce((sum, p) => sum + p.usage.costUsd, 0);
|
|
238
401
|
if (spent >= orchestration.config.costBudget) {
|
|
239
402
|
orchestration.status = 'failed';
|
|
240
403
|
this.log.warn(`Cost budget exceeded: $${spent.toFixed(2)} >= $${orchestration.config.costBudget}`, {
|
|
241
|
-
spent,
|
|
404
|
+
spent,
|
|
405
|
+
budget: orchestration.config.costBudget,
|
|
242
406
|
});
|
|
243
407
|
this.bus.emit('system:warning', {
|
|
244
408
|
reason: 'budget-exceeded',
|
|
@@ -250,7 +414,7 @@ export class SwarmEngine {
|
|
|
250
414
|
}
|
|
251
415
|
}
|
|
252
416
|
}
|
|
253
|
-
if (orchestration.status !== 'failed') {
|
|
417
|
+
if (orchestration.status !== 'failed' && orchestration.status !== 'cancelled') {
|
|
254
418
|
orchestration.status = 'completed';
|
|
255
419
|
}
|
|
256
420
|
}
|
|
@@ -260,8 +424,50 @@ export class SwarmEngine {
|
|
|
260
424
|
error: error instanceof Error ? error.message : String(error),
|
|
261
425
|
});
|
|
262
426
|
}
|
|
427
|
+
// Post-failure causal analysis (Feature 1: Causal Inference)
|
|
428
|
+
// Note: suggestIntervention scans all historical orchestrations — skip when graph is large
|
|
429
|
+
if (orchestration.status === 'failed' && this.causalEngine && this.executionGraph) {
|
|
430
|
+
try {
|
|
431
|
+
const orchCount = this.executionGraph.getNodesByType('orchestration').length;
|
|
432
|
+
if (orchCount <= 200) {
|
|
433
|
+
const suggestions = this.causalEngine.suggestIntervention(orchestration.config.id);
|
|
434
|
+
if (suggestions.length > 0) {
|
|
435
|
+
this.log.info(`Causal analysis suggests: ${suggestions[0].reasoning}`);
|
|
436
|
+
this.bus.emit('system:warning', { type: 'causal-suggestion', suggestions }, 'engine');
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
catch (e) {
|
|
441
|
+
this.log.debug(`Causal suggestion failed: ${e}`);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
// Self-audit on failure: red-team the engine's own ML subsystems
|
|
445
|
+
// Skipped in mock mode and when insufficient historical data to avoid test overhead
|
|
446
|
+
if (orchestration.status === 'failed' && this.metaAdversarial && !this.options.mock) {
|
|
447
|
+
try {
|
|
448
|
+
const orchCount = this.executionGraph?.getNodesByType('orchestration').length ?? 0;
|
|
449
|
+
if (orchCount >= 5) {
|
|
450
|
+
const report = this.metaAdversarial.runFullAudit({
|
|
451
|
+
patternSelector: this.metaSelector ?? undefined,
|
|
452
|
+
dropout: this.predictiveDropout ?? undefined,
|
|
453
|
+
causal: this.causalEngine ? {
|
|
454
|
+
estimateCausalEffect: (t, tv, cv) =>
|
|
455
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
456
|
+
this.causalEngine.estimateCausalEffect(t, tv, cv),
|
|
457
|
+
} : undefined,
|
|
458
|
+
});
|
|
459
|
+
if (report.overallRisk !== 'low') {
|
|
460
|
+
this.log.warn(`Self-audit found ${report.vulnerabilities.length} ML vulnerabilities (risk: ${report.overallRisk})`);
|
|
461
|
+
this.bus.emit('system:warning', { type: 'self-audit', overallRisk: report.overallRisk, recommendations: report.recommendations }, 'engine');
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
catch (e) {
|
|
466
|
+
this.log.debug(`Meta-adversarial audit failed: ${e}`);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
263
469
|
orchestration.completedAt = new Date();
|
|
264
|
-
continuousVerifier
|
|
470
|
+
continuousVerifier.stop();
|
|
265
471
|
// Clean up shared context files
|
|
266
472
|
for (const f of this.sharedContextFiles) {
|
|
267
473
|
try {
|
|
@@ -270,91 +476,17 @@ export class SwarmEngine {
|
|
|
270
476
|
catch { }
|
|
271
477
|
}
|
|
272
478
|
this.sharedContextFiles = [];
|
|
273
|
-
//
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
agentCount: p.agents.length,
|
|
278
|
-
tokens: p.usage.totalTokens,
|
|
279
|
-
durationMs: p.usage.durationMs,
|
|
280
|
-
confidence: p.agents[0]?.result?.confidence ?? 'unknown',
|
|
281
|
-
}));
|
|
282
|
-
const traceData = {
|
|
283
|
-
orchestrationId: config.id,
|
|
284
|
-
task: config.description,
|
|
285
|
-
pattern: config.pattern,
|
|
286
|
-
status: orchestration.status,
|
|
287
|
-
totalTokens: orchestration.usage.totalTokens,
|
|
288
|
-
costUsd: orchestration.usage.costUsd,
|
|
289
|
-
durationMs: orchestration.usage.durationMs,
|
|
290
|
-
phaseCount: orchestration.phases.length,
|
|
291
|
-
agentCount: orchestration.phases.reduce((n, p) => n + p.agents.length, 0),
|
|
292
|
-
phases: phaseData,
|
|
293
|
-
reflections: this.reflections.map(r => redactSecrets(r)),
|
|
294
|
-
};
|
|
295
|
-
// Store execution trace for learning
|
|
296
|
-
if (this.traceStore) {
|
|
297
|
-
try {
|
|
298
|
-
this.traceStore.store(traceData);
|
|
299
|
-
}
|
|
300
|
-
catch { /* Don't fail orchestration if trace storage fails */ }
|
|
301
|
-
// Extract heuristics from the trace for future learning
|
|
302
|
-
if (this.heuristicStore) {
|
|
303
|
-
try {
|
|
304
|
-
this.heuristicStore.extractFromTrace(traceData);
|
|
305
|
-
}
|
|
306
|
-
catch { /* Don't fail orchestration if heuristic extraction fails */ }
|
|
307
|
-
}
|
|
308
|
-
// Auto-compound knowledge from completed orchestrations
|
|
309
|
-
if (orchestration.status === 'completed' && this.compounder) {
|
|
310
|
-
try {
|
|
311
|
-
const doc = this.compounder.extractFromTrace({ ...traceData, status: 'completed' }, this.reflections);
|
|
312
|
-
if (doc)
|
|
313
|
-
this.compounder.store(doc);
|
|
314
|
-
}
|
|
315
|
-
catch { /* Don't fail orchestration if compounding fails */ }
|
|
316
|
-
}
|
|
317
|
-
// Auto-capture outcomes to memory
|
|
479
|
+
// Record learning outcomes (traces, heuristics, model routing, autonomy)
|
|
480
|
+
await this.learningEngine.recordOutcome(orchestration, config, this.reflections);
|
|
481
|
+
// Train trajectory predictor on final outcome
|
|
482
|
+
if (this.trajectoryPredictor) {
|
|
318
483
|
try {
|
|
319
|
-
|
|
320
|
-
const memEntries = traceToMemoryEntries(traceData);
|
|
321
|
-
// Store in memory if SwarmMemory is available
|
|
322
|
-
let memory;
|
|
323
|
-
try {
|
|
324
|
-
const { SwarmMemory } = await import('../memory/index.js');
|
|
325
|
-
memory = new SwarmMemory();
|
|
326
|
-
for (const entry of memEntries) {
|
|
327
|
-
memory.store({ type: entry.type, title: entry.title, content: entry.content });
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
catch { /* Memory not available */ }
|
|
331
|
-
finally {
|
|
332
|
-
try {
|
|
333
|
-
memory?.close();
|
|
334
|
-
}
|
|
335
|
-
catch { }
|
|
336
|
-
}
|
|
484
|
+
this.trajectoryPredictor.recordOutcome(orchestration, orchestration.status === 'completed');
|
|
337
485
|
}
|
|
338
|
-
catch {
|
|
339
|
-
|
|
340
|
-
// Record model outcomes for routing
|
|
341
|
-
if (this.modelRouter) {
|
|
342
|
-
for (const phase of orchestration.phases) {
|
|
343
|
-
for (const agent of phase.agents) {
|
|
344
|
-
const model = agent.config.model;
|
|
345
|
-
if (!model)
|
|
346
|
-
continue;
|
|
347
|
-
const success = agent.status === 'shutdown' || agent.status === 'idle';
|
|
348
|
-
this.modelRouter.record(model, agent.config.name, success, success ? 1.0 : 0.0);
|
|
349
|
-
}
|
|
486
|
+
catch (e) {
|
|
487
|
+
this.log.debug(`Trajectory training failed: ${e}`);
|
|
350
488
|
}
|
|
351
489
|
}
|
|
352
|
-
// Record autonomy outcome
|
|
353
|
-
if (this.autonomyTracker) {
|
|
354
|
-
const success = orchestration.status === 'completed';
|
|
355
|
-
const intervention = orchestration.phases.some(p => p.config.requiresApproval);
|
|
356
|
-
this.autonomyTracker.record(config.pattern, success, intervention);
|
|
357
|
-
}
|
|
358
490
|
// Compute total usage
|
|
359
491
|
orchestration.usage = orchestration.phases.reduce((total, phase) => mergeUsageStats(total, phase.usage), emptyUsageStats());
|
|
360
492
|
this.bus.emit(orchestration.status === 'completed' ? 'orchestration:complete' : 'orchestration:failed', {
|
|
@@ -377,7 +509,10 @@ export class SwarmEngine {
|
|
|
377
509
|
async runPattern(patternName, task, options) {
|
|
378
510
|
const pattern = this.patterns.get(patternName);
|
|
379
511
|
if (!pattern) {
|
|
380
|
-
throw new Error(`Unknown pattern: ${patternName}. Available: ${this.patterns
|
|
512
|
+
throw new Error(`Unknown pattern: ${patternName}. Available: ${this.patterns
|
|
513
|
+
.list()
|
|
514
|
+
.map((p) => p.name)
|
|
515
|
+
.join(', ')}`);
|
|
381
516
|
}
|
|
382
517
|
// Inject task into all agent prompts
|
|
383
518
|
const config = {
|
|
@@ -385,9 +520,9 @@ export class SwarmEngine {
|
|
|
385
520
|
name: `${patternName}: ${task.slice(0, 50)}`,
|
|
386
521
|
description: task,
|
|
387
522
|
pattern: patternName,
|
|
388
|
-
phases: pattern.phases.map(phase => ({
|
|
523
|
+
phases: pattern.phases.map((phase) => ({
|
|
389
524
|
...phase,
|
|
390
|
-
agents: phase.agents.map(a => ({
|
|
525
|
+
agents: phase.agents.map((a) => ({
|
|
391
526
|
...a,
|
|
392
527
|
prompt: `${task}\n\n${a.prompt ?? ''}`.trim(),
|
|
393
528
|
})),
|
|
@@ -396,17 +531,7 @@ export class SwarmEngine {
|
|
|
396
531
|
};
|
|
397
532
|
// Substitute pattern parameters into agent prompts
|
|
398
533
|
if (options?.params && pattern.parameters) {
|
|
399
|
-
|
|
400
|
-
for (const agent of phase.agents) {
|
|
401
|
-
if (agent.prompt) {
|
|
402
|
-
for (const [key, value] of Object.entries(options.params)) {
|
|
403
|
-
// Use string replacement instead of regex to avoid ReDoS from user-controlled keys
|
|
404
|
-
const placeholder = '${' + key + '}';
|
|
405
|
-
agent.prompt = agent.prompt.split(placeholder).join(String(value));
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
}
|
|
534
|
+
this.substituteParams(config.phases, options.params);
|
|
410
535
|
}
|
|
411
536
|
return this.execute(config);
|
|
412
537
|
}
|
|
@@ -417,37 +542,24 @@ export class SwarmEngine {
|
|
|
417
542
|
plan(patternName, task, options) {
|
|
418
543
|
const pattern = this.patterns.get(patternName);
|
|
419
544
|
if (!pattern) {
|
|
420
|
-
throw new Error(`Unknown pattern: ${patternName}. Available: ${this.patterns
|
|
545
|
+
throw new Error(`Unknown pattern: ${patternName}. Available: ${this.patterns
|
|
546
|
+
.list()
|
|
547
|
+
.map((p) => p.name)
|
|
548
|
+
.join(', ')}`);
|
|
421
549
|
}
|
|
422
550
|
// Check for similar past traces to inform planning
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
if (similar.length > 0) {
|
|
427
|
-
let traceContext = '';
|
|
428
|
-
const best = similar.find(t => t.status === 'completed');
|
|
429
|
-
if (best) {
|
|
430
|
-
traceContext = `Past successful orchestration for similar task used pattern "${best.pattern}" with ${best.agentCount} agents (${best.totalTokens} tokens, ${Math.round(best.durationMs / 1000)}s).`;
|
|
431
|
-
}
|
|
432
|
-
const failed = similar.find(t => t.status === 'failed');
|
|
433
|
-
if (failed && failed.reflections.length > 0) {
|
|
434
|
-
traceContext += ` Previous attempt failed. Lessons: ${failed.reflections[0]?.slice(0, 200)}`;
|
|
435
|
-
}
|
|
436
|
-
if (traceContext) {
|
|
437
|
-
this.log.info('Found similar past traces', { context: traceContext.slice(0, 200) });
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
catch { /* Don't fail planning if trace lookup fails */ }
|
|
551
|
+
const traceContext = this.learningEngine.findSimilarTraces(task);
|
|
552
|
+
if (traceContext) {
|
|
553
|
+
this.log.info('Found similar past traces', { context: traceContext.slice(0, 200) });
|
|
442
554
|
}
|
|
443
555
|
const id = generateId();
|
|
444
|
-
const phases = pattern.phases.map(phase => ({
|
|
556
|
+
const phases = pattern.phases.map((phase) => ({
|
|
445
557
|
name: phase.name,
|
|
446
558
|
parallel: phase.parallel,
|
|
447
559
|
dependsOn: phase.dependsOn,
|
|
448
560
|
requiresApproval: phase.requiresApproval,
|
|
449
561
|
tokenBudget: phase.tokenBudget,
|
|
450
|
-
agents: phase.agents.map(a => {
|
|
562
|
+
agents: phase.agents.map((a) => {
|
|
451
563
|
const agentConfig = this.registry.getConfig(a.type);
|
|
452
564
|
return {
|
|
453
565
|
name: a.name,
|
|
@@ -462,14 +574,7 @@ export class SwarmEngine {
|
|
|
462
574
|
}));
|
|
463
575
|
// Substitute pattern parameters if provided
|
|
464
576
|
if (options?.params && pattern.parameters) {
|
|
465
|
-
|
|
466
|
-
for (const agent of phase.agents) {
|
|
467
|
-
for (const [key, value] of Object.entries(options.params)) {
|
|
468
|
-
const placeholder = '${' + key + '}';
|
|
469
|
-
agent.prompt = agent.prompt.split(placeholder).join(String(value));
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
}
|
|
577
|
+
this.substituteParams(phases, options.params);
|
|
473
578
|
}
|
|
474
579
|
const planResult = {
|
|
475
580
|
id,
|
|
@@ -485,33 +590,58 @@ export class SwarmEngine {
|
|
|
485
590
|
if (rewrites.length > 0) {
|
|
486
591
|
this.log.info('Plan rewritten', { rewrites });
|
|
487
592
|
}
|
|
488
|
-
//
|
|
489
|
-
|
|
593
|
+
// Inject learned heuristics and past solutions into the plan
|
|
594
|
+
this.learningEngine.injectHeuristics(rewrittenPlan);
|
|
595
|
+
this.learningEngine.injectSolutions(rewrittenPlan);
|
|
596
|
+
// Meta-pattern recommendation (Feature 4: Meta-Pattern Selector)
|
|
597
|
+
if (this.metaSelector) {
|
|
490
598
|
try {
|
|
491
|
-
const
|
|
492
|
-
if (
|
|
493
|
-
|
|
494
|
-
for (const agent of rewrittenPlan.phases[0]?.agents ?? []) {
|
|
495
|
-
agent.prompt = `${context}\n\n${agent.prompt}`;
|
|
496
|
-
}
|
|
497
|
-
this.log.info('Injected heuristics into plan', { count: heuristics.length });
|
|
599
|
+
const recommendation = this.metaSelector.selectPattern(task);
|
|
600
|
+
if (recommendation.confidence > 0.6) {
|
|
601
|
+
this.log.info(`Meta-selector recommends: ${recommendation.pattern} (${Math.round(recommendation.confidence * 100)}% confidence)`);
|
|
498
602
|
}
|
|
499
603
|
}
|
|
500
|
-
catch {
|
|
604
|
+
catch (e) {
|
|
605
|
+
this.log.debug(`Meta-pattern selection failed: ${e}`);
|
|
606
|
+
}
|
|
501
607
|
}
|
|
502
|
-
//
|
|
503
|
-
if (this.
|
|
608
|
+
// Task discovery: log discovered backlog items from historical failure patterns (skip in mock mode)
|
|
609
|
+
if (this.taskDiscovery && !this.options.mock) {
|
|
504
610
|
try {
|
|
505
|
-
const
|
|
506
|
-
if (
|
|
507
|
-
|
|
508
|
-
for (const agent of rewrittenPlan.phases[0]?.agents ?? []) {
|
|
509
|
-
agent.prompt = `${solutionContext}\n\n${agent.prompt}`;
|
|
510
|
-
}
|
|
511
|
-
this.log.info('Injected past solutions into plan', { count: solutions.length });
|
|
611
|
+
const backlog = this.taskDiscovery.generateBacklog(3);
|
|
612
|
+
if (backlog.length > 0) {
|
|
613
|
+
this.log.info(`Task discovery: ${backlog.length} actionable items found (top: ${backlog[0]?.title})`);
|
|
512
614
|
}
|
|
513
615
|
}
|
|
514
|
-
catch {
|
|
616
|
+
catch (e) {
|
|
617
|
+
this.log.debug(`Task discovery failed: ${e}`);
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
// Pattern synthesis: log insights from topology analysis (skip in mock mode)
|
|
621
|
+
if (this.patternSynthesizer && !this.options.mock) {
|
|
622
|
+
try {
|
|
623
|
+
const insights = this.patternSynthesizer.analyzeTopologyDiffs(3);
|
|
624
|
+
if (insights.length > 0) {
|
|
625
|
+
const top = insights[0];
|
|
626
|
+
this.log.info(`Pattern synthesis top insight: "${top.feature}" (correlation: ${top.successCorrelation.toFixed(2)}, n=${top.sampleSize})`);
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
catch (e) {
|
|
630
|
+
this.log.debug(`Pattern synthesis failed: ${e}`);
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
// Pattern synthesis: suggest novel pattern from historical success topology
|
|
634
|
+
if (this.patternSynthesizer && !this.options.mock) {
|
|
635
|
+
try {
|
|
636
|
+
const synthesized = this.patternSynthesizer.synthesizePattern(task);
|
|
637
|
+
if (synthesized && synthesized.confidence > 0.5) {
|
|
638
|
+
this.log.info(`Pattern synthesis suggests: ${synthesized.name} (${Math.round(synthesized.confidence * 100)}% confidence)`);
|
|
639
|
+
this.bus.emit('system:warning', { type: 'pattern-synthesis', pattern: synthesized }, 'engine');
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
catch (e) {
|
|
643
|
+
this.log.debug(`Pattern synthesis suggestion failed: ${e}`);
|
|
644
|
+
}
|
|
515
645
|
}
|
|
516
646
|
// Search over candidate plans to find the best one
|
|
517
647
|
const searcher = new PlanSearcher(this.costModel, this.traceStore ?? undefined);
|
|
@@ -525,7 +655,7 @@ export class SwarmEngine {
|
|
|
525
655
|
bestPlan.estimates = this.costModel.estimate(bestPlan);
|
|
526
656
|
bestPlan.rewrites = rewrites;
|
|
527
657
|
if (candidates.length > 1) {
|
|
528
|
-
bestPlan._alternatives = candidates.slice(1).map(c => ({
|
|
658
|
+
bestPlan._alternatives = candidates.slice(1).map((c) => ({
|
|
529
659
|
source: c.source,
|
|
530
660
|
costUsd: c.cost.costUsd,
|
|
531
661
|
quality: c.cost.qualityScore,
|
|
@@ -544,11 +674,31 @@ export class SwarmEngine {
|
|
|
544
674
|
const dropout = this.replanner.evaluateDropout(orchestration, phase.config);
|
|
545
675
|
if (dropout) {
|
|
546
676
|
this.log.info(`Agent dropout: ${dropout.reason}`);
|
|
547
|
-
phase.config.agents = phase.config.agents.filter(a => dropout.agentsToKeep.includes(a.name));
|
|
677
|
+
phase.config.agents = phase.config.agents.filter((a) => dropout.agentsToKeep.includes(a.name));
|
|
548
678
|
this.bus.emit('system:warning', { type: 'agent-dropout', ...dropout }, 'adaptive');
|
|
549
679
|
}
|
|
550
680
|
}
|
|
681
|
+
// Predictive dropout from ML model (Feature 5: Predictive Dropout)
|
|
682
|
+
if (this.predictiveDropout && phase.config.agents.length > 1) {
|
|
683
|
+
try {
|
|
684
|
+
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 || '');
|
|
685
|
+
const toDrop = recommendations.filter(r => r.shouldDropOut);
|
|
686
|
+
if (toDrop.length > 0) {
|
|
687
|
+
this.log.info(`Predictive dropout: skipping ${toDrop.map(d => d.agentType).join(', ')}`);
|
|
688
|
+
phase.config.agents = phase.config.agents.filter(a => !toDrop.some(d => d.agentType === a.type));
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
catch (e) {
|
|
692
|
+
this.log.debug(`Predictive dropout failed: ${e}`);
|
|
693
|
+
}
|
|
694
|
+
}
|
|
551
695
|
const maxRetries = phase.config.maxRetries ?? 2;
|
|
696
|
+
const runContext = {
|
|
697
|
+
phaseOutputs: this.phaseOutputs,
|
|
698
|
+
livingSpec: this.livingSpec,
|
|
699
|
+
sharedContextFiles: this.sharedContextFiles,
|
|
700
|
+
cwd: this.options.cwd,
|
|
701
|
+
};
|
|
552
702
|
this.log.info(`Phase started: ${phase.config.name}`, {
|
|
553
703
|
agents: phase.config.agents.length,
|
|
554
704
|
parallel: phase.config.parallel,
|
|
@@ -562,7 +712,7 @@ export class SwarmEngine {
|
|
|
562
712
|
try {
|
|
563
713
|
if (phase.config.parallel) {
|
|
564
714
|
// Run all agents in parallel (with retry)
|
|
565
|
-
const results = await Promise.allSettled(phase.config.agents.map(assignment => this.executeAgentWithRetry(assignment, phase, orchestration, maxRetries)));
|
|
715
|
+
const results = await Promise.allSettled(phase.config.agents.map((assignment) => this.agentRunner.executeAgentWithRetry(assignment, phase, orchestration, maxRetries, runContext)));
|
|
566
716
|
// Log cache savings estimate for parallel phases
|
|
567
717
|
if (phase.config.agents.length > 1) {
|
|
568
718
|
const phaseContext = this.compaction.buildContextPrefix(this.phaseOutputs);
|
|
@@ -573,7 +723,7 @@ export class SwarmEngine {
|
|
|
573
723
|
}
|
|
574
724
|
}
|
|
575
725
|
// Check for failures
|
|
576
|
-
const failures = results.filter(r => r.status === 'rejected');
|
|
726
|
+
const failures = results.filter((r) => r.status === 'rejected');
|
|
577
727
|
if (failures.length > 0) {
|
|
578
728
|
const failureCount = failures.length;
|
|
579
729
|
this.log.warn(`Phase had ${failureCount} agent failures`, {
|
|
@@ -592,9 +742,16 @@ export class SwarmEngine {
|
|
|
592
742
|
}
|
|
593
743
|
}
|
|
594
744
|
else {
|
|
595
|
-
// Run agents sequentially (with retry)
|
|
745
|
+
// Run agents sequentially (with retry) — accumulate intra-phase outputs for live re-routing (Feature 1)
|
|
746
|
+
const intraPhaseOutputs = new Map();
|
|
596
747
|
for (const assignment of phase.config.agents) {
|
|
597
|
-
await this.executeAgentWithRetry(assignment, phase, orchestration, maxRetries);
|
|
748
|
+
const agentInstance = await this.agentRunner.executeAgentWithRetry(assignment, phase, orchestration, maxRetries, { ...runContext, intraPhaseOutputs });
|
|
749
|
+
// After agent completes, add its output to intra-phase buffer for next sequential agent
|
|
750
|
+
if (agentInstance?.result?.output) {
|
|
751
|
+
const existing = intraPhaseOutputs.get(phase.config.name) || [];
|
|
752
|
+
existing.push(agentInstance.result.output);
|
|
753
|
+
intraPhaseOutputs.set(phase.config.name, existing);
|
|
754
|
+
}
|
|
598
755
|
// Check phase token budget after each agent
|
|
599
756
|
if (phase.config.tokenBudget) {
|
|
600
757
|
const phaseTokens = phase.agents.reduce((sum, a) => sum + a.usage.totalTokens, 0);
|
|
@@ -615,8 +772,23 @@ export class SwarmEngine {
|
|
|
615
772
|
}
|
|
616
773
|
phase.completedAt = new Date();
|
|
617
774
|
phase.usage = phase.agents.reduce((total, agent) => mergeUsageStats(total, agent.usage), emptyUsageStats());
|
|
775
|
+
// Record review feedback into the knowledge graph (Feature 7)
|
|
776
|
+
if (phase.config.kind === 'review' && this.reviewFeedback) {
|
|
777
|
+
for (const agent of phase.agents) {
|
|
778
|
+
if (agent.result?.output) {
|
|
779
|
+
try {
|
|
780
|
+
this.reviewFeedback.recordFindings(orchestration.config.id, phase.config.name, agent.result.output, agent.config.name);
|
|
781
|
+
const findings = parseReviewOutput(agent.result.output, agent.config.name);
|
|
782
|
+
this.reviewFeedback.adjustContextWeights(orchestration.config.id, findings);
|
|
783
|
+
}
|
|
784
|
+
catch (e) {
|
|
785
|
+
this.log.debug(`Review feedback recording failed: ${e}`);
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
}
|
|
618
790
|
// Run verification after implementation phases
|
|
619
|
-
if (
|
|
791
|
+
if (phase.config.kind === 'implement' || phase.config.kind === 'integrate') {
|
|
620
792
|
const results = await this.verifier.verify();
|
|
621
793
|
const summary = Verifier.summarize(results);
|
|
622
794
|
this.log.info(`Verification: ${summary}`, { phase: phase.config.name });
|
|
@@ -647,8 +819,8 @@ export class SwarmEngine {
|
|
|
647
819
|
});
|
|
648
820
|
// Collect agent outputs for inter-phase context
|
|
649
821
|
const outputs = phase.agents
|
|
650
|
-
.filter(a => a.result?.output)
|
|
651
|
-
.map(a => `[${a.config.name}]: ${a.result.output.slice(0, 2000)}`);
|
|
822
|
+
.filter((a) => a.result?.output)
|
|
823
|
+
.map((a) => `[${a.config.name}]: ${a.result.output.slice(0, 2000)}`);
|
|
652
824
|
// Update living spec with phase outputs
|
|
653
825
|
if (this.livingSpec) {
|
|
654
826
|
this.livingSpec = updateSpecFromPhase(this.livingSpec, phase.config.name, outputs);
|
|
@@ -665,121 +837,6 @@ export class SwarmEngine {
|
|
|
665
837
|
}
|
|
666
838
|
return outputs;
|
|
667
839
|
}
|
|
668
|
-
/**
|
|
669
|
-
* Execute a single agent within a phase.
|
|
670
|
-
*/
|
|
671
|
-
async executeAgent(assignment, phase, orchestration) {
|
|
672
|
-
// Spawn from registry, passing all assignment overrides
|
|
673
|
-
const instance = this.registry.spawn(assignment.type, {
|
|
674
|
-
name: assignment.name,
|
|
675
|
-
model: assignment.model,
|
|
676
|
-
backend: assignment.backend,
|
|
677
|
-
backendModel: assignment.backendModel,
|
|
678
|
-
});
|
|
679
|
-
phase.agents.push(instance);
|
|
680
|
-
// Build task prompt with cache-friendly ordering:
|
|
681
|
-
// Shared prefix (orchestration + phase context) comes first for API cache hits,
|
|
682
|
-
// agent-specific content (definition + task) comes last.
|
|
683
|
-
const orchestrationContext = `## Orchestration: ${orchestration.config.name}\nPattern: ${orchestration.config.pattern}`;
|
|
684
|
-
const phaseContext = this.compaction.buildContextPrefix(this.phaseOutputs);
|
|
685
|
-
const agentPrompt = instance.config.prompt;
|
|
686
|
-
const baseTask = assignment.prompt || `Execute task as ${assignment.type}`;
|
|
687
|
-
const optimized = optimizeForCache(orchestrationContext, phaseContext, agentPrompt, baseTask);
|
|
688
|
-
// For parallel phases with large shared prefix, use shared context file
|
|
689
|
-
const sharedContextPath = phase.config.parallel
|
|
690
|
-
? this.writeSharedContext(optimized.sharedPrefix)
|
|
691
|
-
: null;
|
|
692
|
-
// Cascade model selection: if no explicit model override, pick starting tier by complexity
|
|
693
|
-
if (!assignment.model) {
|
|
694
|
-
const complexity = classifyTaskComplexity(baseTask);
|
|
695
|
-
const startTier = selectStartingTier(complexity);
|
|
696
|
-
if (startTier !== instance.config.model) {
|
|
697
|
-
instance.config.model = startTier;
|
|
698
|
-
}
|
|
699
|
-
}
|
|
700
|
-
// Inject living spec into task context
|
|
701
|
-
const specPrefix = this.livingSpec ? formatLivingSpec(this.livingSpec) + '\n\n' : '';
|
|
702
|
-
const task = sharedContextPath
|
|
703
|
-
? `Read the shared context file at ${sharedContextPath} first, then:\n\n${specPrefix}${optimized.agentSuffix}`
|
|
704
|
-
: `${specPrefix}${optimized.full}`;
|
|
705
|
-
// Transition to running
|
|
706
|
-
this.registry.updateStatus(instance.id, 'running');
|
|
707
|
-
// Create worktree if enabled and assignment requests it
|
|
708
|
-
let worktreeId;
|
|
709
|
-
if (this.worktreeManager && assignment.worktree) {
|
|
710
|
-
try {
|
|
711
|
-
const wt = this.worktreeManager.create(instance.id, assignment.name);
|
|
712
|
-
worktreeId = wt.id;
|
|
713
|
-
this.bus.emit('worktree:created', { agentId: instance.id, worktreeId: wt.id, path: wt.path }, instance.id);
|
|
714
|
-
}
|
|
715
|
-
catch (err) {
|
|
716
|
-
this.log.warn(`Failed to create worktree for ${assignment.name}: ${err}`);
|
|
717
|
-
}
|
|
718
|
-
}
|
|
719
|
-
// Execute with timeout
|
|
720
|
-
const timeoutMs = (instance.config.timeout ?? 300) * 1000; // default 5 min
|
|
721
|
-
let timer;
|
|
722
|
-
const timeoutPromise = new Promise((_, reject) => {
|
|
723
|
-
timer = setTimeout(() => reject(new Error(`Agent timeout after ${timeoutMs / 1000}s`)), timeoutMs);
|
|
724
|
-
});
|
|
725
|
-
try {
|
|
726
|
-
await Promise.race([
|
|
727
|
-
this.executor.execute(instance, task),
|
|
728
|
-
timeoutPromise,
|
|
729
|
-
]);
|
|
730
|
-
clearTimeout(timer);
|
|
731
|
-
// Merge worktree if created
|
|
732
|
-
if (worktreeId && this.worktreeManager) {
|
|
733
|
-
try {
|
|
734
|
-
this.worktreeManager.merge(worktreeId);
|
|
735
|
-
this.bus.emit('worktree:merged', { agentId: instance.id, worktreeId }, instance.id);
|
|
736
|
-
}
|
|
737
|
-
catch (err) {
|
|
738
|
-
this.log.warn(`Failed to merge worktree ${worktreeId}: ${err}`);
|
|
739
|
-
}
|
|
740
|
-
}
|
|
741
|
-
// Transition to idle (completed successfully)
|
|
742
|
-
this.registry.updateStatus(instance.id, 'idle');
|
|
743
|
-
// Then shutdown
|
|
744
|
-
this.registry.updateStatus(instance.id, 'shutdown');
|
|
745
|
-
return instance;
|
|
746
|
-
}
|
|
747
|
-
catch (error) {
|
|
748
|
-
clearTimeout(timer);
|
|
749
|
-
if (String(error).includes('timeout')) {
|
|
750
|
-
this.registry.updateStatus(instance.id, 'timeout');
|
|
751
|
-
}
|
|
752
|
-
else {
|
|
753
|
-
this.registry.updateStatus(instance.id, 'error');
|
|
754
|
-
}
|
|
755
|
-
instance.error = error instanceof Error ? error.message : String(error);
|
|
756
|
-
throw error;
|
|
757
|
-
}
|
|
758
|
-
}
|
|
759
|
-
async executeAgentWithRetry(assignment, phase, orchestration, maxRetries = 2) {
|
|
760
|
-
let lastError;
|
|
761
|
-
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
762
|
-
try {
|
|
763
|
-
return await this.executeAgent(assignment, phase, orchestration);
|
|
764
|
-
}
|
|
765
|
-
catch (error) {
|
|
766
|
-
lastError = error instanceof Error ? error : new Error(String(error));
|
|
767
|
-
// Remove the failed instance from phase agents to prevent duplicates on retry
|
|
768
|
-
const failedIndex = phase.agents.findIndex(a => a.config.name === assignment.name && (a.status === 'error' || a.status === 'timeout'));
|
|
769
|
-
if (failedIndex >= 0)
|
|
770
|
-
phase.agents.splice(failedIndex, 1);
|
|
771
|
-
if (attempt < maxRetries) {
|
|
772
|
-
const delayMs = Math.min(1000 * Math.pow(2, attempt) + Math.random() * 1000, 30000);
|
|
773
|
-
this.log.warn(`Agent failed, retrying in ${Math.round(delayMs / 1000)}s (attempt ${attempt + 1}/${maxRetries})`, {
|
|
774
|
-
agent: assignment.name,
|
|
775
|
-
error: lastError.message,
|
|
776
|
-
});
|
|
777
|
-
await new Promise(resolve => setTimeout(resolve, delayMs));
|
|
778
|
-
}
|
|
779
|
-
}
|
|
780
|
-
}
|
|
781
|
-
throw lastError;
|
|
782
|
-
}
|
|
783
840
|
// ─── Resume ────────────────────────────────────────────────────
|
|
784
841
|
/**
|
|
785
842
|
* Resume an orchestration from a checkpoint.
|
|
@@ -817,10 +874,8 @@ export class SwarmEngine {
|
|
|
817
874
|
const { createInterface } = await import('readline');
|
|
818
875
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
819
876
|
return new Promise((resolve) => {
|
|
820
|
-
const agentList = agents.map(a => ` ${a.name} (${a.type})`).join('\n');
|
|
821
|
-
rl.question(`\n⏸ Phase "${phaseName}" requires approval.\n` +
|
|
822
|
-
` Agents:\n${agentList}\n` +
|
|
823
|
-
` Proceed? [Y/n] `, (answer) => {
|
|
877
|
+
const agentList = agents.map((a) => ` ${a.name} (${a.type})`).join('\n');
|
|
878
|
+
rl.question(`\n⏸ Phase "${phaseName}" requires approval.\n` + ` Agents:\n${agentList}\n` + ` Proceed? [Y/n] `, (answer) => {
|
|
824
879
|
rl.close();
|
|
825
880
|
resolve(answer.toLowerCase() !== 'n');
|
|
826
881
|
});
|
|
@@ -856,26 +911,7 @@ export class SwarmEngine {
|
|
|
856
911
|
// Best effort — lifecycle may not allow transition
|
|
857
912
|
}
|
|
858
913
|
}
|
|
859
|
-
|
|
860
|
-
this.stats.close();
|
|
861
|
-
}
|
|
862
|
-
catch { /* best effort */ }
|
|
863
|
-
try {
|
|
864
|
-
this.traceStore?.close();
|
|
865
|
-
}
|
|
866
|
-
catch { /* best effort */ }
|
|
867
|
-
try {
|
|
868
|
-
this.heuristicStore?.close();
|
|
869
|
-
}
|
|
870
|
-
catch { /* best effort */ }
|
|
871
|
-
try {
|
|
872
|
-
this.modelRouter?.close();
|
|
873
|
-
}
|
|
874
|
-
catch { }
|
|
875
|
-
try {
|
|
876
|
-
this.autonomyTracker?.close();
|
|
877
|
-
}
|
|
878
|
-
catch { }
|
|
914
|
+
this.close();
|
|
879
915
|
this.bus.emit('system:warning', { message: 'Graceful shutdown complete' }, 'engine');
|
|
880
916
|
};
|
|
881
917
|
process.on('SIGINT', handler);
|
|
@@ -889,28 +925,18 @@ export class SwarmEngine {
|
|
|
889
925
|
}
|
|
890
926
|
this.signalHandlers = [];
|
|
891
927
|
}
|
|
892
|
-
/** Close all resources (stats, traces, heuristics, model router, autonomy tracker). */
|
|
928
|
+
/** Close all resources (stats, traces, heuristics, model router, autonomy tracker, database). */
|
|
893
929
|
close() {
|
|
894
930
|
try {
|
|
895
931
|
this.stats.close();
|
|
896
932
|
}
|
|
897
933
|
catch { }
|
|
934
|
+
this.learningEngine.close();
|
|
898
935
|
try {
|
|
899
|
-
this.
|
|
900
|
-
}
|
|
901
|
-
catch { }
|
|
902
|
-
try {
|
|
903
|
-
this.heuristicStore?.close();
|
|
904
|
-
}
|
|
905
|
-
catch { }
|
|
906
|
-
try {
|
|
907
|
-
this.modelRouter?.close();
|
|
908
|
-
}
|
|
909
|
-
catch { }
|
|
910
|
-
try {
|
|
911
|
-
this.autonomyTracker?.close();
|
|
936
|
+
this.executionGraph?.close();
|
|
912
937
|
}
|
|
913
938
|
catch { }
|
|
939
|
+
closeDatabase();
|
|
914
940
|
this.removeSignalHandlers();
|
|
915
941
|
}
|
|
916
942
|
// ─── Queries ───────────────────────────────────────────────────
|
|
@@ -930,33 +956,46 @@ export class SwarmEngine {
|
|
|
930
956
|
* Get active orchestrations.
|
|
931
957
|
*/
|
|
932
958
|
getActiveOrchestrations() {
|
|
933
|
-
return [...this.orchestrations.values()].filter(o => o.status === 'running' || o.status === 'paused');
|
|
959
|
+
return [...this.orchestrations.values()].filter((o) => o.status === 'running' || o.status === 'paused');
|
|
934
960
|
}
|
|
935
961
|
get patternRegistry() {
|
|
936
962
|
return this.patterns;
|
|
937
963
|
}
|
|
938
|
-
|
|
964
|
+
get graph() {
|
|
965
|
+
return this.executionGraph;
|
|
966
|
+
}
|
|
967
|
+
// ─── Helpers ───────────────────────────────────────────────────
|
|
968
|
+
tryInit(factory, name) {
|
|
969
|
+
try {
|
|
970
|
+
return factory();
|
|
971
|
+
}
|
|
972
|
+
catch (e) {
|
|
973
|
+
this.log.warn(`${name} init failed`, { error: e instanceof Error ? e.message : e });
|
|
974
|
+
return null;
|
|
975
|
+
}
|
|
976
|
+
}
|
|
939
977
|
/**
|
|
940
|
-
*
|
|
941
|
-
*
|
|
978
|
+
* Substitute ${key} placeholders in agent prompts.
|
|
979
|
+
* Uses string split/join instead of regex to avoid ReDoS from user-controlled keys.
|
|
942
980
|
*/
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
981
|
+
substituteParams(phases, params) {
|
|
982
|
+
for (const phase of phases) {
|
|
983
|
+
for (const agent of phase.agents) {
|
|
984
|
+
if (agent.prompt) {
|
|
985
|
+
for (const [key, value] of Object.entries(params)) {
|
|
986
|
+
const placeholder = '${' + key + '}';
|
|
987
|
+
agent.prompt = agent.prompt.split(placeholder).join(String(value));
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
}
|
|
953
992
|
}
|
|
954
993
|
// ─── Factory ───────────────────────────────────────────────────
|
|
955
994
|
createInstance(config) {
|
|
956
995
|
return {
|
|
957
996
|
config,
|
|
958
997
|
status: 'pending',
|
|
959
|
-
phases: config.phases.map(phaseConfig => ({
|
|
998
|
+
phases: config.phases.map((phaseConfig) => ({
|
|
960
999
|
config: phaseConfig,
|
|
961
1000
|
status: 'pending',
|
|
962
1001
|
agents: [],
|