claude-flow 2.0.0-alpha.71 → 2.0.0-alpha.73
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/.claude/agents/MIGRATION_SUMMARY.md +215 -0
- package/.claude/agents/README.md +82 -0
- package/.claude/agents/analysis/code-review/analyze-code-quality.md +180 -0
- package/.claude/agents/architecture/system-design/arch-system-design.md +156 -0
- package/.claude/agents/base-template-generator.md +42 -0
- package/.claude/agents/consensus/README.md +246 -0
- package/.claude/agents/consensus/byzantine-coordinator.md +63 -0
- package/.claude/agents/consensus/crdt-synchronizer.md +997 -0
- package/.claude/agents/consensus/gossip-coordinator.md +63 -0
- package/.claude/agents/consensus/performance-benchmarker.md +851 -0
- package/.claude/agents/consensus/quorum-manager.md +823 -0
- package/.claude/agents/consensus/raft-manager.md +63 -0
- package/.claude/agents/consensus/security-manager.md +622 -0
- package/.claude/agents/core/coder.md +211 -0
- package/.claude/agents/core/planner.md +116 -0
- package/.claude/agents/core/researcher.md +136 -0
- package/.claude/agents/core/reviewer.md +272 -0
- package/.claude/agents/core/tester.md +266 -0
- package/.claude/agents/data/ml/data-ml-model.md +193 -0
- package/.claude/agents/development/backend/dev-backend-api.md +142 -0
- package/.claude/agents/devops/ci-cd/ops-cicd-github.md +164 -0
- package/.claude/agents/documentation/api-docs/docs-api-openapi.md +174 -0
- package/.claude/agents/github/code-review-swarm.md +538 -0
- package/.claude/agents/github/github-modes.md +173 -0
- package/.claude/agents/github/issue-tracker.md +319 -0
- package/.claude/agents/github/multi-repo-swarm.md +553 -0
- package/.claude/agents/github/pr-manager.md +191 -0
- package/.claude/agents/github/project-board-sync.md +509 -0
- package/.claude/agents/github/release-manager.md +367 -0
- package/.claude/agents/github/release-swarm.md +583 -0
- package/.claude/agents/github/repo-architect.md +398 -0
- package/.claude/agents/github/swarm-issue.md +573 -0
- package/.claude/agents/github/swarm-pr.md +428 -0
- package/.claude/agents/github/sync-coordinator.md +452 -0
- package/.claude/agents/github/workflow-automation.md +635 -0
- package/.claude/agents/hive-mind/collective-intelligence-coordinator.md +82 -0
- package/.claude/agents/hive-mind/consensus-builder.md +102 -0
- package/.claude/agents/hive-mind/swarm-memory-manager.md +120 -0
- package/.claude/agents/optimization/README.md +243 -0
- package/.claude/agents/optimization/benchmark-suite.md +658 -0
- package/.claude/agents/optimization/load-balancer.md +424 -0
- package/.claude/agents/optimization/performance-monitor.md +665 -0
- package/.claude/agents/optimization/resource-allocator.md +667 -0
- package/.claude/agents/optimization/topology-optimizer.md +801 -0
- package/.claude/agents/sparc/architecture.md +472 -0
- package/.claude/agents/sparc/pseudocode.md +318 -0
- package/.claude/agents/sparc/refinement.md +525 -0
- package/.claude/agents/sparc/specification.md +276 -0
- package/.claude/agents/specialized/mobile/spec-mobile-react-native.md +226 -0
- package/.claude/agents/swarm/README.md +183 -0
- package/.claude/agents/swarm/adaptive-coordinator.md +396 -0
- package/.claude/agents/swarm/hierarchical-coordinator.md +256 -0
- package/.claude/agents/swarm/mesh-coordinator.md +392 -0
- package/.claude/agents/templates/automation-smart-agent.md +205 -0
- package/.claude/agents/templates/coordinator-swarm-init.md +90 -0
- package/.claude/agents/templates/github-pr-manager.md +177 -0
- package/.claude/agents/templates/implementer-sparc-coder.md +259 -0
- package/.claude/agents/templates/memory-coordinator.md +187 -0
- package/.claude/agents/templates/migration-plan.md +746 -0
- package/.claude/agents/templates/orchestrator-task.md +139 -0
- package/.claude/agents/templates/performance-analyzer.md +199 -0
- package/.claude/agents/templates/sparc-coordinator.md +183 -0
- package/.claude/agents/testing/unit/tdd-london-swarm.md +244 -0
- package/.claude/agents/testing/validation/production-validator.md +395 -0
- package/.claude/settings.json +20 -0
- package/.claude/settings.local.json +5 -1
- package/bin/claude-flow +1 -1
- package/package.json +1 -1
- package/src/cli/help-text.js +2 -2
- package/src/cli/simple-cli.js +1 -1
- package/src/cli/simple-commands/init/agent-copier.js +217 -0
- package/src/cli/simple-commands/init/index.js +23 -0
- package/src/cli/simple-commands/init/templates/CLAUDE.md +293 -14
- package/src/cli/simple-commands/init/templates/settings.json +24 -4
- package/src/swarm/advanced-orchestrator.ts +1200 -0
- package/src/swarm/claude-code-interface.ts +1268 -0
- package/src/swarm/hive-mind-integration.ts +1127 -0
- package/src/swarm/mcp-integration-wrapper.ts +860 -0
- package/src/swarm/result-aggregator.ts +1046 -0
|
@@ -0,0 +1,1268 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Code Coordination Interface
|
|
3
|
+
*
|
|
4
|
+
* This module provides the interface layer for coordinating with Claude Code
|
|
5
|
+
* instances, managing agent spawning through the claude CLI, handling process
|
|
6
|
+
* lifecycle, and enabling seamless communication between the swarm system
|
|
7
|
+
* and individual Claude agents.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { EventEmitter } from 'node:events';
|
|
11
|
+
import { spawn, ChildProcess } from 'node:child_process';
|
|
12
|
+
import { performance } from 'node:perf_hooks';
|
|
13
|
+
import * as path from 'node:path';
|
|
14
|
+
import * as fs from 'node:fs/promises';
|
|
15
|
+
import { Logger } from '../core/logger.js';
|
|
16
|
+
import { generateId } from '../utils/helpers.js';
|
|
17
|
+
import { MemoryManager } from '../memory/manager.js';
|
|
18
|
+
import TaskExecutor, {
|
|
19
|
+
ClaudeExecutionOptions,
|
|
20
|
+
ExecutionResult,
|
|
21
|
+
ExecutionContext
|
|
22
|
+
} from './executor.js';
|
|
23
|
+
import {
|
|
24
|
+
SwarmAgent,
|
|
25
|
+
SwarmTask,
|
|
26
|
+
TaskDefinition,
|
|
27
|
+
AgentState,
|
|
28
|
+
AgentCapabilities,
|
|
29
|
+
TaskResult,
|
|
30
|
+
SwarmExecutionContext,
|
|
31
|
+
} from './types.js';
|
|
32
|
+
|
|
33
|
+
export interface ClaudeCodeConfig {
|
|
34
|
+
claudeExecutablePath: string;
|
|
35
|
+
defaultModel: string;
|
|
36
|
+
maxTokens: number;
|
|
37
|
+
temperature: number;
|
|
38
|
+
timeout: number;
|
|
39
|
+
maxConcurrentAgents: number;
|
|
40
|
+
enableStreaming: boolean;
|
|
41
|
+
enableLogging: boolean;
|
|
42
|
+
workingDirectory: string;
|
|
43
|
+
environmentVariables: Record<string, string>;
|
|
44
|
+
agentPoolSize: number;
|
|
45
|
+
processRecycling: boolean;
|
|
46
|
+
healthCheckInterval: number;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface ClaudeAgent {
|
|
50
|
+
id: string;
|
|
51
|
+
processId: number;
|
|
52
|
+
process: ChildProcess;
|
|
53
|
+
type: string;
|
|
54
|
+
capabilities: string[];
|
|
55
|
+
status: 'initializing' | 'idle' | 'busy' | 'error' | 'terminated';
|
|
56
|
+
currentTask?: string;
|
|
57
|
+
spawnedAt: Date;
|
|
58
|
+
lastActivity: Date;
|
|
59
|
+
totalTasks: number;
|
|
60
|
+
totalDuration: number;
|
|
61
|
+
metrics: ClaudeAgentMetrics;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface ClaudeAgentMetrics {
|
|
65
|
+
tasksCompleted: number;
|
|
66
|
+
tasksFailed: number;
|
|
67
|
+
averageResponseTime: number;
|
|
68
|
+
totalTokensUsed: number;
|
|
69
|
+
memoryUsage: number;
|
|
70
|
+
cpuUsage: number;
|
|
71
|
+
errorRate: number;
|
|
72
|
+
successRate: number;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface ClaudeTaskExecution {
|
|
76
|
+
id: string;
|
|
77
|
+
taskId: string;
|
|
78
|
+
agentId: string;
|
|
79
|
+
startTime: Date;
|
|
80
|
+
endTime?: Date;
|
|
81
|
+
status: 'queued' | 'running' | 'completed' | 'failed' | 'cancelled';
|
|
82
|
+
input: any;
|
|
83
|
+
output?: any;
|
|
84
|
+
error?: string;
|
|
85
|
+
duration?: number;
|
|
86
|
+
tokensUsed?: number;
|
|
87
|
+
retryCount: number;
|
|
88
|
+
maxRetries: number;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export interface ClaudeSpawnOptions {
|
|
92
|
+
type: string;
|
|
93
|
+
name?: string;
|
|
94
|
+
capabilities?: string[];
|
|
95
|
+
systemPrompt?: string;
|
|
96
|
+
model?: string;
|
|
97
|
+
maxTokens?: number;
|
|
98
|
+
temperature?: number;
|
|
99
|
+
workingDirectory?: string;
|
|
100
|
+
environment?: Record<string, string>;
|
|
101
|
+
tools?: string[];
|
|
102
|
+
priority?: number;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export interface ProcessPool {
|
|
106
|
+
idle: ClaudeAgent[];
|
|
107
|
+
busy: ClaudeAgent[];
|
|
108
|
+
failed: ClaudeAgent[];
|
|
109
|
+
totalSpawned: number;
|
|
110
|
+
totalTerminated: number;
|
|
111
|
+
recyclingEnabled: boolean;
|
|
112
|
+
maxAge: number;
|
|
113
|
+
maxTasks: number;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export class ClaudeCodeInterface extends EventEmitter {
|
|
117
|
+
private logger: Logger;
|
|
118
|
+
private config: ClaudeCodeConfig;
|
|
119
|
+
private memoryManager: MemoryManager;
|
|
120
|
+
private processPool: ProcessPool;
|
|
121
|
+
private activeExecutions: Map<string, ClaudeTaskExecution> = new Map();
|
|
122
|
+
private agents: Map<string, ClaudeAgent> = new Map();
|
|
123
|
+
private taskExecutor: TaskExecutor;
|
|
124
|
+
private healthCheckInterval?: NodeJS.Timeout;
|
|
125
|
+
private isInitialized: boolean = false;
|
|
126
|
+
|
|
127
|
+
constructor(
|
|
128
|
+
config: Partial<ClaudeCodeConfig> = {},
|
|
129
|
+
memoryManager: MemoryManager
|
|
130
|
+
) {
|
|
131
|
+
super();
|
|
132
|
+
|
|
133
|
+
this.logger = new Logger('ClaudeCodeInterface');
|
|
134
|
+
this.config = this.createDefaultConfig(config);
|
|
135
|
+
this.memoryManager = memoryManager;
|
|
136
|
+
this.processPool = this.initializeProcessPool();
|
|
137
|
+
|
|
138
|
+
this.taskExecutor = new TaskExecutor({
|
|
139
|
+
timeoutMs: this.config.timeout,
|
|
140
|
+
enableMetrics: true,
|
|
141
|
+
captureOutput: true,
|
|
142
|
+
streamOutput: this.config.enableStreaming,
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
this.setupEventHandlers();
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Initialize the Claude Code interface
|
|
150
|
+
*/
|
|
151
|
+
async initialize(): Promise<void> {
|
|
152
|
+
if (this.isInitialized) {
|
|
153
|
+
this.logger.warn('Claude Code interface already initialized');
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
this.logger.info('Initializing Claude Code interface...');
|
|
158
|
+
|
|
159
|
+
try {
|
|
160
|
+
// Verify Claude executable exists
|
|
161
|
+
await this.verifyClaudeExecutable();
|
|
162
|
+
|
|
163
|
+
// Initialize task executor
|
|
164
|
+
await this.taskExecutor.initialize();
|
|
165
|
+
|
|
166
|
+
// Pre-warm agent pool if configured
|
|
167
|
+
if (this.config.agentPoolSize > 0) {
|
|
168
|
+
await this.prewarmAgentPool();
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Start health checks
|
|
172
|
+
this.startHealthChecks();
|
|
173
|
+
|
|
174
|
+
this.isInitialized = true;
|
|
175
|
+
this.logger.info('Claude Code interface initialized successfully', {
|
|
176
|
+
poolSize: this.processPool.idle.length,
|
|
177
|
+
maxConcurrent: this.config.maxConcurrentAgents,
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
this.emit('initialized');
|
|
181
|
+
|
|
182
|
+
} catch (error) {
|
|
183
|
+
this.logger.error('Failed to initialize Claude Code interface', error);
|
|
184
|
+
throw error;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Shutdown the interface gracefully
|
|
190
|
+
*/
|
|
191
|
+
async shutdown(): Promise<void> {
|
|
192
|
+
if (!this.isInitialized) return;
|
|
193
|
+
|
|
194
|
+
this.logger.info('Shutting down Claude Code interface...');
|
|
195
|
+
|
|
196
|
+
try {
|
|
197
|
+
// Stop health checks
|
|
198
|
+
if (this.healthCheckInterval) {
|
|
199
|
+
clearInterval(this.healthCheckInterval);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Cancel active executions
|
|
203
|
+
const cancellationPromises = Array.from(this.activeExecutions.keys())
|
|
204
|
+
.map(executionId => this.cancelExecution(executionId, 'Interface shutdown'));
|
|
205
|
+
|
|
206
|
+
await Promise.allSettled(cancellationPromises);
|
|
207
|
+
|
|
208
|
+
// Terminate all agents
|
|
209
|
+
await this.terminateAllAgents();
|
|
210
|
+
|
|
211
|
+
// Shutdown task executor
|
|
212
|
+
await this.taskExecutor.shutdown();
|
|
213
|
+
|
|
214
|
+
this.isInitialized = false;
|
|
215
|
+
this.logger.info('Claude Code interface shut down successfully');
|
|
216
|
+
this.emit('shutdown');
|
|
217
|
+
|
|
218
|
+
} catch (error) {
|
|
219
|
+
this.logger.error('Error during Claude Code interface shutdown', error);
|
|
220
|
+
throw error;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Spawn a new Claude agent with specified configuration
|
|
226
|
+
*/
|
|
227
|
+
async spawnAgent(options: ClaudeSpawnOptions): Promise<string> {
|
|
228
|
+
this.logger.info('Spawning Claude agent', {
|
|
229
|
+
type: options.type,
|
|
230
|
+
name: options.name,
|
|
231
|
+
capabilities: options.capabilities,
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
try {
|
|
235
|
+
// Check if we can spawn more agents
|
|
236
|
+
if (this.getTotalActiveAgents() >= this.config.maxConcurrentAgents) {
|
|
237
|
+
throw new Error('Maximum concurrent agents limit reached');
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Build Claude command
|
|
241
|
+
const command = this.buildClaudeCommand(options);
|
|
242
|
+
|
|
243
|
+
// Spawn process
|
|
244
|
+
const process = spawn(command.executable, command.args, {
|
|
245
|
+
cwd: options.workingDirectory || this.config.workingDirectory,
|
|
246
|
+
env: {
|
|
247
|
+
...process.env,
|
|
248
|
+
...this.config.environmentVariables,
|
|
249
|
+
...options.environment,
|
|
250
|
+
},
|
|
251
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
252
|
+
detached: false,
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
if (!process.pid) {
|
|
256
|
+
throw new Error('Failed to spawn Claude process');
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Create agent record
|
|
260
|
+
const agentId = generateId('claude-agent');
|
|
261
|
+
const agent: ClaudeAgent = {
|
|
262
|
+
id: agentId,
|
|
263
|
+
processId: process.pid,
|
|
264
|
+
process,
|
|
265
|
+
type: options.type,
|
|
266
|
+
capabilities: options.capabilities || [],
|
|
267
|
+
status: 'initializing',
|
|
268
|
+
spawnedAt: new Date(),
|
|
269
|
+
lastActivity: new Date(),
|
|
270
|
+
totalTasks: 0,
|
|
271
|
+
totalDuration: 0,
|
|
272
|
+
metrics: this.initializeAgentMetrics(),
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
this.agents.set(agentId, agent);
|
|
276
|
+
this.processPool.idle.push(agent);
|
|
277
|
+
this.processPool.totalSpawned++;
|
|
278
|
+
|
|
279
|
+
// Setup process event handlers
|
|
280
|
+
this.setupProcessEventHandlers(agent);
|
|
281
|
+
|
|
282
|
+
// Wait for agent to be ready
|
|
283
|
+
await this.waitForAgentReady(agent);
|
|
284
|
+
|
|
285
|
+
agent.status = 'idle';
|
|
286
|
+
agent.lastActivity = new Date();
|
|
287
|
+
|
|
288
|
+
this.logger.info('Claude agent spawned successfully', {
|
|
289
|
+
agentId,
|
|
290
|
+
processId: process.pid,
|
|
291
|
+
type: options.type,
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
this.emit('agent:spawned', {
|
|
295
|
+
agentId,
|
|
296
|
+
type: options.type,
|
|
297
|
+
processId: process.pid,
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
return agentId;
|
|
301
|
+
|
|
302
|
+
} catch (error) {
|
|
303
|
+
this.logger.error('Failed to spawn Claude agent', {
|
|
304
|
+
type: options.type,
|
|
305
|
+
error: error instanceof Error ? error.message : String(error),
|
|
306
|
+
});
|
|
307
|
+
throw error;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Execute a task using a Claude agent
|
|
313
|
+
*/
|
|
314
|
+
async executeTask(
|
|
315
|
+
taskDefinition: TaskDefinition,
|
|
316
|
+
agentId?: string,
|
|
317
|
+
options: Partial<ClaudeExecutionOptions> = {}
|
|
318
|
+
): Promise<ClaudeTaskExecution> {
|
|
319
|
+
const executionId = generateId('claude-execution');
|
|
320
|
+
|
|
321
|
+
this.logger.info('Executing task with Claude agent', {
|
|
322
|
+
executionId,
|
|
323
|
+
taskId: taskDefinition.id.id,
|
|
324
|
+
agentId,
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
try {
|
|
328
|
+
// Get or select agent
|
|
329
|
+
const agent = agentId ? this.agents.get(agentId) : await this.selectOptimalAgent(taskDefinition);
|
|
330
|
+
|
|
331
|
+
if (!agent) {
|
|
332
|
+
throw new Error(agentId ? `Agent not found: ${agentId}` : 'No suitable agent available');
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
if (agent.status !== 'idle') {
|
|
336
|
+
throw new Error(`Agent ${agent.id} is not available (status: ${agent.status})`);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// Create execution record
|
|
340
|
+
const execution: ClaudeTaskExecution = {
|
|
341
|
+
id: executionId,
|
|
342
|
+
taskId: taskDefinition.id.id,
|
|
343
|
+
agentId: agent.id,
|
|
344
|
+
startTime: new Date(),
|
|
345
|
+
status: 'queued',
|
|
346
|
+
input: {
|
|
347
|
+
task: taskDefinition,
|
|
348
|
+
options,
|
|
349
|
+
},
|
|
350
|
+
retryCount: 0,
|
|
351
|
+
maxRetries: options.maxRetries || 3,
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
this.activeExecutions.set(executionId, execution);
|
|
355
|
+
|
|
356
|
+
// Update agent status
|
|
357
|
+
agent.status = 'busy';
|
|
358
|
+
agent.currentTask = executionId;
|
|
359
|
+
agent.lastActivity = new Date();
|
|
360
|
+
|
|
361
|
+
// Move agent from idle to busy pool
|
|
362
|
+
this.moveAgentToBusyPool(agent);
|
|
363
|
+
|
|
364
|
+
// Execute task
|
|
365
|
+
execution.status = 'running';
|
|
366
|
+
const result = await this.executeTaskWithAgent(agent, taskDefinition, options);
|
|
367
|
+
|
|
368
|
+
// Update execution record
|
|
369
|
+
execution.endTime = new Date();
|
|
370
|
+
execution.duration = execution.endTime.getTime() - execution.startTime.getTime();
|
|
371
|
+
execution.output = result.result;
|
|
372
|
+
execution.tokensUsed = result.metadata?.tokensUsed;
|
|
373
|
+
|
|
374
|
+
if (result.success) {
|
|
375
|
+
execution.status = 'completed';
|
|
376
|
+
agent.metrics.tasksCompleted++;
|
|
377
|
+
} else {
|
|
378
|
+
execution.status = 'failed';
|
|
379
|
+
execution.error = result.error;
|
|
380
|
+
agent.metrics.tasksFailed++;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// Update agent metrics
|
|
384
|
+
this.updateAgentMetrics(agent, execution);
|
|
385
|
+
|
|
386
|
+
// Return agent to idle pool
|
|
387
|
+
this.returnAgentToIdlePool(agent);
|
|
388
|
+
|
|
389
|
+
this.logger.info('Task execution completed', {
|
|
390
|
+
executionId,
|
|
391
|
+
success: result.success,
|
|
392
|
+
duration: execution.duration,
|
|
393
|
+
tokensUsed: execution.tokensUsed,
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
this.emit('task:completed', {
|
|
397
|
+
executionId,
|
|
398
|
+
taskId: taskDefinition.id.id,
|
|
399
|
+
agentId: agent.id,
|
|
400
|
+
success: result.success,
|
|
401
|
+
duration: execution.duration,
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
return execution;
|
|
405
|
+
|
|
406
|
+
} catch (error) {
|
|
407
|
+
const execution = this.activeExecutions.get(executionId);
|
|
408
|
+
if (execution) {
|
|
409
|
+
execution.status = 'failed';
|
|
410
|
+
execution.error = error instanceof Error ? error.message : String(error);
|
|
411
|
+
execution.endTime = new Date();
|
|
412
|
+
execution.duration = execution.endTime.getTime() - execution.startTime.getTime();
|
|
413
|
+
|
|
414
|
+
// Return agent to pool if it was assigned
|
|
415
|
+
const agent = this.agents.get(execution.agentId);
|
|
416
|
+
if (agent) {
|
|
417
|
+
this.returnAgentToIdlePool(agent);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
this.logger.error('Task execution failed', {
|
|
422
|
+
executionId,
|
|
423
|
+
error: error instanceof Error ? error.message : String(error),
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
throw error;
|
|
427
|
+
} finally {
|
|
428
|
+
this.activeExecutions.delete(executionId);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* Cancel a running task execution
|
|
434
|
+
*/
|
|
435
|
+
async cancelExecution(executionId: string, reason: string): Promise<void> {
|
|
436
|
+
const execution = this.activeExecutions.get(executionId);
|
|
437
|
+
if (!execution) {
|
|
438
|
+
throw new Error(`Execution not found: ${executionId}`);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
this.logger.info('Cancelling task execution', {
|
|
442
|
+
executionId,
|
|
443
|
+
reason,
|
|
444
|
+
taskId: execution.taskId,
|
|
445
|
+
agentId: execution.agentId,
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
try {
|
|
449
|
+
execution.status = 'cancelled';
|
|
450
|
+
execution.error = reason;
|
|
451
|
+
execution.endTime = new Date();
|
|
452
|
+
execution.duration = execution.endTime.getTime() - execution.startTime.getTime();
|
|
453
|
+
|
|
454
|
+
// Cancel agent task if running
|
|
455
|
+
const agent = this.agents.get(execution.agentId);
|
|
456
|
+
if (agent && agent.currentTask === executionId) {
|
|
457
|
+
await this.cancelAgentTask(agent);
|
|
458
|
+
this.returnAgentToIdlePool(agent);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
this.emit('task:cancelled', {
|
|
462
|
+
executionId,
|
|
463
|
+
reason,
|
|
464
|
+
taskId: execution.taskId,
|
|
465
|
+
agentId: execution.agentId,
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
} finally {
|
|
469
|
+
this.activeExecutions.delete(executionId);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* Terminate a specific agent
|
|
475
|
+
*/
|
|
476
|
+
async terminateAgent(agentId: string, reason: string = 'Manual termination'): Promise<void> {
|
|
477
|
+
const agent = this.agents.get(agentId);
|
|
478
|
+
if (!agent) {
|
|
479
|
+
throw new Error(`Agent not found: ${agentId}`);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
this.logger.info('Terminating Claude agent', {
|
|
483
|
+
agentId,
|
|
484
|
+
processId: agent.processId,
|
|
485
|
+
reason,
|
|
486
|
+
});
|
|
487
|
+
|
|
488
|
+
try {
|
|
489
|
+
// Cancel current task if any
|
|
490
|
+
if (agent.currentTask) {
|
|
491
|
+
await this.cancelExecution(agent.currentTask, 'Agent termination');
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
// Update status
|
|
495
|
+
agent.status = 'terminated';
|
|
496
|
+
|
|
497
|
+
// Terminate process
|
|
498
|
+
await this.terminateProcess(agent.process);
|
|
499
|
+
|
|
500
|
+
// Remove from pools and agents map
|
|
501
|
+
this.removeAgentFromPools(agent);
|
|
502
|
+
this.agents.delete(agentId);
|
|
503
|
+
this.processPool.totalTerminated++;
|
|
504
|
+
|
|
505
|
+
this.logger.info('Claude agent terminated successfully', {
|
|
506
|
+
agentId,
|
|
507
|
+
reason,
|
|
508
|
+
totalTasks: agent.totalTasks,
|
|
509
|
+
totalDuration: agent.totalDuration,
|
|
510
|
+
});
|
|
511
|
+
|
|
512
|
+
this.emit('agent:terminated', {
|
|
513
|
+
agentId,
|
|
514
|
+
reason,
|
|
515
|
+
metrics: agent.metrics,
|
|
516
|
+
});
|
|
517
|
+
|
|
518
|
+
} catch (error) {
|
|
519
|
+
this.logger.error('Error terminating agent', {
|
|
520
|
+
agentId,
|
|
521
|
+
error: error instanceof Error ? error.message : String(error),
|
|
522
|
+
});
|
|
523
|
+
throw error;
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* Get agent status and metrics
|
|
529
|
+
*/
|
|
530
|
+
getAgentStatus(agentId: string): ClaudeAgent | null {
|
|
531
|
+
return this.agents.get(agentId) || null;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
/**
|
|
535
|
+
* Get all active agents
|
|
536
|
+
*/
|
|
537
|
+
getAllAgents(): ClaudeAgent[] {
|
|
538
|
+
return Array.from(this.agents.values());
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
/**
|
|
542
|
+
* Get execution status
|
|
543
|
+
*/
|
|
544
|
+
getExecutionStatus(executionId: string): ClaudeTaskExecution | null {
|
|
545
|
+
return this.activeExecutions.get(executionId) || null;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
/**
|
|
549
|
+
* Get comprehensive interface metrics
|
|
550
|
+
*/
|
|
551
|
+
getInterfaceMetrics(): {
|
|
552
|
+
agents: {
|
|
553
|
+
total: number;
|
|
554
|
+
idle: number;
|
|
555
|
+
busy: number;
|
|
556
|
+
failed: number;
|
|
557
|
+
terminated: number;
|
|
558
|
+
};
|
|
559
|
+
executions: {
|
|
560
|
+
active: number;
|
|
561
|
+
completed: number;
|
|
562
|
+
failed: number;
|
|
563
|
+
cancelled: number;
|
|
564
|
+
};
|
|
565
|
+
performance: {
|
|
566
|
+
averageResponseTime: number;
|
|
567
|
+
totalTokensUsed: number;
|
|
568
|
+
successRate: number;
|
|
569
|
+
throughput: number;
|
|
570
|
+
};
|
|
571
|
+
pool: {
|
|
572
|
+
totalSpawned: number;
|
|
573
|
+
totalTerminated: number;
|
|
574
|
+
recyclingEnabled: boolean;
|
|
575
|
+
poolUtilization: number;
|
|
576
|
+
};
|
|
577
|
+
} {
|
|
578
|
+
const agents = Array.from(this.agents.values());
|
|
579
|
+
const executions = Array.from(this.activeExecutions.values());
|
|
580
|
+
|
|
581
|
+
const totalCompleted = agents.reduce((sum, a) => sum + a.metrics.tasksCompleted, 0);
|
|
582
|
+
const totalFailed = agents.reduce((sum, a) => sum + a.metrics.tasksFailed, 0);
|
|
583
|
+
const totalTokens = agents.reduce((sum, a) => sum + a.metrics.totalTokensUsed, 0);
|
|
584
|
+
const avgResponseTime = agents.length > 0
|
|
585
|
+
? agents.reduce((sum, a) => sum + a.metrics.averageResponseTime, 0) / agents.length
|
|
586
|
+
: 0;
|
|
587
|
+
|
|
588
|
+
return {
|
|
589
|
+
agents: {
|
|
590
|
+
total: agents.length,
|
|
591
|
+
idle: this.processPool.idle.length,
|
|
592
|
+
busy: this.processPool.busy.length,
|
|
593
|
+
failed: this.processPool.failed.length,
|
|
594
|
+
terminated: this.processPool.totalTerminated,
|
|
595
|
+
},
|
|
596
|
+
executions: {
|
|
597
|
+
active: executions.filter(e => e.status === 'running').length,
|
|
598
|
+
completed: totalCompleted,
|
|
599
|
+
failed: totalFailed,
|
|
600
|
+
cancelled: executions.filter(e => e.status === 'cancelled').length,
|
|
601
|
+
},
|
|
602
|
+
performance: {
|
|
603
|
+
averageResponseTime: avgResponseTime,
|
|
604
|
+
totalTokensUsed: totalTokens,
|
|
605
|
+
successRate: totalCompleted + totalFailed > 0 ? totalCompleted / (totalCompleted + totalFailed) : 0,
|
|
606
|
+
throughput: this.calculateThroughput(),
|
|
607
|
+
},
|
|
608
|
+
pool: {
|
|
609
|
+
totalSpawned: this.processPool.totalSpawned,
|
|
610
|
+
totalTerminated: this.processPool.totalTerminated,
|
|
611
|
+
recyclingEnabled: this.processPool.recyclingEnabled,
|
|
612
|
+
poolUtilization: this.calculatePoolUtilization(),
|
|
613
|
+
},
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
// Private methods
|
|
618
|
+
|
|
619
|
+
private async verifyClaudeExecutable(): Promise<void> {
|
|
620
|
+
try {
|
|
621
|
+
const { spawn } = await import('node:child_process');
|
|
622
|
+
const process = spawn(this.config.claudeExecutablePath, ['--version'], {
|
|
623
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
624
|
+
});
|
|
625
|
+
|
|
626
|
+
return new Promise((resolve, reject) => {
|
|
627
|
+
let output = '';
|
|
628
|
+
|
|
629
|
+
process.stdout?.on('data', (data) => {
|
|
630
|
+
output += data.toString();
|
|
631
|
+
});
|
|
632
|
+
|
|
633
|
+
process.on('close', (code) => {
|
|
634
|
+
if (code === 0) {
|
|
635
|
+
this.logger.info('Claude executable verified', {
|
|
636
|
+
path: this.config.claudeExecutablePath,
|
|
637
|
+
version: output.trim(),
|
|
638
|
+
});
|
|
639
|
+
resolve();
|
|
640
|
+
} else {
|
|
641
|
+
reject(new Error(`Claude executable verification failed with code ${code}`));
|
|
642
|
+
}
|
|
643
|
+
});
|
|
644
|
+
|
|
645
|
+
process.on('error', reject);
|
|
646
|
+
});
|
|
647
|
+
|
|
648
|
+
} catch (error) {
|
|
649
|
+
throw new Error(`Claude executable not found: ${this.config.claudeExecutablePath}`);
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
private async prewarmAgentPool(): Promise<void> {
|
|
654
|
+
this.logger.info('Pre-warming agent pool', {
|
|
655
|
+
targetSize: this.config.agentPoolSize,
|
|
656
|
+
});
|
|
657
|
+
|
|
658
|
+
const promises: Promise<string>[] = [];
|
|
659
|
+
|
|
660
|
+
for (let i = 0; i < this.config.agentPoolSize; i++) {
|
|
661
|
+
promises.push(this.spawnAgent({
|
|
662
|
+
type: 'general',
|
|
663
|
+
name: `pool-agent-${i}`,
|
|
664
|
+
capabilities: ['general'],
|
|
665
|
+
}));
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
const results = await Promise.allSettled(promises);
|
|
669
|
+
const successful = results.filter(r => r.status === 'fulfilled').length;
|
|
670
|
+
const failed = results.filter(r => r.status === 'rejected').length;
|
|
671
|
+
|
|
672
|
+
this.logger.info('Agent pool pre-warming completed', {
|
|
673
|
+
successful,
|
|
674
|
+
failed,
|
|
675
|
+
targetSize: this.config.agentPoolSize,
|
|
676
|
+
});
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
private buildClaudeCommand(options: ClaudeSpawnOptions): {
|
|
680
|
+
executable: string;
|
|
681
|
+
args: string[];
|
|
682
|
+
} {
|
|
683
|
+
const args: string[] = [];
|
|
684
|
+
|
|
685
|
+
// Add model
|
|
686
|
+
args.push('--model', options.model || this.config.defaultModel);
|
|
687
|
+
|
|
688
|
+
// Add max tokens
|
|
689
|
+
args.push('--max-tokens', String(options.maxTokens || this.config.maxTokens));
|
|
690
|
+
|
|
691
|
+
// Add temperature
|
|
692
|
+
args.push('--temperature', String(options.temperature || this.config.temperature));
|
|
693
|
+
|
|
694
|
+
// Add system prompt if provided
|
|
695
|
+
if (options.systemPrompt) {
|
|
696
|
+
args.push('--system', options.systemPrompt);
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
// Add tools if specified
|
|
700
|
+
if (options.tools && options.tools.length > 0) {
|
|
701
|
+
args.push('--allowedTools', options.tools.join(','));
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
// Enable streaming if configured
|
|
705
|
+
if (this.config.enableStreaming) {
|
|
706
|
+
args.push('--stream');
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
// Skip permissions for swarm execution
|
|
710
|
+
args.push('--dangerously-skip-permissions');
|
|
711
|
+
|
|
712
|
+
return {
|
|
713
|
+
executable: this.config.claudeExecutablePath,
|
|
714
|
+
args,
|
|
715
|
+
};
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
private setupProcessEventHandlers(agent: ClaudeAgent): void {
|
|
719
|
+
const { process } = agent;
|
|
720
|
+
|
|
721
|
+
process.on('exit', (code, signal) => {
|
|
722
|
+
this.logger.info('Claude agent process exited', {
|
|
723
|
+
agentId: agent.id,
|
|
724
|
+
processId: agent.processId,
|
|
725
|
+
code,
|
|
726
|
+
signal,
|
|
727
|
+
});
|
|
728
|
+
|
|
729
|
+
if (agent.status !== 'terminated') {
|
|
730
|
+
agent.status = 'error';
|
|
731
|
+
this.moveAgentToFailedPool(agent);
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
this.emit('agent:exited', {
|
|
735
|
+
agentId: agent.id,
|
|
736
|
+
code,
|
|
737
|
+
signal,
|
|
738
|
+
});
|
|
739
|
+
});
|
|
740
|
+
|
|
741
|
+
process.on('error', (error) => {
|
|
742
|
+
this.logger.error('Claude agent process error', {
|
|
743
|
+
agentId: agent.id,
|
|
744
|
+
processId: agent.processId,
|
|
745
|
+
error: error.message,
|
|
746
|
+
});
|
|
747
|
+
|
|
748
|
+
agent.status = 'error';
|
|
749
|
+
this.moveAgentToFailedPool(agent);
|
|
750
|
+
|
|
751
|
+
this.emit('agent:error', {
|
|
752
|
+
agentId: agent.id,
|
|
753
|
+
error: error.message,
|
|
754
|
+
});
|
|
755
|
+
});
|
|
756
|
+
|
|
757
|
+
// Handle stdout/stderr if needed
|
|
758
|
+
if (this.config.enableLogging) {
|
|
759
|
+
process.stdout?.on('data', (data) => {
|
|
760
|
+
this.logger.debug('Agent stdout', {
|
|
761
|
+
agentId: agent.id,
|
|
762
|
+
data: data.toString().trim(),
|
|
763
|
+
});
|
|
764
|
+
});
|
|
765
|
+
|
|
766
|
+
process.stderr?.on('data', (data) => {
|
|
767
|
+
this.logger.debug('Agent stderr', {
|
|
768
|
+
agentId: agent.id,
|
|
769
|
+
data: data.toString().trim(),
|
|
770
|
+
});
|
|
771
|
+
});
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
private async waitForAgentReady(agent: ClaudeAgent, timeout: number = 30000): Promise<void> {
|
|
776
|
+
return new Promise((resolve, reject) => {
|
|
777
|
+
const startTime = Date.now();
|
|
778
|
+
const checkInterval = 1000; // 1 second
|
|
779
|
+
|
|
780
|
+
const checkReady = () => {
|
|
781
|
+
const elapsed = Date.now() - startTime;
|
|
782
|
+
|
|
783
|
+
if (elapsed > timeout) {
|
|
784
|
+
reject(new Error(`Agent ${agent.id} failed to become ready within ${timeout}ms`));
|
|
785
|
+
return;
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
// Check if process is still running
|
|
789
|
+
if (agent.process.killed || agent.process.exitCode !== null) {
|
|
790
|
+
reject(new Error(`Agent ${agent.id} process terminated during initialization`));
|
|
791
|
+
return;
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
// For now, assume agent is ready after a short delay
|
|
795
|
+
// In a real implementation, you might check for specific output or response
|
|
796
|
+
if (elapsed > 2000) { // 2 seconds
|
|
797
|
+
resolve();
|
|
798
|
+
} else {
|
|
799
|
+
setTimeout(checkReady, checkInterval);
|
|
800
|
+
}
|
|
801
|
+
};
|
|
802
|
+
|
|
803
|
+
checkReady();
|
|
804
|
+
});
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
private async selectOptimalAgent(taskDefinition: TaskDefinition): Promise<ClaudeAgent | null> {
|
|
808
|
+
const availableAgents = this.processPool.idle.filter(agent => agent.status === 'idle');
|
|
809
|
+
|
|
810
|
+
if (availableAgents.length === 0) {
|
|
811
|
+
// Try to spawn a new agent if under limit
|
|
812
|
+
if (this.getTotalActiveAgents() < this.config.maxConcurrentAgents) {
|
|
813
|
+
const agentId = await this.spawnAgent({
|
|
814
|
+
type: 'task-specific',
|
|
815
|
+
capabilities: taskDefinition.requirements.capabilities,
|
|
816
|
+
});
|
|
817
|
+
return this.agents.get(agentId) || null;
|
|
818
|
+
}
|
|
819
|
+
return null;
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
// Select agent based on capabilities and performance
|
|
823
|
+
const scoredAgents = availableAgents.map(agent => ({
|
|
824
|
+
agent,
|
|
825
|
+
score: this.calculateAgentScore(agent, taskDefinition),
|
|
826
|
+
}));
|
|
827
|
+
|
|
828
|
+
scoredAgents.sort((a, b) => b.score - a.score);
|
|
829
|
+
return scoredAgents[0].agent;
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
private calculateAgentScore(agent: ClaudeAgent, taskDefinition: TaskDefinition): number {
|
|
833
|
+
let score = 0;
|
|
834
|
+
|
|
835
|
+
// Capability match
|
|
836
|
+
const requiredCapabilities = taskDefinition.requirements.capabilities;
|
|
837
|
+
const matchingCapabilities = agent.capabilities.filter(cap =>
|
|
838
|
+
requiredCapabilities.includes(cap)
|
|
839
|
+
);
|
|
840
|
+
score += (matchingCapabilities.length / requiredCapabilities.length) * 100;
|
|
841
|
+
|
|
842
|
+
// Performance metrics
|
|
843
|
+
score += agent.metrics.successRate * 50;
|
|
844
|
+
score += Math.max(0, 50 - agent.metrics.averageResponseTime / 1000) * 10; // Prefer faster agents
|
|
845
|
+
|
|
846
|
+
// Load balancing - prefer agents with fewer completed tasks
|
|
847
|
+
const maxTasks = Math.max(...this.processPool.idle.map(a => a.totalTasks), 1);
|
|
848
|
+
score += (1 - agent.totalTasks / maxTasks) * 20;
|
|
849
|
+
|
|
850
|
+
return score;
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
private async executeTaskWithAgent(
|
|
854
|
+
agent: ClaudeAgent,
|
|
855
|
+
taskDefinition: TaskDefinition,
|
|
856
|
+
options: Partial<ClaudeExecutionOptions>
|
|
857
|
+
): Promise<ExecutionResult> {
|
|
858
|
+
const startTime = performance.now();
|
|
859
|
+
|
|
860
|
+
try {
|
|
861
|
+
// Create execution context for the agent
|
|
862
|
+
const context: ExecutionContext = {
|
|
863
|
+
task: taskDefinition,
|
|
864
|
+
agent: this.convertToAgentState(agent),
|
|
865
|
+
workingDirectory: options.workingDirectory || this.config.workingDirectory,
|
|
866
|
+
tempDirectory: path.join(this.config.workingDirectory, 'temp', agent.id),
|
|
867
|
+
logDirectory: path.join(this.config.workingDirectory, 'logs', agent.id),
|
|
868
|
+
environment: {
|
|
869
|
+
...this.config.environmentVariables,
|
|
870
|
+
CLAUDE_AGENT_ID: agent.id,
|
|
871
|
+
CLAUDE_TASK_ID: taskDefinition.id.id,
|
|
872
|
+
},
|
|
873
|
+
resources: {
|
|
874
|
+
maxMemory: taskDefinition.requirements.memoryRequired || 512 * 1024 * 1024,
|
|
875
|
+
maxCpuTime: taskDefinition.requirements.maxDuration || 300000,
|
|
876
|
+
maxDiskSpace: 1024 * 1024 * 1024,
|
|
877
|
+
maxNetworkConnections: 10,
|
|
878
|
+
maxFileHandles: 100,
|
|
879
|
+
priority: 1,
|
|
880
|
+
},
|
|
881
|
+
};
|
|
882
|
+
|
|
883
|
+
// Execute using task executor
|
|
884
|
+
const result = await this.taskExecutor.executeClaudeTask(
|
|
885
|
+
taskDefinition,
|
|
886
|
+
context.agent,
|
|
887
|
+
{
|
|
888
|
+
model: options.model || this.config.defaultModel,
|
|
889
|
+
maxTokens: options.maxTokens || this.config.maxTokens,
|
|
890
|
+
temperature: options.temperature || this.config.temperature,
|
|
891
|
+
timeout: options.timeout || this.config.timeout,
|
|
892
|
+
claudePath: this.config.claudeExecutablePath,
|
|
893
|
+
...options,
|
|
894
|
+
}
|
|
895
|
+
);
|
|
896
|
+
|
|
897
|
+
const duration = performance.now() - startTime;
|
|
898
|
+
|
|
899
|
+
// Update agent activity
|
|
900
|
+
agent.lastActivity = new Date();
|
|
901
|
+
agent.totalTasks++;
|
|
902
|
+
agent.totalDuration += duration;
|
|
903
|
+
|
|
904
|
+
return result;
|
|
905
|
+
|
|
906
|
+
} catch (error) {
|
|
907
|
+
const duration = performance.now() - startTime;
|
|
908
|
+
agent.totalDuration += duration;
|
|
909
|
+
|
|
910
|
+
throw error;
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
private convertToAgentState(agent: ClaudeAgent): AgentState {
|
|
915
|
+
// Convert ClaudeAgent to AgentState for compatibility
|
|
916
|
+
return {
|
|
917
|
+
id: {
|
|
918
|
+
id: agent.id,
|
|
919
|
+
swarmId: 'claude-interface',
|
|
920
|
+
type: agent.type as any,
|
|
921
|
+
instance: 1,
|
|
922
|
+
},
|
|
923
|
+
name: `Claude-${agent.id}`,
|
|
924
|
+
type: agent.type as any,
|
|
925
|
+
status: agent.status as any,
|
|
926
|
+
capabilities: this.createAgentCapabilities(agent.capabilities),
|
|
927
|
+
metrics: {
|
|
928
|
+
tasksCompleted: agent.metrics.tasksCompleted,
|
|
929
|
+
tasksFailed: agent.metrics.tasksFailed,
|
|
930
|
+
averageExecutionTime: agent.metrics.averageResponseTime,
|
|
931
|
+
successRate: agent.metrics.successRate,
|
|
932
|
+
cpuUsage: agent.metrics.cpuUsage,
|
|
933
|
+
memoryUsage: agent.metrics.memoryUsage,
|
|
934
|
+
diskUsage: 0,
|
|
935
|
+
networkUsage: 0,
|
|
936
|
+
codeQuality: 0.8,
|
|
937
|
+
testCoverage: 0.7,
|
|
938
|
+
bugRate: 0.1,
|
|
939
|
+
userSatisfaction: 0.9,
|
|
940
|
+
totalUptime: Date.now() - agent.spawnedAt.getTime(),
|
|
941
|
+
lastActivity: agent.lastActivity,
|
|
942
|
+
responseTime: agent.metrics.averageResponseTime,
|
|
943
|
+
},
|
|
944
|
+
currentTask: agent.currentTask ? {
|
|
945
|
+
id: agent.currentTask,
|
|
946
|
+
swarmId: 'claude-interface',
|
|
947
|
+
sequence: 0,
|
|
948
|
+
priority: 1,
|
|
949
|
+
} : undefined,
|
|
950
|
+
workload: agent.status === 'busy' ? 1 : 0,
|
|
951
|
+
health: agent.status === 'error' ? 0 : 1,
|
|
952
|
+
config: {
|
|
953
|
+
autonomyLevel: 0.8,
|
|
954
|
+
learningEnabled: false,
|
|
955
|
+
adaptationEnabled: false,
|
|
956
|
+
maxTasksPerHour: 60,
|
|
957
|
+
maxConcurrentTasks: 1,
|
|
958
|
+
timeoutThreshold: this.config.timeout,
|
|
959
|
+
reportingInterval: 10000,
|
|
960
|
+
heartbeatInterval: 5000,
|
|
961
|
+
permissions: ['read', 'write', 'execute'],
|
|
962
|
+
trustedAgents: [],
|
|
963
|
+
expertise: {},
|
|
964
|
+
preferences: {},
|
|
965
|
+
},
|
|
966
|
+
environment: {
|
|
967
|
+
runtime: 'claude',
|
|
968
|
+
version: '1.0.0',
|
|
969
|
+
workingDirectory: this.config.workingDirectory,
|
|
970
|
+
tempDirectory: path.join(this.config.workingDirectory, 'temp', agent.id),
|
|
971
|
+
logDirectory: path.join(this.config.workingDirectory, 'logs', agent.id),
|
|
972
|
+
apiEndpoints: {},
|
|
973
|
+
credentials: {},
|
|
974
|
+
availableTools: agent.capabilities,
|
|
975
|
+
toolConfigs: {},
|
|
976
|
+
},
|
|
977
|
+
endpoints: [],
|
|
978
|
+
lastHeartbeat: agent.lastActivity,
|
|
979
|
+
taskHistory: [],
|
|
980
|
+
errorHistory: [],
|
|
981
|
+
parentAgent: undefined,
|
|
982
|
+
childAgents: [],
|
|
983
|
+
collaborators: [],
|
|
984
|
+
};
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
private createAgentCapabilities(capabilities: string[]): AgentCapabilities {
|
|
988
|
+
return {
|
|
989
|
+
codeGeneration: capabilities.includes('coding') || capabilities.includes('codeGeneration'),
|
|
990
|
+
codeReview: capabilities.includes('review') || capabilities.includes('codeReview'),
|
|
991
|
+
testing: capabilities.includes('testing'),
|
|
992
|
+
documentation: capabilities.includes('documentation'),
|
|
993
|
+
research: capabilities.includes('research'),
|
|
994
|
+
analysis: capabilities.includes('analysis'),
|
|
995
|
+
webSearch: capabilities.includes('webSearch'),
|
|
996
|
+
apiIntegration: capabilities.includes('apiIntegration'),
|
|
997
|
+
fileSystem: capabilities.includes('fileSystem'),
|
|
998
|
+
terminalAccess: capabilities.includes('terminal'),
|
|
999
|
+
languages: capabilities.filter(c => ['javascript', 'typescript', 'python', 'java'].includes(c)),
|
|
1000
|
+
frameworks: capabilities.filter(c => ['react', 'node', 'express'].includes(c)),
|
|
1001
|
+
domains: capabilities.filter(c => ['web', 'api', 'database'].includes(c)),
|
|
1002
|
+
tools: capabilities.filter(c => ['bash', 'git', 'npm'].includes(c)),
|
|
1003
|
+
maxConcurrentTasks: 1,
|
|
1004
|
+
maxMemoryUsage: 512 * 1024 * 1024,
|
|
1005
|
+
maxExecutionTime: this.config.timeout,
|
|
1006
|
+
reliability: 0.9,
|
|
1007
|
+
speed: 1.0,
|
|
1008
|
+
quality: 0.8,
|
|
1009
|
+
};
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
private async cancelAgentTask(agent: ClaudeAgent): Promise<void> {
|
|
1013
|
+
if (agent.process && !agent.process.killed) {
|
|
1014
|
+
// Send interrupt signal
|
|
1015
|
+
agent.process.kill('SIGINT');
|
|
1016
|
+
|
|
1017
|
+
// Wait briefly for graceful shutdown
|
|
1018
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
1019
|
+
|
|
1020
|
+
// Force kill if still running
|
|
1021
|
+
if (!agent.process.killed) {
|
|
1022
|
+
agent.process.kill('SIGKILL');
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
agent.currentTask = undefined;
|
|
1027
|
+
agent.status = 'idle';
|
|
1028
|
+
agent.lastActivity = new Date();
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
private async terminateProcess(process: ChildProcess): Promise<void> {
|
|
1032
|
+
if (process.killed || process.exitCode !== null) {
|
|
1033
|
+
return;
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
// Send termination signal
|
|
1037
|
+
process.kill('SIGTERM');
|
|
1038
|
+
|
|
1039
|
+
// Wait for graceful shutdown
|
|
1040
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
1041
|
+
|
|
1042
|
+
// Force kill if still running
|
|
1043
|
+
if (!process.killed && process.exitCode === null) {
|
|
1044
|
+
process.kill('SIGKILL');
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
private async terminateAllAgents(): Promise<void> {
|
|
1049
|
+
const terminationPromises = Array.from(this.agents.keys())
|
|
1050
|
+
.map(agentId => this.terminateAgent(agentId, 'Interface shutdown'));
|
|
1051
|
+
|
|
1052
|
+
await Promise.allSettled(terminationPromises);
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
private moveAgentToBusyPool(agent: ClaudeAgent): void {
|
|
1056
|
+
const idleIndex = this.processPool.idle.indexOf(agent);
|
|
1057
|
+
if (idleIndex !== -1) {
|
|
1058
|
+
this.processPool.idle.splice(idleIndex, 1);
|
|
1059
|
+
this.processPool.busy.push(agent);
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
private returnAgentToIdlePool(agent: ClaudeAgent): void {
|
|
1064
|
+
agent.status = 'idle';
|
|
1065
|
+
agent.currentTask = undefined;
|
|
1066
|
+
agent.lastActivity = new Date();
|
|
1067
|
+
|
|
1068
|
+
const busyIndex = this.processPool.busy.indexOf(agent);
|
|
1069
|
+
if (busyIndex !== -1) {
|
|
1070
|
+
this.processPool.busy.splice(busyIndex, 1);
|
|
1071
|
+
this.processPool.idle.push(agent);
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
private moveAgentToFailedPool(agent: ClaudeAgent): void {
|
|
1076
|
+
// Remove from other pools
|
|
1077
|
+
this.removeAgentFromPools(agent);
|
|
1078
|
+
this.processPool.failed.push(agent);
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
private removeAgentFromPools(agent: ClaudeAgent): void {
|
|
1082
|
+
const idleIndex = this.processPool.idle.indexOf(agent);
|
|
1083
|
+
if (idleIndex !== -1) {
|
|
1084
|
+
this.processPool.idle.splice(idleIndex, 1);
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
const busyIndex = this.processPool.busy.indexOf(agent);
|
|
1088
|
+
if (busyIndex !== -1) {
|
|
1089
|
+
this.processPool.busy.splice(busyIndex, 1);
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
const failedIndex = this.processPool.failed.indexOf(agent);
|
|
1093
|
+
if (failedIndex !== -1) {
|
|
1094
|
+
this.processPool.failed.splice(failedIndex, 1);
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
private updateAgentMetrics(agent: ClaudeAgent, execution: ClaudeTaskExecution): void {
|
|
1099
|
+
const metrics = agent.metrics;
|
|
1100
|
+
|
|
1101
|
+
// Update averages
|
|
1102
|
+
const totalTasks = metrics.tasksCompleted + metrics.tasksFailed;
|
|
1103
|
+
if (execution.duration) {
|
|
1104
|
+
metrics.averageResponseTime = totalTasks > 0
|
|
1105
|
+
? ((metrics.averageResponseTime * (totalTasks - 1)) + execution.duration) / totalTasks
|
|
1106
|
+
: execution.duration;
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
// Update success rate
|
|
1110
|
+
metrics.successRate = totalTasks > 0
|
|
1111
|
+
? metrics.tasksCompleted / totalTasks
|
|
1112
|
+
: 0;
|
|
1113
|
+
|
|
1114
|
+
// Update error rate
|
|
1115
|
+
metrics.errorRate = 1 - metrics.successRate;
|
|
1116
|
+
|
|
1117
|
+
// Update token usage if available
|
|
1118
|
+
if (execution.tokensUsed) {
|
|
1119
|
+
metrics.totalTokensUsed += execution.tokensUsed;
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
private getTotalActiveAgents(): number {
|
|
1124
|
+
return this.processPool.idle.length + this.processPool.busy.length;
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
private calculateThroughput(): number {
|
|
1128
|
+
const agents = Array.from(this.agents.values());
|
|
1129
|
+
const totalTasks = agents.reduce((sum, a) => sum + a.totalTasks, 0);
|
|
1130
|
+
const totalTime = agents.reduce((sum, a) => sum + a.totalDuration, 0);
|
|
1131
|
+
|
|
1132
|
+
return totalTime > 0 ? (totalTasks / totalTime) * 60000 : 0; // tasks per minute
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
private calculatePoolUtilization(): number {
|
|
1136
|
+
const total = this.getTotalActiveAgents();
|
|
1137
|
+
const busy = this.processPool.busy.length;
|
|
1138
|
+
|
|
1139
|
+
return total > 0 ? busy / total : 0;
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
private startHealthChecks(): void {
|
|
1143
|
+
this.healthCheckInterval = setInterval(() => {
|
|
1144
|
+
this.performHealthCheck();
|
|
1145
|
+
}, this.config.healthCheckInterval);
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
private performHealthCheck(): void {
|
|
1149
|
+
const now = Date.now();
|
|
1150
|
+
|
|
1151
|
+
for (const agent of this.agents.values()) {
|
|
1152
|
+
// Check for stalled agents
|
|
1153
|
+
const inactiveTime = now - agent.lastActivity.getTime();
|
|
1154
|
+
|
|
1155
|
+
if (agent.status === 'busy' && inactiveTime > this.config.timeout * 2) {
|
|
1156
|
+
this.logger.warn('Agent appears stalled', {
|
|
1157
|
+
agentId: agent.id,
|
|
1158
|
+
inactiveTime,
|
|
1159
|
+
currentTask: agent.currentTask,
|
|
1160
|
+
});
|
|
1161
|
+
|
|
1162
|
+
// Try to recover the agent
|
|
1163
|
+
this.recoverStalledAgent(agent);
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
// Check for failed processes
|
|
1167
|
+
if (agent.process.killed || agent.process.exitCode !== null) {
|
|
1168
|
+
if (agent.status !== 'terminated') {
|
|
1169
|
+
this.logger.warn('Agent process died unexpectedly', {
|
|
1170
|
+
agentId: agent.id,
|
|
1171
|
+
exitCode: agent.process.exitCode,
|
|
1172
|
+
});
|
|
1173
|
+
|
|
1174
|
+
agent.status = 'error';
|
|
1175
|
+
this.moveAgentToFailedPool(agent);
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
private async recoverStalledAgent(agent: ClaudeAgent): Promise<void> {
|
|
1182
|
+
try {
|
|
1183
|
+
if (agent.currentTask) {
|
|
1184
|
+
await this.cancelExecution(agent.currentTask, 'Agent recovery');
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
this.returnAgentToIdlePool(agent);
|
|
1188
|
+
|
|
1189
|
+
this.logger.info('Agent recovered from stalled state', {
|
|
1190
|
+
agentId: agent.id,
|
|
1191
|
+
});
|
|
1192
|
+
|
|
1193
|
+
} catch (error) {
|
|
1194
|
+
this.logger.error('Failed to recover stalled agent', {
|
|
1195
|
+
agentId: agent.id,
|
|
1196
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1197
|
+
});
|
|
1198
|
+
|
|
1199
|
+
// Terminate the problematic agent
|
|
1200
|
+
await this.terminateAgent(agent.id, 'Recovery failed');
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
private initializeProcessPool(): ProcessPool {
|
|
1205
|
+
return {
|
|
1206
|
+
idle: [],
|
|
1207
|
+
busy: [],
|
|
1208
|
+
failed: [],
|
|
1209
|
+
totalSpawned: 0,
|
|
1210
|
+
totalTerminated: 0,
|
|
1211
|
+
recyclingEnabled: this.config.processRecycling,
|
|
1212
|
+
maxAge: 3600000, // 1 hour
|
|
1213
|
+
maxTasks: 100,
|
|
1214
|
+
};
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
private initializeAgentMetrics(): ClaudeAgentMetrics {
|
|
1218
|
+
return {
|
|
1219
|
+
tasksCompleted: 0,
|
|
1220
|
+
tasksFailed: 0,
|
|
1221
|
+
averageResponseTime: 0,
|
|
1222
|
+
totalTokensUsed: 0,
|
|
1223
|
+
memoryUsage: 0,
|
|
1224
|
+
cpuUsage: 0,
|
|
1225
|
+
errorRate: 0,
|
|
1226
|
+
successRate: 0,
|
|
1227
|
+
};
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
private createDefaultConfig(config: Partial<ClaudeCodeConfig>): ClaudeCodeConfig {
|
|
1231
|
+
return {
|
|
1232
|
+
claudeExecutablePath: 'claude',
|
|
1233
|
+
defaultModel: 'claude-3-5-sonnet-20241022',
|
|
1234
|
+
maxTokens: 4096,
|
|
1235
|
+
temperature: 0.7,
|
|
1236
|
+
timeout: 300000, // 5 minutes
|
|
1237
|
+
maxConcurrentAgents: 10,
|
|
1238
|
+
enableStreaming: false,
|
|
1239
|
+
enableLogging: true,
|
|
1240
|
+
workingDirectory: process.cwd(),
|
|
1241
|
+
environmentVariables: {},
|
|
1242
|
+
agentPoolSize: 0,
|
|
1243
|
+
processRecycling: true,
|
|
1244
|
+
healthCheckInterval: 30000, // 30 seconds
|
|
1245
|
+
...config,
|
|
1246
|
+
};
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
private setupEventHandlers(): void {
|
|
1250
|
+
this.on('agent:spawned', (data) => {
|
|
1251
|
+
this.logger.info('Agent spawned event', data);
|
|
1252
|
+
});
|
|
1253
|
+
|
|
1254
|
+
this.on('agent:terminated', (data) => {
|
|
1255
|
+
this.logger.info('Agent terminated event', data);
|
|
1256
|
+
});
|
|
1257
|
+
|
|
1258
|
+
this.on('task:completed', (data) => {
|
|
1259
|
+
this.logger.info('Task completed event', data);
|
|
1260
|
+
});
|
|
1261
|
+
|
|
1262
|
+
this.on('task:cancelled', (data) => {
|
|
1263
|
+
this.logger.warn('Task cancelled event', data);
|
|
1264
|
+
});
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
|
|
1268
|
+
export default ClaudeCodeInterface;
|