UNPKG

claude-flow-tbowman01

Version:

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

906 lines (786 loc) 26.7 kB
/** * SwarmOrchestrator Class * * Orchestrates task distribution, agent coordination, and * execution strategies within the Hive Mind swarm. */ import { EventEmitter } from 'events'; import { HiveMind } from '../core/HiveMind.js'; import { Agent } from '../core/Agent.js'; import { DatabaseManager } from '../core/DatabaseManager.js'; import { MCPToolWrapper } from './MCPToolWrapper.js'; import { Task, TaskStrategy, ExecutionPlan, OrchestrationResult, TaskAssignment, } from '../types.js'; export class SwarmOrchestrator extends EventEmitter { private hiveMind: HiveMind; private db: DatabaseManager; private mcpWrapper: MCPToolWrapper; private executionPlans: Map<string, ExecutionPlan>; private taskAssignments: Map<string, TaskAssignment[]>; private activeExecutions: Map<string, any>; private isActive: boolean = false; constructor(hiveMind: HiveMind) { super(); this.hiveMind = hiveMind; this.executionPlans = new Map(); this.taskAssignments = new Map(); this.activeExecutions = new Map(); } /** * Initialize the orchestrator */ async initialize(): Promise<void> { this.db = await DatabaseManager.getInstance(); this.mcpWrapper = new MCPToolWrapper(); await this.mcpWrapper.initialize(); // Start orchestration loops this.startTaskDistributor(); this.startProgressMonitor(); this.startLoadBalancer(); this.isActive = true; this.emit('initialized'); } /** * Submit a task for orchestration */ async submitTask(task: Task): Promise<void> { // Create execution plan based on strategy const plan = await this.createExecutionPlan(task); this.executionPlans.set(task.id, plan); // Orchestrate task using MCP tools const orchestrationResult = await this.mcpWrapper.orchestrateTask({ task: task.description, priority: task.priority, strategy: task.strategy, dependencies: task.dependencies, }); if (orchestrationResult.success) { // Start task execution await this.executeTask(task, plan); } else { this.emit('orchestrationError', { task, error: orchestrationResult.error }); } } /** * Create execution plan for a task */ private async createExecutionPlan(task: Task): Promise<ExecutionPlan> { const strategy = this.getStrategyImplementation(task.strategy); // Analyze task complexity const analysis = await this.analyzeTaskComplexity(task); // Determine phases based on strategy and complexity const phases = strategy.determinePhases(task, analysis); // Create assignments for each phase const phaseAssignments = await Promise.all( phases.map((phase) => this.createPhaseAssignments(task, phase, analysis)), ); return { taskId: task.id, strategy: task.strategy, phases, phaseAssignments, dependencies: task.dependencies, checkpoints: this.createCheckpoints(phases), parallelizable: strategy.isParallelizable(task), estimatedDuration: analysis.estimatedDuration, resourceRequirements: analysis.resourceRequirements, }; } /** * Execute task according to plan */ private async executeTask(task: Task, plan: ExecutionPlan): Promise<void> { const execution = { taskId: task.id, plan, startTime: Date.now(), currentPhase: 0, phaseResults: [], status: 'executing', }; this.activeExecutions.set(task.id, execution); try { // Execute phases according to strategy if (plan.parallelizable) { await this.executeParallel(task, plan, execution); } else { await this.executeSequential(task, plan, execution); } // Mark task as completed execution.status = 'completed'; await this.completeTask(task, execution); } catch (error) { execution.status = 'failed'; execution.error = error; await this.handleTaskFailure(task, execution, error); } finally { this.activeExecutions.delete(task.id); } } /** * Execute phases in parallel */ private async executeParallel(task: Task, plan: ExecutionPlan, execution: any): Promise<void> { const parallelPhases = plan.phases.filter((_, index) => plan.phaseAssignments[index].some((a) => a.canRunParallel), ); const results = await Promise.all( parallelPhases.map((phase) => this.executePhase(task, phase, plan, execution)), ); execution.phaseResults = results; } /** * Execute phases sequentially */ private async executeSequential(task: Task, plan: ExecutionPlan, execution: any): Promise<void> { for (let i = 0; i < plan.phases.length; i++) { const phase = plan.phases[i]; execution.currentPhase = i; const result = await this.executePhase(task, phase, plan, execution); execution.phaseResults.push(result); // Check checkpoint if (plan.checkpoints[i]) { await this.evaluateCheckpoint(task, plan.checkpoints[i], execution); } } } /** * Execute a single phase */ private async executePhase( task: Task, phase: string, plan: ExecutionPlan, execution: any, ): Promise<any> { const phaseIndex = plan.phases.indexOf(phase); const assignments = plan.phaseAssignments[phaseIndex]; // Assign agents to phase tasks const agentAssignments = await this.assignAgentsToPhase(task, phase, assignments); // Execute phase tasks const phaseResults = await Promise.all( agentAssignments.map((assignment) => this.executeAssignment(task, phase, assignment)), ); // Aggregate phase results return this.aggregatePhaseResults(phase, phaseResults); } /** * Assign agents to phase tasks */ private async assignAgentsToPhase( task: Task, phase: string, assignments: TaskAssignment[], ): Promise<any[]> { const agentAssignments = []; for (const assignment of assignments) { // Find suitable agent const agent = await this.findSuitableAgent(assignment.requiredCapabilities); if (agent) { await this.assignTaskToAgent(task.id, agent.id); agentAssignments.push({ agent, assignment, phase, }); } else { // Queue for later assignment this.queueAssignment(task.id, assignment); } } return agentAssignments; } /** * Execute a specific assignment */ private async executeAssignment(task: Task, phase: string, assignment: any): Promise<any> { const { agent, assignment: taskAssignment } = assignment; // Send execution command to agent await agent.assignTask(task.id, { phase, role: taskAssignment.role, responsibilities: taskAssignment.responsibilities, expectedOutput: taskAssignment.expectedOutput, }); // Wait for completion or timeout return this.waitForAgentCompletion(agent, task.id, taskAssignment.timeout); } /** * Assign task to a specific agent */ async assignTaskToAgent(taskId: string, agentId: string): Promise<void> { // Update database const task = await this.db.getTask(taskId); const assignedAgents = JSON.parse(task.assigned_agents || '[]'); if (!assignedAgents.includes(agentId)) { assignedAgents.push(agentId); await this.db.updateTask(taskId, { assigned_agents: JSON.stringify(assignedAgents), status: 'assigned', }); } // Update agent await this.db.updateAgent(agentId, { current_task_id: taskId, status: 'busy', }); this.emit('taskAssigned', { taskId, agentId }); } /** * Cancel a task */ async cancelTask(taskId: string): Promise<void> { const execution = this.activeExecutions.get(taskId); if (execution) { execution.status = 'cancelled'; // Notify assigned agents const task = await this.db.getTask(taskId); const assignedAgents = JSON.parse(task.assigned_agents || '[]'); for (const agentId of assignedAgents) { await this.notifyAgentTaskCancelled(agentId, taskId); } } this.activeExecutions.delete(taskId); this.executionPlans.delete(taskId); this.emit('taskCancelled', { taskId }); } /** * Rebalance agent assignments */ async rebalance(): Promise<void> { // Get current load distribution const loadDistribution = await this.analyzeLoadDistribution(); // Use MCP tool for load balancing const balanceResult = await this.mcpWrapper.loadBalance({ tasks: loadDistribution.unassignedTasks, }); if (balanceResult.success && balanceResult.data.reassignments) { await this.applyReassignments(balanceResult.data.reassignments); } this.emit('rebalanced', { loadDistribution }); } /** * Strategy implementations */ private getStrategyImplementation(strategy: TaskStrategy): any { const strategies = { parallel: { determinePhases: (task: Task) => ['preparation', 'parallel-execution', 'aggregation'], isParallelizable: () => true, maxConcurrency: 5, }, sequential: { determinePhases: (task: Task) => ['analysis', 'planning', 'execution', 'validation'], isParallelizable: () => false, maxConcurrency: 1, }, adaptive: { determinePhases: (task: Task, analysis: any) => { if (analysis.complexity === 'high') { return ['deep-analysis', 'planning', 'phased-execution', 'integration', 'validation']; } return ['quick-analysis', 'execution', 'validation']; }, isParallelizable: (task: Task) => !task.requireConsensus, maxConcurrency: 3, }, consensus: { determinePhases: () => ['proposal', 'discussion', 'voting', 'execution', 'ratification'], isParallelizable: () => false, maxConcurrency: 1, }, }; return strategies[strategy] || strategies.adaptive; } /** * Analyze task complexity */ private async analyzeTaskComplexity(task: Task): Promise<any> { const analysis = await this.mcpWrapper.analyzePattern({ action: 'analyze', operation: 'task_complexity', metadata: { description: task.description, priority: task.priority, dependencies: task.dependencies.length, requiresConsensus: task.requireConsensus, }, }); return { complexity: analysis.data?.complexity || 'medium', estimatedDuration: analysis.data?.estimatedDuration || 3600000, resourceRequirements: analysis.data?.resourceRequirements || { minAgents: 1, maxAgents: task.maxAgents, capabilities: task.requiredCapabilities, }, }; } /** * Create phase assignments */ private async createPhaseAssignments( task: Task, phase: string, analysis: any, ): Promise<TaskAssignment[]> { const assignments: TaskAssignment[] = []; // Define assignments based on phase switch (phase) { case 'analysis': case 'deep-analysis': assignments.push({ role: 'analyst', requiredCapabilities: ['data_analysis', 'pattern_recognition'], responsibilities: ['Analyze task requirements', 'Identify patterns', 'Assess complexity'], expectedOutput: 'Analysis report', timeout: 300000, // 5 minutes canRunParallel: false, }); break; case 'planning': assignments.push({ role: 'architect', requiredCapabilities: ['system_design', 'architecture_patterns'], responsibilities: ['Design solution', 'Create implementation plan', 'Define interfaces'], expectedOutput: 'Implementation plan', timeout: 600000, // 10 minutes canRunParallel: false, }); break; case 'execution': case 'parallel-execution': // Multiple execution assignments based on complexity const executionCount = Math.min(analysis.resourceRequirements.maxAgents, 3); for (let i = 0; i < executionCount; i++) { assignments.push({ role: 'executor', requiredCapabilities: task.requiredCapabilities, responsibilities: ['Implement solution', 'Execute plan', 'Handle errors'], expectedOutput: 'Execution results', timeout: 1800000, // 30 minutes canRunParallel: true, }); } break; case 'validation': assignments.push({ role: 'validator', requiredCapabilities: ['quality_assurance', 'test_generation'], responsibilities: ['Validate results', 'Run tests', 'Ensure quality'], expectedOutput: 'Validation report', timeout: 600000, // 10 minutes canRunParallel: false, }); break; case 'consensus': case 'voting': assignments.push({ role: 'consensus-coordinator', requiredCapabilities: ['consensus_building'], responsibilities: ['Coordinate voting', 'Collect opinions', 'Determine consensus'], expectedOutput: 'Consensus decision', timeout: 300000, // 5 minutes canRunParallel: false, }); break; } return assignments; } /** * Create execution checkpoints */ private createCheckpoints(phases: string[]): any[] { return phases.map((phase, index) => ({ phase, index, requiredProgress: Math.round(((index + 1) / phases.length) * 100), validationCriteria: this.getValidationCriteria(phase), failureThreshold: 0.3, })); } /** * Get validation criteria for a phase */ private getValidationCriteria(phase: string): any[] { const criteria: Record<string, any[]> = { analysis: [ { name: 'completeness', weight: 0.4 }, { name: 'accuracy', weight: 0.6 }, ], planning: [ { name: 'feasibility', weight: 0.5 }, { name: 'completeness', weight: 0.5 }, ], execution: [ { name: 'correctness', weight: 0.7 }, { name: 'performance', weight: 0.3 }, ], validation: [ { name: 'test_coverage', weight: 0.5 }, { name: 'quality_score', weight: 0.5 }, ], }; return criteria[phase] || [{ name: 'completion', weight: 1.0 }]; } /** * Find suitable agent for capabilities */ private async findSuitableAgent(requiredCapabilities: string[]): Promise<Agent | null> { const agents = await this.hiveMind.getAgents(); // Filter available agents with required capabilities const suitableAgents = agents.filter( (agent) => agent.status === 'idle' && requiredCapabilities.every((cap) => agent.capabilities.includes(cap)), ); if (suitableAgents.length === 0) { return null; } // Select best agent based on performance history return this.selectBestAgent(suitableAgents, requiredCapabilities); } /** * Select best agent from candidates */ private async selectBestAgent(agents: Agent[], capabilities: string[]): Promise<Agent> { // Simple selection - in production would use performance metrics const scores = await Promise.all( agents.map(async (agent) => { const performance = await this.db.getAgentPerformance(agent.id); return { agent, score: performance?.successRate || 0.5, }; }), ); scores.sort((a, b) => b.score - a.score); return scores[0].agent; } /** * Wait for agent to complete task */ private async waitForAgentCompletion( agent: Agent, taskId: string, timeout: number, ): Promise<any> { return new Promise((resolve, reject) => { const timer = setTimeout(() => { reject(new Error(`Agent ${agent.id} timeout on task ${taskId}`)); }, timeout); const checkCompletion = async () => { const agentState = await this.db.getAgent(agent.id); if (agentState.current_task_id !== taskId) { clearTimeout(timer); clearInterval(interval); // Get task result const task = await this.db.getTask(taskId); resolve(task.result ? JSON.parse(task.result) : {}); } }; const interval = setInterval(checkCompletion, 1000); }); } /** * Aggregate results from phase execution */ private aggregatePhaseResults(phase: string, results: any[]): any { return { phase, results, summary: this.summarizeResults(results), timestamp: new Date(), }; } /** * Summarize phase results */ private summarizeResults(results: any[]): any { const successful = results.filter((r) => r.success).length; const total = results.length; return { successRate: total > 0 ? successful / total : 0, totalExecutions: total, aggregatedData: results.map((r) => r.data).filter(Boolean), }; } /** * Queue assignment for later */ private queueAssignment(taskId: string, assignment: TaskAssignment): void { if (!this.taskAssignments.has(taskId)) { this.taskAssignments.set(taskId, []); } this.taskAssignments.get(taskId)!.push(assignment); this.emit('assignmentQueued', { taskId, assignment }); } /** * Evaluate checkpoint */ private async evaluateCheckpoint(task: Task, checkpoint: any, execution: any): Promise<void> { const phaseResult = execution.phaseResults[checkpoint.index]; if (!phaseResult) return; let score = 0; for (const criterion of checkpoint.validationCriteria) { const criterionScore = this.evaluateCriterion(phaseResult, criterion); score += criterionScore * criterion.weight; } if (score < checkpoint.failureThreshold) { throw new Error(`Checkpoint failed at phase ${checkpoint.phase}: score ${score}`); } this.emit('checkpointPassed', { task, checkpoint, score }); } /** * Evaluate validation criterion */ private evaluateCriterion(result: any, criterion: any): number { // Simplified evaluation - in production would be more sophisticated if (result.summary && result.summary.successRate !== undefined) { return result.summary.successRate; } return 0.7; // Default passing score } /** * Complete task execution */ private async completeTask(task: Task, execution: any): Promise<void> { const finalResult = { success: true, executionTime: Date.now() - execution.startTime, phases: execution.phaseResults, summary: this.createExecutionSummary(execution), }; await this.db.updateTask(task.id, { status: 'completed', result: JSON.stringify(finalResult), progress: 100, completed_at: new Date(), }); this.emit('taskCompleted', { task, result: finalResult }); } /** * Handle task failure */ private async handleTaskFailure(task: Task, execution: any, error: any): Promise<void> { await this.db.updateTask(task.id, { status: 'failed', error: error.message, completed_at: new Date(), }); this.emit('taskFailed', { task, error }); } /** * Create execution summary */ private createExecutionSummary(execution: any): any { const phaseCount = execution.phaseResults.length; const successfulPhases = execution.phaseResults.filter( (r) => r.summary?.successRate > 0.5, ).length; return { totalPhases: phaseCount, successfulPhases, overallSuccess: phaseCount > 0 ? successfulPhases / phaseCount : 0, executionTime: Date.now() - execution.startTime, }; } /** * Notify agent of task cancellation */ private async notifyAgentTaskCancelled(agentId: string, taskId: string): Promise<void> { // Send cancellation message to agent await this.db.createCommunication({ from_agent_id: 'orchestrator', to_agent_id: agentId, swarm_id: this.hiveMind.id, message_type: 'task_cancellation', content: JSON.stringify({ taskId, reason: 'User cancelled' }), priority: 'urgent', }); } /** * Analyze load distribution */ private async analyzeLoadDistribution(): Promise<any> { const agents = await this.hiveMind.getAgents(); const tasks = await this.db.getActiveTasks(this.hiveMind.id); const busyAgents = agents.filter((a) => a.status === 'busy'); const idleAgents = agents.filter((a) => a.status === 'idle'); const unassignedTasks = tasks.filter( (t) => !t.assigned_agents || JSON.parse(t.assigned_agents).length === 0, ); return { totalAgents: agents.length, busyAgents: busyAgents.length, idleAgents: idleAgents.length, activeTasks: tasks.length, unassignedTasks: unassignedTasks.map((t) => ({ id: t.id, priority: t.priority, requiredCapabilities: JSON.parse(t.required_capabilities || '[]'), })), loadFactor: agents.length > 0 ? busyAgents.length / agents.length : 0, }; } /** * Apply load balancing reassignments */ private async applyReassignments(reassignments: any[]): Promise<void> { for (const reassignment of reassignments) { await this.reassignTask(reassignment.taskId, reassignment.fromAgent, reassignment.toAgent); } } /** * Reassign task from one agent to another */ private async reassignTask( taskId: string, fromAgentId: string, toAgentId: string, ): Promise<void> { // Update task assignment await this.db.reassignTask(taskId, toAgentId); // Update agent states await this.db.updateAgent(fromAgentId, { current_task_id: null, status: 'idle', }); await this.db.updateAgent(toAgentId, { current_task_id: taskId, status: 'busy', }); // Notify agents await this.notifyAgentReassignment(fromAgentId, toAgentId, taskId); } /** * Notify agents of reassignment */ private async notifyAgentReassignment( fromAgentId: string, toAgentId: string, taskId: string, ): Promise<void> { // Notify source agent await this.db.createCommunication({ from_agent_id: 'orchestrator', to_agent_id: fromAgentId, swarm_id: this.hiveMind.id, message_type: 'task_reassignment', content: JSON.stringify({ taskId, reassignedTo: toAgentId }), priority: 'high', }); // Notify target agent const task = await this.db.getTask(taskId); const plan = this.executionPlans.get(taskId); await this.db.createCommunication({ from_agent_id: 'orchestrator', to_agent_id: toAgentId, swarm_id: this.hiveMind.id, message_type: 'task_assignment', content: JSON.stringify({ taskId, task: task.description, executionPlan: plan, }), priority: 'high', }); } /** * Start task distributor loop */ private startTaskDistributor(): void { setInterval(async () => { if (!this.isActive) return; try { // Check for queued assignments for (const [taskId, assignments] of this.taskAssignments) { for (const assignment of assignments) { const agent = await this.findSuitableAgent(assignment.requiredCapabilities); if (agent) { await this.assignTaskToAgent(taskId, agent.id); // Remove from queue const remaining = assignments.filter((a) => a !== assignment); if (remaining.length === 0) { this.taskAssignments.delete(taskId); } else { this.taskAssignments.set(taskId, remaining); } } } } } catch (error) { this.emit('error', error); } }, 5000); // Every 5 seconds } /** * Start progress monitor loop */ private startProgressMonitor(): void { setInterval(async () => { if (!this.isActive) return; try { // Monitor active executions for (const [taskId, execution] of this.activeExecutions) { const task = await this.db.getTask(taskId); if (task.status === 'in_progress') { const progress = this.calculateProgress(execution); if (progress !== task.progress) { await this.db.updateTask(taskId, { progress }); this.emit('progressUpdate', { taskId, progress }); } } } } catch (error) { this.emit('error', error); } }, 2000); // Every 2 seconds } /** * Start load balancer loop */ private startLoadBalancer(): void { setInterval(async () => { if (!this.isActive) return; try { const load = await this.analyzeLoadDistribution(); // Trigger rebalancing if needed if ( load.loadFactor > 0.8 && load.idleAgents.length > 0 && load.unassignedTasks.length > 0 ) { await this.rebalance(); } } catch (error) { this.emit('error', error); } }, 30000); // Every 30 seconds } /** * Calculate task progress */ private calculateProgress(execution: any): number { if (!execution.plan || !execution.plan.phases) return 0; const totalPhases = execution.plan.phases.length; const completedPhases = execution.currentPhase; return Math.round((completedPhases / totalPhases) * 100); } /** * Shutdown orchestrator */ async shutdown(): Promise<void> { this.isActive = false; // Cancel all active executions for (const taskId of this.activeExecutions.keys()) { await this.cancelTask(taskId); } this.emit('shutdown'); } }