@ruvector/edge-net 0.1.2 → 0.1.3
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/README.md +160 -0
- package/agents.js +965 -0
- package/package.json +19 -3
package/README.md
CHANGED
|
@@ -44,6 +44,7 @@ A distributed computing platform that enables collective resource sharing for AI
|
|
|
44
44
|
- [Exotic AI Capabilities](#exotic-ai-capabilities)
|
|
45
45
|
- [Core Architecture & Capabilities](#core-architecture--capabilities)
|
|
46
46
|
- [Self-Learning Hooks & MCP Integration](#self-learning-hooks--mcp-integration)
|
|
47
|
+
- [Distributed AI Agents & Workers](#distributed-ai-agents--workers)
|
|
47
48
|
|
|
48
49
|
---
|
|
49
50
|
|
|
@@ -1238,6 +1239,165 @@ ruvector hooks remember <content> -t <type> # Store memory
|
|
|
1238
1239
|
ruvector hooks recall <query> # Semantic search
|
|
1239
1240
|
```
|
|
1240
1241
|
|
|
1242
|
+
---
|
|
1243
|
+
|
|
1244
|
+
## Distributed AI Agents & Workers
|
|
1245
|
+
|
|
1246
|
+
Edge-net enables spawning AI agents and distributed worker pools across the collective compute network. This transforms passive compute contribution into active distributed AI execution.
|
|
1247
|
+
|
|
1248
|
+
### Architecture
|
|
1249
|
+
|
|
1250
|
+
```
|
|
1251
|
+
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
1252
|
+
│ DISTRIBUTED AI AGENT SYSTEM │
|
|
1253
|
+
├─────────────────────────────────────────────────────────────────────────────┤
|
|
1254
|
+
│ │
|
|
1255
|
+
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
|
|
1256
|
+
│ │ AgentSpawner │ │ WorkerPool │ │ TaskOrchestrator│ │
|
|
1257
|
+
│ │ │ │ │ │ │ │
|
|
1258
|
+
│ │ • Type routing │ │ • Load balance │ │ • Workflows │ │
|
|
1259
|
+
│ │ • rUv costing │ │ • Auto-scaling │ │ • Dependencies │ │
|
|
1260
|
+
│ │ • Priority mgmt │ │ • Fault tolerant│ │ • Parallel exec │ │
|
|
1261
|
+
│ └────────┬────────┘ └────────┬────────┘ └────────┬────────┘ │
|
|
1262
|
+
│ │ │ │ │
|
|
1263
|
+
│ └───────────────────────┼───────────────────────┘ │
|
|
1264
|
+
│ │ │
|
|
1265
|
+
│ ┌────────────┴────────────┐ │
|
|
1266
|
+
│ │ Edge-Net P2P Network │ │
|
|
1267
|
+
│ │ (WebRTC Data Channels) │ │
|
|
1268
|
+
│ └──────────────────────────┘ │
|
|
1269
|
+
│ │
|
|
1270
|
+
│ Agent Types: researcher | coder | reviewer | tester | analyst | │
|
|
1271
|
+
│ optimizer | coordinator | embedder │
|
|
1272
|
+
│ │
|
|
1273
|
+
└─────────────────────────────────────────────────────────────────────────────┘
|
|
1274
|
+
```
|
|
1275
|
+
|
|
1276
|
+
### Agent Types
|
|
1277
|
+
|
|
1278
|
+
| Type | Capabilities | Base rUv | Use Cases |
|
|
1279
|
+
|------|-------------|----------|-----------|
|
|
1280
|
+
| **researcher** | search, analyze, summarize, extract | 10 | Codebase analysis, documentation research |
|
|
1281
|
+
| **coder** | code, refactor, debug, test | 15 | Feature implementation, bug fixes |
|
|
1282
|
+
| **reviewer** | review, audit, validate, suggest | 12 | Code review, security audit |
|
|
1283
|
+
| **tester** | test, benchmark, validate, report | 10 | Test creation, coverage analysis |
|
|
1284
|
+
| **analyst** | analyze, metrics, report, visualize | 8 | Performance analysis, data insights |
|
|
1285
|
+
| **optimizer** | optimize, profile, benchmark, improve | 15 | Performance tuning, efficiency |
|
|
1286
|
+
| **coordinator** | orchestrate, route, schedule, monitor | 20 | Multi-agent workflows |
|
|
1287
|
+
| **embedder** | embed, vectorize, similarity, search | 5 | Vector operations, semantic search |
|
|
1288
|
+
|
|
1289
|
+
### CLI Commands
|
|
1290
|
+
|
|
1291
|
+
```bash
|
|
1292
|
+
# Show Edge-Net information
|
|
1293
|
+
ruvector edge-net info
|
|
1294
|
+
|
|
1295
|
+
# Spawn a distributed AI agent
|
|
1296
|
+
ruvector edge-net spawn researcher "Analyze the authentication system"
|
|
1297
|
+
ruvector edge-net spawn coder "Implement user profile feature" --max-ruv 50 --priority high
|
|
1298
|
+
|
|
1299
|
+
# Create and use worker pools
|
|
1300
|
+
ruvector edge-net pool create --size 10 --capabilities compute,embed
|
|
1301
|
+
ruvector edge-net pool execute "Process batch embeddings"
|
|
1302
|
+
|
|
1303
|
+
# Run multi-agent workflows
|
|
1304
|
+
ruvector edge-net workflow code-review
|
|
1305
|
+
ruvector edge-net workflow feature-dev
|
|
1306
|
+
ruvector edge-net workflow optimization
|
|
1307
|
+
|
|
1308
|
+
# Check network status
|
|
1309
|
+
ruvector edge-net status
|
|
1310
|
+
```
|
|
1311
|
+
|
|
1312
|
+
### MCP Tools
|
|
1313
|
+
|
|
1314
|
+
The following MCP tools are available when `ruvector` is configured as an MCP server:
|
|
1315
|
+
|
|
1316
|
+
| Tool | Description | Parameters |
|
|
1317
|
+
|------|-------------|------------|
|
|
1318
|
+
| `edge_net_info` | Get Edge-Net information | - |
|
|
1319
|
+
| `edge_net_spawn` | Spawn distributed agent | type, task, max_ruv, priority |
|
|
1320
|
+
| `edge_net_pool_create` | Create worker pool | min_workers, max_workers |
|
|
1321
|
+
| `edge_net_pool_execute` | Execute on pool | task, pool_id |
|
|
1322
|
+
| `edge_net_workflow` | Run workflow | name (code-review, feature-dev, etc.) |
|
|
1323
|
+
| `edge_net_status` | Network status | - |
|
|
1324
|
+
|
|
1325
|
+
### Workflows
|
|
1326
|
+
|
|
1327
|
+
Pre-built multi-agent workflows:
|
|
1328
|
+
|
|
1329
|
+
| Workflow | Steps | Est. rUv | Description |
|
|
1330
|
+
|----------|-------|----------|-------------|
|
|
1331
|
+
| **code-review** | analyst → reviewer → tester → optimizer | 45 | Comprehensive code analysis |
|
|
1332
|
+
| **feature-dev** | researcher → coder → tester → reviewer | 60 | Full feature development cycle |
|
|
1333
|
+
| **bug-fix** | analyst → coder → tester | 35 | Bug diagnosis and fix |
|
|
1334
|
+
| **optimization** | analyst → optimizer → coder → tester | 50 | Performance improvement |
|
|
1335
|
+
| **research** | researcher → analyst → embedder | 30 | Deep research with embeddings |
|
|
1336
|
+
|
|
1337
|
+
### JavaScript API
|
|
1338
|
+
|
|
1339
|
+
```javascript
|
|
1340
|
+
import { AgentSpawner, WorkerPool, TaskOrchestrator, AGENT_TYPES } from '@ruvector/edge-net/agents';
|
|
1341
|
+
|
|
1342
|
+
// Spawn a distributed agent
|
|
1343
|
+
const spawner = new AgentSpawner(edgeNetNode);
|
|
1344
|
+
const agent = await spawner.spawn('coder', 'Implement authentication', {
|
|
1345
|
+
maxRuv: 30,
|
|
1346
|
+
priority: 'high'
|
|
1347
|
+
});
|
|
1348
|
+
|
|
1349
|
+
// Create a worker pool
|
|
1350
|
+
const pool = new WorkerPool(edgeNetNode, {
|
|
1351
|
+
minWorkers: 5,
|
|
1352
|
+
maxWorkers: 20,
|
|
1353
|
+
capabilities: ['compute', 'embed', 'analyze']
|
|
1354
|
+
});
|
|
1355
|
+
await pool.scale(10);
|
|
1356
|
+
|
|
1357
|
+
// Execute tasks on the pool
|
|
1358
|
+
const result = await pool.execute({
|
|
1359
|
+
type: 'parallel',
|
|
1360
|
+
task: 'Process batch data',
|
|
1361
|
+
data: largeDataset
|
|
1362
|
+
});
|
|
1363
|
+
|
|
1364
|
+
// Run multi-agent workflow
|
|
1365
|
+
const orchestrator = new TaskOrchestrator(edgeNetNode, spawner);
|
|
1366
|
+
await orchestrator.runWorkflow('feature-dev', 'Add user authentication');
|
|
1367
|
+
```
|
|
1368
|
+
|
|
1369
|
+
### Event System
|
|
1370
|
+
|
|
1371
|
+
Agents and workers emit events for monitoring:
|
|
1372
|
+
|
|
1373
|
+
```javascript
|
|
1374
|
+
agent.on('started', ({ id, type }) => console.log(`Agent ${id} started`));
|
|
1375
|
+
agent.on('progress', ({ progress }) => console.log(`Progress: ${progress}%`));
|
|
1376
|
+
agent.on('completed', ({ result }) => console.log('Done:', result));
|
|
1377
|
+
agent.on('error', ({ error }) => console.error('Error:', error));
|
|
1378
|
+
|
|
1379
|
+
pool.on('scaled', ({ workers }) => console.log(`Pool scaled to ${workers}`));
|
|
1380
|
+
pool.on('task_completed', ({ taskId }) => console.log(`Task ${taskId} done`));
|
|
1381
|
+
```
|
|
1382
|
+
|
|
1383
|
+
### rUv Economics for Agents
|
|
1384
|
+
|
|
1385
|
+
| Factor | Impact |
|
|
1386
|
+
|--------|--------|
|
|
1387
|
+
| **Base Cost** | Agent type determines base rUv per task |
|
|
1388
|
+
| **Task Complexity** | Longer/complex tasks cost more |
|
|
1389
|
+
| **Priority** | High priority = 1.5x cost, Critical = 2x |
|
|
1390
|
+
| **Network Load** | Dynamic pricing based on availability |
|
|
1391
|
+
| **Early Adopter** | 10x multiplier during genesis phase |
|
|
1392
|
+
|
|
1393
|
+
### Security Considerations
|
|
1394
|
+
|
|
1395
|
+
- All agent communications are encrypted via DTLS
|
|
1396
|
+
- Task execution sandboxed in WebWorkers
|
|
1397
|
+
- rUv spending limits prevent runaway costs
|
|
1398
|
+
- Input validation on all MCP tools
|
|
1399
|
+
- Rate limiting on agent spawning
|
|
1400
|
+
|
|
1241
1401
|
### Claude Code Hook Events
|
|
1242
1402
|
|
|
1243
1403
|
| Event | Trigger | Action |
|
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
|
+
};
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ruvector/edge-net",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "Distributed compute intelligence network - contribute browser compute, earn credits. Features Time Crystal coordination, Neural DAG attention,
|
|
5
|
+
"description": "Distributed compute intelligence network with AI agents and workers - contribute browser compute, spawn distributed AI agents, earn credits. Features Time Crystal coordination, Neural DAG attention, P2P swarm intelligence, and multi-agent workflows.",
|
|
6
6
|
"main": "ruvector_edge_net.js",
|
|
7
7
|
"module": "ruvector_edge_net.js",
|
|
8
8
|
"types": "ruvector_edge_net.d.ts",
|
|
@@ -29,7 +29,13 @@
|
|
|
29
29
|
"time-crystal",
|
|
30
30
|
"dag-attention",
|
|
31
31
|
"swarm-intelligence",
|
|
32
|
-
"neural-network"
|
|
32
|
+
"neural-network",
|
|
33
|
+
"ai-agents",
|
|
34
|
+
"distributed-agents",
|
|
35
|
+
"worker-pools",
|
|
36
|
+
"multi-agent",
|
|
37
|
+
"webrtc",
|
|
38
|
+
"task-orchestration"
|
|
33
39
|
],
|
|
34
40
|
"author": "RuVector Team <team@ruvector.dev>",
|
|
35
41
|
"license": "MIT",
|
|
@@ -54,6 +60,7 @@
|
|
|
54
60
|
"network.js",
|
|
55
61
|
"networks.js",
|
|
56
62
|
"webrtc.js",
|
|
63
|
+
"agents.js",
|
|
57
64
|
"README.md",
|
|
58
65
|
"LICENSE"
|
|
59
66
|
],
|
|
@@ -64,6 +71,15 @@
|
|
|
64
71
|
},
|
|
65
72
|
"./wasm": {
|
|
66
73
|
"import": "./ruvector_edge_net_bg.wasm"
|
|
74
|
+
},
|
|
75
|
+
"./agents": {
|
|
76
|
+
"import": "./agents.js"
|
|
77
|
+
},
|
|
78
|
+
"./network": {
|
|
79
|
+
"import": "./network.js"
|
|
80
|
+
},
|
|
81
|
+
"./webrtc": {
|
|
82
|
+
"import": "./webrtc.js"
|
|
67
83
|
}
|
|
68
84
|
},
|
|
69
85
|
"sideEffects": [
|