swarm-engine 1.38.0 → 1.41.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +9 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/index.d.ts +11 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -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/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/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/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 +43 -15
- package/dist/runtime/engine.d.ts.map +1 -1
- package/dist/runtime/engine.js +355 -67
- 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/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,9 @@ 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';
|
|
46
52
|
/**
|
|
47
53
|
* The Swarm Orchestration Engine.
|
|
48
54
|
*
|
|
@@ -81,19 +87,134 @@ export class SwarmEngine {
|
|
|
81
87
|
executionGraph = null;
|
|
82
88
|
graphAnalyzer = null;
|
|
83
89
|
graphLearner = null;
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
90
|
+
// Lazy-initialized ML modules (only constructed on first access)
|
|
91
|
+
_reviewFeedback;
|
|
92
|
+
_causalEngine;
|
|
93
|
+
_gnnPredictor;
|
|
94
|
+
_adversarialEvolver;
|
|
95
|
+
_metaSelector;
|
|
96
|
+
_predictiveDropout;
|
|
97
|
+
_gnnWeights;
|
|
98
|
+
_patternSynthesizer;
|
|
99
|
+
_trajectoryPredictor;
|
|
100
|
+
_metaAdversarial;
|
|
101
|
+
_ruleEvolver;
|
|
102
|
+
_taskDiscovery;
|
|
103
|
+
_orchestrationEmbedder;
|
|
104
|
+
swarmMcpServer = null;
|
|
105
|
+
decisions = new Map();
|
|
106
|
+
get reviewFeedback() {
|
|
107
|
+
if (this._reviewFeedback === undefined) {
|
|
108
|
+
this._reviewFeedback = this.executionGraph
|
|
109
|
+
? this.tryInit(() => new ReviewFeedbackRecorder(this.executionGraph), 'ReviewFeedbackRecorder')
|
|
110
|
+
: null;
|
|
111
|
+
}
|
|
112
|
+
return this._reviewFeedback;
|
|
113
|
+
}
|
|
114
|
+
get causalEngine() {
|
|
115
|
+
if (this._causalEngine === undefined) {
|
|
116
|
+
this._causalEngine = this.executionGraph
|
|
117
|
+
? this.tryInit(() => new CausalGraphEngine(this.executionGraph), 'CausalGraphEngine')
|
|
118
|
+
: null;
|
|
119
|
+
}
|
|
120
|
+
return this._causalEngine;
|
|
121
|
+
}
|
|
122
|
+
get gnnPredictor() {
|
|
123
|
+
if (this._gnnPredictor === undefined) {
|
|
124
|
+
this._gnnPredictor = this.executionGraph
|
|
125
|
+
? this.tryInit(() => new FailurePropagationPredictor(this.executionGraph), 'FailurePropagationPredictor')
|
|
126
|
+
: null;
|
|
127
|
+
}
|
|
128
|
+
return this._gnnPredictor;
|
|
129
|
+
}
|
|
130
|
+
get adversarialEvolver() {
|
|
131
|
+
if (this._adversarialEvolver === undefined) {
|
|
132
|
+
this._adversarialEvolver = this.executionGraph
|
|
133
|
+
? this.tryInit(() => new AdversarialEvolver(this.executionGraph), 'AdversarialEvolver')
|
|
134
|
+
: null;
|
|
135
|
+
}
|
|
136
|
+
return this._adversarialEvolver;
|
|
137
|
+
}
|
|
138
|
+
get metaSelector() {
|
|
139
|
+
if (this._metaSelector === undefined) {
|
|
140
|
+
this._metaSelector = this.executionGraph
|
|
141
|
+
? this.tryInit(() => new MetaPatternSelector(this.executionGraph), 'MetaPatternSelector')
|
|
142
|
+
: null;
|
|
143
|
+
}
|
|
144
|
+
return this._metaSelector;
|
|
145
|
+
}
|
|
146
|
+
get predictiveDropout() {
|
|
147
|
+
if (this._predictiveDropout === undefined) {
|
|
148
|
+
this._predictiveDropout = this.executionGraph
|
|
149
|
+
? this.tryInit(() => new PredictiveDropout(this.executionGraph), 'PredictiveDropout')
|
|
150
|
+
: null;
|
|
151
|
+
}
|
|
152
|
+
return this._predictiveDropout;
|
|
153
|
+
}
|
|
154
|
+
get gnnWeights() {
|
|
155
|
+
if (this._gnnWeights === undefined) {
|
|
156
|
+
if (this.gnnPredictor) {
|
|
157
|
+
try {
|
|
158
|
+
this._gnnWeights = this.gnnPredictor.initWeights();
|
|
159
|
+
}
|
|
160
|
+
catch {
|
|
161
|
+
this._gnnWeights = null;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
this._gnnWeights = null;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return this._gnnWeights;
|
|
169
|
+
}
|
|
170
|
+
get patternSynthesizer() {
|
|
171
|
+
if (this._patternSynthesizer === undefined) {
|
|
172
|
+
this._patternSynthesizer = this.executionGraph
|
|
173
|
+
? this.tryInit(() => new PatternSynthesizer(this.executionGraph), 'PatternSynthesizer')
|
|
174
|
+
: null;
|
|
175
|
+
}
|
|
176
|
+
return this._patternSynthesizer;
|
|
177
|
+
}
|
|
178
|
+
get trajectoryPredictor() {
|
|
179
|
+
if (this._trajectoryPredictor === undefined) {
|
|
180
|
+
this._trajectoryPredictor = this.executionGraph
|
|
181
|
+
? this.tryInit(() => new TrajectoryPredictor(this.executionGraph), 'TrajectoryPredictor')
|
|
182
|
+
: null;
|
|
183
|
+
}
|
|
184
|
+
return this._trajectoryPredictor;
|
|
185
|
+
}
|
|
186
|
+
get metaAdversarial() {
|
|
187
|
+
if (this._metaAdversarial === undefined) {
|
|
188
|
+
this._metaAdversarial = this.executionGraph
|
|
189
|
+
? this.tryInit(() => new MetaAdversarialTester(this.executionGraph), 'MetaAdversarialTester')
|
|
190
|
+
: null;
|
|
191
|
+
}
|
|
192
|
+
return this._metaAdversarial;
|
|
193
|
+
}
|
|
194
|
+
get ruleEvolver() {
|
|
195
|
+
if (this._ruleEvolver === undefined) {
|
|
196
|
+
this._ruleEvolver = this.executionGraph
|
|
197
|
+
? this.tryInit(() => new RuleEvolver(this.executionGraph), 'RuleEvolver')
|
|
198
|
+
: null;
|
|
199
|
+
}
|
|
200
|
+
return this._ruleEvolver;
|
|
201
|
+
}
|
|
202
|
+
get taskDiscovery() {
|
|
203
|
+
if (this._taskDiscovery === undefined) {
|
|
204
|
+
this._taskDiscovery = this.executionGraph
|
|
205
|
+
? this.tryInit(() => new TaskDiscovery(this.executionGraph), 'TaskDiscovery')
|
|
206
|
+
: null;
|
|
207
|
+
}
|
|
208
|
+
return this._taskDiscovery;
|
|
209
|
+
}
|
|
210
|
+
get orchestrationEmbedder() {
|
|
211
|
+
if (this._orchestrationEmbedder === undefined) {
|
|
212
|
+
this._orchestrationEmbedder = this.executionGraph
|
|
213
|
+
? this.tryInit(() => new OrchestrationEmbedder(this.executionGraph), 'OrchestrationEmbedder')
|
|
214
|
+
: null;
|
|
215
|
+
}
|
|
216
|
+
return this._orchestrationEmbedder;
|
|
217
|
+
}
|
|
97
218
|
constructor(options) {
|
|
98
219
|
this.options = options;
|
|
99
220
|
this.registry = options.registry;
|
|
@@ -127,46 +248,36 @@ export class SwarmEngine {
|
|
|
127
248
|
this.compounder = this.tryInit(() => new KnowledgeCompounder(), 'KnowledgeCompounder');
|
|
128
249
|
this.executionGraph = this.tryInit(() => new ExecutionGraph(), 'ExecutionGraph');
|
|
129
250
|
this.executionGraph?.attachToEventBus(this.bus);
|
|
251
|
+
// Create SwarmMcpServer for SDK tool injection
|
|
252
|
+
try {
|
|
253
|
+
this.swarmMcpServer = new SwarmMcpServer({
|
|
254
|
+
phaseOutputs: this.phaseOutputs,
|
|
255
|
+
graph: this.executionGraph,
|
|
256
|
+
decisions: this.decisions,
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
catch {
|
|
260
|
+
this.swarmMcpServer = null;
|
|
261
|
+
}
|
|
130
262
|
// 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')
|
|
263
|
+
this.graphLearner = this.executionGraph
|
|
264
|
+
? this.tryInit(() => new GraphLearner(this.executionGraph), 'GraphLearner')
|
|
143
265
|
: null;
|
|
144
|
-
this.
|
|
145
|
-
? this.tryInit(() => new
|
|
266
|
+
this.graphAnalyzer = this.executionGraph
|
|
267
|
+
? this.tryInit(() => new GraphAnalyzer(this.executionGraph), 'GraphAnalyzer')
|
|
146
268
|
: 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;
|
|
269
|
+
// ML modules (reviewFeedback, causalEngine, gnnPredictor, adversarialEvolver,
|
|
270
|
+
// metaSelector, predictiveDropout, gnnWeights, patternSynthesizer, trajectoryPredictor,
|
|
271
|
+
// metaAdversarial, ruleEvolver, taskDiscovery, orchestrationEmbedder) are lazy-initialized
|
|
272
|
+
// via property getters — they are only constructed on first access.
|
|
164
273
|
// Prune stale graph data on init (Feature 6: graph decay)
|
|
165
274
|
if (this.executionGraph) {
|
|
166
275
|
try {
|
|
167
276
|
this.executionGraph.pruneOlderThan(90);
|
|
168
277
|
}
|
|
169
|
-
catch {
|
|
278
|
+
catch {
|
|
279
|
+
/* silent */
|
|
280
|
+
}
|
|
170
281
|
}
|
|
171
282
|
// Rule evolution proposal on startup (skip in mock mode and when no historical data)
|
|
172
283
|
if (this.ruleEvolver && !this.options.mock) {
|
|
@@ -219,10 +330,35 @@ export class SwarmEngine {
|
|
|
219
330
|
/**
|
|
220
331
|
* Execute an orchestration from config.
|
|
221
332
|
*/
|
|
222
|
-
async execute(config) {
|
|
333
|
+
async execute(config, executeOptions) {
|
|
223
334
|
// Re-use existing instance if present (e.g., when resuming from checkpoint)
|
|
224
335
|
const orchestration = this.orchestrations.get(config.id) ?? this.createInstance(config);
|
|
225
336
|
this.orchestrations.set(config.id, orchestration);
|
|
337
|
+
// Dry run mode: execute only the first phase in 'plan' mode, then return
|
|
338
|
+
if (executeOptions?.dryRun) {
|
|
339
|
+
this.log.info('Dry run mode: running first phase in plan mode');
|
|
340
|
+
const firstPhase = orchestration.phases[0];
|
|
341
|
+
if (firstPhase) {
|
|
342
|
+
this.executor.setPermissionMode('plan');
|
|
343
|
+
try {
|
|
344
|
+
this.phaseOutputs.clear();
|
|
345
|
+
orchestration.status = 'running';
|
|
346
|
+
orchestration.startedAt = new Date();
|
|
347
|
+
const outputs = await this.executePhase(orchestration, firstPhase);
|
|
348
|
+
this.phaseOutputs.set(firstPhase.config.name, outputs);
|
|
349
|
+
orchestration.status = 'completed';
|
|
350
|
+
}
|
|
351
|
+
catch (error) {
|
|
352
|
+
orchestration.status = 'failed';
|
|
353
|
+
this.log.error('Dry run failed', { error: error instanceof Error ? error.message : String(error) });
|
|
354
|
+
}
|
|
355
|
+
finally {
|
|
356
|
+
this.executor.setPermissionMode(this.options.permissionMode ?? 'default');
|
|
357
|
+
orchestration.completedAt = new Date();
|
|
358
|
+
}
|
|
359
|
+
return orchestration;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
226
362
|
this.log.info(`Starting orchestration: ${config.name}`, {
|
|
227
363
|
pattern: config.pattern,
|
|
228
364
|
phases: config.phases.length,
|
|
@@ -233,9 +369,45 @@ export class SwarmEngine {
|
|
|
233
369
|
pattern: config.pattern,
|
|
234
370
|
phaseCount: config.phases.length,
|
|
235
371
|
}, 'engine');
|
|
372
|
+
// Single-agent fast path for simple tasks
|
|
373
|
+
if (config.phases?.length === 1 && config.phases[0].agents?.length === 1) {
|
|
374
|
+
try {
|
|
375
|
+
const complexity = classifyTaskComplexity(config.description || '');
|
|
376
|
+
if (complexity === 'simple') {
|
|
377
|
+
this.log.info('Simple task detected — using single-agent fast path');
|
|
378
|
+
this.bus.emit('system:warning', { type: 'single-agent-bypass', task: config.description }, 'engine');
|
|
379
|
+
try {
|
|
380
|
+
const phase = config.phases[0];
|
|
381
|
+
const agent = phase.agents[0];
|
|
382
|
+
const runContext = {
|
|
383
|
+
phaseOutputs: new Map(),
|
|
384
|
+
intraPhaseOutputs: new Map(),
|
|
385
|
+
livingSpec: null,
|
|
386
|
+
sharedContextFiles: [],
|
|
387
|
+
};
|
|
388
|
+
const instance = await this.agentRunner.executeAgentWithRetry(agent, orchestration.phases[0], orchestration, 0, runContext);
|
|
389
|
+
orchestration.status = instance.result?.output ? 'completed' : 'failed';
|
|
390
|
+
orchestration.startedAt = new Date();
|
|
391
|
+
orchestration.completedAt = new Date();
|
|
392
|
+
orchestration.phases[0].status = orchestration.status;
|
|
393
|
+
if (instance.result)
|
|
394
|
+
orchestration.phases[0].agents = [instance];
|
|
395
|
+
orchestration.usage = instance.usage;
|
|
396
|
+
return orchestration;
|
|
397
|
+
}
|
|
398
|
+
catch (e) {
|
|
399
|
+
this.log.debug(`Single-agent bypass failed, falling through to normal execution: ${e}`);
|
|
400
|
+
// Fall through to normal execution
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
catch {
|
|
405
|
+
/* classification failed — fall through */
|
|
406
|
+
}
|
|
407
|
+
}
|
|
236
408
|
orchestration.status = 'running';
|
|
237
409
|
orchestration.startedAt = new Date();
|
|
238
|
-
this.phaseOutputs
|
|
410
|
+
this.phaseOutputs.clear();
|
|
239
411
|
// TODO: Checkpoint doesn't store phase outputs — resumed phases lose inter-phase context
|
|
240
412
|
this.reflections = [];
|
|
241
413
|
this.sharedContextFiles = [];
|
|
@@ -257,7 +429,7 @@ export class SwarmEngine {
|
|
|
257
429
|
if (this.gnnPredictor && this.gnnWeights) {
|
|
258
430
|
try {
|
|
259
431
|
const risks = this.gnnPredictor.predict(orchestration.config.id, this.gnnWeights);
|
|
260
|
-
const highRisk = risks.filter(r => r.riskScore > 0.7);
|
|
432
|
+
const highRisk = risks.filter((r) => r.riskScore > 0.7);
|
|
261
433
|
if (highRisk.length > 0) {
|
|
262
434
|
this.log.warn(`GNN predicts high failure risk for ${highRisk.length} nodes`);
|
|
263
435
|
this.bus.emit('system:warning', { type: 'gnn-risk', risks: highRisk }, 'engine');
|
|
@@ -341,17 +513,31 @@ export class SwarmEngine {
|
|
|
341
513
|
const reflection = this.reflectionEngine.reflect(phase);
|
|
342
514
|
const patternStats = this.graphLearner?.getPatternStats(orchestration.config.pattern)?.[0];
|
|
343
515
|
const gateOutputs = this.phaseOutputs.get(phase.config.name) || [];
|
|
344
|
-
const gate = this.graphAnalyzer.evaluatePhaseConfidence({
|
|
516
|
+
const gate = this.graphAnalyzer.evaluatePhaseConfidence({
|
|
517
|
+
kind: phase.config.kind ?? 'implement',
|
|
518
|
+
agentCount: phase.agents.length,
|
|
519
|
+
outputs: gateOutputs,
|
|
520
|
+
usage: phase.usage,
|
|
521
|
+
}, { confidence: reflection.confidence }, patternStats
|
|
522
|
+
? { successRate: patternStats.successRate, avgDurationMs: patternStats.avgDurationMs }
|
|
523
|
+
: undefined);
|
|
345
524
|
if (gate.result === 'halt') {
|
|
346
525
|
this.log.warn(`Phase gate HALT: ${gate.reason}`);
|
|
347
526
|
orchestration.status = 'failed';
|
|
348
527
|
}
|
|
349
528
|
if (gate.result === 'warn') {
|
|
350
529
|
this.log.warn(`Phase gate WARNING: ${gate.reason}`);
|
|
351
|
-
this.bus.emit('system:warning', {
|
|
530
|
+
this.bus.emit('system:warning', {
|
|
531
|
+
type: 'gate-warning',
|
|
532
|
+
orchestrationId: orchestration.config.id,
|
|
533
|
+
phase: phase.config.name,
|
|
534
|
+
reason: gate.reason,
|
|
535
|
+
}, 'engine');
|
|
352
536
|
}
|
|
353
537
|
}
|
|
354
|
-
catch {
|
|
538
|
+
catch {
|
|
539
|
+
/* Graph gate evaluation failed — continue */
|
|
540
|
+
}
|
|
355
541
|
}
|
|
356
542
|
// Trajectory prediction after each completed phase
|
|
357
543
|
if (this.trajectoryPredictor) {
|
|
@@ -376,9 +562,17 @@ export class SwarmEngine {
|
|
|
376
562
|
if (this.trajectoryPredictor) {
|
|
377
563
|
try {
|
|
378
564
|
const forecastCompleted = orchestration.phases
|
|
379
|
-
.filter(p => p.status === 'completed')
|
|
380
|
-
.map(p => ({
|
|
381
|
-
|
|
565
|
+
.filter((p) => p.status === 'completed')
|
|
566
|
+
.map((p) => ({
|
|
567
|
+
name: p.config.name,
|
|
568
|
+
status: p.status,
|
|
569
|
+
tokens: p.usage?.totalTokens || 0,
|
|
570
|
+
durationMs: p.usage?.durationMs || 0,
|
|
571
|
+
confidence: 'medium',
|
|
572
|
+
}));
|
|
573
|
+
const forecastRemaining = orchestration.phases
|
|
574
|
+
.filter((p) => p.status === 'pending')
|
|
575
|
+
.map((p) => p.config.name);
|
|
382
576
|
if (forecastRemaining.length > 0) {
|
|
383
577
|
const forecast = this.trajectoryPredictor.predictTrajectory(orchestration.config.id, forecastCompleted, forecastRemaining, orchestration.config.pattern);
|
|
384
578
|
if (forecast.overallSuccessProb < 0.3) {
|
|
@@ -450,11 +644,13 @@ export class SwarmEngine {
|
|
|
450
644
|
const report = this.metaAdversarial.runFullAudit({
|
|
451
645
|
patternSelector: this.metaSelector ?? undefined,
|
|
452
646
|
dropout: this.predictiveDropout ?? undefined,
|
|
453
|
-
causal: this.causalEngine
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
647
|
+
causal: this.causalEngine
|
|
648
|
+
? {
|
|
649
|
+
estimateCausalEffect: (t, tv, cv) =>
|
|
650
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
651
|
+
this.causalEngine.estimateCausalEffect(t, tv, cv),
|
|
652
|
+
}
|
|
653
|
+
: undefined,
|
|
458
654
|
});
|
|
459
655
|
if (report.overallRisk !== 'low') {
|
|
460
656
|
this.log.warn(`Self-audit found ${report.vulnerabilities.length} ML vulnerabilities (risk: ${report.overallRisk})`);
|
|
@@ -661,6 +857,24 @@ export class SwarmEngine {
|
|
|
661
857
|
quality: c.cost.qualityScore,
|
|
662
858
|
}));
|
|
663
859
|
}
|
|
860
|
+
// Emit plan preview event so listeners can inspect before execution
|
|
861
|
+
try {
|
|
862
|
+
this.bus.emit('system:warning', {
|
|
863
|
+
type: 'plan-preview',
|
|
864
|
+
pattern: patternName,
|
|
865
|
+
phases: bestPlan.phases.map((p) => ({
|
|
866
|
+
name: p.name,
|
|
867
|
+
kind: p.kind,
|
|
868
|
+
agentCount: p.agents?.length || 0,
|
|
869
|
+
agents: p.agents?.map((a) => ({ type: a.type, model: a.model })) || [],
|
|
870
|
+
})),
|
|
871
|
+
estimatedCost: bestPlan.estimates,
|
|
872
|
+
task: task.slice(0, 200),
|
|
873
|
+
}, 'engine');
|
|
874
|
+
}
|
|
875
|
+
catch {
|
|
876
|
+
/* plan preview emit failed — non-critical */
|
|
877
|
+
}
|
|
664
878
|
return bestPlan;
|
|
665
879
|
}
|
|
666
880
|
/**
|
|
@@ -681,20 +895,86 @@ export class SwarmEngine {
|
|
|
681
895
|
// Predictive dropout from ML model (Feature 5: Predictive Dropout)
|
|
682
896
|
if (this.predictiveDropout && phase.config.agents.length > 1) {
|
|
683
897
|
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);
|
|
898
|
+
const recommendations = this.predictiveDropout.predictRedundant(phase.config.agents.map((a) => ({ id: a.name || a.type, agentType: a.type })), phase.config.kind ?? 'implement', 0, orchestration.config.pattern || '');
|
|
899
|
+
const toDrop = recommendations.filter((r) => r.shouldDropOut);
|
|
686
900
|
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));
|
|
901
|
+
this.log.info(`Predictive dropout: skipping ${toDrop.map((d) => d.agentType).join(', ')}`);
|
|
902
|
+
phase.config.agents = phase.config.agents.filter((a) => !toDrop.some((d) => d.agentType === a.type));
|
|
689
903
|
}
|
|
690
904
|
}
|
|
691
905
|
catch (e) {
|
|
692
906
|
this.log.debug(`Predictive dropout failed: ${e}`);
|
|
693
907
|
}
|
|
694
908
|
}
|
|
909
|
+
// Feature 5: Diff-based review context — inject git diff before review phases
|
|
910
|
+
if (phase.config.kind === 'review' && !this.options.mock) {
|
|
911
|
+
try {
|
|
912
|
+
const cwd = this.options.cwd || process.cwd();
|
|
913
|
+
const { stdout: diff } = await execFileAsync('git', ['diff', '--stat', 'HEAD'], {
|
|
914
|
+
cwd,
|
|
915
|
+
timeout: 10000,
|
|
916
|
+
encoding: 'utf-8',
|
|
917
|
+
});
|
|
918
|
+
const trimmedDiff = diff.slice(0, 3000);
|
|
919
|
+
if (trimmedDiff.trim()) {
|
|
920
|
+
const { stdout: diffDetail } = await execFileAsync('git', ['diff', 'HEAD'], {
|
|
921
|
+
cwd,
|
|
922
|
+
timeout: 10000,
|
|
923
|
+
encoding: 'utf-8',
|
|
924
|
+
});
|
|
925
|
+
const existing = this.phaseOutputs.get(phase.config.name) || [];
|
|
926
|
+
existing.unshift(`## Git Diff Summary\n\`\`\`\n${trimmedDiff}\n\`\`\`\n\n## Detailed Changes\n\`\`\`diff\n${diffDetail.slice(0, 5000)}\n\`\`\``);
|
|
927
|
+
this.phaseOutputs.set(phase.config.name, existing);
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
catch (e) {
|
|
931
|
+
this.log.debug(`Diff context failed: ${e}`);
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
// Feature 6: Pre-review lint wiring — run lint/typecheck before review phases
|
|
935
|
+
if (phase.config.kind === 'review' && this.verifier && !this.options.mock) {
|
|
936
|
+
try {
|
|
937
|
+
const lintResults = await this.verifier.verify();
|
|
938
|
+
const failures = lintResults.filter((r) => !r.passed);
|
|
939
|
+
if (failures.length > 0) {
|
|
940
|
+
const lintOutput = failures.map((f) => `${f.step}: ${f.output?.slice(0, 500)}`).join('\n');
|
|
941
|
+
const existing = this.phaseOutputs.get(phase.config.name) || [];
|
|
942
|
+
existing.unshift(`## Pre-Review Lint/Typecheck Results\n${lintOutput}`);
|
|
943
|
+
this.phaseOutputs.set(phase.config.name, existing);
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
catch (e) {
|
|
947
|
+
this.log.debug(`Pre-review checks failed: ${e}`);
|
|
948
|
+
}
|
|
949
|
+
}
|
|
695
950
|
const maxRetries = phase.config.maxRetries ?? 2;
|
|
951
|
+
// Smart phase output routing: filter prior outputs by relevance instead of passing everything
|
|
952
|
+
let filteredOutputs = this.phaseOutputs;
|
|
953
|
+
if (phase.config.kind === 'review') {
|
|
954
|
+
// Review phase only needs implement/integrate/test outputs, not research
|
|
955
|
+
const reviewFiltered = new Map();
|
|
956
|
+
for (const [phaseName, outputs] of this.phaseOutputs) {
|
|
957
|
+
if (phaseName.includes('implement') || phaseName.includes('integrate') || phaseName.includes('test')) {
|
|
958
|
+
reviewFiltered.set(phaseName, outputs);
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
// Fall back to all outputs if nothing matched
|
|
962
|
+
if (reviewFiltered.size > 0)
|
|
963
|
+
filteredOutputs = reviewFiltered;
|
|
964
|
+
}
|
|
965
|
+
if (phase.config.kind === 'test') {
|
|
966
|
+
// Test phase only needs implement outputs
|
|
967
|
+
const testFiltered = new Map();
|
|
968
|
+
for (const [phaseName, outputs] of this.phaseOutputs) {
|
|
969
|
+
if (phaseName.includes('implement')) {
|
|
970
|
+
testFiltered.set(phaseName, outputs);
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
if (testFiltered.size > 0)
|
|
974
|
+
filteredOutputs = testFiltered;
|
|
975
|
+
}
|
|
696
976
|
const runContext = {
|
|
697
|
-
phaseOutputs:
|
|
977
|
+
phaseOutputs: filteredOutputs,
|
|
698
978
|
livingSpec: this.livingSpec,
|
|
699
979
|
sharedContextFiles: this.sharedContextFiles,
|
|
700
980
|
cwd: this.options.cwd,
|
|
@@ -788,7 +1068,7 @@ export class SwarmEngine {
|
|
|
788
1068
|
}
|
|
789
1069
|
}
|
|
790
1070
|
// Run verification after implementation phases
|
|
791
|
-
if (phase.config.kind === 'implement' || phase.config.kind === 'integrate') {
|
|
1071
|
+
if ((phase.config.kind === 'implement' || phase.config.kind === 'integrate') && !this.options.mock) {
|
|
792
1072
|
const results = await this.verifier.verify();
|
|
793
1073
|
const summary = Verifier.summarize(results);
|
|
794
1074
|
this.log.info(`Verification: ${summary}`, { phase: phase.config.name });
|
|
@@ -817,10 +1097,18 @@ export class SwarmEngine {
|
|
|
817
1097
|
agents: phase.agents.length,
|
|
818
1098
|
usage: phase.usage,
|
|
819
1099
|
});
|
|
820
|
-
// Collect agent outputs for inter-phase context
|
|
1100
|
+
// Collect agent outputs for inter-phase context (structured handoff)
|
|
821
1101
|
const outputs = phase.agents
|
|
822
1102
|
.filter((a) => a.result?.output)
|
|
823
|
-
.map((a) =>
|
|
1103
|
+
.map((a) => {
|
|
1104
|
+
try {
|
|
1105
|
+
const handoff = HandoffParser.parseAgentOutput(a.result.output);
|
|
1106
|
+
return HandoffParser.serializeForNextPhase([{ agentName: a.config.name, handoff }], 4000);
|
|
1107
|
+
}
|
|
1108
|
+
catch {
|
|
1109
|
+
return `[${a.config.name}]: ${a.result.output.slice(0, 2000)}`;
|
|
1110
|
+
}
|
|
1111
|
+
});
|
|
824
1112
|
// Update living spec with phase outputs
|
|
825
1113
|
if (this.livingSpec) {
|
|
826
1114
|
this.livingSpec = updateSpecFromPhase(this.livingSpec, phase.config.name, outputs);
|