devflow-ai
Version:
Enterprise-grade AI agent orchestration with swarm management UI dashboard
1,729 lines (1,482 loc) • 94.8 kB
text/typescript
import { EventEmitter } from 'events';
import { promises as fs } from 'node:fs';
import { Logger } from '../core/logger.js';
import { generateId } from '../utils/helpers.js';
import {
SwarmId,
AgentId,
TaskId,
AgentState,
TaskDefinition,
SwarmObjective,
SwarmConfig,
SwarmStatus,
SwarmProgress,
SwarmResults,
SwarmMetrics,
SwarmMode,
SwarmStrategy,
AgentType,
TaskType,
TaskStatus,
TaskPriority,
SwarmEvent,
EventType,
SwarmEventEmitter,
ValidationResult,
SWARM_CONSTANTS,
} from './types.js';
import { AutoStrategy } from './strategies/auto.js';
import { getClaudeFlowRoot, getClaudeFlowBin } from '../utils/paths.js';
import { SwarmJsonOutputAggregator } from './json-output-aggregator.js';
export class SwarmCoordinator extends EventEmitter implements SwarmEventEmitter {
private logger: Logger;
private config: SwarmConfig;
private swarmId: SwarmId;
// Core state management
private agents: Map<string, AgentState> = new Map();
private tasks: Map<string, TaskDefinition> = new Map();
private objectives: Map<string, SwarmObjective> = new Map();
// Execution state
private _isRunning: boolean = false;
private status: SwarmStatus = 'planning';
private startTime?: Date;
private endTime?: Date;
// Performance tracking
private metrics: SwarmMetrics;
private events: SwarmEvent[] = [];
private lastHeartbeat: Date = new Date();
// JSON output aggregation (optional)
private jsonOutputAggregator?: SwarmJsonOutputAggregator;
// Background processes
private heartbeatTimer?: NodeJS.Timeout;
private monitoringTimer?: NodeJS.Timeout;
private cleanupTimer?: NodeJS.Timeout;
private executionIntervals?: Map<string, NodeJS.Timeout>;
// Strategy instances
private autoStrategy: AutoStrategy;
constructor(config: Partial<SwarmConfig> = {}) {
super();
// Configure logger based on config or default to quiet mode
const logLevel = (config as any).logging?.level || 'error';
const logFormat = (config as any).logging?.format || 'text';
const logDestination = (config as any).logging?.destination || 'console';
this.logger = new Logger(
{ level: logLevel, format: logFormat, destination: logDestination },
{ component: 'SwarmCoordinator' },
);
this.swarmId = this.generateSwarmId();
// Initialize configuration with defaults
this.config = this.mergeWithDefaults(config);
// Initialize metrics
this.metrics = this.initializeMetrics();
// Initialize strategy instances
this.autoStrategy = new AutoStrategy(config);
// Setup event handlers
this.setupEventHandlers();
this.logger.info('SwarmCoordinator initialized', {
swarmId: this.swarmId.id,
mode: this.config.mode,
strategy: this.config.strategy,
});
}
// ===== LIFECYCLE MANAGEMENT =====
async initialize(): Promise<void> {
if (this._isRunning) {
throw new Error('Swarm coordinator already running');
}
this.logger.info('Initializing swarm coordinator...');
this.status = 'initializing';
try {
// Validate configuration
const validation = await this.validateConfiguration();
if (!validation.valid) {
throw new Error(
`Configuration validation failed: ${validation.errors.map((e) => e.message).join(', ')}`,
);
}
// Initialize subsystems
await this.initializeSubsystems();
// Start background processes
this.startBackgroundProcesses();
this._isRunning = true;
this.startTime = new Date();
this.status = 'executing';
this.emitSwarmEvent({
id: generateId('event'),
timestamp: new Date(),
type: 'swarm.started',
source: this.swarmId.id,
data: { swarmId: this.swarmId },
broadcast: true,
processed: false,
});
this.logger.info('Swarm coordinator initialized successfully');
} catch (error) {
this.status = 'failed';
this.logger.error('Failed to initialize swarm coordinator', { error });
throw error;
}
}
async shutdown(): Promise<void> {
if (!this._isRunning) {
return;
}
this.logger.info('Shutting down swarm coordinator...');
this.status = 'paused';
try {
// Stop background processes
this.stopBackgroundProcesses();
// Gracefully stop all agents
await this.stopAllAgents();
// Complete any running tasks
await this.completeRunningTasks();
// Save final state
await this.saveState();
this._isRunning = false;
this.endTime = new Date();
this.status = 'completed';
this.emitSwarmEvent({
id: generateId('event'),
timestamp: new Date(),
type: 'swarm.completed',
source: this.swarmId.id,
data: {
swarmId: this.swarmId,
metrics: this.metrics,
duration: this.endTime.getTime() - (this.startTime?.getTime() || 0),
},
broadcast: true,
processed: false,
});
this.logger.info('Swarm coordinator shut down successfully');
} catch (error) {
this.logger.error('Error during swarm coordinator shutdown', { error });
throw error;
}
}
async pause(): Promise<void> {
if (!this._isRunning || this.status === 'paused') {
return;
}
this.logger.info('Pausing swarm coordinator...');
this.status = 'paused';
// Pause all agents
for (const agent of this.agents.values()) {
if (agent.status === 'busy') {
await this.pauseAgent(agent.id);
}
}
this.emitSwarmEvent({
id: generateId('event'),
timestamp: new Date(),
type: 'swarm.paused',
source: this.swarmId.id,
data: { swarmId: this.swarmId },
broadcast: true,
processed: false,
});
}
async resume(): Promise<void> {
if (!this._isRunning || this.status !== 'paused') {
return;
}
this.logger.info('Resuming swarm coordinator...');
this.status = 'executing';
// Resume all paused agents
for (const agent of this.agents.values()) {
if (agent.status === 'paused') {
await this.resumeAgent(agent.id);
}
}
this.emitSwarmEvent({
id: generateId('event'),
timestamp: new Date(),
type: 'swarm.resumed',
source: this.swarmId.id,
data: { swarmId: this.swarmId },
broadcast: true,
processed: false,
});
}
// ===== OBJECTIVE MANAGEMENT =====
async createObjective(
name: string,
description: string,
strategy: SwarmStrategy = 'auto',
requirements: Partial<SwarmObjective['requirements']> = {},
): Promise<string> {
const objectiveId = generateId('objective');
const objective: SwarmObjective = {
id: objectiveId,
name,
description,
strategy,
mode: this.config.mode,
requirements: {
minAgents: 1,
maxAgents: this.config.maxAgents,
agentTypes: this.determineRequiredAgentTypes(strategy),
estimatedDuration: 60 * 60 * 1000, // 1 hour default
maxDuration: 4 * 60 * 60 * 1000, // 4 hours default
qualityThreshold: this.config.qualityThreshold,
reviewCoverage: 0.8,
testCoverage: 0.7,
reliabilityTarget: 0.95,
...requirements,
},
constraints: {
minQuality: this.config.qualityThreshold,
requiredApprovals: [],
allowedFailures: Math.floor(this.config.maxAgents * 0.1),
recoveryTime: 5 * 60 * 1000, // 5 minutes
milestones: [],
},
tasks: [],
dependencies: [],
status: 'planning',
progress: this.initializeProgress(),
createdAt: new Date(),
metrics: this.initializeMetrics(),
};
// Decompose objective into tasks using optimized AUTO strategy
if (objective.strategy === 'auto') {
const decompositionResult = await this.autoStrategy.decomposeObjective(objective);
objective.tasks = decompositionResult.tasks;
objective.dependencies = this.convertDependenciesToTaskDependencies(
decompositionResult.dependencies,
);
} else {
objective.tasks = await this.decomposeObjective(objective);
objective.dependencies = this.analyzeDependencies(objective.tasks);
}
this.objectives.set(objectiveId, objective);
this.logger.info('Created objective', {
objectiveId,
name,
strategy,
taskCount: objective.tasks.length,
});
return objectiveId;
}
async executeObjective(objectiveId: string): Promise<void> {
const objective = this.objectives.get(objectiveId);
if (!objective) {
throw new Error(`Objective not found: ${objectiveId}`);
}
if (objective.status !== 'planning') {
throw new Error(`Objective already ${objective.status}`);
}
this.logger.info('Executing objective', { objectiveId, name: objective.name });
objective.status = 'executing';
objective.startedAt = new Date();
try {
// Ensure we have required agents
await this.ensureRequiredAgents(objective);
// Schedule initial tasks
await this.scheduleInitialTasks(objective);
// Start task execution loop
this.startTaskExecutionLoop(objective);
} catch (error) {
objective.status = 'failed';
this.logger.error('Failed to execute objective', { objectiveId, error });
throw error;
}
}
// ===== AGENT MANAGEMENT =====
async registerAgent(
name: string,
type: AgentType,
capabilities: Partial<AgentState['capabilities']> = {},
): Promise<string> {
const agentId: AgentId = {
id: generateId('agent'),
swarmId: this.swarmId.id,
type,
instance: this.getNextInstanceNumber(type),
};
const agentState: AgentState = {
id: agentId,
name,
type,
status: 'initializing',
capabilities: {
// Default capabilities
codeGeneration: false,
codeReview: false,
testing: false,
documentation: false,
research: false,
analysis: false,
webSearch: false,
apiIntegration: false,
fileSystem: true,
terminalAccess: true,
languages: [],
frameworks: [],
domains: [],
tools: [],
maxConcurrentTasks: 3,
maxMemoryUsage: SWARM_CONSTANTS.DEFAULT_MEMORY_LIMIT,
maxExecutionTime: SWARM_CONSTANTS.DEFAULT_TASK_TIMEOUT,
reliability: 0.8,
speed: 1.0,
quality: 0.8,
...capabilities,
},
metrics: {
tasksCompleted: 0,
tasksFailed: 0,
averageExecutionTime: 0,
successRate: 0,
cpuUsage: 0,
memoryUsage: 0,
diskUsage: 0,
networkUsage: 0,
codeQuality: 0,
testCoverage: 0,
bugRate: 0,
userSatisfaction: 0,
totalUptime: 0,
lastActivity: new Date(),
responseTime: 0,
},
workload: 0,
health: 1.0,
config: {
autonomyLevel: 0.7,
learningEnabled: true,
adaptationEnabled: true,
maxTasksPerHour: 10,
maxConcurrentTasks: capabilities.maxConcurrentTasks || 3,
timeoutThreshold: SWARM_CONSTANTS.DEFAULT_TASK_TIMEOUT,
reportingInterval: 30000,
heartbeatInterval: SWARM_CONSTANTS.DEFAULT_HEARTBEAT_INTERVAL,
permissions: this.getDefaultPermissions(type),
trustedAgents: [],
expertise: {},
preferences: {},
},
environment: {
runtime: 'deno',
version: '1.0.0',
workingDirectory: `/tmp/swarm/${this.swarmId.id}/agents/${agentId.id}`,
tempDirectory: `/tmp/swarm/${this.swarmId.id}/agents/${agentId.id}/temp`,
logDirectory: `/tmp/swarm/${this.swarmId.id}/agents/${agentId.id}/logs`,
apiEndpoints: {},
credentials: {},
availableTools: [],
toolConfigs: {},
},
endpoints: [],
lastHeartbeat: new Date(),
taskHistory: [],
errorHistory: [],
childAgents: [],
collaborators: [],
};
this.agents.set(agentId.id, agentState);
// Track agent in JSON output if enabled
this.trackAgentInJsonOutput(agentState);
// Initialize agent capabilities based on type
await this.initializeAgentCapabilities(agentState);
// Start agent
await this.startAgent(agentId.id);
this.logger.info('Registered agent', {
agentId: agentId.id,
name,
type,
capabilities: Object.keys(capabilities),
});
this.emitSwarmEvent({
id: generateId('event'),
timestamp: new Date(),
type: 'agent.created',
source: agentId.id,
data: { agent: agentState },
broadcast: false,
processed: false,
});
return agentId.id;
}
async unregisterAgent(agentId: string): Promise<void> {
const agent = this.agents.get(agentId);
if (!agent) {
return;
}
this.logger.info('Unregistering agent', { agentId, name: agent.name });
// Stop agent gracefully
await this.stopAgent(agentId);
// Reassign any active tasks
if (agent.currentTask) {
await this.reassignTask(agent.currentTask.id);
}
// Remove from agents map
this.agents.delete(agentId);
this.emitSwarmEvent({
id: generateId('event'),
timestamp: new Date(),
type: 'agent.stopped',
source: agentId,
data: { agentId },
broadcast: false,
processed: false,
});
}
async startAgent(agentId: string): Promise<void> {
const agent = this.agents.get(agentId);
if (!agent) {
throw new Error(`Agent not found: ${agentId}`);
}
if (agent.status !== 'initializing' && agent.status !== 'offline') {
return;
}
this.logger.info('Starting agent', { agentId, name: agent.name });
try {
// Initialize agent environment
await this.initializeAgentEnvironment(agent);
// Start agent heartbeat
this.startAgentHeartbeat(agent);
agent.status = 'idle';
agent.lastHeartbeat = new Date();
this.emitSwarmEvent({
id: generateId('event'),
timestamp: new Date(),
type: 'agent.started',
source: agentId,
data: { agent },
broadcast: false,
processed: false,
});
} catch (error) {
agent.status = 'error';
agent.errorHistory.push({
timestamp: new Date(),
type: 'startup_error',
message: error instanceof Error ? error.message : String(error),
stack: error.stack,
context: { agentId },
severity: 'high',
resolved: false,
});
this.logger.error('Failed to start agent', { agentId, error });
throw error;
}
}
async stopAgent(agentId: string): Promise<void> {
const agent = this.agents.get(agentId);
if (!agent) {
return;
}
if (agent.status === 'offline' || agent.status === 'terminated') {
return;
}
this.logger.info('Stopping agent', { agentId, name: agent.name });
agent.status = 'terminating';
try {
// Cancel current task if any
if (agent.currentTask) {
await this.cancelTask(agent.currentTask.id, 'Agent stopping');
}
// Stop heartbeat
this.stopAgentHeartbeat(agent);
// Cleanup agent environment
await this.cleanupAgentEnvironment(agent);
agent.status = 'terminated';
} catch (error) {
agent.status = 'error';
this.logger.error('Error stopping agent', { agentId, error });
}
}
async pauseAgent(agentId: string): Promise<void> {
const agent = this.agents.get(agentId);
if (!agent || agent.status !== 'busy') {
return;
}
agent.status = 'paused';
this.logger.info('Paused agent', { agentId });
}
async resumeAgent(agentId: string): Promise<void> {
const agent = this.agents.get(agentId);
if (!agent || agent.status !== 'paused') {
return;
}
agent.status = 'busy';
this.logger.info('Resumed agent', { agentId });
}
// ===== TASK MANAGEMENT =====
async createTask(
type: TaskType,
name: string,
description: string,
instructions: string,
options: Partial<TaskDefinition> = {},
): Promise<string> {
const taskId: TaskId = {
id: generateId('task'),
swarmId: this.swarmId.id,
sequence: this.tasks.size + 1,
priority: 1,
};
const task: TaskDefinition = {
id: taskId,
type,
name,
description,
instructions,
requirements: {
capabilities: this.getRequiredCapabilities(type),
tools: this.getRequiredTools(type),
permissions: this.getRequiredPermissions(type),
...options.requirements,
},
constraints: {
dependencies: [],
dependents: [],
conflicts: [],
maxRetries: SWARM_CONSTANTS.MAX_RETRIES,
timeoutAfter: SWARM_CONSTANTS.DEFAULT_TASK_TIMEOUT,
...options.constraints,
},
priority: 'normal',
input: options.input || {},
context: options.context || {},
examples: options.examples || [],
status: 'created',
createdAt: new Date(),
updatedAt: new Date(),
attempts: [],
statusHistory: [
{
timestamp: new Date(),
from: 'created' as TaskStatus,
to: 'created' as TaskStatus,
reason: 'Task created',
triggeredBy: 'system',
},
],
...options,
};
this.tasks.set(taskId.id, task);
// Track task in JSON output if enabled
this.trackTaskInJsonOutput(task);
this.logger.info('Created task', {
taskId: taskId.id,
type,
name,
priority: task.priority,
});
this.emitSwarmEvent({
id: generateId('event'),
timestamp: new Date(),
type: 'task.created',
source: this.swarmId.id,
data: { task },
broadcast: false,
processed: false,
});
return taskId.id;
}
async assignTask(taskId: string, agentId?: string): Promise<void> {
const task = this.tasks.get(taskId);
if (!task) {
throw new Error(`Task not found: ${taskId}`);
}
if (task.status !== 'created' && task.status !== 'queued') {
throw new Error(`Task cannot be assigned, current status: ${task.status}`);
}
// Select agent if not specified
if (!agentId) {
agentId = await this.selectAgentForTask(task);
if (!agentId) {
throw new Error('No suitable agent available for task');
}
}
const agent = this.agents.get(agentId);
if (!agent) {
throw new Error(`Agent not found: ${agentId}`);
}
if (agent.status !== 'idle') {
throw new Error(`Agent not available: ${agent.status}`);
}
// Assign task
task.assignedTo = agent.id;
task.assignedAt = new Date();
task.status = 'assigned';
agent.currentTask = task.id;
agent.status = 'busy';
// Update status history
task.statusHistory.push({
timestamp: new Date(),
from: task.statusHistory[task.statusHistory.length - 1].to,
to: 'assigned',
reason: `Assigned to agent ${agent.name}`,
triggeredBy: 'system',
});
this.logger.info('Assigned task', {
taskId,
agentId,
agentName: agent.name,
});
this.emitSwarmEvent({
id: generateId('event'),
timestamp: new Date(),
type: 'task.assigned',
source: agentId,
data: { task, agent },
broadcast: false,
processed: false,
});
// Start task execution
await this.startTaskExecution(task);
}
async startTaskExecution(task: TaskDefinition): Promise<void> {
if (!task.assignedTo) {
throw new Error('Task not assigned to any agent');
}
const agent = this.agents.get(task.assignedTo.id);
if (!agent) {
throw new Error(`Agent not found: ${task.assignedTo.id}`);
}
this.logger.info('Starting task execution', {
taskId: task.id.id,
agentId: agent.id.id,
});
task.status = 'running';
task.startedAt = new Date();
// Create attempt record
const attempt = {
attemptNumber: task.attempts.length + 1,
agent: agent.id,
startedAt: new Date(),
status: 'running' as TaskStatus,
resourcesUsed: {},
};
task.attempts.push(attempt);
// Update status history
task.statusHistory.push({
timestamp: new Date(),
from: 'assigned',
to: 'running',
reason: 'Task execution started',
triggeredBy: agent.id,
});
this.emitSwarmEvent({
id: generateId('event'),
timestamp: new Date(),
type: 'task.started',
source: agent.id.id,
data: { task, agent, attempt },
broadcast: false,
processed: false,
});
try {
// Execute task (this would spawn actual Claude process)
const result = await this.executeTaskWithAgent(task, agent);
await this.completeTask(task.id.id, result);
} catch (error) {
await this.failTask(task.id.id, error);
}
}
async completeTask(taskId: string, result: any): Promise<void> {
const task = this.tasks.get(taskId);
if (!task) {
throw new Error(`Task not found: ${taskId}`);
}
const agent = task.assignedTo ? this.agents.get(task.assignedTo.id) : null;
if (!agent) {
throw new Error('Task not assigned to any agent');
}
this.logger.info('Completing task', { taskId, agentId: agent.id.id });
task.status = 'completed';
task.completedAt = new Date();
task.result = {
output: result,
artifacts: {},
metadata: {},
quality: this.assessTaskQuality(task, result),
completeness: 1.0,
accuracy: 1.0,
executionTime: task.completedAt.getTime() - (task.startedAt?.getTime() || 0),
resourcesUsed: {},
validated: false,
};
// Update attempt
const currentAttempt = task.attempts[task.attempts.length - 1];
if (currentAttempt) {
currentAttempt.completedAt = new Date();
currentAttempt.status = 'completed';
currentAttempt.result = task.result;
}
// Update agent state
agent.status = 'idle';
agent.currentTask = undefined;
agent.metrics.tasksCompleted++;
agent.metrics.lastActivity = new Date();
agent.taskHistory.push(task.id);
// Update agent metrics
this.updateAgentMetrics(agent, task);
// Update status history
task.statusHistory.push({
timestamp: new Date(),
from: 'running',
to: 'completed',
reason: 'Task completed successfully',
triggeredBy: agent.id,
});
this.emitSwarmEvent({
id: generateId('event'),
timestamp: new Date(),
type: 'task.completed',
source: agent.id.id,
data: { task, agent, result: task.result },
broadcast: false,
processed: false,
});
// Check for dependent tasks
await this.processDependentTasks(task);
}
async failTask(taskId: string, error: any): Promise<void> {
const task = this.tasks.get(taskId);
if (!task) {
throw new Error(`Task not found: ${taskId}`);
}
const agent = task.assignedTo ? this.agents.get(task.assignedTo.id) : null;
if (!agent) {
throw new Error('Task not assigned to any agent');
}
this.logger.warn('Task failed', {
taskId,
agentId: agent.id.id,
error: error instanceof Error ? error.message : String(error),
});
task.error = {
type: error.constructor.name,
message: error instanceof Error ? error.message : String(error),
code: error.code,
stack: error.stack,
context: { taskId, agentId: agent.id.id },
recoverable: this.isRecoverableError(error),
retryable: this.isRetryableError(error),
};
// Update attempt
const currentAttempt = task.attempts[task.attempts.length - 1];
if (currentAttempt) {
currentAttempt.completedAt = new Date();
currentAttempt.status = 'failed';
currentAttempt.error = task.error;
}
// Update agent state
agent.status = 'idle';
agent.currentTask = undefined;
agent.metrics.tasksFailed++;
agent.metrics.lastActivity = new Date();
// Add to error history
agent.errorHistory.push({
timestamp: new Date(),
type: 'task_failure',
message: error instanceof Error ? error.message : String(error),
stack: error.stack,
context: { taskId },
severity: 'medium',
resolved: false,
});
// Determine if we should retry
const shouldRetry =
task.error.retryable &&
task.attempts.length < (task.constraints.maxRetries || SWARM_CONSTANTS.MAX_RETRIES);
if (shouldRetry) {
task.status = 'retrying';
task.assignedTo = undefined;
// Update status history
task.statusHistory.push({
timestamp: new Date(),
from: 'running',
to: 'retrying',
reason: `Task failed, will retry: ${error instanceof Error ? error.message : String(error)}`,
triggeredBy: agent.id,
});
this.emitSwarmEvent({
id: generateId('event'),
timestamp: new Date(),
type: 'task.retried',
source: agent.id.id,
data: { task, error: task.error, attempt: task.attempts.length },
broadcast: false,
processed: false,
});
// Schedule retry with exponential backoff
const retryDelay = Math.pow(2, task.attempts.length) * 1000;
setTimeout(() => {
this.assignTask(taskId).catch((retryError) => {
this.logger.error('Failed to retry task', { taskId, retryError });
});
}, retryDelay);
} else {
task.status = 'failed';
task.completedAt = new Date();
// Update status history
task.statusHistory.push({
timestamp: new Date(),
from: 'running',
to: 'failed',
reason: `Task failed permanently: ${error instanceof Error ? error.message : String(error)}`,
triggeredBy: agent.id,
});
this.emitSwarmEvent({
id: generateId('event'),
timestamp: new Date(),
type: 'task.failed',
source: agent.id.id,
data: { task, error: task.error },
broadcast: false,
processed: false,
});
// Handle failure cascade
await this.handleTaskFailureCascade(task);
}
}
async cancelTask(taskId: string, reason: string): Promise<void> {
const task = this.tasks.get(taskId);
if (!task) {
throw new Error(`Task not found: ${taskId}`);
}
const agent = task.assignedTo ? this.agents.get(task.assignedTo.id) : null;
this.logger.info('Cancelling task', { taskId, reason });
task.status = 'cancelled';
task.completedAt = new Date();
if (agent) {
agent.status = 'idle';
agent.currentTask = undefined;
}
// Update status history
task.statusHistory.push({
timestamp: new Date(),
from: task.statusHistory[task.statusHistory.length - 1].to,
to: 'cancelled',
reason: `Task cancelled: ${reason}`,
triggeredBy: 'system',
});
this.emitSwarmEvent({
id: generateId('event'),
timestamp: new Date(),
type: 'task.cancelled',
source: this.swarmId.id,
data: { task, reason },
broadcast: false,
processed: false,
});
}
// ===== ADVANCED FEATURES =====
async selectAgentForTask(task: TaskDefinition): Promise<string | null> {
const availableAgents = Array.from(this.agents.values()).filter(
(agent) => agent.status === 'idle' && this.agentCanHandleTask(agent, task),
);
if (availableAgents.length === 0) {
return null;
}
// Score agents based on multiple criteria
const scoredAgents = availableAgents.map((agent) => ({
agent,
score: this.calculateAgentScore(agent, task),
}));
// Sort by score (highest first)
scoredAgents.sort((a, b) => b.score - a.score);
return scoredAgents[0].agent.id.id;
}
private calculateAgentScore(agent: AgentState, task: TaskDefinition): number {
let score = 0;
// Capability match (40% weight)
const capabilityMatch = this.calculateCapabilityMatch(agent, task);
score += capabilityMatch * 0.4;
// Performance history (30% weight)
const performanceScore = agent.metrics.successRate * agent.capabilities.reliability;
score += performanceScore * 0.3;
// Current workload (20% weight)
const workloadScore = 1 - agent.workload;
score += workloadScore * 0.2;
// Quality rating (10% weight)
score += agent.capabilities.quality * 0.1;
return score;
}
private calculateCapabilityMatch(agent: AgentState, task: TaskDefinition): number {
const requiredCapabilities = task.requirements.capabilities;
let matches = 0;
let total = requiredCapabilities.length;
for (const capability of requiredCapabilities) {
if (this.agentHasCapability(agent, capability)) {
matches++;
}
}
return total > 0 ? matches / total : 1.0;
}
private agentHasCapability(agent: AgentState, capability: string): boolean {
const caps = agent.capabilities;
switch (capability) {
case 'code-generation':
return caps.codeGeneration;
case 'code-review':
return caps.codeReview;
case 'testing':
return caps.testing;
case 'documentation':
return caps.documentation;
case 'research':
return caps.research;
case 'analysis':
return caps.analysis;
case 'web-search':
return caps.webSearch;
case 'api-integration':
return caps.apiIntegration;
case 'file-system':
return caps.fileSystem;
case 'terminal-access':
return caps.terminalAccess;
case 'validation':
return caps.testing; // Validation is part of testing capability
default:
return (
caps.domains.includes(capability) ||
caps.languages.includes(capability) ||
caps.frameworks.includes(capability) ||
caps.tools.includes(capability)
);
}
}
private agentCanHandleTask(agent: AgentState, task: TaskDefinition): boolean {
// Check if agent type is suitable
if (task.requirements.agentType && agent.type !== task.requirements.agentType) {
return false;
}
// Check if agent has required capabilities
for (const capability of task.requirements.capabilities) {
if (!this.agentHasCapability(agent, capability)) {
return false;
}
}
// Check reliability requirement
if (
task.requirements.minReliability &&
agent.capabilities.reliability < task.requirements.minReliability
) {
return false;
}
// Check if agent has capacity
if (agent.workload >= 1.0) {
return false;
}
return true;
}
// ===== HELPER METHODS =====
private generateSwarmId(): SwarmId {
return {
id: generateId('swarm'),
timestamp: Date.now(),
namespace: 'default',
};
}
private mergeWithDefaults(config: Partial<SwarmConfig>): SwarmConfig {
return {
name: 'Unnamed Swarm',
description: 'Auto-generated swarm',
version: '1.0.0',
mode: 'centralized',
strategy: 'auto',
coordinationStrategy: {
name: 'default',
description: 'Default coordination strategy',
agentSelection: 'capability-based',
taskScheduling: 'priority',
loadBalancing: 'work-stealing',
faultTolerance: 'retry',
communication: 'event-driven',
},
maxAgents: 10,
maxTasks: 100,
maxDuration: 4 * 60 * 60 * 1000, // 4 hours
resourceLimits: {
memory: SWARM_CONSTANTS.DEFAULT_MEMORY_LIMIT,
cpu: SWARM_CONSTANTS.DEFAULT_CPU_LIMIT,
disk: SWARM_CONSTANTS.DEFAULT_DISK_LIMIT,
},
qualityThreshold: SWARM_CONSTANTS.DEFAULT_QUALITY_THRESHOLD,
reviewRequired: true,
testingRequired: true,
monitoring: {
metricsEnabled: true,
loggingEnabled: true,
tracingEnabled: false,
metricsInterval: 10000,
heartbeatInterval: SWARM_CONSTANTS.DEFAULT_HEARTBEAT_INTERVAL,
healthCheckInterval: 30000,
retentionPeriod: 24 * 60 * 60 * 1000, // 24 hours
maxLogSize: 100 * 1024 * 1024, // 100MB
maxMetricPoints: 10000,
alertingEnabled: true,
alertThresholds: {
errorRate: 0.1,
responseTime: 5000,
memoryUsage: 0.8,
cpuUsage: 0.8,
},
exportEnabled: false,
exportFormat: 'json',
exportDestination: '/tmp/swarm-metrics',
},
memory: {
namespace: 'default',
partitions: [],
permissions: {
read: 'swarm',
write: 'team',
delete: 'private',
share: 'team',
},
persistent: true,
backupEnabled: true,
distributed: false,
consistency: 'eventual',
cacheEnabled: true,
compressionEnabled: false,
},
security: {
authenticationRequired: false,
authorizationRequired: false,
encryptionEnabled: false,
defaultPermissions: ['read', 'write'],
adminRoles: ['admin', 'coordinator'],
auditEnabled: true,
auditLevel: 'info',
inputValidation: true,
outputSanitization: true,
},
performance: {
maxConcurrency: 10,
defaultTimeout: SWARM_CONSTANTS.DEFAULT_TASK_TIMEOUT,
cacheEnabled: true,
cacheSize: 100,
cacheTtl: 3600000, // 1 hour
optimizationEnabled: true,
adaptiveScheduling: true,
predictiveLoading: false,
resourcePooling: true,
connectionPooling: true,
memoryPooling: false,
},
...config,
};
}
private initializeMetrics(): SwarmMetrics {
return {
throughput: 0,
latency: 0,
efficiency: 0,
reliability: 0,
averageQuality: 0,
defectRate: 0,
reworkRate: 0,
resourceUtilization: {},
costEfficiency: 0,
agentUtilization: 0,
agentSatisfaction: 0,
collaborationEffectiveness: 0,
scheduleVariance: 0,
deadlineAdherence: 0,
};
}
private initializeProgress(): SwarmProgress {
return {
totalTasks: 0,
completedTasks: 0,
failedTasks: 0,
runningTasks: 0,
estimatedCompletion: new Date(),
timeRemaining: 0,
percentComplete: 0,
averageQuality: 0,
passedReviews: 0,
passedTests: 0,
resourceUtilization: {},
costSpent: 0,
activeAgents: 0,
idleAgents: 0,
busyAgents: 0,
};
}
// ===== EVENT HANDLING =====
private setupEventHandlers(): void {
// Handle agent heartbeats
this.on('agent.heartbeat', (data: any) => {
const agent = this.agents.get(data.agentId);
if (agent) {
agent.lastHeartbeat = new Date();
agent.health = data.health || 1.0;
agent.metrics = { ...agent.metrics, ...data.metrics };
}
});
// Handle task completion events
this.on('task.completed', (data: any) => {
this.updateSwarmMetrics();
this.checkObjectiveCompletion();
});
// Handle task failure events
this.on('task.failed', (data: any) => {
this.updateSwarmMetrics();
this.checkObjectiveFailure(data.task);
});
// Handle agent errors
this.on('agent.error', (data: any) => {
this.handleAgentError(data.agentId, data.error);
});
}
// ===== SWARM EVENT EMITTER IMPLEMENTATION =====
emitSwarmEvent(event: SwarmEvent): boolean {
this.events.push(event);
// Limit event history
if (this.events.length > 1000) {
this.events = this.events.slice(-500);
}
return this.emit(event.type, event);
}
emitSwarmEvents(events: SwarmEvent[]): boolean {
let success = true;
for (const event of events) {
if (!this.emitSwarmEvent(event)) {
success = false;
}
}
return success;
}
onSwarmEvent(type: EventType, handler: (event: SwarmEvent) => void): this {
return this.on(type, handler);
}
offSwarmEvent(type: EventType, handler: (event: SwarmEvent) => void): this {
return this.off(type, handler);
}
filterEvents(predicate: (event: SwarmEvent) => boolean): SwarmEvent[] {
return this.events.filter(predicate);
}
correlateEvents(correlationId: string): SwarmEvent[] {
return this.events.filter((event) => event.correlationId === correlationId);
}
// ===== PUBLIC API METHODS =====
getSwarmId(): SwarmId {
return this.swarmId;
}
getStatus(): SwarmStatus {
return this.status;
}
getAgents(): AgentState[] {
return Array.from(this.agents.values());
}
getAgent(agentId: string): AgentState | undefined {
return this.agents.get(agentId);
}
getTasks(): TaskDefinition[] {
return Array.from(this.tasks.values());
}
getTask(taskId: string): TaskDefinition | undefined {
return this.tasks.get(taskId);
}
getObjectives(): SwarmObjective[] {
return Array.from(this.objectives.values());
}
getObjective(objectiveId: string): SwarmObjective | undefined {
return this.objectives.get(objectiveId);
}
getMetrics(): SwarmMetrics {
return { ...this.metrics };
}
getEvents(): SwarmEvent[] {
return [...this.events];
}
isRunning(): boolean {
return this._isRunning;
}
getUptime(): number {
if (!this.startTime) return 0;
const endTime = this.endTime || new Date();
return endTime.getTime() - this.startTime.getTime();
}
getSwarmStatus(): {
status: SwarmStatus;
objectives: number;
tasks: { completed: number; failed: number; total: number };
agents: { total: number };
} {
const tasks = Array.from(this.tasks.values());
const completedTasks = tasks.filter((t) => t.status === 'completed').length;
const failedTasks = tasks.filter((t) => t.status === 'failed').length;
return {
status: this.status,
objectives: this.objectives.size,
tasks: {
completed: completedTasks,
failed: failedTasks,
total: tasks.length,
},
agents: {
total: this.agents.size,
},
};
}
// ===== STUB METHODS (TO BE IMPLEMENTED) =====
private async validateConfiguration(): Promise<ValidationResult> {
// Implementation needed
return {
valid: true,
errors: [],
warnings: [],
validatedAt: new Date(),
validator: 'SwarmCoordinator',
context: {},
};
}
private async initializeSubsystems(): Promise<void> {
// Implementation needed
}
private startBackgroundProcesses(): void {
// Start heartbeat monitoring
this.heartbeatTimer = setInterval(() => {
this.processHeartbeats();
}, this.config.monitoring.heartbeatInterval);
// Start performance monitoring
this.monitoringTimer = setInterval(() => {
this.updateSwarmMetrics();
}, this.config.monitoring.metricsInterval);
// Start cleanup process
this.cleanupTimer = setInterval(() => {
this.performCleanup();
}, 60000); // Every minute
}
private stopBackgroundProcesses(): void {
if (this.heartbeatTimer) {
clearInterval(this.heartbeatTimer);
this.heartbeatTimer = undefined;
}
if (this.monitoringTimer) {
clearInterval(this.monitoringTimer);
this.monitoringTimer = undefined;
}
if (this.cleanupTimer) {
clearInterval(this.cleanupTimer);
this.cleanupTimer = undefined;
}
// Stop all execution intervals
if (this.executionIntervals) {
for (const [objectiveId, interval] of this.executionIntervals) {
clearInterval(interval);
}
this.executionIntervals.clear();
}
}
private async stopAllAgents(): Promise<void> {
const stopPromises = Array.from(this.agents.keys()).map((agentId) => this.stopAgent(agentId));
await Promise.allSettled(stopPromises);
}
private async completeRunningTasks(): Promise<void> {
const runningTasks = Array.from(this.tasks.values()).filter(
(task) => task.status === 'running',
);
// Wait for tasks to complete or timeout
const timeout = 30000; // 30 seconds
const deadline = Date.now() + timeout;
while (runningTasks.some((task) => task.status === 'running') && Date.now() < deadline) {
await new Promise((resolve) => setTimeout(resolve, 1000));
}
// Cancel any remaining running tasks
for (const task of runningTasks) {
if (task.status === 'running') {
await this.cancelTask(task.id.id, 'Swarm shutdown');
}
}
}
private async saveState(): Promise<void> {
// Implementation needed - save swarm state to persistence layer
}
private determineRequiredAgentTypes(strategy: SwarmStrategy): AgentType[] {
switch (strategy) {
case 'research':
return ['researcher', 'analyst'];
case 'development':
return ['coder', 'tester', 'reviewer'];
case 'analysis':
return ['analyst', 'researcher'];
case 'testing':
return ['tester', 'coder'];
case 'optimization':
return ['analyst', 'coder'];
case 'maintenance':
return ['coder', 'monitor'];
default:
return ['coordinator', 'coder', 'analyst'];
}
}
private getAgentTypeInstructions(agentType: string): string {
switch (agentType) {
case 'coder':
return '- Focus on implementation, code quality, and best practices\n- Create clean, maintainable code\n- Consider architecture and design patterns';
case 'tester':
return '- Focus on testing, edge cases, and quality assurance\n- Create comprehensive test suites\n- Identify potential bugs and issues';
case 'analyst':
return '- Focus on analysis, research, and understanding\n- Break down complex problems\n- Provide insights and recommendations';
case 'researcher':
return '- Focus on gathering information and best practices\n- Research existing solutions and patterns\n- Document findings and recommendations';
case 'reviewer':
return '- Focus on code review and quality checks\n- Identify improvements and optimizations\n- Ensure standards compliance';
case 'coordinator':
return '- Focus on coordination and integration\n- Ensure all parts work together\n- Manage dependencies and interfaces';
case 'monitor':
return '- Focus on monitoring and observability\n- Set up logging and metrics\n- Ensure system health tracking';
default:
return '- Execute the task to the best of your ability\n- Follow best practices for your domain';
}
}
private getAgentCapabilities(agentType: string): string[] {
switch (agentType) {
case 'coder':
return ['code-generation', 'file-system', 'debugging'];
case 'tester':
return ['testing', 'code-generation', 'analysis'];
case 'analyst':
return ['analysis', 'documentation', 'research'];
case 'researcher':
return ['research', 'documentation', 'analysis'];
case 'reviewer':
return ['code-review', 'analysis', 'documentation'];
case 'coordinator':
return ['coordination', 'analysis', 'documentation'];
case 'monitor':
return ['monitoring', 'analysis', 'documentation'];
default:
return ['analysis', 'documentation'];
}
}
private async decomposeObjective(objective: SwarmObjective): Promise<TaskDefinition[]> {
// Decompose objective into tasks with clear instructions for Claude
this.logger.info('Decomposing objective', {
objectiveId: objective.id,
description: objective.description,
});
const tasks: TaskDefinition[] = [];
// Extract target directory from objective
const targetDirMatch = objective.description.match(
/(?:in|to|at)\s+([^\s]+\/[^\s]+)|([^\s]+\/[^\s]+)$/,
);
const targetDir = targetDirMatch ? targetDirMatch[1] || targetDirMatch[2] : null;
const targetPath = targetDir
? targetDir.startsWith('/')
? targetDir
: `${getClaudeFlowRoot()}/${targetDir}`
: null;
// Check if objective requests "each agent" or "each agent type" for parallel execution
const eachAgentPattern = /\beach\s+agent(?:\s+type)?\b/i;
const requestsParallelAgents = eachAgentPattern.test(objective.description);
// Create tasks with specific prompts for Claude
if (requestsParallelAgents && this.config.mode === 'parallel') {
// Create parallel tasks for each agent type
const agentTypes = this.determineRequiredAgentTypes(objective.strategy);
this.logger.info('Creating parallel tasks for each agent type', {
agentTypes,
mode: this.config.mode,
});
for (const agentType of agentTypes) {
const taskId = this.createTaskForObjective(`${agentType}-task`, agentType as TaskType, {
title: `${agentType.charAt(0).toUpperCase() + agentType.slice(1)} Agent Task`,
description: `${agentType} agent executing: ${objective.description}`,
instructions: `You are a ${agentType} agent. Please execute the following task from your perspective:
${objective.description}
${targetPath ? `Target Directory: ${targetPath}` : ''}
As a ${agentType} agent, focus on aspects relevant to your role:
${this.getAgentTypeInstructions(agentType)}
Work independently but be aware that other agents are working on this same objective from their perspectives.`,
priority: 'high' as TaskPriority,
estimatedDuration: 10 * 60 * 1000,
requiredCapabilities: this.getAgentCapabilities(agentType),
});
tasks.push(taskId);
}
} else if (objective.strategy === 'development') {
// Task 1: Analyze and Plan
const task1 = this.createTaskForObjective('analyze-requirements', 'analysis', {
title: 'Analyze Requirements and Plan Implementation',
description: `Analyze the requirements and create a plan for: ${objective.description}`,
instructions: `Please analyze the following request and create a detailed implementation plan:
Request: ${objective.description}
Target Directory: ${targetPath || 'Not specified - determine appropriate location'}
Your analysis should include:
1. Understanding of what needs to be built
2. Technology choices and rationale
3. Project structure and file organization
4. Key components and their responsibilities
5. Any external dependencies needed
Please provide a clear, structured plan that the next tasks can follow.`,
priority: 'high' as TaskPriority,
estimatedDuration: 5 * 60 * 1000,
requiredCapabilities: ['analysis', 'documentation'],
});
tasks.push(task1);
// Task 2: Implementation
const task2 = this.createTaskForObjective('create-implementation', 'coding', {
title: 'Implement the Solution',
description: `Create the implementation for: ${objective.description}`,
instructions: `Please implement the following request:
Request: ${objective.description}
Target Directory: ${targetPath || 'Create in an appropriate location'}
Based on the analysis from the previous task, please:
1. Create all necessary files and directories
2. Implement the core functionality as requested
3. Ensure the code is well-structured and follows best practices
4. Include appropriate error handling
5. Add any necessary configuration files (package.json, requirements.txt, etc.)
Focus on creating a working implementation that matches the user's request exactly.`,
priority: 'high' as TaskPriority,
estimatedDuration: 10 * 60 * 1000,
requiredCapabilities: ['code-generation', 'file-system'],
dependencies: [task1.id.id],
});
tasks.push(task2);
// Task 3: Testing
const task3 = this.createTaskForObjective('write-tests', 'testing', {
title: 'Create Tests',
description: `Write tests for the implementation`,
instructions: `Please create comprehensive tests for the implementation created in the previous task.
Target Directory: ${targetPath || 'Use the same directory as the implementation'}
Create appropriate test files that:
1. Test the main functionality
2. Cover edge cases
3. Ensure the implementation works as expected
4. Use appropriate testing frameworks for the technology stack
5. Include both unit tests and integration tests where applicable`,
priority: 'medium' as TaskPriority,
estimatedDuration: 5 * 60 * 1000,
requiredCapabilities: ['testing', 'code-generation'],
dependencies: [task2.id.id],
});
tasks.push(task3);
// Task 4: Documentation
const task4 = this.createTaskForObjective('create-documentation', 'documentation', {
title: 'Create Documentation',
description: `Document the implementation`,
instructions: `Please create comprehensive documentation for the implemented solution.
Target Directory: ${targetPath || 'Use the same directory as the implementation'}
Create documentation that includes:
1. README.md with project overview, setup instructions, and usage examples
2. API documentation (if applicable)
3. Configuration options
4. Architecture overview
5. Deployment instructions (if applicable)
6. Any other relevant documentation
Make sure the documentation is clear, complete, and helps users understand and use the implementation.`,
priority: 'medium' as TaskPriority,
estimatedDuration: 5 * 60 * 1000,
requiredCapabilities: ['documentation'],
dependencies: [task2.id.id],
});
tasks.push(task4);
} else {
// For other strategies, create a comprehensive single task
tasks.push(
this.createTaskForObjective('execute-objective', 'generic', {
titl