UNPKG

claude-flow-tbowman01

Version:

Enterprise-grade AI agent orchestration with ruv-swarm integration (Alpha Release)

563 lines 22.8 kB
import { EventEmitter } from 'node:events'; import { Logger } from '../core/logger.js'; import { EventBus } from '../core/event-bus.js'; import { generateId } from '../utils/helpers.js'; import { SwarmMonitor } from './swarm-monitor.js'; import { MemoryManager } from '../memory/manager.js'; export class SwarmCoordinator extends EventEmitter { logger; config; agents; objectives; tasks; monitor; scheduler; memoryManager; backgroundWorkers; isRunning = false; workStealer; circuitBreaker; constructor(config = {}) { super(); this.logger = new Logger('SwarmCoordinator'); this.config = { maxAgents: 10, maxConcurrentTasks: 5, taskTimeout: 300000, // 5 minutes enableMonitoring: true, enableWorkStealing: true, enableCircuitBreaker: true, memoryNamespace: 'swarm', coordinationStrategy: 'hybrid', backgroundTaskInterval: 5000, // 5 seconds healthCheckInterval: 10000, // 10 seconds maxRetries: 3, backoffMultiplier: 2, ...config, }; this.agents = new Map(); this.objectives = new Map(); this.tasks = new Map(); this.backgroundWorkers = new Map(); // Initialize memory manager const eventBus = EventBus.getInstance(); this.memoryManager = new MemoryManager({ backend: 'sqlite', namespace: this.config.memoryNamespace, cacheSizeMB: 50, syncOnExit: true, maxEntries: 10000, ttlMinutes: 60, }, eventBus, this.logger); if (this.config.enableMonitoring) { this.monitor = new SwarmMonitor({ updateInterval: 1000, enableAlerts: true, enableHistory: true, }); } this.setupEventHandlers(); } setupEventHandlers() { // Monitor events if (this.monitor) { this.monitor.on('alert', (alert) => { this.handleMonitorAlert(alert); }); } // Add custom event handlers for swarm coordination this.on('task:completed', (data) => { this.handleTaskCompleted(data.taskId, data.result); }); this.on('task:failed', (data) => { this.handleTaskFailed(data.taskId, data.error); }); } async start() { if (this.isRunning) { this.logger.warn('Swarm coordinator already running'); return; } this.logger.info('Starting swarm coordinator...'); this.isRunning = true; // Start subsystems await this.memoryManager.initialize(); if (this.monitor) { await this.monitor.start(); } // Start background workers this.startBackgroundWorkers(); this.emit('coordinator:started'); } async stop() { if (!this.isRunning) { return; } this.logger.info('Stopping swarm coordinator...'); this.isRunning = false; // Stop background workers this.stopBackgroundWorkers(); // Stop subsystems await this.scheduler.shutdown(); if (this.monitor) { this.monitor.stop(); } this.emit('coordinator:stopped'); } startBackgroundWorkers() { // Task processor worker const taskProcessor = setInterval(() => { this.processBackgroundTasks(); }, this.config.backgroundTaskInterval); this.backgroundWorkers.set('taskProcessor', taskProcessor); // Health check worker const healthChecker = setInterval(() => { this.performHealthChecks(); }, this.config.healthCheckInterval); this.backgroundWorkers.set('healthChecker', healthChecker); // Work stealing worker if (this.workStealer) { const workStealerWorker = setInterval(() => { this.performWorkStealing(); }, this.config.backgroundTaskInterval); this.backgroundWorkers.set('workStealer', workStealerWorker); } // Memory sync worker const memorySync = setInterval(() => { this.syncMemoryState(); }, this.config.backgroundTaskInterval * 2); this.backgroundWorkers.set('memorySync', memorySync); } stopBackgroundWorkers() { for (const [name, worker] of this.backgroundWorkers) { clearInterval(worker); this.logger.debug(`Stopped background worker: ${name}`); } this.backgroundWorkers.clear(); } async createObjective(description, strategy = 'auto') { const objectiveId = generateId('objective'); const objective = { id: objectiveId, description, strategy, tasks: [], status: 'planning', createdAt: new Date(), }; this.objectives.set(objectiveId, objective); this.logger.info(`Created objective: ${objectiveId} - ${description}`); // Decompose objective into tasks const tasks = await this.decomposeObjective(objective); objective.tasks = tasks; // Store in memory await this.memoryManager.store({ id: `objective:${objectiveId}`, agentId: 'swarm-coordinator', type: 'objective', content: JSON.stringify(objective), namespace: this.config.memoryNamespace, timestamp: new Date(), metadata: { type: 'objective', strategy, taskCount: tasks.length, }, }); this.emit('objective:created', objective); return objectiveId; } async decomposeObjective(objective) { const tasks = []; switch (objective.strategy) { case 'research': tasks.push(this.createTask('research', 'Gather information and research materials', 1), this.createTask('analysis', 'Analyze research findings', 2, ['research']), this.createTask('synthesis', 'Synthesize insights and create report', 3, ['analysis'])); break; case 'development': tasks.push(this.createTask('planning', 'Plan architecture and design', 1), this.createTask('implementation', 'Implement core functionality', 2, ['planning']), this.createTask('testing', 'Test and validate implementation', 3, ['implementation']), this.createTask('documentation', 'Create documentation', 3, ['implementation']), this.createTask('review', 'Peer review and refinement', 4, ['testing', 'documentation'])); break; case 'analysis': tasks.push(this.createTask('data-collection', 'Collect and prepare data', 1), this.createTask('analysis', 'Perform detailed analysis', 2, ['data-collection']), this.createTask('visualization', 'Create visualizations', 3, ['analysis']), this.createTask('reporting', 'Generate final report', 4, ['analysis', 'visualization'])); break; default: // auto // Use AI to decompose based on objective description tasks.push(this.createTask('exploration', 'Explore and understand requirements', 1), this.createTask('planning', 'Create execution plan', 2, ['exploration']), this.createTask('execution', 'Execute main tasks', 3, ['planning']), this.createTask('validation', 'Validate and test results', 4, ['execution']), this.createTask('completion', 'Finalize and document', 5, ['validation'])); } // Register tasks tasks.forEach((task) => { this.tasks.set(task.id, task); }); return tasks; } createTask(type, description, priority, dependencies = []) { return { id: generateId('task'), type, description, priority, dependencies, status: 'pending', createdAt: new Date(), retryCount: 0, maxRetries: this.config.maxRetries, timeout: this.config.taskTimeout, }; } async registerAgent(name, type, capabilities = []) { const agentId = generateId('agent'); const agent = { id: agentId, name, type, status: 'idle', capabilities, metrics: { tasksCompleted: 0, tasksFailed: 0, totalDuration: 0, lastActivity: new Date(), }, }; this.agents.set(agentId, agent); if (this.monitor) { this.monitor.registerAgent(agentId, name); } // Register with work stealer if enabled if (this.workStealer) { this.workStealer.registerWorker(agentId, 1); } this.logger.info(`Registered agent: ${name} (${agentId}) - Type: ${type}`); this.emit('agent:registered', agent); return agentId; } async assignTask(taskId, agentId) { const task = this.tasks.get(taskId); const agent = this.agents.get(agentId); if (!task || !agent) { throw new Error('Task or agent not found'); } if (agent.status !== 'idle') { throw new Error('Agent is not available'); } // Check circuit breaker if (this.circuitBreaker && !this.circuitBreaker.canExecute(agentId)) { throw new Error('Agent circuit breaker is open'); } task.assignedTo = agentId; task.status = 'running'; task.startedAt = new Date(); agent.status = 'busy'; agent.currentTask = task; if (this.monitor) { this.monitor.taskStarted(agentId, taskId, task.description); } this.logger.info(`Assigned task ${taskId} to agent ${agentId}`); this.emit('task:assigned', { task, agent }); // Execute task in background this.executeTask(task, agent); } async executeTask(task, agent) { try { // Simulate task execution // In real implementation, this would spawn actual Claude instances const result = await this.simulateTaskExecution(task, agent); await this.handleTaskCompleted(task.id, result); } catch (error) { await this.handleTaskFailed(task.id, error); } } async simulateTaskExecution(task, agent) { // This is where we would actually spawn Claude processes // For now, simulate with timeout return new Promise((resolve, reject) => { const timeout = setTimeout(() => { reject(new Error('Task timeout')); }, task.timeout || this.config.taskTimeout); // Simulate work setTimeout(() => { clearTimeout(timeout); resolve({ taskId: task.id, agentId: agent.id, result: `Completed ${task.type} task`, timestamp: new Date(), }); }, Math.random() * 5000 + 2000); }); } async handleTaskCompleted(taskId, result) { const task = this.tasks.get(taskId); if (!task) return; const agent = task.assignedTo ? this.agents.get(task.assignedTo) : null; task.status = 'completed'; task.completedAt = new Date(); task.result = result; if (agent) { agent.status = 'idle'; agent.currentTask = undefined; agent.metrics.tasksCompleted++; agent.metrics.totalDuration += task.completedAt.getTime() - (task.startedAt?.getTime() || 0); agent.metrics.lastActivity = new Date(); if (this.monitor) { this.monitor.taskCompleted(agent.id, taskId); } if (this.circuitBreaker) { this.circuitBreaker.recordSuccess(agent.id); } } // Store result in memory await this.memoryManager.store({ id: `task:${taskId}:result`, agentId: agent?.id || 'unknown', type: 'task-result', content: JSON.stringify(result), namespace: this.config.memoryNamespace, timestamp: new Date(), metadata: { type: 'task-result', taskType: task.type, agentId: agent?.id, }, }); this.logger.info(`Task ${taskId} completed successfully`); this.emit('task:completed', { task, result }); // Check if objective is complete this.checkObjectiveCompletion(task); } async handleTaskFailed(taskId, error) { const task = this.tasks.get(taskId); if (!task) return; const agent = task.assignedTo ? this.agents.get(task.assignedTo) : null; task.error = (error instanceof Error ? error.message : String(error)) || String(error); task.retryCount++; if (agent) { agent.status = 'idle'; agent.currentTask = undefined; agent.metrics.tasksFailed++; agent.metrics.lastActivity = new Date(); if (this.monitor) { this.monitor.taskFailed(agent.id, taskId, task.error); } if (this.circuitBreaker) { this.circuitBreaker.recordFailure(agent.id); } } // Retry logic if (task.retryCount < task.maxRetries) { task.status = 'pending'; task.assignedTo = undefined; this.logger.warn(`Task ${taskId} failed, will retry (${task.retryCount}/${task.maxRetries})`); this.emit('task:retry', { task, error }); } else { task.status = 'failed'; task.completedAt = new Date(); this.logger.error(`Task ${taskId} failed after ${task.retryCount} retries`); this.emit('task:failed', { task, error }); } } checkObjectiveCompletion(completedTask) { for (const [objectiveId, objective] of this.objectives) { if (objective.status !== 'executing') continue; const allTasksComplete = objective.tasks.every((task) => { const t = this.tasks.get(task.id); return t && (t.status === 'completed' || t.status === 'failed'); }); if (allTasksComplete) { objective.status = 'completed'; objective.completedAt = new Date(); this.logger.info(`Objective ${objectiveId} completed`); this.emit('objective:completed', objective); } } } async processBackgroundTasks() { if (!this.isRunning) return; try { // Process pending tasks const pendingTasks = Array.from(this.tasks.values()).filter((t) => t.status === 'pending' && this.areDependenciesMet(t)); // Get available agents const availableAgents = Array.from(this.agents.values()).filter((a) => a.status === 'idle'); // Assign tasks to agents for (const task of pendingTasks) { if (availableAgents.length === 0) break; const agent = this.selectBestAgent(task, availableAgents); if (agent) { try { await this.assignTask(task.id, agent.id); availableAgents.splice(availableAgents.indexOf(agent), 1); } catch (error) { this.logger.error(`Failed to assign task ${task.id}:`, error); } } } } catch (error) { this.logger.error('Error processing background tasks:', error); } } areDependenciesMet(task) { return task.dependencies.every((depId) => { const dep = this.tasks.get(depId); return dep && dep.status === 'completed'; }); } selectBestAgent(task, availableAgents) { // Simple selection based on task type and agent capabilities const compatibleAgents = availableAgents.filter((agent) => { // Match task type to agent type if (task.type.includes('research') && agent.type === 'researcher') return true; if (task.type.includes('implement') && agent.type === 'coder') return true; if (task.type.includes('analysis') && agent.type === 'analyst') return true; if (task.type.includes('review') && agent.type === 'reviewer') return true; return agent.type === 'coordinator'; // Coordinator can do any task }); if (compatibleAgents.length === 0) { return availableAgents[0]; // Fallback to any available agent } // Select agent with best performance metrics return compatibleAgents.reduce((best, agent) => { const bestRatio = best.metrics.tasksCompleted / (best.metrics.tasksFailed + 1); const agentRatio = agent.metrics.tasksCompleted / (agent.metrics.tasksFailed + 1); return agentRatio > bestRatio ? agent : best; }); } async performHealthChecks() { if (!this.isRunning) return; try { const now = new Date(); for (const [agentId, agent] of this.agents) { // Check for stalled agents if (agent.status === 'busy' && agent.currentTask) { const taskDuration = now.getTime() - (agent.currentTask.startedAt?.getTime() || 0); if (taskDuration > this.config.taskTimeout) { this.logger.warn(`Agent ${agentId} appears stalled on task ${agent.currentTask.id}`); await this.handleTaskFailed(agent.currentTask.id, new Error('Task timeout')); } } // Check agent health const inactivityTime = now.getTime() - agent.metrics.lastActivity.getTime(); if (inactivityTime > this.config.healthCheckInterval * 3) { this.logger.warn(`Agent ${agentId} has been inactive for ${Math.round(inactivityTime / 1000)}s`); } } } catch (error) { this.logger.error('Error performing health checks:', error); } } async performWorkStealing() { if (!this.isRunning || !this.workStealer) return; try { // Get agent workloads const workloads = new Map(); for (const [agentId, agent] of this.agents) { workloads.set(agentId, agent.status === 'busy' ? 1 : 0); } // Update work stealer this.workStealer.updateLoads(workloads); // Check for work stealing opportunities const stealingSuggestions = this.workStealer.suggestWorkStealing(); for (const suggestion of stealingSuggestions) { this.logger.debug(`Work stealing suggestion: ${suggestion.from} -> ${suggestion.to}`); // In a real implementation, we would reassign tasks here } } catch (error) { this.logger.error('Error performing work stealing:', error); } } async syncMemoryState() { if (!this.isRunning) return; try { // Sync current state to memory const state = { objectives: Array.from(this.objectives.values()), tasks: Array.from(this.tasks.values()), agents: Array.from(this.agents.values()).map((a) => ({ ...a, currentTask: undefined, // Don't store transient state })), timestamp: new Date(), }; await this.memoryManager.store({ id: 'swarm:state', agentId: 'swarm-coordinator', type: 'swarm-state', content: JSON.stringify(state), namespace: this.config.memoryNamespace, timestamp: new Date(), metadata: { type: 'swarm-state', objectiveCount: state.objectives.length, taskCount: state.tasks.length, agentCount: state.agents.length, }, }); } catch (error) { this.logger.error('Error syncing memory state:', error); } } handleMonitorAlert(alert) { this.logger.warn(`Monitor alert: ${alert.message}`); this.emit('monitor:alert', alert); } handleAgentMessage(message) { this.logger.debug(`Agent message: ${message.type} from ${message.from}`); this.emit('agent:message', message); } // Public API methods async executeObjective(objectiveId) { const objective = this.objectives.get(objectiveId); if (!objective) { throw new Error('Objective not found'); } objective.status = 'executing'; this.logger.info(`Executing objective: ${objectiveId}`); this.emit('objective:started', objective); // Tasks will be processed by background workers } getObjectiveStatus(objectiveId) { return this.objectives.get(objectiveId); } getAgentStatus(agentId) { return this.agents.get(agentId); } getSwarmStatus() { const tasks = Array.from(this.tasks.values()); const agents = Array.from(this.agents.values()); return { objectives: this.objectives.size, tasks: { total: tasks.length, pending: tasks.filter((t) => t.status === 'pending').length, running: tasks.filter((t) => t.status === 'running').length, completed: tasks.filter((t) => t.status === 'completed').length, failed: tasks.filter((t) => t.status === 'failed').length, }, agents: { total: agents.length, idle: agents.filter((a) => a.status === 'idle').length, busy: agents.filter((a) => a.status === 'busy').length, failed: agents.filter((a) => a.status === 'failed').length, }, uptime: this.monitor ? this.monitor.getSummary().uptime : 0, }; } } //# sourceMappingURL=swarm-coordinator.js.map