@ruvector/edge-net 0.1.2 → 0.1.4

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/agents.js ADDED
@@ -0,0 +1,965 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Edge-Net Agent System
4
+ *
5
+ * Distributed AI agent execution across the Edge-Net collective.
6
+ * Spawn agents, create worker pools, and orchestrate multi-agent workflows.
7
+ */
8
+
9
+ import { EventEmitter } from 'events';
10
+ import { randomBytes, createHash } from 'crypto';
11
+
12
+ // Agent types and their capabilities
13
+ export const AGENT_TYPES = {
14
+ researcher: {
15
+ name: 'Researcher',
16
+ capabilities: ['search', 'analyze', 'summarize', 'extract'],
17
+ baseRuv: 10,
18
+ description: 'Analyzes and researches information',
19
+ },
20
+ coder: {
21
+ name: 'Coder',
22
+ capabilities: ['code', 'refactor', 'debug', 'test'],
23
+ baseRuv: 15,
24
+ description: 'Writes and improves code',
25
+ },
26
+ reviewer: {
27
+ name: 'Reviewer',
28
+ capabilities: ['review', 'audit', 'validate', 'suggest'],
29
+ baseRuv: 12,
30
+ description: 'Reviews code and provides feedback',
31
+ },
32
+ tester: {
33
+ name: 'Tester',
34
+ capabilities: ['test', 'benchmark', 'validate', 'report'],
35
+ baseRuv: 10,
36
+ description: 'Tests and validates implementations',
37
+ },
38
+ analyst: {
39
+ name: 'Analyst',
40
+ capabilities: ['analyze', 'metrics', 'report', 'visualize'],
41
+ baseRuv: 8,
42
+ description: 'Analyzes data and generates reports',
43
+ },
44
+ optimizer: {
45
+ name: 'Optimizer',
46
+ capabilities: ['optimize', 'profile', 'benchmark', 'improve'],
47
+ baseRuv: 15,
48
+ description: 'Optimizes performance and efficiency',
49
+ },
50
+ coordinator: {
51
+ name: 'Coordinator',
52
+ capabilities: ['orchestrate', 'route', 'schedule', 'monitor'],
53
+ baseRuv: 20,
54
+ description: 'Coordinates multi-agent workflows',
55
+ },
56
+ embedder: {
57
+ name: 'Embedder',
58
+ capabilities: ['embed', 'vectorize', 'similarity', 'search'],
59
+ baseRuv: 5,
60
+ description: 'Generates embeddings and vector operations',
61
+ },
62
+ };
63
+
64
+ // Task status enum
65
+ export const TaskStatus = {
66
+ PENDING: 'pending',
67
+ QUEUED: 'queued',
68
+ ASSIGNED: 'assigned',
69
+ RUNNING: 'running',
70
+ COMPLETED: 'completed',
71
+ FAILED: 'failed',
72
+ CANCELLED: 'cancelled',
73
+ };
74
+
75
+ /**
76
+ * Distributed Agent
77
+ *
78
+ * Represents an AI agent running on the Edge-Net network.
79
+ */
80
+ export class DistributedAgent extends EventEmitter {
81
+ constructor(options) {
82
+ super();
83
+ this.id = `agent-${randomBytes(8).toString('hex')}`;
84
+ this.type = options.type || 'researcher';
85
+ this.task = options.task;
86
+ this.config = AGENT_TYPES[this.type] || AGENT_TYPES.researcher;
87
+ this.maxRuv = options.maxRuv || this.config.baseRuv;
88
+ this.priority = options.priority || 'medium';
89
+ this.timeout = options.timeout || 300000; // 5 min default
90
+
91
+ this.status = TaskStatus.PENDING;
92
+ this.assignedNode = null;
93
+ this.progress = 0;
94
+ this.result = null;
95
+ this.error = null;
96
+ this.startTime = null;
97
+ this.endTime = null;
98
+ this.ruvSpent = 0;
99
+
100
+ this.subtasks = [];
101
+ this.logs = [];
102
+ }
103
+
104
+ /**
105
+ * Get agent info
106
+ */
107
+ getInfo() {
108
+ return {
109
+ id: this.id,
110
+ type: this.type,
111
+ task: this.task,
112
+ status: this.status,
113
+ progress: this.progress,
114
+ assignedNode: this.assignedNode,
115
+ maxRuv: this.maxRuv,
116
+ ruvSpent: this.ruvSpent,
117
+ startTime: this.startTime,
118
+ endTime: this.endTime,
119
+ duration: this.endTime && this.startTime
120
+ ? this.endTime - this.startTime
121
+ : null,
122
+ };
123
+ }
124
+
125
+ /**
126
+ * Update agent progress
127
+ */
128
+ updateProgress(progress, message) {
129
+ this.progress = Math.min(100, Math.max(0, progress));
130
+ this.log(`Progress: ${this.progress}% - ${message}`);
131
+ this.emit('progress', { progress: this.progress, message });
132
+ }
133
+
134
+ /**
135
+ * Log message
136
+ */
137
+ log(message) {
138
+ const entry = {
139
+ timestamp: Date.now(),
140
+ message,
141
+ };
142
+ this.logs.push(entry);
143
+ this.emit('log', entry);
144
+ }
145
+
146
+ /**
147
+ * Mark as completed
148
+ */
149
+ complete(result) {
150
+ this.status = TaskStatus.COMPLETED;
151
+ this.result = result;
152
+ this.progress = 100;
153
+ this.endTime = Date.now();
154
+ this.log('Agent completed successfully');
155
+ this.emit('complete', result);
156
+ }
157
+
158
+ /**
159
+ * Mark as failed
160
+ */
161
+ fail(error) {
162
+ this.status = TaskStatus.FAILED;
163
+ this.error = error;
164
+ this.endTime = Date.now();
165
+ this.log(`Agent failed: ${error}`);
166
+ this.emit('error', error);
167
+ }
168
+
169
+ /**
170
+ * Cancel the agent
171
+ */
172
+ cancel() {
173
+ this.status = TaskStatus.CANCELLED;
174
+ this.endTime = Date.now();
175
+ this.log('Agent cancelled');
176
+ this.emit('cancelled');
177
+ }
178
+ }
179
+
180
+ /**
181
+ * Agent Spawner
182
+ *
183
+ * Spawns and manages distributed agents across the Edge-Net network.
184
+ */
185
+ export class AgentSpawner extends EventEmitter {
186
+ constructor(networkManager, options = {}) {
187
+ super();
188
+ this.network = networkManager;
189
+ this.agents = new Map();
190
+ this.pendingQueue = [];
191
+ this.maxConcurrent = options.maxConcurrent || 10;
192
+ this.defaultTimeout = options.defaultTimeout || 300000;
193
+
194
+ // Agent routing table (learned from outcomes)
195
+ this.routingTable = new Map();
196
+
197
+ // Stats
198
+ this.stats = {
199
+ totalSpawned: 0,
200
+ completed: 0,
201
+ failed: 0,
202
+ totalRuvSpent: 0,
203
+ };
204
+ }
205
+
206
+ /**
207
+ * Spawn a new agent on the network
208
+ */
209
+ async spawn(options) {
210
+ const agent = new DistributedAgent({
211
+ ...options,
212
+ timeout: options.timeout || this.defaultTimeout,
213
+ });
214
+
215
+ this.agents.set(agent.id, agent);
216
+ this.stats.totalSpawned++;
217
+
218
+ agent.log(`Agent spawned: ${agent.type} - ${agent.task}`);
219
+ agent.status = TaskStatus.QUEUED;
220
+
221
+ // Find best node for this agent type
222
+ const targetNode = await this.findBestNode(agent);
223
+
224
+ if (targetNode) {
225
+ await this.assignToNode(agent, targetNode);
226
+ } else {
227
+ // Queue for later assignment
228
+ this.pendingQueue.push(agent);
229
+ agent.log('Queued - waiting for available node');
230
+ }
231
+
232
+ this.emit('agent-spawned', agent);
233
+ return agent;
234
+ }
235
+
236
+ /**
237
+ * Find the best node for an agent based on capabilities and load
238
+ */
239
+ async findBestNode(agent) {
240
+ if (!this.network) return null;
241
+
242
+ const peers = this.network.getPeerList ?
243
+ this.network.getPeerList() :
244
+ Array.from(this.network.peers?.values() || []);
245
+
246
+ if (peers.length === 0) return null;
247
+
248
+ // Score each peer based on:
249
+ // 1. Capability match
250
+ // 2. Current load
251
+ // 3. Historical performance
252
+ // 4. Latency
253
+ const scoredPeers = peers.map(peer => {
254
+ let score = 50; // Base score
255
+
256
+ // Check capabilities
257
+ const peerCaps = peer.capabilities || [];
258
+ const requiredCaps = agent.config.capabilities;
259
+ const capMatch = requiredCaps.filter(c => peerCaps.includes(c)).length;
260
+ score += capMatch * 10;
261
+
262
+ // Check load (lower is better)
263
+ const load = peer.load || 0;
264
+ score -= load * 20;
265
+
266
+ // Check historical performance
267
+ const history = this.routingTable.get(`${peer.piKey || peer.id}-${agent.type}`);
268
+ if (history) {
269
+ score += history.successRate * 30;
270
+ score -= history.avgLatency / 1000; // Penalize high latency
271
+ }
272
+
273
+ return { peer, score };
274
+ });
275
+
276
+ // Sort by score (highest first)
277
+ scoredPeers.sort((a, b) => b.score - a.score);
278
+
279
+ return scoredPeers[0]?.peer || null;
280
+ }
281
+
282
+ /**
283
+ * Assign agent to a specific node
284
+ */
285
+ async assignToNode(agent, node) {
286
+ agent.status = TaskStatus.ASSIGNED;
287
+ agent.assignedNode = node.piKey || node.id;
288
+ agent.startTime = Date.now();
289
+ agent.log(`Assigned to node: ${agent.assignedNode.slice(0, 12)}...`);
290
+
291
+ // Send task to node via network
292
+ if (this.network?.sendToPeer) {
293
+ await this.network.sendToPeer(agent.assignedNode, {
294
+ type: 'agent_task',
295
+ agentId: agent.id,
296
+ agentType: agent.type,
297
+ task: agent.task,
298
+ maxRuv: agent.maxRuv,
299
+ timeout: agent.timeout,
300
+ });
301
+ }
302
+
303
+ agent.status = TaskStatus.RUNNING;
304
+ this.emit('agent-assigned', { agent, node });
305
+
306
+ // Set timeout
307
+ setTimeout(() => {
308
+ if (agent.status === TaskStatus.RUNNING) {
309
+ agent.fail('Timeout exceeded');
310
+ }
311
+ }, agent.timeout);
312
+ }
313
+
314
+ /**
315
+ * Handle task result from network
316
+ */
317
+ handleResult(agentId, result) {
318
+ const agent = this.agents.get(agentId);
319
+ if (!agent) return;
320
+
321
+ if (result.success) {
322
+ agent.complete(result.data);
323
+ this.stats.completed++;
324
+ this.updateRoutingTable(agent, true, result.latency);
325
+ } else {
326
+ agent.fail(result.error);
327
+ this.stats.failed++;
328
+ this.updateRoutingTable(agent, false, result.latency);
329
+ }
330
+
331
+ agent.ruvSpent = result.ruvSpent || agent.config.baseRuv;
332
+ this.stats.totalRuvSpent += agent.ruvSpent;
333
+ }
334
+
335
+ /**
336
+ * Update routing table with outcome
337
+ */
338
+ updateRoutingTable(agent, success, latency) {
339
+ const key = `${agent.assignedNode}-${agent.type}`;
340
+ const existing = this.routingTable.get(key) || {
341
+ attempts: 0,
342
+ successes: 0,
343
+ totalLatency: 0,
344
+ };
345
+
346
+ existing.attempts++;
347
+ if (success) existing.successes++;
348
+ existing.totalLatency += latency || 0;
349
+ existing.successRate = existing.successes / existing.attempts;
350
+ existing.avgLatency = existing.totalLatency / existing.attempts;
351
+
352
+ this.routingTable.set(key, existing);
353
+ }
354
+
355
+ /**
356
+ * Get agent by ID
357
+ */
358
+ getAgent(agentId) {
359
+ return this.agents.get(agentId);
360
+ }
361
+
362
+ /**
363
+ * List all agents
364
+ */
365
+ listAgents(filter = {}) {
366
+ let agents = Array.from(this.agents.values());
367
+
368
+ if (filter.status) {
369
+ agents = agents.filter(a => a.status === filter.status);
370
+ }
371
+ if (filter.type) {
372
+ agents = agents.filter(a => a.type === filter.type);
373
+ }
374
+
375
+ return agents.map(a => a.getInfo());
376
+ }
377
+
378
+ /**
379
+ * Get spawner stats
380
+ */
381
+ getStats() {
382
+ return {
383
+ ...this.stats,
384
+ activeAgents: Array.from(this.agents.values())
385
+ .filter(a => a.status === TaskStatus.RUNNING).length,
386
+ queuedAgents: this.pendingQueue.length,
387
+ successRate: this.stats.completed /
388
+ (this.stats.completed + this.stats.failed) || 0,
389
+ };
390
+ }
391
+ }
392
+
393
+ /**
394
+ * Worker Pool
395
+ *
396
+ * Manages a pool of distributed workers for parallel task execution.
397
+ */
398
+ export class WorkerPool extends EventEmitter {
399
+ constructor(networkManager, options = {}) {
400
+ super();
401
+ this.id = `pool-${randomBytes(6).toString('hex')}`;
402
+ this.network = networkManager;
403
+ this.size = options.size || 5;
404
+ this.capabilities = options.capabilities || ['compute', 'embed'];
405
+ this.maxTasksPerWorker = options.maxTasksPerWorker || 10;
406
+
407
+ this.workers = new Map();
408
+ this.taskQueue = [];
409
+ this.activeTasks = new Map();
410
+ this.results = new Map();
411
+
412
+ this.status = 'initializing';
413
+ this.stats = {
414
+ tasksCompleted: 0,
415
+ tasksFailed: 0,
416
+ totalProcessingTime: 0,
417
+ };
418
+ }
419
+
420
+ /**
421
+ * Initialize the worker pool
422
+ */
423
+ async initialize() {
424
+ this.status = 'recruiting';
425
+ this.emit('status', 'Recruiting workers...');
426
+
427
+ // Find available workers from network
428
+ const peers = this.network?.getPeerList?.() ||
429
+ Array.from(this.network?.peers?.values() || []);
430
+
431
+ // Filter peers by capabilities
432
+ const eligiblePeers = peers.filter(peer => {
433
+ const peerCaps = peer.capabilities || [];
434
+ return this.capabilities.some(c => peerCaps.includes(c));
435
+ });
436
+
437
+ // Recruit up to pool size
438
+ const recruited = eligiblePeers.slice(0, this.size);
439
+
440
+ for (const peer of recruited) {
441
+ this.workers.set(peer.piKey || peer.id, {
442
+ id: peer.piKey || peer.id,
443
+ peer,
444
+ status: 'idle',
445
+ currentTasks: 0,
446
+ completedTasks: 0,
447
+ lastSeen: Date.now(),
448
+ });
449
+ }
450
+
451
+ // If not enough real workers, create virtual workers for local execution
452
+ while (this.workers.size < this.size) {
453
+ const virtualId = `virtual-${randomBytes(4).toString('hex')}`;
454
+ this.workers.set(virtualId, {
455
+ id: virtualId,
456
+ peer: null,
457
+ status: 'idle',
458
+ currentTasks: 0,
459
+ completedTasks: 0,
460
+ isVirtual: true,
461
+ });
462
+ }
463
+
464
+ this.status = 'ready';
465
+ this.emit('ready', {
466
+ poolId: this.id,
467
+ workers: this.workers.size,
468
+ realWorkers: Array.from(this.workers.values())
469
+ .filter(w => !w.isVirtual).length,
470
+ });
471
+
472
+ return this;
473
+ }
474
+
475
+ /**
476
+ * Execute tasks in parallel across workers
477
+ */
478
+ async execute(options) {
479
+ const {
480
+ task,
481
+ data,
482
+ strategy = 'parallel',
483
+ chunkSize = null,
484
+ } = options;
485
+
486
+ const batchId = `batch-${randomBytes(6).toString('hex')}`;
487
+ const startTime = Date.now();
488
+
489
+ // Split data into chunks for workers
490
+ let chunks;
491
+ if (Array.isArray(data)) {
492
+ const size = chunkSize || Math.ceil(data.length / this.workers.size);
493
+ chunks = [];
494
+ for (let i = 0; i < data.length; i += size) {
495
+ chunks.push(data.slice(i, i + size));
496
+ }
497
+ } else {
498
+ chunks = [data];
499
+ }
500
+
501
+ this.emit('batch-start', { batchId, chunks: chunks.length });
502
+
503
+ // Assign chunks to workers
504
+ const promises = chunks.map((chunk, index) =>
505
+ this.assignTask({
506
+ batchId,
507
+ index,
508
+ task,
509
+ data: chunk,
510
+ })
511
+ );
512
+
513
+ // Wait for all or handle based on strategy
514
+ let results;
515
+ if (strategy === 'parallel') {
516
+ results = await Promise.all(promises);
517
+ } else if (strategy === 'race') {
518
+ results = [await Promise.race(promises)];
519
+ } else {
520
+ // Sequential
521
+ results = [];
522
+ for (const promise of promises) {
523
+ results.push(await promise);
524
+ }
525
+ }
526
+
527
+ const endTime = Date.now();
528
+ this.stats.totalProcessingTime += endTime - startTime;
529
+
530
+ this.emit('batch-complete', {
531
+ batchId,
532
+ duration: endTime - startTime,
533
+ results: results.length,
534
+ });
535
+
536
+ // Flatten results if array
537
+ return Array.isArray(data) ? results.flat() : results[0];
538
+ }
539
+
540
+ /**
541
+ * Assign a single task to an available worker
542
+ */
543
+ async assignTask(taskInfo) {
544
+ const taskId = `task-${randomBytes(6).toString('hex')}`;
545
+
546
+ // Find idle worker
547
+ const worker = this.findIdleWorker();
548
+ if (!worker) {
549
+ // Queue task
550
+ return new Promise((resolve, reject) => {
551
+ this.taskQueue.push({ taskInfo, resolve, reject });
552
+ });
553
+ }
554
+
555
+ worker.status = 'busy';
556
+ worker.currentTasks++;
557
+
558
+ this.activeTasks.set(taskId, {
559
+ ...taskInfo,
560
+ workerId: worker.id,
561
+ startTime: Date.now(),
562
+ });
563
+
564
+ try {
565
+ // Execute on worker
566
+ const result = await this.executeOnWorker(worker, taskInfo);
567
+
568
+ worker.completedTasks++;
569
+ this.stats.tasksCompleted++;
570
+ this.results.set(taskId, result);
571
+
572
+ return result;
573
+ } catch (error) {
574
+ this.stats.tasksFailed++;
575
+ throw error;
576
+ } finally {
577
+ worker.currentTasks--;
578
+ if (worker.currentTasks === 0) {
579
+ worker.status = 'idle';
580
+ }
581
+ this.activeTasks.delete(taskId);
582
+
583
+ // Process queued task if any
584
+ if (this.taskQueue.length > 0) {
585
+ const { taskInfo, resolve, reject } = this.taskQueue.shift();
586
+ this.assignTask(taskInfo).then(resolve).catch(reject);
587
+ }
588
+ }
589
+ }
590
+
591
+ /**
592
+ * Find an idle worker
593
+ */
594
+ findIdleWorker() {
595
+ for (const worker of this.workers.values()) {
596
+ if (worker.status === 'idle' ||
597
+ worker.currentTasks < this.maxTasksPerWorker) {
598
+ return worker;
599
+ }
600
+ }
601
+ return null;
602
+ }
603
+
604
+ /**
605
+ * Execute task on a specific worker
606
+ */
607
+ async executeOnWorker(worker, taskInfo) {
608
+ if (worker.isVirtual) {
609
+ // Local execution for virtual workers
610
+ return this.executeLocally(taskInfo);
611
+ }
612
+
613
+ // Send to remote worker via network
614
+ return new Promise((resolve, reject) => {
615
+ const timeout = setTimeout(() => {
616
+ reject(new Error('Worker timeout'));
617
+ }, 60000);
618
+
619
+ // Send task
620
+ if (this.network?.sendToPeer) {
621
+ this.network.sendToPeer(worker.id, {
622
+ type: 'worker_task',
623
+ poolId: this.id,
624
+ task: taskInfo.task,
625
+ data: taskInfo.data,
626
+ });
627
+ }
628
+
629
+ // Listen for result
630
+ const handler = (msg) => {
631
+ if (msg.poolId === this.id && msg.batchId === taskInfo.batchId) {
632
+ clearTimeout(timeout);
633
+ this.network?.off?.('worker_result', handler);
634
+ resolve(msg.result);
635
+ }
636
+ };
637
+
638
+ this.network?.on?.('worker_result', handler);
639
+
640
+ // Fallback to local if no response
641
+ setTimeout(() => {
642
+ clearTimeout(timeout);
643
+ this.executeLocally(taskInfo).then(resolve).catch(reject);
644
+ }, 5000);
645
+ });
646
+ }
647
+
648
+ /**
649
+ * Execute task locally (for virtual workers or fallback)
650
+ */
651
+ async executeLocally(taskInfo) {
652
+ const { task, data } = taskInfo;
653
+
654
+ // Simple local execution based on task type
655
+ switch (task) {
656
+ case 'embed':
657
+ // Simulate embedding
658
+ return Array.isArray(data)
659
+ ? data.map(() => new Array(384).fill(0).map(() => Math.random()))
660
+ : new Array(384).fill(0).map(() => Math.random());
661
+
662
+ case 'process':
663
+ return Array.isArray(data)
664
+ ? data.map(item => ({ processed: true, item }))
665
+ : { processed: true, data };
666
+
667
+ case 'analyze':
668
+ return {
669
+ analyzed: true,
670
+ itemCount: Array.isArray(data) ? data.length : 1,
671
+ timestamp: Date.now(),
672
+ };
673
+
674
+ default:
675
+ return { task, data, executed: true };
676
+ }
677
+ }
678
+
679
+ /**
680
+ * Get pool status
681
+ */
682
+ getStatus() {
683
+ const workers = Array.from(this.workers.values());
684
+ return {
685
+ poolId: this.id,
686
+ status: this.status,
687
+ totalWorkers: workers.length,
688
+ idleWorkers: workers.filter(w => w.status === 'idle').length,
689
+ busyWorkers: workers.filter(w => w.status === 'busy').length,
690
+ virtualWorkers: workers.filter(w => w.isVirtual).length,
691
+ queuedTasks: this.taskQueue.length,
692
+ activeTasks: this.activeTasks.size,
693
+ stats: this.stats,
694
+ };
695
+ }
696
+
697
+ /**
698
+ * Shutdown the pool
699
+ */
700
+ async shutdown() {
701
+ this.status = 'shutting_down';
702
+
703
+ // Wait for active tasks
704
+ while (this.activeTasks.size > 0) {
705
+ await new Promise(r => setTimeout(r, 100));
706
+ }
707
+
708
+ // Clear workers
709
+ this.workers.clear();
710
+ this.status = 'shutdown';
711
+ this.emit('shutdown');
712
+ }
713
+ }
714
+
715
+ /**
716
+ * Task Orchestrator
717
+ *
718
+ * Orchestrates multi-agent workflows and complex task pipelines.
719
+ */
720
+ export class TaskOrchestrator extends EventEmitter {
721
+ constructor(agentSpawner, workerPool, options = {}) {
722
+ super();
723
+ this.spawner = agentSpawner;
724
+ this.pool = workerPool;
725
+ this.workflows = new Map();
726
+ this.maxConcurrentWorkflows = options.maxConcurrentWorkflows || 5;
727
+ }
728
+
729
+ /**
730
+ * Create a workflow
731
+ */
732
+ createWorkflow(name, steps) {
733
+ const workflow = {
734
+ id: `wf-${randomBytes(6).toString('hex')}`,
735
+ name,
736
+ steps,
737
+ status: 'created',
738
+ currentStep: 0,
739
+ results: [],
740
+ startTime: null,
741
+ endTime: null,
742
+ };
743
+
744
+ this.workflows.set(workflow.id, workflow);
745
+ return workflow;
746
+ }
747
+
748
+ /**
749
+ * Execute a workflow
750
+ */
751
+ async executeWorkflow(workflowId, input = {}) {
752
+ const workflow = this.workflows.get(workflowId);
753
+ if (!workflow) throw new Error('Workflow not found');
754
+
755
+ workflow.status = 'running';
756
+ workflow.startTime = Date.now();
757
+ workflow.input = input;
758
+
759
+ this.emit('workflow-start', { workflowId, name: workflow.name });
760
+
761
+ try {
762
+ let context = { ...input };
763
+
764
+ for (let i = 0; i < workflow.steps.length; i++) {
765
+ workflow.currentStep = i;
766
+ const step = workflow.steps[i];
767
+
768
+ this.emit('step-start', {
769
+ workflowId,
770
+ step: i,
771
+ type: step.type,
772
+ name: step.name,
773
+ });
774
+
775
+ const result = await this.executeStep(step, context);
776
+ workflow.results.push(result);
777
+
778
+ // Pass result to next step
779
+ context = { ...context, [step.name || `step${i}`]: result };
780
+
781
+ this.emit('step-complete', {
782
+ workflowId,
783
+ step: i,
784
+ result,
785
+ });
786
+ }
787
+
788
+ workflow.status = 'completed';
789
+ workflow.endTime = Date.now();
790
+
791
+ this.emit('workflow-complete', {
792
+ workflowId,
793
+ duration: workflow.endTime - workflow.startTime,
794
+ results: workflow.results,
795
+ });
796
+
797
+ return {
798
+ success: true,
799
+ results: workflow.results,
800
+ context,
801
+ };
802
+
803
+ } catch (error) {
804
+ workflow.status = 'failed';
805
+ workflow.endTime = Date.now();
806
+ workflow.error = error.message;
807
+
808
+ this.emit('workflow-failed', { workflowId, error: error.message });
809
+
810
+ return {
811
+ success: false,
812
+ error: error.message,
813
+ failedStep: workflow.currentStep,
814
+ };
815
+ }
816
+ }
817
+
818
+ /**
819
+ * Execute a single workflow step
820
+ */
821
+ async executeStep(step, context) {
822
+ switch (step.type) {
823
+ case 'agent':
824
+ return this.executeAgentStep(step, context);
825
+
826
+ case 'parallel':
827
+ return this.executeParallelStep(step, context);
828
+
829
+ case 'pool':
830
+ return this.executePoolStep(step, context);
831
+
832
+ case 'condition':
833
+ return this.executeConditionStep(step, context);
834
+
835
+ case 'transform':
836
+ return this.executeTransformStep(step, context);
837
+
838
+ default:
839
+ throw new Error(`Unknown step type: ${step.type}`);
840
+ }
841
+ }
842
+
843
+ /**
844
+ * Execute an agent step
845
+ */
846
+ async executeAgentStep(step, context) {
847
+ const task = typeof step.task === 'function'
848
+ ? step.task(context)
849
+ : step.task;
850
+
851
+ const agent = await this.spawner.spawn({
852
+ type: step.agentType || 'researcher',
853
+ task,
854
+ maxRuv: step.maxRuv,
855
+ priority: step.priority,
856
+ });
857
+
858
+ return new Promise((resolve, reject) => {
859
+ agent.on('complete', resolve);
860
+ agent.on('error', reject);
861
+
862
+ // Simulate completion for now
863
+ setTimeout(() => {
864
+ agent.complete({
865
+ task,
866
+ result: `Completed: ${task}`,
867
+ context,
868
+ });
869
+ }, 1000);
870
+ });
871
+ }
872
+
873
+ /**
874
+ * Execute parallel agents
875
+ */
876
+ async executeParallelStep(step, context) {
877
+ const promises = step.agents.map(agentConfig =>
878
+ this.executeAgentStep(agentConfig, context)
879
+ );
880
+
881
+ return Promise.all(promises);
882
+ }
883
+
884
+ /**
885
+ * Execute worker pool step
886
+ */
887
+ async executePoolStep(step, context) {
888
+ const data = typeof step.data === 'function'
889
+ ? step.data(context)
890
+ : step.data || context.data;
891
+
892
+ return this.pool.execute({
893
+ task: step.task,
894
+ data,
895
+ strategy: step.strategy || 'parallel',
896
+ });
897
+ }
898
+
899
+ /**
900
+ * Execute conditional step
901
+ */
902
+ async executeConditionStep(step, context) {
903
+ const condition = typeof step.condition === 'function'
904
+ ? step.condition(context)
905
+ : step.condition;
906
+
907
+ if (condition) {
908
+ return this.executeStep(step.then, context);
909
+ } else if (step.else) {
910
+ return this.executeStep(step.else, context);
911
+ }
912
+ return null;
913
+ }
914
+
915
+ /**
916
+ * Execute transform step
917
+ */
918
+ async executeTransformStep(step, context) {
919
+ return step.transform(context);
920
+ }
921
+
922
+ /**
923
+ * Get workflow status
924
+ */
925
+ getWorkflowStatus(workflowId) {
926
+ const workflow = this.workflows.get(workflowId);
927
+ if (!workflow) return null;
928
+
929
+ return {
930
+ id: workflow.id,
931
+ name: workflow.name,
932
+ status: workflow.status,
933
+ currentStep: workflow.currentStep,
934
+ totalSteps: workflow.steps.length,
935
+ progress: (workflow.currentStep / workflow.steps.length) * 100,
936
+ startTime: workflow.startTime,
937
+ endTime: workflow.endTime,
938
+ duration: workflow.endTime && workflow.startTime
939
+ ? workflow.endTime - workflow.startTime
940
+ : null,
941
+ };
942
+ }
943
+
944
+ /**
945
+ * List all workflows
946
+ */
947
+ listWorkflows() {
948
+ return Array.from(this.workflows.values()).map(w => ({
949
+ id: w.id,
950
+ name: w.name,
951
+ status: w.status,
952
+ steps: w.steps.length,
953
+ }));
954
+ }
955
+ }
956
+
957
+ // Export default instances
958
+ export default {
959
+ AGENT_TYPES,
960
+ TaskStatus,
961
+ DistributedAgent,
962
+ AgentSpawner,
963
+ WorkerPool,
964
+ TaskOrchestrator,
965
+ };