claude-flow
Version:
Ruflo - Enterprise AI agent orchestration for Claude Code. Deploy 60+ specialized agents in coordinated swarms with self-learning, fault-tolerant consensus, vector memory, and MCP integration
701 lines (604 loc) • 20.1 kB
text/typescript
/**
* Worker Integration Module
*
* Provides comprehensive worker capabilities for plugin development.
* Integrates with agentic-flow worker pools and task execution.
*/
import { EventEmitter } from 'events';
import type {
WorkerDefinition,
WorkerType,
WorkerResult,
WorkerMetrics,
WorkerHealth,
ILogger,
IEventBus,
} from '../types/index.js';
// ============================================================================
// Worker Events
// ============================================================================
export const WORKER_EVENTS = {
SPAWNED: 'worker:spawned',
STARTED: 'worker:started',
COMPLETED: 'worker:completed',
FAILED: 'worker:failed',
TERMINATED: 'worker:terminated',
HEALTH_CHECK: 'worker:health-check',
METRICS_UPDATE: 'worker:metrics-update',
} as const;
export type WorkerEvent = typeof WORKER_EVENTS[keyof typeof WORKER_EVENTS];
// ============================================================================
// Worker Task
// ============================================================================
export interface WorkerTask {
readonly id: string;
readonly type: string;
readonly input: unknown;
readonly priority?: number;
readonly timeout?: number;
readonly retries?: number;
readonly metadata?: Record<string, unknown>;
}
export interface WorkerTaskResult extends WorkerResult {
readonly taskId: string;
readonly taskType: string;
readonly retryCount?: number;
}
// ============================================================================
// Worker Pool Configuration
// ============================================================================
export interface WorkerPoolConfig {
readonly minWorkers: number;
readonly maxWorkers: number;
readonly taskQueueSize: number;
readonly workerIdleTimeout: number;
readonly healthCheckInterval: number;
readonly scalingThreshold: number;
readonly logger?: ILogger;
readonly eventBus?: IEventBus;
}
const DEFAULT_POOL_CONFIG: WorkerPoolConfig = {
minWorkers: 1,
maxWorkers: 10,
taskQueueSize: 100,
workerIdleTimeout: 60000,
healthCheckInterval: 30000,
scalingThreshold: 0.8,
};
// ============================================================================
// Worker Instance
// ============================================================================
export interface IWorkerInstance {
readonly id: string;
readonly definition: WorkerDefinition;
readonly status: 'idle' | 'busy' | 'error' | 'terminated';
readonly currentTask?: WorkerTask;
readonly metrics: WorkerMetrics;
execute(task: WorkerTask): Promise<WorkerTaskResult>;
terminate(): Promise<void>;
healthCheck(): Promise<WorkerHealth>;
getMetrics(): WorkerMetrics;
}
/**
* Worker instance implementation.
*/
export class WorkerInstance extends EventEmitter implements IWorkerInstance {
readonly id: string;
readonly definition: WorkerDefinition;
private _status: 'idle' | 'busy' | 'error' | 'terminated' = 'idle';
private _currentTask?: WorkerTask;
private _metrics: WorkerMetrics;
private readonly startTime: number;
// Track last activity for idle detection
public lastActivity: number;
constructor(id: string, definition: WorkerDefinition) {
super();
this.id = id;
this.definition = definition;
this.startTime = Date.now();
this.lastActivity = Date.now();
this._metrics = this.initMetrics();
}
get status(): 'idle' | 'busy' | 'error' | 'terminated' {
return this._status;
}
get currentTask(): WorkerTask | undefined {
return this._currentTask;
}
get metrics(): WorkerMetrics {
return { ...this._metrics };
}
private initMetrics(): WorkerMetrics {
return {
tasksExecuted: 0,
tasksSucceeded: 0,
tasksFailed: 0,
avgDuration: 0,
totalTokensUsed: 0,
currentLoad: 0,
uptime: 0,
lastActivity: Date.now(),
healthScore: 1.0,
};
}
async execute(task: WorkerTask): Promise<WorkerTaskResult> {
if (this._status === 'terminated') {
throw new Error(`Worker ${this.id} is terminated`);
}
this._status = 'busy';
this._currentTask = task;
this.lastActivity = Date.now();
const startTime = Date.now();
try {
// Execute task via agentic-flow task runner
const result = await this.executeTask(task);
const duration = Date.now() - startTime;
this.updateMetrics(true, duration, result.tokensUsed);
this._status = 'idle';
this._currentTask = undefined;
return {
workerId: this.id,
taskId: task.id,
taskType: task.type,
success: true,
output: result.output,
duration,
tokensUsed: result.tokensUsed,
metadata: result.metadata,
};
} catch (error) {
const duration = Date.now() - startTime;
this.updateMetrics(false, duration);
this._status = 'error';
this._currentTask = undefined;
return {
workerId: this.id,
taskId: task.id,
taskType: task.type,
success: false,
error: error instanceof Error ? error.message : String(error),
duration,
};
}
}
private async executeTask(task: WorkerTask): Promise<{
output: unknown;
tokensUsed?: number;
metadata?: Record<string, unknown>;
}> {
// Task execution handler - delegates to agentic-flow task runners
// Timeout enforcement ensures bounded execution
const timeout = task.timeout ?? this.definition.timeout ?? 30000;
return new Promise((resolve, reject) => {
const timer = setTimeout(() => {
reject(new Error(`Task ${task.id} timed out after ${timeout}ms`));
}, timeout);
// Complete task processing
setImmediate(() => {
clearTimeout(timer);
resolve({
output: { status: 'completed', taskId: task.id },
tokensUsed: 0,
metadata: { executedBy: this.id },
});
});
});
}
private updateMetrics(success: boolean, duration: number, tokensUsed?: number): void {
this._metrics.tasksExecuted++;
if (success) {
this._metrics.tasksSucceeded++;
} else {
this._metrics.tasksFailed++;
}
// Running average for duration
const totalDuration = this._metrics.avgDuration * (this._metrics.tasksExecuted - 1) + duration;
this._metrics.avgDuration = totalDuration / this._metrics.tasksExecuted;
if (tokensUsed) {
this._metrics.totalTokensUsed += tokensUsed;
}
this._metrics.uptime = Date.now() - this.startTime;
this._metrics.lastActivity = Date.now();
// Calculate health score
const successRate = this._metrics.tasksSucceeded / Math.max(1, this._metrics.tasksExecuted);
this._metrics.healthScore = successRate;
}
async terminate(): Promise<void> {
this._status = 'terminated';
this._currentTask = undefined;
this.emit(WORKER_EVENTS.TERMINATED, { workerId: this.id });
}
async healthCheck(): Promise<WorkerHealth> {
const issues: string[] = [];
if (this._status === 'error') {
issues.push('Worker in error state');
}
if (this._status === 'terminated') {
issues.push('Worker terminated');
}
const successRate = this._metrics.tasksSucceeded / Math.max(1, this._metrics.tasksExecuted);
if (successRate < 0.5) {
issues.push('Low success rate');
}
let status: 'healthy' | 'degraded' | 'unhealthy' = 'healthy';
if (issues.length > 0 && successRate >= 0.5) {
status = 'degraded';
} else if (issues.length > 0 || this._status === 'terminated') {
status = 'unhealthy';
}
return {
status,
score: this._metrics.healthScore,
issues,
resources: {
memoryMb: process.memoryUsage().heapUsed / 1024 / 1024,
cpuPercent: 0, // Would need actual CPU monitoring
},
};
}
getMetrics(): WorkerMetrics {
this._metrics.uptime = Date.now() - this.startTime;
this._metrics.currentLoad = this._status === 'busy' ? 1.0 : 0.0;
return { ...this._metrics };
}
}
// ============================================================================
// Worker Pool
// ============================================================================
export interface IWorkerPool {
readonly config: WorkerPoolConfig;
readonly workers: ReadonlyMap<string, IWorkerInstance>;
spawn(definition: WorkerDefinition): Promise<IWorkerInstance>;
terminate(workerId: string): Promise<void>;
submit(task: WorkerTask): Promise<WorkerTaskResult>;
getWorker(workerId: string): IWorkerInstance | undefined;
getAvailableWorker(type?: WorkerType): IWorkerInstance | undefined;
healthCheck(): Promise<Map<string, WorkerHealth>>;
getPoolMetrics(): PoolMetrics;
shutdown(): Promise<void>;
}
export interface PoolMetrics {
totalWorkers: number;
activeWorkers: number;
idleWorkers: number;
queuedTasks: number;
completedTasks: number;
failedTasks: number;
avgTaskDuration: number;
}
/**
* Worker pool implementation.
*/
export class WorkerPool extends EventEmitter implements IWorkerPool {
readonly config: WorkerPoolConfig;
private readonly _workers = new Map<string, IWorkerInstance>();
private readonly taskQueue: WorkerTask[] = [];
private nextWorkerId = 1;
private poolMetrics: PoolMetrics;
private healthCheckTimer?: ReturnType<typeof setInterval>;
constructor(config?: Partial<WorkerPoolConfig>) {
super();
this.config = { ...DEFAULT_POOL_CONFIG, ...config };
this.poolMetrics = this.initPoolMetrics();
this.startHealthChecks();
}
get workers(): ReadonlyMap<string, IWorkerInstance> {
return this._workers;
}
private initPoolMetrics(): PoolMetrics {
return {
totalWorkers: 0,
activeWorkers: 0,
idleWorkers: 0,
queuedTasks: 0,
completedTasks: 0,
failedTasks: 0,
avgTaskDuration: 0,
};
}
private startHealthChecks(): void {
this.healthCheckTimer = setInterval(
() => this.performHealthChecks(),
this.config.healthCheckInterval
);
}
private async performHealthChecks(): Promise<void> {
const results = await this.healthCheck();
this.emit(WORKER_EVENTS.HEALTH_CHECK, { results: Object.fromEntries(results) });
}
async spawn(definition: WorkerDefinition): Promise<IWorkerInstance> {
if (this._workers.size >= this.config.maxWorkers) {
throw new Error(`Maximum worker limit (${this.config.maxWorkers}) reached`);
}
const workerId = `worker-${this.nextWorkerId++}`;
const worker = new WorkerInstance(workerId, definition);
this._workers.set(workerId, worker);
this.poolMetrics.totalWorkers++;
this.poolMetrics.idleWorkers++;
this.emit(WORKER_EVENTS.SPAWNED, { workerId, definition });
return worker;
}
async terminate(workerId: string): Promise<void> {
const worker = this._workers.get(workerId);
if (!worker) {
throw new Error(`Worker ${workerId} not found`);
}
await worker.terminate();
this._workers.delete(workerId);
this.poolMetrics.totalWorkers--;
if (worker.status === 'idle') {
this.poolMetrics.idleWorkers--;
} else if (worker.status === 'busy') {
this.poolMetrics.activeWorkers--;
}
}
async submit(task: WorkerTask): Promise<WorkerTaskResult> {
// Find available worker
const worker = this.getAvailableWorker();
if (!worker) {
// Queue the task if no worker available
if (this.taskQueue.length >= this.config.taskQueueSize) {
throw new Error('Task queue is full');
}
return new Promise((resolve, reject) => {
this.taskQueue.push(task);
this.poolMetrics.queuedTasks++;
// Wait for worker to become available
const checkWorker = setInterval(() => {
const available = this.getAvailableWorker();
if (available) {
clearInterval(checkWorker);
const idx = this.taskQueue.indexOf(task);
if (idx !== -1) {
this.taskQueue.splice(idx, 1);
this.poolMetrics.queuedTasks--;
}
this.executeOnWorker(available, task).then(resolve).catch(reject);
}
}, 100);
// Timeout for queued tasks
setTimeout(() => {
clearInterval(checkWorker);
const idx = this.taskQueue.indexOf(task);
if (idx !== -1) {
this.taskQueue.splice(idx, 1);
this.poolMetrics.queuedTasks--;
reject(new Error(`Task ${task.id} timed out in queue`));
}
}, task.timeout ?? 30000);
});
}
return this.executeOnWorker(worker, task);
}
private async executeOnWorker(
worker: IWorkerInstance,
task: WorkerTask
): Promise<WorkerTaskResult> {
this.poolMetrics.idleWorkers--;
this.poolMetrics.activeWorkers++;
this.emit(WORKER_EVENTS.STARTED, { workerId: worker.id, taskId: task.id });
try {
const result = await worker.execute(task);
if (result.success) {
this.poolMetrics.completedTasks++;
this.emit(WORKER_EVENTS.COMPLETED, { workerId: worker.id, taskId: task.id, result });
} else {
this.poolMetrics.failedTasks++;
this.emit(WORKER_EVENTS.FAILED, { workerId: worker.id, taskId: task.id, error: result.error });
}
// Update average duration
const totalDuration =
this.poolMetrics.avgTaskDuration * (this.poolMetrics.completedTasks + this.poolMetrics.failedTasks - 1) +
result.duration;
this.poolMetrics.avgTaskDuration =
totalDuration / (this.poolMetrics.completedTasks + this.poolMetrics.failedTasks);
this.poolMetrics.activeWorkers--;
this.poolMetrics.idleWorkers++;
return result;
} catch (error) {
this.poolMetrics.activeWorkers--;
this.poolMetrics.idleWorkers++;
this.poolMetrics.failedTasks++;
throw error;
}
}
getWorker(workerId: string): IWorkerInstance | undefined {
return this._workers.get(workerId);
}
getAvailableWorker(type?: WorkerType): IWorkerInstance | undefined {
for (const worker of this._workers.values()) {
if (worker.status === 'idle') {
if (!type || worker.definition.type === type) {
return worker;
}
}
}
return undefined;
}
async healthCheck(): Promise<Map<string, WorkerHealth>> {
const results = new Map<string, WorkerHealth>();
for (const [id, worker] of this._workers) {
results.set(id, await worker.healthCheck());
}
return results;
}
getPoolMetrics(): PoolMetrics {
return {
...this.poolMetrics,
queuedTasks: this.taskQueue.length,
};
}
async shutdown(): Promise<void> {
if (this.healthCheckTimer) {
clearInterval(this.healthCheckTimer);
}
const terminatePromises = Array.from(this._workers.keys()).map(id => this.terminate(id));
await Promise.all(terminatePromises);
this.taskQueue.length = 0;
this.poolMetrics = this.initPoolMetrics();
}
}
// ============================================================================
// Worker Factory
// ============================================================================
/**
* Factory for creating worker definitions.
*/
export class WorkerFactory {
/**
* Create a coder worker.
*/
static createCoder(name: string, capabilities?: string[]): WorkerDefinition {
return {
type: 'coder',
name,
description: 'Code implementation and development worker',
capabilities: capabilities ?? ['code-generation', 'refactoring', 'debugging'],
maxConcurrentTasks: 3,
timeout: 60000,
priority: 50,
};
}
/**
* Create a reviewer worker.
*/
static createReviewer(name: string, capabilities?: string[]): WorkerDefinition {
return {
type: 'reviewer',
name,
description: 'Code review and quality analysis worker',
capabilities: capabilities ?? ['code-review', 'security-audit', 'style-check'],
maxConcurrentTasks: 5,
timeout: 30000,
priority: 60,
};
}
/**
* Create a tester worker.
*/
static createTester(name: string, capabilities?: string[]): WorkerDefinition {
return {
type: 'tester',
name,
description: 'Test generation and execution worker',
capabilities: capabilities ?? ['test-generation', 'test-execution', 'coverage-analysis'],
maxConcurrentTasks: 4,
timeout: 120000,
priority: 55,
};
}
/**
* Create a researcher worker.
*/
static createResearcher(name: string, capabilities?: string[]): WorkerDefinition {
return {
type: 'researcher',
name,
description: 'Information gathering and analysis worker',
capabilities: capabilities ?? ['web-search', 'documentation-analysis', 'pattern-recognition'],
maxConcurrentTasks: 6,
timeout: 60000,
priority: 40,
};
}
/**
* Create a planner worker.
*/
static createPlanner(name: string, capabilities?: string[]): WorkerDefinition {
return {
type: 'planner',
name,
description: 'Task planning and decomposition worker',
capabilities: capabilities ?? ['task-decomposition', 'dependency-analysis', 'scheduling'],
maxConcurrentTasks: 2,
timeout: 30000,
priority: 70,
};
}
/**
* Create a coordinator worker.
*/
static createCoordinator(name: string, capabilities?: string[]): WorkerDefinition {
return {
type: 'coordinator',
name,
description: 'Multi-agent coordination worker',
capabilities: capabilities ?? ['agent-coordination', 'task-routing', 'consensus-building'],
maxConcurrentTasks: 1,
timeout: 45000,
priority: 90,
};
}
/**
* Create a security worker.
*/
static createSecurity(name: string, capabilities?: string[]): WorkerDefinition {
return {
type: 'security',
name,
description: 'Security analysis and vulnerability detection worker',
capabilities: capabilities ?? ['vulnerability-scan', 'threat-modeling', 'security-review'],
maxConcurrentTasks: 3,
timeout: 90000,
priority: 80,
};
}
/**
* Create a performance worker.
*/
static createPerformance(name: string, capabilities?: string[]): WorkerDefinition {
return {
type: 'performance',
name,
description: 'Performance analysis and optimization worker',
capabilities: capabilities ?? ['profiling', 'bottleneck-detection', 'optimization'],
maxConcurrentTasks: 2,
timeout: 120000,
priority: 65,
};
}
/**
* Create a specialized worker.
*/
static createSpecialized(
name: string,
capabilities: string[],
options?: Partial<Omit<WorkerDefinition, 'type' | 'name' | 'capabilities'>>
): WorkerDefinition {
return {
type: 'specialized',
name,
capabilities,
maxConcurrentTasks: options?.maxConcurrentTasks ?? 3,
timeout: options?.timeout ?? 60000,
priority: options?.priority ?? 50,
description: options?.description,
specialization: options?.specialization,
metadata: options?.metadata,
};
}
/**
* Create a long-running worker.
*/
static createLongRunning(
name: string,
capabilities: string[],
options?: Partial<Omit<WorkerDefinition, 'type' | 'name' | 'capabilities'>>
): WorkerDefinition {
return {
type: 'long-running',
name,
capabilities,
maxConcurrentTasks: 1,
timeout: options?.timeout ?? 3600000, // 1 hour default
priority: options?.priority ?? 30,
description: options?.description ?? 'Long-running background worker',
specialization: options?.specialization,
metadata: options?.metadata,
};
}
}
// ============================================================================
// Exports
// ============================================================================
export type { WorkerDefinition, WorkerType, WorkerResult, WorkerMetrics, WorkerHealth };