swarm-engine 1.38.0 → 1.43.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/patterns.d.ts +7 -1
- package/dist/core/patterns.d.ts.map +1 -1
- package/dist/core/patterns.js +23 -0
- package/dist/core/patterns.js.map +1 -1
- package/dist/core/types.d.ts +11 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/index.d.ts +26 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -1
- package/dist/memory/index.d.ts.map +1 -1
- package/dist/memory/index.js +3 -9
- package/dist/memory/index.js.map +1 -1
- package/dist/runtime/acon.d.ts +61 -0
- package/dist/runtime/acon.d.ts.map +1 -0
- package/dist/runtime/acon.js +266 -0
- package/dist/runtime/acon.js.map +1 -0
- package/dist/runtime/adaptive.d.ts +1 -0
- package/dist/runtime/adaptive.d.ts.map +1 -1
- package/dist/runtime/adaptive.js +18 -4
- package/dist/runtime/adaptive.js.map +1 -1
- package/dist/runtime/agent-runner.d.ts +8 -0
- package/dist/runtime/agent-runner.d.ts.map +1 -1
- package/dist/runtime/agent-runner.js +79 -4
- package/dist/runtime/agent-runner.js.map +1 -1
- package/dist/runtime/backends/claude-session.d.ts +49 -0
- package/dist/runtime/backends/claude-session.d.ts.map +1 -0
- package/dist/runtime/backends/claude-session.js +237 -0
- package/dist/runtime/backends/claude-session.js.map +1 -0
- package/dist/runtime/backends/claude.d.ts.map +1 -1
- package/dist/runtime/backends/claude.js +90 -4
- package/dist/runtime/backends/claude.js.map +1 -1
- package/dist/runtime/backends/types.d.ts +31 -1
- package/dist/runtime/backends/types.d.ts.map +1 -1
- package/dist/runtime/benefits.d.ts +170 -0
- package/dist/runtime/benefits.d.ts.map +1 -0
- package/dist/runtime/benefits.js +588 -0
- package/dist/runtime/benefits.js.map +1 -0
- package/dist/runtime/compaction.d.ts +7 -1
- package/dist/runtime/compaction.d.ts.map +1 -1
- package/dist/runtime/compaction.js +12 -2
- package/dist/runtime/compaction.js.map +1 -1
- package/dist/runtime/compactor.d.ts +90 -0
- package/dist/runtime/compactor.d.ts.map +1 -0
- package/dist/runtime/compactor.js +418 -0
- package/dist/runtime/compactor.js.map +1 -0
- package/dist/runtime/context-decay.d.ts +45 -0
- package/dist/runtime/context-decay.d.ts.map +1 -0
- package/dist/runtime/context-decay.js +149 -0
- package/dist/runtime/context-decay.js.map +1 -0
- package/dist/runtime/distiller.d.ts +1 -0
- package/dist/runtime/distiller.d.ts.map +1 -1
- package/dist/runtime/distiller.js +8 -2
- package/dist/runtime/distiller.js.map +1 -1
- package/dist/runtime/engine.d.ts +44 -15
- package/dist/runtime/engine.d.ts.map +1 -1
- package/dist/runtime/engine.js +406 -68
- package/dist/runtime/engine.js.map +1 -1
- package/dist/runtime/execution-graph.d.ts.map +1 -1
- package/dist/runtime/execution-graph.js.map +1 -1
- package/dist/runtime/executor.d.ts +7 -1
- package/dist/runtime/executor.d.ts.map +1 -1
- package/dist/runtime/executor.js +20 -0
- package/dist/runtime/executor.js.map +1 -1
- package/dist/runtime/graph-adversarial.d.ts.map +1 -1
- package/dist/runtime/graph-adversarial.js.map +1 -1
- package/dist/runtime/graph-analyzer.d.ts.map +1 -1
- package/dist/runtime/graph-analyzer.js.map +1 -1
- package/dist/runtime/graph-discovery.d.ts.map +1 -1
- package/dist/runtime/graph-discovery.js +1 -4
- package/dist/runtime/graph-discovery.js.map +1 -1
- package/dist/runtime/graph-dropout.d.ts.map +1 -1
- package/dist/runtime/graph-dropout.js +10 -2
- package/dist/runtime/graph-dropout.js.map +1 -1
- package/dist/runtime/graph-embeddings.d.ts.map +1 -1
- package/dist/runtime/graph-embeddings.js +1 -3
- package/dist/runtime/graph-embeddings.js.map +1 -1
- package/dist/runtime/graph-feedback.d.ts.map +1 -1
- package/dist/runtime/graph-feedback.js +1 -3
- package/dist/runtime/graph-feedback.js.map +1 -1
- package/dist/runtime/graph-gnn.d.ts.map +1 -1
- package/dist/runtime/graph-gnn.js.map +1 -1
- package/dist/runtime/graph-meta-adversarial.d.ts.map +1 -1
- package/dist/runtime/graph-meta-adversarial.js.map +1 -1
- package/dist/runtime/graph-meta.d.ts.map +1 -1
- package/dist/runtime/graph-meta.js +48 -20
- package/dist/runtime/graph-meta.js.map +1 -1
- package/dist/runtime/graph-self-evolve.d.ts.map +1 -1
- package/dist/runtime/graph-self-evolve.js.map +1 -1
- package/dist/runtime/graph-synthesis.d.ts.map +1 -1
- package/dist/runtime/graph-synthesis.js.map +1 -1
- package/dist/runtime/graph-trajectory.d.ts.map +1 -1
- package/dist/runtime/graph-trajectory.js +3 -8
- package/dist/runtime/graph-trajectory.js.map +1 -1
- package/dist/runtime/learning-engine.d.ts.map +1 -1
- package/dist/runtime/learning-engine.js +10 -4
- package/dist/runtime/learning-engine.js.map +1 -1
- package/dist/runtime/output-schemas.d.ts +21 -0
- package/dist/runtime/output-schemas.d.ts.map +1 -0
- package/dist/runtime/output-schemas.js +252 -0
- package/dist/runtime/output-schemas.js.map +1 -0
- package/dist/runtime/prompt-compressor.d.ts +16 -0
- package/dist/runtime/prompt-compressor.d.ts.map +1 -0
- package/dist/runtime/prompt-compressor.js +68 -0
- package/dist/runtime/prompt-compressor.js.map +1 -0
- package/dist/runtime/repo-map.d.ts +40 -0
- package/dist/runtime/repo-map.d.ts.map +1 -0
- package/dist/runtime/repo-map.js +358 -0
- package/dist/runtime/repo-map.js.map +1 -0
- package/dist/runtime/sdk-mcp-server.d.ts +44 -0
- package/dist/runtime/sdk-mcp-server.d.ts.map +1 -0
- package/dist/runtime/sdk-mcp-server.js +133 -0
- package/dist/runtime/sdk-mcp-server.js.map +1 -0
- package/dist/runtime/structured-handoff.d.ts +41 -0
- package/dist/runtime/structured-handoff.d.ts.map +1 -0
- package/dist/runtime/structured-handoff.js +279 -0
- package/dist/runtime/structured-handoff.js.map +1 -0
- package/dist/runtime/token-analytics.d.ts +38 -0
- package/dist/runtime/token-analytics.d.ts.map +1 -0
- package/dist/runtime/token-analytics.js +59 -0
- package/dist/runtime/token-analytics.js.map +1 -0
- package/dist/runtime/verifier.d.ts +10 -0
- package/dist/runtime/verifier.d.ts.map +1 -1
- package/dist/runtime/verifier.js +97 -1
- package/dist/runtime/verifier.js.map +1 -1
- package/package.json +1 -1
package/dist/runtime/engine.js
CHANGED
|
@@ -23,6 +23,9 @@ 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';
|
|
28
31
|
import { ExecutionGraph } from './execution-graph.js';
|
|
@@ -43,6 +46,10 @@ import { MetaAdversarialTester } from './graph-meta-adversarial.js';
|
|
|
43
46
|
import { RuleEvolver } from './graph-self-evolve.js';
|
|
44
47
|
import { TaskDiscovery } from './graph-discovery.js';
|
|
45
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';
|
|
52
|
+
import { BenefitsCollector } from './benefits.js';
|
|
46
53
|
/**
|
|
47
54
|
* The Swarm Orchestration Engine.
|
|
48
55
|
*
|
|
@@ -81,19 +88,135 @@ export class SwarmEngine {
|
|
|
81
88
|
executionGraph = null;
|
|
82
89
|
graphAnalyzer = null;
|
|
83
90
|
graphLearner = null;
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
91
|
+
// Lazy-initialized ML modules (only constructed on first access)
|
|
92
|
+
_reviewFeedback;
|
|
93
|
+
_causalEngine;
|
|
94
|
+
_gnnPredictor;
|
|
95
|
+
_adversarialEvolver;
|
|
96
|
+
_metaSelector;
|
|
97
|
+
_predictiveDropout;
|
|
98
|
+
_gnnWeights;
|
|
99
|
+
_patternSynthesizer;
|
|
100
|
+
_trajectoryPredictor;
|
|
101
|
+
_metaAdversarial;
|
|
102
|
+
_ruleEvolver;
|
|
103
|
+
_taskDiscovery;
|
|
104
|
+
_orchestrationEmbedder;
|
|
105
|
+
swarmMcpServer = null;
|
|
106
|
+
decisions = new Map();
|
|
107
|
+
benefitsCollector = new BenefitsCollector();
|
|
108
|
+
get reviewFeedback() {
|
|
109
|
+
if (this._reviewFeedback === undefined) {
|
|
110
|
+
this._reviewFeedback = this.executionGraph
|
|
111
|
+
? this.tryInit(() => new ReviewFeedbackRecorder(this.executionGraph), 'ReviewFeedbackRecorder')
|
|
112
|
+
: null;
|
|
113
|
+
}
|
|
114
|
+
return this._reviewFeedback;
|
|
115
|
+
}
|
|
116
|
+
get causalEngine() {
|
|
117
|
+
if (this._causalEngine === undefined) {
|
|
118
|
+
this._causalEngine = this.executionGraph
|
|
119
|
+
? this.tryInit(() => new CausalGraphEngine(this.executionGraph), 'CausalGraphEngine')
|
|
120
|
+
: null;
|
|
121
|
+
}
|
|
122
|
+
return this._causalEngine;
|
|
123
|
+
}
|
|
124
|
+
get gnnPredictor() {
|
|
125
|
+
if (this._gnnPredictor === undefined) {
|
|
126
|
+
this._gnnPredictor = this.executionGraph
|
|
127
|
+
? this.tryInit(() => new FailurePropagationPredictor(this.executionGraph), 'FailurePropagationPredictor')
|
|
128
|
+
: null;
|
|
129
|
+
}
|
|
130
|
+
return this._gnnPredictor;
|
|
131
|
+
}
|
|
132
|
+
get adversarialEvolver() {
|
|
133
|
+
if (this._adversarialEvolver === undefined) {
|
|
134
|
+
this._adversarialEvolver = this.executionGraph
|
|
135
|
+
? this.tryInit(() => new AdversarialEvolver(this.executionGraph), 'AdversarialEvolver')
|
|
136
|
+
: null;
|
|
137
|
+
}
|
|
138
|
+
return this._adversarialEvolver;
|
|
139
|
+
}
|
|
140
|
+
get metaSelector() {
|
|
141
|
+
if (this._metaSelector === undefined) {
|
|
142
|
+
this._metaSelector = this.executionGraph
|
|
143
|
+
? this.tryInit(() => new MetaPatternSelector(this.executionGraph), 'MetaPatternSelector')
|
|
144
|
+
: null;
|
|
145
|
+
}
|
|
146
|
+
return this._metaSelector;
|
|
147
|
+
}
|
|
148
|
+
get predictiveDropout() {
|
|
149
|
+
if (this._predictiveDropout === undefined) {
|
|
150
|
+
this._predictiveDropout = this.executionGraph
|
|
151
|
+
? this.tryInit(() => new PredictiveDropout(this.executionGraph), 'PredictiveDropout')
|
|
152
|
+
: null;
|
|
153
|
+
}
|
|
154
|
+
return this._predictiveDropout;
|
|
155
|
+
}
|
|
156
|
+
get gnnWeights() {
|
|
157
|
+
if (this._gnnWeights === undefined) {
|
|
158
|
+
if (this.gnnPredictor) {
|
|
159
|
+
try {
|
|
160
|
+
this._gnnWeights = this.gnnPredictor.initWeights();
|
|
161
|
+
}
|
|
162
|
+
catch {
|
|
163
|
+
this._gnnWeights = null;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
this._gnnWeights = null;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return this._gnnWeights;
|
|
171
|
+
}
|
|
172
|
+
get patternSynthesizer() {
|
|
173
|
+
if (this._patternSynthesizer === undefined) {
|
|
174
|
+
this._patternSynthesizer = this.executionGraph
|
|
175
|
+
? this.tryInit(() => new PatternSynthesizer(this.executionGraph), 'PatternSynthesizer')
|
|
176
|
+
: null;
|
|
177
|
+
}
|
|
178
|
+
return this._patternSynthesizer;
|
|
179
|
+
}
|
|
180
|
+
get trajectoryPredictor() {
|
|
181
|
+
if (this._trajectoryPredictor === undefined) {
|
|
182
|
+
this._trajectoryPredictor = this.executionGraph
|
|
183
|
+
? this.tryInit(() => new TrajectoryPredictor(this.executionGraph), 'TrajectoryPredictor')
|
|
184
|
+
: null;
|
|
185
|
+
}
|
|
186
|
+
return this._trajectoryPredictor;
|
|
187
|
+
}
|
|
188
|
+
get metaAdversarial() {
|
|
189
|
+
if (this._metaAdversarial === undefined) {
|
|
190
|
+
this._metaAdversarial = this.executionGraph
|
|
191
|
+
? this.tryInit(() => new MetaAdversarialTester(this.executionGraph), 'MetaAdversarialTester')
|
|
192
|
+
: null;
|
|
193
|
+
}
|
|
194
|
+
return this._metaAdversarial;
|
|
195
|
+
}
|
|
196
|
+
get ruleEvolver() {
|
|
197
|
+
if (this._ruleEvolver === undefined) {
|
|
198
|
+
this._ruleEvolver = this.executionGraph
|
|
199
|
+
? this.tryInit(() => new RuleEvolver(this.executionGraph), 'RuleEvolver')
|
|
200
|
+
: null;
|
|
201
|
+
}
|
|
202
|
+
return this._ruleEvolver;
|
|
203
|
+
}
|
|
204
|
+
get taskDiscovery() {
|
|
205
|
+
if (this._taskDiscovery === undefined) {
|
|
206
|
+
this._taskDiscovery = this.executionGraph
|
|
207
|
+
? this.tryInit(() => new TaskDiscovery(this.executionGraph), 'TaskDiscovery')
|
|
208
|
+
: null;
|
|
209
|
+
}
|
|
210
|
+
return this._taskDiscovery;
|
|
211
|
+
}
|
|
212
|
+
get orchestrationEmbedder() {
|
|
213
|
+
if (this._orchestrationEmbedder === undefined) {
|
|
214
|
+
this._orchestrationEmbedder = this.executionGraph
|
|
215
|
+
? this.tryInit(() => new OrchestrationEmbedder(this.executionGraph), 'OrchestrationEmbedder')
|
|
216
|
+
: null;
|
|
217
|
+
}
|
|
218
|
+
return this._orchestrationEmbedder;
|
|
219
|
+
}
|
|
97
220
|
constructor(options) {
|
|
98
221
|
this.options = options;
|
|
99
222
|
this.registry = options.registry;
|
|
@@ -127,46 +250,36 @@ export class SwarmEngine {
|
|
|
127
250
|
this.compounder = this.tryInit(() => new KnowledgeCompounder(), 'KnowledgeCompounder');
|
|
128
251
|
this.executionGraph = this.tryInit(() => new ExecutionGraph(), 'ExecutionGraph');
|
|
129
252
|
this.executionGraph?.attachToEventBus(this.bus);
|
|
253
|
+
// Create SwarmMcpServer for SDK tool injection
|
|
254
|
+
try {
|
|
255
|
+
this.swarmMcpServer = new SwarmMcpServer({
|
|
256
|
+
phaseOutputs: this.phaseOutputs,
|
|
257
|
+
graph: this.executionGraph,
|
|
258
|
+
decisions: this.decisions,
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
catch {
|
|
262
|
+
this.swarmMcpServer = null;
|
|
263
|
+
}
|
|
130
264
|
// Create graph analysis deps (reused by AgentRunner + LearningEngine)
|
|
131
|
-
this.graphLearner = this.executionGraph
|
|
132
|
-
|
|
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')
|
|
265
|
+
this.graphLearner = this.executionGraph
|
|
266
|
+
? this.tryInit(() => new GraphLearner(this.executionGraph), 'GraphLearner')
|
|
143
267
|
: null;
|
|
144
|
-
this.
|
|
145
|
-
? this.tryInit(() => new
|
|
268
|
+
this.graphAnalyzer = this.executionGraph
|
|
269
|
+
? this.tryInit(() => new GraphAnalyzer(this.executionGraph), 'GraphAnalyzer')
|
|
146
270
|
: null;
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
//
|
|
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;
|
|
271
|
+
// ML modules (reviewFeedback, causalEngine, gnnPredictor, adversarialEvolver,
|
|
272
|
+
// metaSelector, predictiveDropout, gnnWeights, patternSynthesizer, trajectoryPredictor,
|
|
273
|
+
// metaAdversarial, ruleEvolver, taskDiscovery, orchestrationEmbedder) are lazy-initialized
|
|
274
|
+
// via property getters — they are only constructed on first access.
|
|
164
275
|
// Prune stale graph data on init (Feature 6: graph decay)
|
|
165
276
|
if (this.executionGraph) {
|
|
166
277
|
try {
|
|
167
278
|
this.executionGraph.pruneOlderThan(90);
|
|
168
279
|
}
|
|
169
|
-
catch {
|
|
280
|
+
catch {
|
|
281
|
+
/* silent */
|
|
282
|
+
}
|
|
170
283
|
}
|
|
171
284
|
// Rule evolution proposal on startup (skip in mock mode and when no historical data)
|
|
172
285
|
if (this.ruleEvolver && !this.options.mock) {
|
|
@@ -219,10 +332,35 @@ export class SwarmEngine {
|
|
|
219
332
|
/**
|
|
220
333
|
* Execute an orchestration from config.
|
|
221
334
|
*/
|
|
222
|
-
async execute(config) {
|
|
335
|
+
async execute(config, executeOptions) {
|
|
223
336
|
// Re-use existing instance if present (e.g., when resuming from checkpoint)
|
|
224
337
|
const orchestration = this.orchestrations.get(config.id) ?? this.createInstance(config);
|
|
225
338
|
this.orchestrations.set(config.id, orchestration);
|
|
339
|
+
// Dry run mode: execute only the first phase in 'plan' mode, then return
|
|
340
|
+
if (executeOptions?.dryRun) {
|
|
341
|
+
this.log.info('Dry run mode: running first phase in plan mode');
|
|
342
|
+
const firstPhase = orchestration.phases[0];
|
|
343
|
+
if (firstPhase) {
|
|
344
|
+
this.executor.setPermissionMode('plan');
|
|
345
|
+
try {
|
|
346
|
+
this.phaseOutputs.clear();
|
|
347
|
+
orchestration.status = 'running';
|
|
348
|
+
orchestration.startedAt = new Date();
|
|
349
|
+
const outputs = await this.executePhase(orchestration, firstPhase);
|
|
350
|
+
this.phaseOutputs.set(firstPhase.config.name, outputs);
|
|
351
|
+
orchestration.status = 'completed';
|
|
352
|
+
}
|
|
353
|
+
catch (error) {
|
|
354
|
+
orchestration.status = 'failed';
|
|
355
|
+
this.log.error('Dry run failed', { error: error instanceof Error ? error.message : String(error) });
|
|
356
|
+
}
|
|
357
|
+
finally {
|
|
358
|
+
this.executor.setPermissionMode(this.options.permissionMode ?? 'default');
|
|
359
|
+
orchestration.completedAt = new Date();
|
|
360
|
+
}
|
|
361
|
+
return orchestration;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
226
364
|
this.log.info(`Starting orchestration: ${config.name}`, {
|
|
227
365
|
pattern: config.pattern,
|
|
228
366
|
phases: config.phases.length,
|
|
@@ -233,9 +371,45 @@ export class SwarmEngine {
|
|
|
233
371
|
pattern: config.pattern,
|
|
234
372
|
phaseCount: config.phases.length,
|
|
235
373
|
}, 'engine');
|
|
374
|
+
// Single-agent fast path for simple tasks
|
|
375
|
+
if (config.phases?.length === 1 && config.phases[0].agents?.length === 1) {
|
|
376
|
+
try {
|
|
377
|
+
const complexity = classifyTaskComplexity(config.description || '');
|
|
378
|
+
if (complexity === 'simple') {
|
|
379
|
+
this.log.info('Simple task detected — using single-agent fast path');
|
|
380
|
+
this.bus.emit('system:warning', { type: 'single-agent-bypass', task: config.description }, 'engine');
|
|
381
|
+
try {
|
|
382
|
+
const phase = config.phases[0];
|
|
383
|
+
const agent = phase.agents[0];
|
|
384
|
+
const runContext = {
|
|
385
|
+
phaseOutputs: new Map(),
|
|
386
|
+
intraPhaseOutputs: new Map(),
|
|
387
|
+
livingSpec: null,
|
|
388
|
+
sharedContextFiles: [],
|
|
389
|
+
};
|
|
390
|
+
const instance = await this.agentRunner.executeAgentWithRetry(agent, orchestration.phases[0], orchestration, 0, runContext);
|
|
391
|
+
orchestration.status = instance.result?.output ? 'completed' : 'failed';
|
|
392
|
+
orchestration.startedAt = new Date();
|
|
393
|
+
orchestration.completedAt = new Date();
|
|
394
|
+
orchestration.phases[0].status = orchestration.status;
|
|
395
|
+
if (instance.result)
|
|
396
|
+
orchestration.phases[0].agents = [instance];
|
|
397
|
+
orchestration.usage = instance.usage;
|
|
398
|
+
return orchestration;
|
|
399
|
+
}
|
|
400
|
+
catch (e) {
|
|
401
|
+
this.log.debug(`Single-agent bypass failed, falling through to normal execution: ${e}`);
|
|
402
|
+
// Fall through to normal execution
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
catch {
|
|
407
|
+
/* classification failed — fall through */
|
|
408
|
+
}
|
|
409
|
+
}
|
|
236
410
|
orchestration.status = 'running';
|
|
237
411
|
orchestration.startedAt = new Date();
|
|
238
|
-
this.phaseOutputs
|
|
412
|
+
this.phaseOutputs.clear();
|
|
239
413
|
// TODO: Checkpoint doesn't store phase outputs — resumed phases lose inter-phase context
|
|
240
414
|
this.reflections = [];
|
|
241
415
|
this.sharedContextFiles = [];
|
|
@@ -257,7 +431,8 @@ export class SwarmEngine {
|
|
|
257
431
|
if (this.gnnPredictor && this.gnnWeights) {
|
|
258
432
|
try {
|
|
259
433
|
const risks = this.gnnPredictor.predict(orchestration.config.id, this.gnnWeights);
|
|
260
|
-
const highRisk = risks.filter(r => r.riskScore > 0.7);
|
|
434
|
+
const highRisk = risks.filter((r) => r.riskScore > 0.7);
|
|
435
|
+
this.benefitsCollector.addGnnPrediction(highRisk.length);
|
|
261
436
|
if (highRisk.length > 0) {
|
|
262
437
|
this.log.warn(`GNN predicts high failure risk for ${highRisk.length} nodes`);
|
|
263
438
|
this.bus.emit('system:warning', { type: 'gnn-risk', risks: highRisk }, 'engine');
|
|
@@ -341,17 +516,32 @@ export class SwarmEngine {
|
|
|
341
516
|
const reflection = this.reflectionEngine.reflect(phase);
|
|
342
517
|
const patternStats = this.graphLearner?.getPatternStats(orchestration.config.pattern)?.[0];
|
|
343
518
|
const gateOutputs = this.phaseOutputs.get(phase.config.name) || [];
|
|
344
|
-
const gate = this.graphAnalyzer.evaluatePhaseConfidence({
|
|
519
|
+
const gate = this.graphAnalyzer.evaluatePhaseConfidence({
|
|
520
|
+
kind: phase.config.kind ?? 'implement',
|
|
521
|
+
agentCount: phase.agents.length,
|
|
522
|
+
outputs: gateOutputs,
|
|
523
|
+
usage: phase.usage,
|
|
524
|
+
}, { confidence: reflection.confidence }, patternStats
|
|
525
|
+
? { successRate: patternStats.successRate, avgDurationMs: patternStats.avgDurationMs }
|
|
526
|
+
: undefined);
|
|
527
|
+
this.benefitsCollector.addConfidenceGateEvaluated(gate.result !== 'pass');
|
|
345
528
|
if (gate.result === 'halt') {
|
|
346
529
|
this.log.warn(`Phase gate HALT: ${gate.reason}`);
|
|
347
530
|
orchestration.status = 'failed';
|
|
348
531
|
}
|
|
349
532
|
if (gate.result === 'warn') {
|
|
350
533
|
this.log.warn(`Phase gate WARNING: ${gate.reason}`);
|
|
351
|
-
this.bus.emit('system:warning', {
|
|
534
|
+
this.bus.emit('system:warning', {
|
|
535
|
+
type: 'gate-warning',
|
|
536
|
+
orchestrationId: orchestration.config.id,
|
|
537
|
+
phase: phase.config.name,
|
|
538
|
+
reason: gate.reason,
|
|
539
|
+
}, 'engine');
|
|
352
540
|
}
|
|
353
541
|
}
|
|
354
|
-
catch {
|
|
542
|
+
catch {
|
|
543
|
+
/* Graph gate evaluation failed — continue */
|
|
544
|
+
}
|
|
355
545
|
}
|
|
356
546
|
// Trajectory prediction after each completed phase
|
|
357
547
|
if (this.trajectoryPredictor) {
|
|
@@ -376,9 +566,17 @@ export class SwarmEngine {
|
|
|
376
566
|
if (this.trajectoryPredictor) {
|
|
377
567
|
try {
|
|
378
568
|
const forecastCompleted = orchestration.phases
|
|
379
|
-
.filter(p => p.status === 'completed')
|
|
380
|
-
.map(p => ({
|
|
381
|
-
|
|
569
|
+
.filter((p) => p.status === 'completed')
|
|
570
|
+
.map((p) => ({
|
|
571
|
+
name: p.config.name,
|
|
572
|
+
status: p.status,
|
|
573
|
+
tokens: p.usage?.totalTokens || 0,
|
|
574
|
+
durationMs: p.usage?.durationMs || 0,
|
|
575
|
+
confidence: 'medium',
|
|
576
|
+
}));
|
|
577
|
+
const forecastRemaining = orchestration.phases
|
|
578
|
+
.filter((p) => p.status === 'pending')
|
|
579
|
+
.map((p) => p.config.name);
|
|
382
580
|
if (forecastRemaining.length > 0) {
|
|
383
581
|
const forecast = this.trajectoryPredictor.predictTrajectory(orchestration.config.id, forecastCompleted, forecastRemaining, orchestration.config.pattern);
|
|
384
582
|
if (forecast.overallSuccessProb < 0.3) {
|
|
@@ -450,11 +648,13 @@ export class SwarmEngine {
|
|
|
450
648
|
const report = this.metaAdversarial.runFullAudit({
|
|
451
649
|
patternSelector: this.metaSelector ?? undefined,
|
|
452
650
|
dropout: this.predictiveDropout ?? undefined,
|
|
453
|
-
causal: this.causalEngine
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
651
|
+
causal: this.causalEngine
|
|
652
|
+
? {
|
|
653
|
+
estimateCausalEffect: (t, tv, cv) =>
|
|
654
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
655
|
+
this.causalEngine.estimateCausalEffect(t, tv, cv),
|
|
656
|
+
}
|
|
657
|
+
: undefined,
|
|
458
658
|
});
|
|
459
659
|
if (report.overallRisk !== 'low') {
|
|
460
660
|
this.log.warn(`Self-audit found ${report.vulnerabilities.length} ML vulnerabilities (risk: ${report.overallRisk})`);
|
|
@@ -489,12 +689,38 @@ export class SwarmEngine {
|
|
|
489
689
|
}
|
|
490
690
|
// Compute total usage
|
|
491
691
|
orchestration.usage = orchestration.phases.reduce((total, phase) => mergeUsageStats(total, phase.usage), emptyUsageStats());
|
|
692
|
+
// Build benefits summary
|
|
693
|
+
let patternStats;
|
|
694
|
+
if (this.graphLearner) {
|
|
695
|
+
try {
|
|
696
|
+
const stats = this.graphLearner.getPatternStats(config.pattern)?.[0];
|
|
697
|
+
if (stats)
|
|
698
|
+
patternStats = { totalRuns: stats.totalRuns, successRate: stats.successRate, avgCostUsd: stats.avgCostUsd };
|
|
699
|
+
}
|
|
700
|
+
catch {
|
|
701
|
+
/* pattern stats unavailable */
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
const durationMs = orchestration.completedAt.getTime() - (orchestration.startedAt?.getTime() ?? 0);
|
|
705
|
+
const benefitsSummary = this.benefitsCollector.build({
|
|
706
|
+
actualTotalTokens: orchestration.usage.totalTokens,
|
|
707
|
+
actualCostUsd: orchestration.usage.costUsd,
|
|
708
|
+
durationMs,
|
|
709
|
+
agentsExecuted: orchestration.phases.reduce((s, p) => s + p.agents.length, 0),
|
|
710
|
+
phasesExecuted: orchestration.phases.filter((p) => p.status === 'completed').length,
|
|
711
|
+
historicalAvgCostUsd: patternStats?.avgCostUsd ?? null,
|
|
712
|
+
patternRunsRecorded: patternStats?.totalRuns ?? 0,
|
|
713
|
+
historicalSuccessRate: patternStats?.successRate ?? null,
|
|
714
|
+
});
|
|
715
|
+
orchestration.benefits = benefitsSummary;
|
|
716
|
+
this.benefitsCollector.reset();
|
|
492
717
|
this.bus.emit(orchestration.status === 'completed' ? 'orchestration:complete' : 'orchestration:failed', {
|
|
493
718
|
id: config.id,
|
|
494
719
|
name: config.name,
|
|
495
720
|
status: orchestration.status,
|
|
496
721
|
usage: orchestration.usage,
|
|
497
|
-
|
|
722
|
+
benefits: benefitsSummary,
|
|
723
|
+
durationMs,
|
|
498
724
|
}, 'engine');
|
|
499
725
|
this.log.info(`Orchestration ${orchestration.status}: ${config.name}`, {
|
|
500
726
|
usage: orchestration.usage,
|
|
@@ -661,6 +887,24 @@ export class SwarmEngine {
|
|
|
661
887
|
quality: c.cost.qualityScore,
|
|
662
888
|
}));
|
|
663
889
|
}
|
|
890
|
+
// Emit plan preview event so listeners can inspect before execution
|
|
891
|
+
try {
|
|
892
|
+
this.bus.emit('system:warning', {
|
|
893
|
+
type: 'plan-preview',
|
|
894
|
+
pattern: patternName,
|
|
895
|
+
phases: bestPlan.phases.map((p) => ({
|
|
896
|
+
name: p.name,
|
|
897
|
+
kind: p.kind,
|
|
898
|
+
agentCount: p.agents?.length || 0,
|
|
899
|
+
agents: p.agents?.map((a) => ({ type: a.type, model: a.model })) || [],
|
|
900
|
+
})),
|
|
901
|
+
estimatedCost: bestPlan.estimates,
|
|
902
|
+
task: task.slice(0, 200),
|
|
903
|
+
}, 'engine');
|
|
904
|
+
}
|
|
905
|
+
catch {
|
|
906
|
+
/* plan preview emit failed — non-critical */
|
|
907
|
+
}
|
|
664
908
|
return bestPlan;
|
|
665
909
|
}
|
|
666
910
|
/**
|
|
@@ -674,6 +918,9 @@ export class SwarmEngine {
|
|
|
674
918
|
const dropout = this.replanner.evaluateDropout(orchestration, phase.config);
|
|
675
919
|
if (dropout) {
|
|
676
920
|
this.log.info(`Agent dropout: ${dropout.reason}`);
|
|
921
|
+
const droppedCount = phase.config.agents.length - dropout.agentsToKeep.length;
|
|
922
|
+
for (let i = 0; i < droppedCount; i++)
|
|
923
|
+
this.benefitsCollector.addAgentDroppedRules();
|
|
677
924
|
phase.config.agents = phase.config.agents.filter((a) => dropout.agentsToKeep.includes(a.name));
|
|
678
925
|
this.bus.emit('system:warning', { type: 'agent-dropout', ...dropout }, 'adaptive');
|
|
679
926
|
}
|
|
@@ -681,20 +928,96 @@ export class SwarmEngine {
|
|
|
681
928
|
// Predictive dropout from ML model (Feature 5: Predictive Dropout)
|
|
682
929
|
if (this.predictiveDropout && phase.config.agents.length > 1) {
|
|
683
930
|
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);
|
|
931
|
+
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 || '');
|
|
932
|
+
const toDrop = recommendations.filter((r) => r.shouldDropOut);
|
|
686
933
|
if (toDrop.length > 0) {
|
|
687
|
-
this.log.info(`Predictive dropout: skipping ${toDrop.map(d => d.agentType).join(', ')}`);
|
|
688
|
-
|
|
934
|
+
this.log.info(`Predictive dropout: skipping ${toDrop.map((d) => d.agentType).join(', ')}`);
|
|
935
|
+
for (const d of toDrop)
|
|
936
|
+
this.benefitsCollector.addAgentDroppedML(5000); // ~5K tokens per dropped agent
|
|
937
|
+
phase.config.agents = phase.config.agents.filter((a) => !toDrop.some((d) => d.agentType === a.type));
|
|
689
938
|
}
|
|
690
939
|
}
|
|
691
940
|
catch (e) {
|
|
692
941
|
this.log.debug(`Predictive dropout failed: ${e}`);
|
|
693
942
|
}
|
|
694
943
|
}
|
|
944
|
+
// Feature 5: Diff-based review context — inject git diff before review phases
|
|
945
|
+
if (phase.config.kind === 'review' && !this.options.mock) {
|
|
946
|
+
try {
|
|
947
|
+
const cwd = this.options.cwd || process.cwd();
|
|
948
|
+
const { stdout: diff } = await execFileAsync('git', ['diff', '--stat', 'HEAD'], {
|
|
949
|
+
cwd,
|
|
950
|
+
timeout: 10000,
|
|
951
|
+
encoding: 'utf-8',
|
|
952
|
+
});
|
|
953
|
+
const trimmedDiff = diff.slice(0, 3000);
|
|
954
|
+
if (trimmedDiff.trim()) {
|
|
955
|
+
const { stdout: diffDetail } = await execFileAsync('git', ['diff', 'HEAD'], {
|
|
956
|
+
cwd,
|
|
957
|
+
timeout: 10000,
|
|
958
|
+
encoding: 'utf-8',
|
|
959
|
+
});
|
|
960
|
+
const existing = this.phaseOutputs.get(phase.config.name) || [];
|
|
961
|
+
existing.unshift(`## Git Diff Summary\n\`\`\`\n${trimmedDiff}\n\`\`\`\n\n## Detailed Changes\n\`\`\`diff\n${diffDetail.slice(0, 5000)}\n\`\`\``);
|
|
962
|
+
this.phaseOutputs.set(phase.config.name, existing);
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
catch (e) {
|
|
966
|
+
this.log.debug(`Diff context failed: ${e}`);
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
// Feature 6: Pre-review lint wiring — run lint/typecheck before review phases
|
|
970
|
+
if (phase.config.kind === 'review' && this.verifier && !this.options.mock) {
|
|
971
|
+
try {
|
|
972
|
+
const lintResults = await this.verifier.verify();
|
|
973
|
+
const failures = lintResults.filter((r) => !r.passed);
|
|
974
|
+
if (failures.length > 0) {
|
|
975
|
+
const lintOutput = failures.map((f) => `${f.step}: ${f.output?.slice(0, 500)}`).join('\n');
|
|
976
|
+
const existing = this.phaseOutputs.get(phase.config.name) || [];
|
|
977
|
+
existing.unshift(`## Pre-Review Lint/Typecheck Results\n${lintOutput}`);
|
|
978
|
+
this.phaseOutputs.set(phase.config.name, existing);
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
catch (e) {
|
|
982
|
+
this.log.debug(`Pre-review checks failed: ${e}`);
|
|
983
|
+
}
|
|
984
|
+
}
|
|
695
985
|
const maxRetries = phase.config.maxRetries ?? 2;
|
|
986
|
+
// Smart phase output routing: filter prior outputs by relevance instead of passing everything
|
|
987
|
+
let filteredOutputs = this.phaseOutputs;
|
|
988
|
+
if (phase.config.kind === 'review') {
|
|
989
|
+
// Review phase only needs implement/integrate/test outputs, not research
|
|
990
|
+
const reviewFiltered = new Map();
|
|
991
|
+
for (const [phaseName, outputs] of this.phaseOutputs) {
|
|
992
|
+
if (phaseName.includes('implement') || phaseName.includes('integrate') || phaseName.includes('test')) {
|
|
993
|
+
reviewFiltered.set(phaseName, outputs);
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
// Fall back to all outputs if nothing matched
|
|
997
|
+
if (reviewFiltered.size > 0) {
|
|
998
|
+
const filteredTokens = [...reviewFiltered.values()].flat().reduce((s, o) => s + estimateTokens(o), 0);
|
|
999
|
+
const totalTokens = [...this.phaseOutputs.values()].flat().reduce((s, o) => s + estimateTokens(o), 0);
|
|
1000
|
+
this.benefitsCollector.addSmartRoutingSaved(Math.max(0, totalTokens - filteredTokens));
|
|
1001
|
+
filteredOutputs = reviewFiltered;
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
if (phase.config.kind === 'test') {
|
|
1005
|
+
// Test phase only needs implement outputs
|
|
1006
|
+
const testFiltered = new Map();
|
|
1007
|
+
for (const [phaseName, outputs] of this.phaseOutputs) {
|
|
1008
|
+
if (phaseName.includes('implement')) {
|
|
1009
|
+
testFiltered.set(phaseName, outputs);
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
if (testFiltered.size > 0) {
|
|
1013
|
+
const filteredTokens = [...testFiltered.values()].flat().reduce((s, o) => s + estimateTokens(o), 0);
|
|
1014
|
+
const totalTokens = [...this.phaseOutputs.values()].flat().reduce((s, o) => s + estimateTokens(o), 0);
|
|
1015
|
+
this.benefitsCollector.addSmartRoutingSaved(Math.max(0, totalTokens - filteredTokens));
|
|
1016
|
+
filteredOutputs = testFiltered;
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
696
1019
|
const runContext = {
|
|
697
|
-
phaseOutputs:
|
|
1020
|
+
phaseOutputs: filteredOutputs,
|
|
698
1021
|
livingSpec: this.livingSpec,
|
|
699
1022
|
sharedContextFiles: this.sharedContextFiles,
|
|
700
1023
|
cwd: this.options.cwd,
|
|
@@ -719,6 +1042,7 @@ export class SwarmEngine {
|
|
|
719
1042
|
const sharedTokens = estimateTokens(`## Orchestration: ${orchestration.config.name}\nPattern: ${orchestration.config.pattern}\n\n${phaseContext}`);
|
|
720
1043
|
if (sharedTokens > 0) {
|
|
721
1044
|
const savings = estimateCacheSavings(sharedTokens, phase.config.agents.length, 3.0);
|
|
1045
|
+
this.benefitsCollector.addCacheSavingsUsd(savings.savings ?? 0);
|
|
722
1046
|
this.log.info(`Cache optimization: ${savings.savingsPercent}% estimated savings on shared prefix (${phase.config.agents.length} agents)`, savings);
|
|
723
1047
|
}
|
|
724
1048
|
}
|
|
@@ -778,6 +1102,7 @@ export class SwarmEngine {
|
|
|
778
1102
|
if (agent.result?.output) {
|
|
779
1103
|
try {
|
|
780
1104
|
this.reviewFeedback.recordFindings(orchestration.config.id, phase.config.name, agent.result.output, agent.config.name);
|
|
1105
|
+
this.benefitsCollector.addReviewFindingRecorded();
|
|
781
1106
|
const findings = parseReviewOutput(agent.result.output, agent.config.name);
|
|
782
1107
|
this.reviewFeedback.adjustContextWeights(orchestration.config.id, findings);
|
|
783
1108
|
}
|
|
@@ -788,7 +1113,7 @@ export class SwarmEngine {
|
|
|
788
1113
|
}
|
|
789
1114
|
}
|
|
790
1115
|
// Run verification after implementation phases
|
|
791
|
-
if (phase.config.kind === 'implement' || phase.config.kind === 'integrate') {
|
|
1116
|
+
if ((phase.config.kind === 'implement' || phase.config.kind === 'integrate') && !this.options.mock) {
|
|
792
1117
|
const results = await this.verifier.verify();
|
|
793
1118
|
const summary = Verifier.summarize(results);
|
|
794
1119
|
this.log.info(`Verification: ${summary}`, { phase: phase.config.name });
|
|
@@ -798,6 +1123,10 @@ export class SwarmEngine {
|
|
|
798
1123
|
const adaptation = this.replanner.analyze(orchestration, phase);
|
|
799
1124
|
if (adaptation) {
|
|
800
1125
|
this.replanner.apply(orchestration, adaptation);
|
|
1126
|
+
for (const _s of adaptation.skipPhases || [])
|
|
1127
|
+
this.benefitsCollector.addPhaseSkipped();
|
|
1128
|
+
for (const _o of Object.keys(adaptation.modelOverrides || {}))
|
|
1129
|
+
this.benefitsCollector.addModelDowngrade();
|
|
801
1130
|
this.bus.emit('system:warning', {
|
|
802
1131
|
type: 'adaptive-replan',
|
|
803
1132
|
reason: adaptation.reason,
|
|
@@ -817,13 +1146,22 @@ export class SwarmEngine {
|
|
|
817
1146
|
agents: phase.agents.length,
|
|
818
1147
|
usage: phase.usage,
|
|
819
1148
|
});
|
|
820
|
-
// Collect agent outputs for inter-phase context
|
|
1149
|
+
// Collect agent outputs for inter-phase context (structured handoff)
|
|
821
1150
|
const outputs = phase.agents
|
|
822
1151
|
.filter((a) => a.result?.output)
|
|
823
|
-
.map((a) =>
|
|
1152
|
+
.map((a) => {
|
|
1153
|
+
try {
|
|
1154
|
+
const handoff = HandoffParser.parseAgentOutput(a.result.output);
|
|
1155
|
+
return HandoffParser.serializeForNextPhase([{ agentName: a.config.name, handoff }], 4000);
|
|
1156
|
+
}
|
|
1157
|
+
catch {
|
|
1158
|
+
return `[${a.config.name}]: ${a.result.output.slice(0, 2000)}`;
|
|
1159
|
+
}
|
|
1160
|
+
});
|
|
824
1161
|
// Update living spec with phase outputs
|
|
825
1162
|
if (this.livingSpec) {
|
|
826
1163
|
this.livingSpec = updateSpecFromPhase(this.livingSpec, phase.config.name, outputs);
|
|
1164
|
+
this.benefitsCollector.addLivingSpecUpdate();
|
|
827
1165
|
}
|
|
828
1166
|
// Generate reflection and inject into outputs for next phase
|
|
829
1167
|
const reflection = this.reflectionEngine.reflect(phase);
|