claude-flow 1.0.21 → 1.0.23
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 +58 -12
- package/bin/claude-flow +14 -1
- package/bin/claude-flow-swarm +19 -0
- package/bin/claude-flow-swarm-monitor +18 -0
- package/bin/claude-flow-swarm-ui +40 -0
- package/package.json +18 -4
- package/src/cli/cli-core.ts +2 -2
- package/src/cli/commands/index.ts +329 -7
- package/src/cli/commands/swarm-spawn.ts +79 -0
- package/src/cli/commands/swarm.ts +431 -0
- package/src/cli/create-enhanced-task.js +181 -0
- package/src/cli/main.ts +1 -1
- package/src/cli/simple-cli.js +195 -5
- package/src/cli/simple-cli.ts +9 -9
- package/src/coordination/swarm-monitor.ts +473 -0
- package/src/mcp/swarm-tools.ts +166 -0
- package/src/utils/helpers.ts +44 -1
|
@@ -0,0 +1,473 @@
|
|
|
1
|
+
import { EventEmitter } from 'events';
|
|
2
|
+
import * as os from 'os';
|
|
3
|
+
import * as fs from 'fs/promises';
|
|
4
|
+
import * as path from 'path';
|
|
5
|
+
import { Logger } from '../core/logger';
|
|
6
|
+
import { performance } from 'perf_hooks';
|
|
7
|
+
|
|
8
|
+
interface AgentMetrics {
|
|
9
|
+
id: string;
|
|
10
|
+
name: string;
|
|
11
|
+
status: 'idle' | 'running' | 'completed' | 'failed' | 'stalled';
|
|
12
|
+
currentTask?: string;
|
|
13
|
+
startTime?: number;
|
|
14
|
+
endTime?: number;
|
|
15
|
+
duration?: number;
|
|
16
|
+
cpuUsage?: number;
|
|
17
|
+
memoryUsage?: number;
|
|
18
|
+
taskCount: number;
|
|
19
|
+
successCount: number;
|
|
20
|
+
failureCount: number;
|
|
21
|
+
averageTaskDuration: number;
|
|
22
|
+
lastActivity: number;
|
|
23
|
+
outputSize?: number;
|
|
24
|
+
errorRate: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface SystemMetrics {
|
|
28
|
+
timestamp: number;
|
|
29
|
+
cpuUsage: number;
|
|
30
|
+
memoryUsage: number;
|
|
31
|
+
totalMemory: number;
|
|
32
|
+
freeMemory: number;
|
|
33
|
+
loadAverage: number[];
|
|
34
|
+
activeAgents: number;
|
|
35
|
+
totalTasks: number;
|
|
36
|
+
completedTasks: number;
|
|
37
|
+
failedTasks: number;
|
|
38
|
+
pendingTasks: number;
|
|
39
|
+
averageTaskDuration: number;
|
|
40
|
+
throughput: number; // tasks per minute
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
interface Alert {
|
|
44
|
+
id: string;
|
|
45
|
+
timestamp: number;
|
|
46
|
+
level: 'info' | 'warning' | 'error' | 'critical';
|
|
47
|
+
type: 'agent_failure' | 'high_cpu' | 'high_memory' | 'stalled_agent' | 'low_throughput' | 'error_rate';
|
|
48
|
+
message: string;
|
|
49
|
+
details?: any;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
interface MonitoringConfig {
|
|
53
|
+
updateInterval: number; // milliseconds
|
|
54
|
+
metricsRetention: number; // hours
|
|
55
|
+
cpuThreshold: number; // percentage
|
|
56
|
+
memoryThreshold: number; // percentage
|
|
57
|
+
stallTimeout: number; // milliseconds
|
|
58
|
+
errorRateThreshold: number; // percentage
|
|
59
|
+
throughputThreshold: number; // tasks per minute
|
|
60
|
+
enableAlerts: boolean;
|
|
61
|
+
enableHistory: boolean;
|
|
62
|
+
historyPath?: string;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export class SwarmMonitor extends EventEmitter {
|
|
66
|
+
private logger: Logger;
|
|
67
|
+
private config: MonitoringConfig;
|
|
68
|
+
private agentMetrics: Map<string, AgentMetrics> = new Map();
|
|
69
|
+
private systemMetrics: SystemMetrics[] = [];
|
|
70
|
+
private alerts: Alert[] = [];
|
|
71
|
+
private monitoringInterval?: NodeJS.Timeout;
|
|
72
|
+
private startTime: number;
|
|
73
|
+
private taskStartTimes: Map<string, number> = new Map();
|
|
74
|
+
private taskCompletionTimes: number[] = [];
|
|
75
|
+
private lastThroughputCheck: number;
|
|
76
|
+
private tasksInLastMinute: number = 0;
|
|
77
|
+
|
|
78
|
+
constructor(config?: Partial<MonitoringConfig>) {
|
|
79
|
+
super();
|
|
80
|
+
this.logger = new Logger('SwarmMonitor');
|
|
81
|
+
this.config = {
|
|
82
|
+
updateInterval: 1000, // 1 second
|
|
83
|
+
metricsRetention: 24, // 24 hours
|
|
84
|
+
cpuThreshold: 80, // 80%
|
|
85
|
+
memoryThreshold: 85, // 85%
|
|
86
|
+
stallTimeout: 300000, // 5 minutes
|
|
87
|
+
errorRateThreshold: 10, // 10%
|
|
88
|
+
throughputThreshold: 1, // 1 task per minute minimum
|
|
89
|
+
enableAlerts: true,
|
|
90
|
+
enableHistory: true,
|
|
91
|
+
historyPath: './monitoring/history',
|
|
92
|
+
...config
|
|
93
|
+
};
|
|
94
|
+
this.startTime = Date.now();
|
|
95
|
+
this.lastThroughputCheck = Date.now();
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async start(): Promise<void> {
|
|
99
|
+
this.logger.info('Starting swarm monitoring...');
|
|
100
|
+
|
|
101
|
+
// Create history directory if needed
|
|
102
|
+
if (this.config.enableHistory && this.config.historyPath) {
|
|
103
|
+
await fs.mkdir(this.config.historyPath, { recursive: true });
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Start periodic monitoring
|
|
107
|
+
this.monitoringInterval = setInterval(() => {
|
|
108
|
+
this.collectMetrics();
|
|
109
|
+
}, this.config.updateInterval);
|
|
110
|
+
|
|
111
|
+
// Start initial collection
|
|
112
|
+
await this.collectMetrics();
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
stop(): void {
|
|
116
|
+
this.logger.info('Stopping swarm monitoring...');
|
|
117
|
+
if (this.monitoringInterval) {
|
|
118
|
+
clearInterval(this.monitoringInterval);
|
|
119
|
+
this.monitoringInterval = undefined;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Agent registration and tracking
|
|
124
|
+
registerAgent(agentId: string, name: string): void {
|
|
125
|
+
this.agentMetrics.set(agentId, {
|
|
126
|
+
id: agentId,
|
|
127
|
+
name,
|
|
128
|
+
status: 'idle',
|
|
129
|
+
taskCount: 0,
|
|
130
|
+
successCount: 0,
|
|
131
|
+
failureCount: 0,
|
|
132
|
+
averageTaskDuration: 0,
|
|
133
|
+
lastActivity: Date.now(),
|
|
134
|
+
errorRate: 0
|
|
135
|
+
});
|
|
136
|
+
this.logger.debug(`Registered agent: ${name} (${agentId})`);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
unregisterAgent(agentId: string): void {
|
|
140
|
+
const metrics = this.agentMetrics.get(agentId);
|
|
141
|
+
if (metrics) {
|
|
142
|
+
this.logger.debug(`Unregistered agent: ${metrics.name} (${agentId})`);
|
|
143
|
+
this.agentMetrics.delete(agentId);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Task tracking
|
|
148
|
+
taskStarted(agentId: string, taskId: string, taskDescription?: string): void {
|
|
149
|
+
const metrics = this.agentMetrics.get(agentId);
|
|
150
|
+
if (metrics) {
|
|
151
|
+
metrics.status = 'running';
|
|
152
|
+
metrics.currentTask = taskDescription || taskId;
|
|
153
|
+
metrics.startTime = Date.now();
|
|
154
|
+
metrics.lastActivity = Date.now();
|
|
155
|
+
metrics.taskCount++;
|
|
156
|
+
this.taskStartTimes.set(taskId, Date.now());
|
|
157
|
+
this.emit('task:started', { agentId, taskId, taskDescription });
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
taskCompleted(agentId: string, taskId: string, outputSize?: number): void {
|
|
162
|
+
const metrics = this.agentMetrics.get(agentId);
|
|
163
|
+
const startTime = this.taskStartTimes.get(taskId);
|
|
164
|
+
|
|
165
|
+
if (metrics && startTime) {
|
|
166
|
+
const duration = Date.now() - startTime;
|
|
167
|
+
metrics.status = 'completed';
|
|
168
|
+
metrics.endTime = Date.now();
|
|
169
|
+
metrics.duration = duration;
|
|
170
|
+
metrics.lastActivity = Date.now();
|
|
171
|
+
metrics.successCount++;
|
|
172
|
+
metrics.outputSize = outputSize;
|
|
173
|
+
|
|
174
|
+
// Update average duration
|
|
175
|
+
const totalDuration = metrics.averageTaskDuration * (metrics.successCount - 1) + duration;
|
|
176
|
+
metrics.averageTaskDuration = totalDuration / metrics.successCount;
|
|
177
|
+
|
|
178
|
+
// Update error rate
|
|
179
|
+
metrics.errorRate = (metrics.failureCount / metrics.taskCount) * 100;
|
|
180
|
+
|
|
181
|
+
// Track for throughput calculation
|
|
182
|
+
this.taskCompletionTimes.push(Date.now());
|
|
183
|
+
this.tasksInLastMinute++;
|
|
184
|
+
|
|
185
|
+
this.taskStartTimes.delete(taskId);
|
|
186
|
+
this.emit('task:completed', { agentId, taskId, duration, outputSize });
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
taskFailed(agentId: string, taskId: string, error: string): void {
|
|
191
|
+
const metrics = this.agentMetrics.get(agentId);
|
|
192
|
+
const startTime = this.taskStartTimes.get(taskId);
|
|
193
|
+
|
|
194
|
+
if (metrics) {
|
|
195
|
+
const duration = startTime ? Date.now() - startTime : 0;
|
|
196
|
+
metrics.status = 'failed';
|
|
197
|
+
metrics.endTime = Date.now();
|
|
198
|
+
metrics.duration = duration;
|
|
199
|
+
metrics.lastActivity = Date.now();
|
|
200
|
+
metrics.failureCount++;
|
|
201
|
+
|
|
202
|
+
// Update error rate
|
|
203
|
+
metrics.errorRate = (metrics.failureCount / metrics.taskCount) * 100;
|
|
204
|
+
|
|
205
|
+
this.taskStartTimes.delete(taskId);
|
|
206
|
+
this.emit('task:failed', { agentId, taskId, error, duration });
|
|
207
|
+
|
|
208
|
+
// Check error rate threshold
|
|
209
|
+
if (metrics.errorRate > this.config.errorRateThreshold) {
|
|
210
|
+
this.createAlert('error_rate', 'critical',
|
|
211
|
+
`Agent ${metrics.name} has high error rate: ${metrics.errorRate.toFixed(1)}%`);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Metrics collection
|
|
217
|
+
private async collectMetrics(): Promise<void> {
|
|
218
|
+
try {
|
|
219
|
+
// Collect system metrics
|
|
220
|
+
const cpuUsage = this.getCPUUsage();
|
|
221
|
+
const memInfo = this.getMemoryInfo();
|
|
222
|
+
const loadAvg = os.loadavg();
|
|
223
|
+
|
|
224
|
+
// Calculate throughput
|
|
225
|
+
const now = Date.now();
|
|
226
|
+
const minuteAgo = now - 60000;
|
|
227
|
+
this.taskCompletionTimes = this.taskCompletionTimes.filter(time => time > minuteAgo);
|
|
228
|
+
const throughput = this.taskCompletionTimes.length;
|
|
229
|
+
|
|
230
|
+
// Calculate task statistics
|
|
231
|
+
let totalTasks = 0;
|
|
232
|
+
let completedTasks = 0;
|
|
233
|
+
let failedTasks = 0;
|
|
234
|
+
let activeAgents = 0;
|
|
235
|
+
let totalDuration = 0;
|
|
236
|
+
let durationCount = 0;
|
|
237
|
+
|
|
238
|
+
// Check for stalled agents
|
|
239
|
+
for (const [agentId, metrics] of this.agentMetrics) {
|
|
240
|
+
if (metrics.status === 'running') {
|
|
241
|
+
activeAgents++;
|
|
242
|
+
|
|
243
|
+
// Check for stalled agent
|
|
244
|
+
const stallTime = now - metrics.lastActivity;
|
|
245
|
+
if (stallTime > this.config.stallTimeout) {
|
|
246
|
+
metrics.status = 'stalled';
|
|
247
|
+
this.createAlert('stalled_agent', 'warning',
|
|
248
|
+
`Agent ${metrics.name} appears to be stalled (${Math.round(stallTime / 1000)}s inactive)`);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
totalTasks += metrics.taskCount;
|
|
253
|
+
completedTasks += metrics.successCount;
|
|
254
|
+
failedTasks += metrics.failureCount;
|
|
255
|
+
|
|
256
|
+
if (metrics.averageTaskDuration > 0) {
|
|
257
|
+
totalDuration += metrics.averageTaskDuration * metrics.successCount;
|
|
258
|
+
durationCount += metrics.successCount;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const avgDuration = durationCount > 0 ? totalDuration / durationCount : 0;
|
|
263
|
+
const pendingTasks = totalTasks - completedTasks - failedTasks;
|
|
264
|
+
|
|
265
|
+
// Create system metrics
|
|
266
|
+
const systemMetrics: SystemMetrics = {
|
|
267
|
+
timestamp: now,
|
|
268
|
+
cpuUsage,
|
|
269
|
+
memoryUsage: memInfo.usagePercent,
|
|
270
|
+
totalMemory: memInfo.total,
|
|
271
|
+
freeMemory: memInfo.free,
|
|
272
|
+
loadAverage: loadAvg,
|
|
273
|
+
activeAgents,
|
|
274
|
+
totalTasks,
|
|
275
|
+
completedTasks,
|
|
276
|
+
failedTasks,
|
|
277
|
+
pendingTasks,
|
|
278
|
+
averageTaskDuration: avgDuration,
|
|
279
|
+
throughput
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
this.systemMetrics.push(systemMetrics);
|
|
283
|
+
|
|
284
|
+
// Check system thresholds
|
|
285
|
+
if (this.config.enableAlerts) {
|
|
286
|
+
this.checkThresholds(systemMetrics);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Clean old metrics
|
|
290
|
+
this.cleanOldMetrics();
|
|
291
|
+
|
|
292
|
+
// Save history if enabled
|
|
293
|
+
if (this.config.enableHistory) {
|
|
294
|
+
await this.saveHistory(systemMetrics);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Emit metrics update
|
|
298
|
+
this.emit('metrics:updated', {
|
|
299
|
+
system: systemMetrics,
|
|
300
|
+
agents: Array.from(this.agentMetrics.values())
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
} catch (error) {
|
|
304
|
+
this.logger.error('Error collecting metrics:', error);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
private getCPUUsage(): number {
|
|
309
|
+
const cpus = os.cpus();
|
|
310
|
+
let totalIdle = 0;
|
|
311
|
+
let totalTick = 0;
|
|
312
|
+
|
|
313
|
+
cpus.forEach(cpu => {
|
|
314
|
+
for (const type in cpu.times) {
|
|
315
|
+
totalTick += cpu.times[type as keyof typeof cpu.times];
|
|
316
|
+
}
|
|
317
|
+
totalIdle += cpu.times.idle;
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
return 100 - Math.floor(totalIdle / totalTick * 100);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
private getMemoryInfo(): { total: number; free: number; used: number; usagePercent: number } {
|
|
324
|
+
const total = os.totalmem();
|
|
325
|
+
const free = os.freemem();
|
|
326
|
+
const used = total - free;
|
|
327
|
+
const usagePercent = (used / total) * 100;
|
|
328
|
+
|
|
329
|
+
return { total, free, used, usagePercent };
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
private checkThresholds(metrics: SystemMetrics): void {
|
|
333
|
+
// CPU threshold
|
|
334
|
+
if (metrics.cpuUsage > this.config.cpuThreshold) {
|
|
335
|
+
this.createAlert('high_cpu', 'warning',
|
|
336
|
+
`High CPU usage detected: ${metrics.cpuUsage}%`);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// Memory threshold
|
|
340
|
+
if (metrics.memoryUsage > this.config.memoryThreshold) {
|
|
341
|
+
this.createAlert('high_memory', 'warning',
|
|
342
|
+
`High memory usage detected: ${metrics.memoryUsage.toFixed(1)}%`);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// Throughput threshold
|
|
346
|
+
if (metrics.activeAgents > 0 && metrics.throughput < this.config.throughputThreshold) {
|
|
347
|
+
this.createAlert('low_throughput', 'warning',
|
|
348
|
+
`Low throughput detected: ${metrics.throughput} tasks/min`);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
private createAlert(type: Alert['type'], level: Alert['level'], message: string, details?: any): void {
|
|
353
|
+
const alert: Alert = {
|
|
354
|
+
id: `${type}_${Date.now()}`,
|
|
355
|
+
timestamp: Date.now(),
|
|
356
|
+
level,
|
|
357
|
+
type,
|
|
358
|
+
message,
|
|
359
|
+
details
|
|
360
|
+
};
|
|
361
|
+
|
|
362
|
+
this.alerts.push(alert);
|
|
363
|
+
this.emit('alert', alert);
|
|
364
|
+
this.logger[level](message);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
private cleanOldMetrics(): void {
|
|
368
|
+
const retentionTime = this.config.metricsRetention * 60 * 60 * 1000;
|
|
369
|
+
const cutoff = Date.now() - retentionTime;
|
|
370
|
+
|
|
371
|
+
this.systemMetrics = this.systemMetrics.filter(m => m.timestamp > cutoff);
|
|
372
|
+
this.alerts = this.alerts.filter(a => a.timestamp > cutoff);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
private async saveHistory(metrics: SystemMetrics): Promise<void> {
|
|
376
|
+
if (!this.config.historyPath) return;
|
|
377
|
+
|
|
378
|
+
try {
|
|
379
|
+
const date = new Date();
|
|
380
|
+
const filename = `metrics_${date.toISOString().split('T')[0]}.jsonl`;
|
|
381
|
+
const filepath = path.join(this.config.historyPath, filename);
|
|
382
|
+
|
|
383
|
+
const line = JSON.stringify({
|
|
384
|
+
...metrics,
|
|
385
|
+
agents: Array.from(this.agentMetrics.values())
|
|
386
|
+
}) + '\n';
|
|
387
|
+
|
|
388
|
+
await fs.appendFile(filepath, line);
|
|
389
|
+
} catch (error) {
|
|
390
|
+
this.logger.error('Error saving history:', error);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// Getters for current state
|
|
395
|
+
getSystemMetrics(): SystemMetrics | undefined {
|
|
396
|
+
return this.systemMetrics[this.systemMetrics.length - 1];
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
getAgentMetrics(agentId?: string): AgentMetrics | AgentMetrics[] | undefined {
|
|
400
|
+
if (agentId) {
|
|
401
|
+
return this.agentMetrics.get(agentId);
|
|
402
|
+
}
|
|
403
|
+
return Array.from(this.agentMetrics.values());
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
getAlerts(since?: number): Alert[] {
|
|
407
|
+
if (since) {
|
|
408
|
+
return this.alerts.filter(a => a.timestamp > since);
|
|
409
|
+
}
|
|
410
|
+
return this.alerts;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
getHistoricalMetrics(hours: number = 1): SystemMetrics[] {
|
|
414
|
+
const since = Date.now() - (hours * 60 * 60 * 1000);
|
|
415
|
+
return this.systemMetrics.filter(m => m.timestamp > since);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// Summary statistics
|
|
419
|
+
getSummary(): {
|
|
420
|
+
uptime: number;
|
|
421
|
+
totalAgents: number;
|
|
422
|
+
activeAgents: number;
|
|
423
|
+
totalTasks: number;
|
|
424
|
+
completedTasks: number;
|
|
425
|
+
failedTasks: number;
|
|
426
|
+
successRate: number;
|
|
427
|
+
averageDuration: number;
|
|
428
|
+
currentThroughput: number;
|
|
429
|
+
alerts: number;
|
|
430
|
+
} {
|
|
431
|
+
const current = this.getSystemMetrics();
|
|
432
|
+
const uptime = Date.now() - this.startTime;
|
|
433
|
+
const totalAgents = this.agentMetrics.size;
|
|
434
|
+
const activeAgents = current?.activeAgents || 0;
|
|
435
|
+
const totalTasks = current?.totalTasks || 0;
|
|
436
|
+
const completedTasks = current?.completedTasks || 0;
|
|
437
|
+
const failedTasks = current?.failedTasks || 0;
|
|
438
|
+
const successRate = totalTasks > 0 ? (completedTasks / totalTasks) * 100 : 0;
|
|
439
|
+
const averageDuration = current?.averageTaskDuration || 0;
|
|
440
|
+
const currentThroughput = current?.throughput || 0;
|
|
441
|
+
const alerts = this.alerts.filter(a => a.timestamp > Date.now() - 3600000).length; // Last hour
|
|
442
|
+
|
|
443
|
+
return {
|
|
444
|
+
uptime,
|
|
445
|
+
totalAgents,
|
|
446
|
+
activeAgents,
|
|
447
|
+
totalTasks,
|
|
448
|
+
completedTasks,
|
|
449
|
+
failedTasks,
|
|
450
|
+
successRate,
|
|
451
|
+
averageDuration,
|
|
452
|
+
currentThroughput,
|
|
453
|
+
alerts
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// Export monitoring data
|
|
458
|
+
async exportMetrics(filepath: string): Promise<void> {
|
|
459
|
+
const data = {
|
|
460
|
+
summary: this.getSummary(),
|
|
461
|
+
systemMetrics: this.systemMetrics,
|
|
462
|
+
agentMetrics: Array.from(this.agentMetrics.values()),
|
|
463
|
+
alerts: this.alerts,
|
|
464
|
+
exported: new Date().toISOString()
|
|
465
|
+
};
|
|
466
|
+
|
|
467
|
+
await fs.writeFile(filepath, JSON.stringify(data, null, 2));
|
|
468
|
+
this.logger.info(`Exported metrics to ${filepath}`);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// Export types for external use
|
|
473
|
+
export type { AgentMetrics, SystemMetrics, Alert, MonitoringConfig };
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP tools for swarm orchestration
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { Tool } from '@modelcontextprotocol/sdk/types.js';
|
|
6
|
+
import { spawnSwarmAgent, getSwarmState } from '../cli/commands/swarm-spawn.ts';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Dispatch agent tool for swarm orchestration
|
|
10
|
+
*/
|
|
11
|
+
export const dispatchAgentTool: Tool = {
|
|
12
|
+
name: 'dispatch_agent',
|
|
13
|
+
description: 'Spawn a new agent in the swarm to handle a specific task',
|
|
14
|
+
inputSchema: {
|
|
15
|
+
type: 'object',
|
|
16
|
+
properties: {
|
|
17
|
+
type: {
|
|
18
|
+
type: 'string',
|
|
19
|
+
enum: ['researcher', 'developer', 'analyst', 'reviewer', 'coordinator'],
|
|
20
|
+
description: 'The type of agent to spawn',
|
|
21
|
+
},
|
|
22
|
+
task: {
|
|
23
|
+
type: 'string',
|
|
24
|
+
description: 'The specific task for the agent to complete',
|
|
25
|
+
},
|
|
26
|
+
name: {
|
|
27
|
+
type: 'string',
|
|
28
|
+
description: 'Optional name for the agent',
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
required: ['type', 'task'],
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Handle dispatch agent tool execution
|
|
37
|
+
*/
|
|
38
|
+
export async function handleDispatchAgent(args: any): Promise<any> {
|
|
39
|
+
const { type, task, name } = args;
|
|
40
|
+
|
|
41
|
+
// Get swarm ID from environment
|
|
42
|
+
const swarmId = Deno.env.get('CLAUDE_SWARM_ID');
|
|
43
|
+
if (!swarmId) {
|
|
44
|
+
throw new Error('Not running in swarm context');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Get parent agent ID if available
|
|
48
|
+
const parentId = Deno.env.get('CLAUDE_SWARM_AGENT_ID');
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
// Spawn the agent
|
|
52
|
+
const agent = await spawnSwarmAgent(swarmId, type, task, parentId);
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
success: true,
|
|
56
|
+
agentId: agent.id,
|
|
57
|
+
agentName: agent.name,
|
|
58
|
+
terminalId: agent.terminalId,
|
|
59
|
+
message: `Successfully spawned ${agent.name} to work on: ${task}`,
|
|
60
|
+
};
|
|
61
|
+
} catch (error) {
|
|
62
|
+
return {
|
|
63
|
+
success: false,
|
|
64
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Memory store tool for swarm coordination
|
|
71
|
+
*/
|
|
72
|
+
export const memoryStoreTool: Tool = {
|
|
73
|
+
name: 'memory_store',
|
|
74
|
+
description: 'Store data in the shared swarm memory for coordination',
|
|
75
|
+
inputSchema: {
|
|
76
|
+
type: 'object',
|
|
77
|
+
properties: {
|
|
78
|
+
key: {
|
|
79
|
+
type: 'string',
|
|
80
|
+
description: 'The key to store data under',
|
|
81
|
+
},
|
|
82
|
+
value: {
|
|
83
|
+
type: 'object',
|
|
84
|
+
description: 'The data to store (JSON object)',
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
required: ['key', 'value'],
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Memory retrieve tool for swarm coordination
|
|
93
|
+
*/
|
|
94
|
+
export const memoryRetrieveTool: Tool = {
|
|
95
|
+
name: 'memory_retrieve',
|
|
96
|
+
description: 'Retrieve data from the shared swarm memory',
|
|
97
|
+
inputSchema: {
|
|
98
|
+
type: 'object',
|
|
99
|
+
properties: {
|
|
100
|
+
key: {
|
|
101
|
+
type: 'string',
|
|
102
|
+
description: 'The key to retrieve data from',
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
required: ['key'],
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Swarm status tool
|
|
111
|
+
*/
|
|
112
|
+
export const swarmStatusTool: Tool = {
|
|
113
|
+
name: 'swarm_status',
|
|
114
|
+
description: 'Get the current status of the swarm and all agents',
|
|
115
|
+
inputSchema: {
|
|
116
|
+
type: 'object',
|
|
117
|
+
properties: {},
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Handle swarm status tool execution
|
|
123
|
+
*/
|
|
124
|
+
export async function handleSwarmStatus(args: any): Promise<any> {
|
|
125
|
+
const swarmId = Deno.env.get('CLAUDE_SWARM_ID');
|
|
126
|
+
if (!swarmId) {
|
|
127
|
+
throw new Error('Not running in swarm context');
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const state = getSwarmState(swarmId);
|
|
131
|
+
if (!state) {
|
|
132
|
+
throw new Error('Swarm state not found');
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const agents = Array.from(state.agents.values()).map(agent => ({
|
|
136
|
+
id: agent.id,
|
|
137
|
+
type: agent.type,
|
|
138
|
+
name: agent.name,
|
|
139
|
+
task: agent.task,
|
|
140
|
+
status: agent.status,
|
|
141
|
+
parentId: agent.parentId,
|
|
142
|
+
}));
|
|
143
|
+
|
|
144
|
+
const runtime = Math.floor((Date.now() - state.startTime) / 1000);
|
|
145
|
+
|
|
146
|
+
return {
|
|
147
|
+
swarmId: state.swarmId,
|
|
148
|
+
objective: state.objective,
|
|
149
|
+
runtime: `${runtime}s`,
|
|
150
|
+
totalAgents: agents.length,
|
|
151
|
+
activeAgents: agents.filter(a => a.status === 'active').length,
|
|
152
|
+
completedAgents: agents.filter(a => a.status === 'completed').length,
|
|
153
|
+
failedAgents: agents.filter(a => a.status === 'failed').length,
|
|
154
|
+
agents,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Export all swarm tools
|
|
160
|
+
*/
|
|
161
|
+
export const swarmTools = [
|
|
162
|
+
dispatchAgentTool,
|
|
163
|
+
memoryStoreTool,
|
|
164
|
+
memoryRetrieveTool,
|
|
165
|
+
swarmStatusTool,
|
|
166
|
+
];
|
package/src/utils/helpers.ts
CHANGED
|
@@ -4,6 +4,20 @@
|
|
|
4
4
|
|
|
5
5
|
// Utility helper functions
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Simple calculator function that adds two numbers
|
|
9
|
+
*/
|
|
10
|
+
export function add(a: number, b: number): number {
|
|
11
|
+
return a + b;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Simple hello world function
|
|
16
|
+
*/
|
|
17
|
+
export function helloWorld(): string {
|
|
18
|
+
return "Hello, World!";
|
|
19
|
+
}
|
|
20
|
+
|
|
7
21
|
/**
|
|
8
22
|
* Generates a unique identifier
|
|
9
23
|
*/
|
|
@@ -408,6 +422,34 @@ export interface CircuitBreaker {
|
|
|
408
422
|
reset(): void;
|
|
409
423
|
}
|
|
410
424
|
|
|
425
|
+
/**
|
|
426
|
+
* Simple calculator function with basic operations
|
|
427
|
+
*/
|
|
428
|
+
export function calculator(a: number, b: number, operation: '+' | '-' | '*' | '/' | '^' | '%'): number {
|
|
429
|
+
switch (operation) {
|
|
430
|
+
case '+':
|
|
431
|
+
return a + b;
|
|
432
|
+
case '-':
|
|
433
|
+
return a - b;
|
|
434
|
+
case '*':
|
|
435
|
+
return a * b;
|
|
436
|
+
case '/':
|
|
437
|
+
if (b === 0) {
|
|
438
|
+
throw new Error('Division by zero');
|
|
439
|
+
}
|
|
440
|
+
return a / b;
|
|
441
|
+
case '^':
|
|
442
|
+
return Math.pow(a, b);
|
|
443
|
+
case '%':
|
|
444
|
+
if (b === 0) {
|
|
445
|
+
throw new Error('Modulo by zero');
|
|
446
|
+
}
|
|
447
|
+
return a % b;
|
|
448
|
+
default:
|
|
449
|
+
throw new Error(`Invalid operation: ${operation}`);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
411
453
|
/**
|
|
412
454
|
* Creates a circuit breaker
|
|
413
455
|
*/
|
|
@@ -473,4 +515,5 @@ export function circuitBreaker(
|
|
|
473
515
|
state.state = 'closed';
|
|
474
516
|
},
|
|
475
517
|
};
|
|
476
|
-
}
|
|
518
|
+
}
|
|
519
|
+
|