vibe-coder-mcp
Version:
Production-ready MCP server with complete agent integration, multi-transport support, and comprehensive development automation tools for AI-assisted workflows.
796 lines (795 loc) • 37.8 kB
JavaScript
import { EventEmitter } from 'events';
import { promises as fs } from 'fs';
import { join } from 'path';
import crypto from 'crypto';
import { ErrorFactory, createErrorContext } from '../utils/enhanced-errors.js';
import { createSuccess, createFailure } from './unified-lifecycle-manager.js';
import { getVibeTaskManagerOutputDir } from '../utils/config-loader.js';
import logger from '../../../logger.js';
export function createOrchestrationId(id) {
if (!id || id.trim().length === 0) {
throw new Error('Orchestration ID cannot be empty');
}
return id;
}
export function createWorkflowId(id) {
if (!id || id.trim().length === 0) {
throw new Error('Workflow ID cannot be empty');
}
return id;
}
export function createAgentId(id) {
if (!id || id.trim().length === 0) {
throw new Error('Agent ID cannot be empty');
}
return id;
}
export function createExecutionId(id) {
if (!id || id.trim().length === 0) {
throw new Error('Execution ID cannot be empty');
}
return id;
}
export function createScheduleId(id) {
if (!id || id.trim().length === 0) {
throw new Error('Schedule ID cannot be empty');
}
return id;
}
export class UnifiedOrchestrationEngine extends EventEmitter {
static instance = null;
config;
dataDirectory;
initialized = false;
agents = new Map();
workflows = new Map();
assignments = new Map();
executions = new Map();
schedule = new Map();
orchestrationCount = 0;
workflowCount = 0;
taskExecutionCount = 0;
totalExecutionTime = 0;
errorCount = 0;
heartbeatTimer = null;
schedulerTimer = null;
watchdogTimer = null;
cleanupTimer = null;
metricsTimer = null;
constructor(config) {
super();
this.config = config;
this.dataDirectory = getVibeTaskManagerOutputDir();
logger.info('Unified Orchestration Engine initialized');
}
static getInstance(config) {
if (!UnifiedOrchestrationEngine.instance) {
if (!config) {
throw new Error('Configuration required for first initialization');
}
UnifiedOrchestrationEngine.instance = new UnifiedOrchestrationEngine(config);
}
return UnifiedOrchestrationEngine.instance;
}
static resetInstance() {
if (UnifiedOrchestrationEngine.instance) {
UnifiedOrchestrationEngine.instance.dispose();
UnifiedOrchestrationEngine.instance = null;
}
}
async initialize() {
if (this.initialized) {
return createSuccess(undefined);
}
try {
await this.createDataDirectories();
await this.loadPersistedState();
this.startBackgroundProcesses();
this.initialized = true;
this.emit('initialized');
logger.info('Orchestration engine initialized successfully');
return createSuccess(undefined);
}
catch (error) {
return createFailure(ErrorFactory.createError('system', `Failed to initialize orchestration engine: ${error instanceof Error ? error.message : 'Unknown error'}`, createErrorContext('UnifiedOrchestrationEngine', 'initialize').build(), { cause: error instanceof Error ? error : undefined }));
}
}
async createDataDirectories() {
const directories = [
join(this.dataDirectory, 'orchestration'),
join(this.dataDirectory, 'orchestration', 'agents'),
join(this.dataDirectory, 'orchestration', 'workflows'),
join(this.dataDirectory, 'orchestration', 'executions'),
join(this.dataDirectory, 'orchestration', 'schedules'),
join(this.dataDirectory, 'orchestration', 'logs')
];
for (const dir of directories) {
await fs.mkdir(dir, { recursive: true });
}
}
async loadPersistedState() {
if (!this.config.workflowManagement.persistState) {
return;
}
try {
const workflowsPath = join(this.dataDirectory, 'orchestration', 'workflows.json');
if (await this.fileExists(workflowsPath)) {
const workflowsData = await fs.readFile(workflowsPath, 'utf-8');
const workflows = JSON.parse(workflowsData);
for (const workflow of workflows) {
this.workflows.set(workflow.id, workflow);
}
}
const agentsPath = join(this.dataDirectory, 'orchestration', 'agents.json');
if (await this.fileExists(agentsPath)) {
const agentsData = await fs.readFile(agentsPath, 'utf-8');
const agents = JSON.parse(agentsData);
for (const agent of agents) {
this.agents.set(agent.id, agent);
}
}
logger.info('Persisted orchestration state loaded successfully');
}
catch (error) {
logger.warn('Failed to load persisted state, starting fresh:', error);
}
}
startBackgroundProcesses() {
if (this.config.agentManagement.heartbeatTimeout > 0) {
this.heartbeatTimer = setInterval(() => {
this.checkAgentHeartbeats().catch(error => {
logger.error('Agent heartbeat check failed:', error);
});
}, this.config.heartbeatInterval);
}
if (this.config.taskScheduling.schedulingInterval > 0) {
this.schedulerTimer = setInterval(() => {
this.processScheduledTasks().catch(error => {
logger.error('Task scheduling failed:', error);
});
}, this.config.taskScheduling.schedulingInterval);
}
if (this.config.executionMonitoring.watchdogEnabled) {
this.watchdogTimer = setInterval(() => {
this.runExecutionWatchdog().catch(error => {
logger.error('Execution watchdog failed:', error);
});
}, this.config.executionMonitoring.watchdogInterval);
}
if (this.config.workflowManagement.autoCleanup) {
this.cleanupTimer = setInterval(() => {
this.cleanupCompletedWorkflows().catch(error => {
logger.error('Workflow cleanup failed:', error);
});
}, this.config.workflowManagement.cleanupInterval);
}
this.metricsTimer = setInterval(() => {
this.collectMetrics();
}, 60 * 1000);
logger.info('Orchestration background processes started');
}
async registerAgent(agentInfo) {
try {
const agentId = createAgentId(crypto.randomUUID());
const agent = {
id: agentId,
...agentInfo,
metadata: {
version: '1.0.0',
heartbeatInterval: this.config.heartbeatInterval,
lastHeartbeat: new Date(),
registeredAt: new Date()
}
};
this.agents.set(agentId, agent);
this.emit('agentRegistered', agent);
logger.info(`Agent registered: ${agentId} (${agent.name})`);
return createSuccess(agentId);
}
catch (error) {
return createFailure(ErrorFactory.createError('system', `Failed to register agent: ${error instanceof Error ? error.message : 'Unknown error'}`, createErrorContext('UnifiedOrchestrationEngine', 'registerAgent').build(), { cause: error instanceof Error ? error : undefined }));
}
}
async updateAgentStatus(agentId, status) {
try {
const agent = this.agents.get(agentId);
if (!agent) {
return createFailure(ErrorFactory.createError('validation', `Agent not found: ${agentId}`, createErrorContext('UnifiedOrchestrationEngine', 'updateAgentStatus').build()));
}
const oldStatus = agent.status;
agent.status = status;
agent.metadata.lastHeartbeat = new Date();
this.emit('agentStatusChanged', { agentId, oldStatus, newStatus: status });
logger.debug(`Agent status updated: ${agentId} ${oldStatus} -> ${status}`);
return createSuccess(undefined);
}
catch (error) {
return createFailure(ErrorFactory.createError('system', `Failed to update agent status: ${error instanceof Error ? error.message : 'Unknown error'}`, createErrorContext('UnifiedOrchestrationEngine', 'updateAgentStatus').build(), { cause: error instanceof Error ? error : undefined }));
}
}
async getAvailableAgents(requiredCapabilities) {
try {
const availableAgents = Array.from(this.agents.values()).filter(agent => {
if (agent.status !== 'online' && agent.status !== 'idle') {
return false;
}
if (agent.currentTasks.length >= agent.maxConcurrentTasks) {
return false;
}
if (requiredCapabilities && requiredCapabilities.length > 0) {
const hasRequiredCapabilities = requiredCapabilities.every(cap => agent.capabilities.includes(cap));
if (!hasRequiredCapabilities) {
return false;
}
}
return true;
});
availableAgents.sort((a, b) => {
const loadDiff = a.currentLoad - b.currentLoad;
if (Math.abs(loadDiff) > 0.1) {
return loadDiff;
}
return b.performance.successRate - a.performance.successRate;
});
return createSuccess(availableAgents);
}
catch (error) {
return createFailure(ErrorFactory.createError('system', `Failed to get available agents: ${error instanceof Error ? error.message : 'Unknown error'}`, createErrorContext('UnifiedOrchestrationEngine', 'getAvailableAgents').build(), { cause: error instanceof Error ? error : undefined }));
}
}
async createWorkflow(projectId, sessionId, tasks, priority = 'medium') {
try {
const workflowId = createWorkflowId(crypto.randomUUID());
const workflow = {
id: workflowId,
phase: 'initialization',
status: 'pending',
projectId,
sessionId,
tasks,
assignedAgents: [],
startTime: new Date(),
progress: {
totalTasks: tasks.length,
completedTasks: 0,
failedTasks: 0,
percentage: 0
},
metadata: {
initiator: sessionId,
priority,
estimatedDuration: tasks.length * 300000,
}
};
this.workflows.set(workflowId, workflow);
this.workflowCount++;
this.emit('workflowCreated', workflow);
logger.info(`Workflow created: ${workflowId} with ${tasks.length} tasks`);
return createSuccess(workflowId);
}
catch (error) {
return createFailure(ErrorFactory.createError('system', `Failed to create workflow: ${error instanceof Error ? error.message : 'Unknown error'}`, createErrorContext('UnifiedOrchestrationEngine', 'createWorkflow').build(), { cause: error instanceof Error ? error : undefined }));
}
}
async updateWorkflowPhase(workflowId, phase) {
try {
const workflow = this.workflows.get(workflowId);
if (!workflow) {
return createFailure(ErrorFactory.createError('validation', `Workflow not found: ${workflowId}`, createErrorContext('UnifiedOrchestrationEngine', 'updateWorkflowPhase').build()));
}
const oldPhase = workflow.phase;
workflow.phase = phase;
if (phase === 'execution') {
workflow.status = 'running';
}
else if (phase === 'completion') {
workflow.status = 'completed';
workflow.endTime = new Date();
workflow.metadata.actualDuration = workflow.endTime.getTime() - workflow.startTime.getTime();
}
else if (phase === 'error_recovery') {
workflow.status = 'error';
}
this.emit('workflowPhaseChanged', { workflowId, oldPhase, newPhase: phase });
logger.debug(`Workflow phase updated: ${workflowId} ${oldPhase} -> ${phase}`);
return createSuccess(undefined);
}
catch (error) {
return createFailure(ErrorFactory.createError('system', `Failed to update workflow phase: ${error instanceof Error ? error.message : 'Unknown error'}`, createErrorContext('UnifiedOrchestrationEngine', 'updateWorkflowPhase').build(), { cause: error instanceof Error ? error : undefined }));
}
}
async scheduleTask(task, workflowId, constraints) {
try {
const scheduleId = createScheduleId(crypto.randomUUID());
const scheduleEntry = {
id: scheduleId,
taskId: task.id,
workflowId,
scheduledAt: new Date(),
priority: task.priority,
dependencies: task.dependencies,
constraints: {
requiredCapabilities: ['task_execution'],
preferredAgents: [],
excludedAgents: [],
maxRetries: this.config.recovery.maxRetries,
timeoutMs: this.config.defaultTimeout,
...constraints
},
status: 'pending',
createdAt: new Date(),
updatedAt: new Date()
};
this.schedule.set(scheduleId, scheduleEntry);
this.emit('taskScheduled', scheduleEntry);
logger.debug(`Task scheduled: ${task.id} in workflow ${workflowId}`);
return createSuccess(scheduleId);
}
catch (error) {
return createFailure(ErrorFactory.createError('system', `Failed to schedule task: ${error instanceof Error ? error.message : 'Unknown error'}`, createErrorContext('UnifiedOrchestrationEngine', 'scheduleTask').build(), { cause: error instanceof Error ? error : undefined }));
}
}
async assignTask(scheduleId, agentId) {
try {
const scheduleEntry = this.schedule.get(scheduleId);
if (!scheduleEntry) {
return createFailure(ErrorFactory.createError('validation', `Schedule entry not found: ${scheduleId}`, createErrorContext('UnifiedOrchestrationEngine', 'assignTask').build()));
}
const agent = this.agents.get(agentId);
if (!agent) {
return createFailure(ErrorFactory.createError('validation', `Agent not found: ${agentId}`, createErrorContext('UnifiedOrchestrationEngine', 'assignTask').build()));
}
if (agent.currentTasks.length >= agent.maxConcurrentTasks) {
return createFailure(ErrorFactory.createError('resource', `Agent at capacity: ${agentId}`, createErrorContext('UnifiedOrchestrationEngine', 'assignTask').build()));
}
const assignment = {
id: crypto.randomUUID(),
taskId: scheduleEntry.taskId,
agentId,
workflowId: scheduleEntry.workflowId,
assignedAt: new Date(),
status: 'scheduled',
priority: scheduleEntry.priority,
estimatedDuration: 300000,
retryCount: 0,
maxRetries: scheduleEntry.constraints.maxRetries,
metadata: {}
};
this.assignments.set(assignment.id, assignment);
scheduleEntry.status = 'assigned';
scheduleEntry.assignedAgent = agentId;
scheduleEntry.updatedAt = new Date();
agent.currentTasks.push(scheduleEntry.taskId);
agent.currentLoad = agent.currentTasks.length / agent.maxConcurrentTasks;
agent.status = agent.currentTasks.length > 0 ? 'busy' : 'idle';
this.emit('taskAssigned', assignment);
logger.info(`Task assigned: ${scheduleEntry.taskId} -> ${agentId}`);
return createSuccess(assignment);
}
catch (error) {
return createFailure(ErrorFactory.createError('system', `Failed to assign task: ${error instanceof Error ? error.message : 'Unknown error'}`, createErrorContext('UnifiedOrchestrationEngine', 'assignTask').build(), { cause: error instanceof Error ? error : undefined }));
}
}
async startExecution(assignmentId) {
try {
const assignment = this.assignments.get(assignmentId);
if (!assignment) {
return createFailure(ErrorFactory.createError('validation', `Assignment not found: ${assignmentId}`, createErrorContext('UnifiedOrchestrationEngine', 'startExecution').build()));
}
const executionId = createExecutionId(crypto.randomUUID());
const execution = {
id: executionId,
workflowId: assignment.workflowId,
taskId: assignment.taskId,
agentId: assignment.agentId,
status: 'running',
startTime: new Date(),
progress: 0,
logs: [],
errors: [],
metrics: {
memoryUsage: 0,
cpuUsage: 0,
responseTime: 0
},
watchdog: {
enabled: this.config.executionMonitoring.watchdogEnabled,
timeoutMs: this.config.defaultTimeout,
lastCheck: new Date(),
violations: 0
}
};
this.executions.set(executionId, execution);
assignment.status = 'running';
assignment.startedAt = new Date();
this.taskExecutionCount++;
this.emit('executionStarted', execution);
logger.info(`Execution started: ${executionId} for task ${assignment.taskId}`);
return createSuccess(executionId);
}
catch (error) {
return createFailure(ErrorFactory.createError('system', `Failed to start execution: ${error instanceof Error ? error.message : 'Unknown error'}`, createErrorContext('UnifiedOrchestrationEngine', 'startExecution').build(), { cause: error instanceof Error ? error : undefined }));
}
}
async updateExecutionProgress(executionId, progress, logs) {
try {
const execution = this.executions.get(executionId);
if (!execution) {
return createFailure(ErrorFactory.createError('validation', `Execution not found: ${executionId}`, createErrorContext('UnifiedOrchestrationEngine', 'updateExecutionProgress').build()));
}
execution.progress = Math.max(0, Math.min(100, progress));
execution.watchdog.lastCheck = new Date();
if (logs) {
execution.logs.push(...logs);
}
this.emit('executionProgress', { executionId, progress });
return createSuccess(undefined);
}
catch (error) {
return createFailure(ErrorFactory.createError('system', `Failed to update execution progress: ${error instanceof Error ? error.message : 'Unknown error'}`, createErrorContext('UnifiedOrchestrationEngine', 'updateExecutionProgress').build(), { cause: error instanceof Error ? error : undefined }));
}
}
async completeExecution(executionId, success, result) {
try {
const execution = this.executions.get(executionId);
if (!execution) {
return createFailure(ErrorFactory.createError('validation', `Execution not found: ${executionId}`, createErrorContext('UnifiedOrchestrationEngine', 'completeExecution').build()));
}
execution.status = success ? 'completed' : 'failed';
execution.endTime = new Date();
execution.progress = success ? 100 : execution.progress;
const assignment = Array.from(this.assignments.values()).find(a => a.taskId === execution.taskId && a.agentId === execution.agentId);
if (assignment) {
assignment.status = success ? 'completed' : 'failed';
assignment.completedAt = new Date();
assignment.actualDuration = execution.endTime.getTime() - execution.startTime.getTime();
}
const agent = this.agents.get(execution.agentId);
if (agent) {
agent.currentTasks = agent.currentTasks.filter(taskId => taskId !== execution.taskId);
agent.currentLoad = agent.currentTasks.length / agent.maxConcurrentTasks;
agent.status = agent.currentTasks.length > 0 ? 'busy' : 'idle';
const executionTime = execution.endTime.getTime() - execution.startTime.getTime();
agent.performance.averageTaskTime = (agent.performance.averageTaskTime + executionTime) / 2;
if (success) {
agent.performance.successRate = Math.min(1, agent.performance.successRate + 0.01);
}
else {
agent.performance.errorRate = Math.min(1, agent.performance.errorRate + 0.01);
this.errorCount++;
}
agent.performance.lastActivity = new Date();
}
const workflow = this.workflows.get(execution.workflowId);
if (workflow) {
if (success) {
workflow.progress.completedTasks++;
}
else {
workflow.progress.failedTasks++;
}
workflow.progress.percentage =
(workflow.progress.completedTasks / workflow.progress.totalTasks) * 100;
}
this.totalExecutionTime += execution.endTime.getTime() - execution.startTime.getTime();
this.emit('executionCompleted', { execution, success, result });
logger.info(`Execution completed: ${executionId} (${success ? 'success' : 'failed'})`);
return createSuccess(undefined);
}
catch (error) {
return createFailure(ErrorFactory.createError('system', `Failed to complete execution: ${error instanceof Error ? error.message : 'Unknown error'}`, createErrorContext('UnifiedOrchestrationEngine', 'completeExecution').build(), { cause: error instanceof Error ? error : undefined }));
}
}
async checkAgentHeartbeats() {
const now = new Date();
const timeout = this.config.agentManagement.heartbeatTimeout;
for (const [agentId, agent] of this.agents.entries()) {
const timeSinceHeartbeat = now.getTime() - agent.metadata.lastHeartbeat.getTime();
if (timeSinceHeartbeat > timeout && agent.status !== 'offline') {
agent.status = 'offline';
this.emit('agentTimeout', { agentId, timeSinceHeartbeat });
logger.warn(`Agent timeout: ${agentId} (${timeSinceHeartbeat}ms)`);
if (this.config.agentManagement.autoRecovery) {
await this.handleAgentRecovery(agentId);
}
}
}
}
async processScheduledTasks() {
const pendingTasks = Array.from(this.schedule.values())
.filter(entry => entry.status === 'pending')
.sort((a, b) => {
const priorityWeights = this.config.taskScheduling.priorityWeights;
const priorityDiff = priorityWeights[b.priority] - priorityWeights[a.priority];
if (priorityDiff !== 0)
return priorityDiff;
return a.scheduledAt.getTime() - b.scheduledAt.getTime();
});
const batchSize = Math.min(pendingTasks.length, this.config.taskScheduling.batchSize);
for (let i = 0; i < batchSize; i++) {
const task = pendingTasks[i];
const agentsResult = await this.getAvailableAgents(task.constraints.requiredCapabilities);
if (!agentsResult.success || agentsResult.data.length === 0) {
continue;
}
const selectedAgent = this.selectAgentByStrategy(agentsResult.data, task);
if (selectedAgent) {
await this.assignTask(task.id, selectedAgent.id);
}
}
}
async runExecutionWatchdog() {
const now = new Date();
for (const [executionId, execution] of this.executions.entries()) {
if (execution.status !== 'running')
continue;
const timeSinceLastCheck = now.getTime() - execution.watchdog.lastCheck.getTime();
if (timeSinceLastCheck > execution.watchdog.timeoutMs) {
execution.watchdog.violations++;
if (execution.watchdog.violations >= 3) {
execution.status = 'timeout';
execution.endTime = new Date();
this.emit('executionTimeout', execution);
logger.warn(`Execution timeout: ${executionId}`);
if (this.config.recovery.autoRetry) {
await this.handleExecutionTimeout(executionId);
}
}
}
}
}
async cleanupCompletedWorkflows() {
const cutoffTime = new Date(Date.now() - 24 * 60 * 60 * 1000);
const completedWorkflows = [];
for (const [workflowId, workflow] of this.workflows.entries()) {
if ((workflow.status === 'completed' || workflow.status === 'failed') &&
workflow.endTime && workflow.endTime < cutoffTime) {
completedWorkflows.push(workflowId);
}
}
for (const workflowId of completedWorkflows) {
this.workflows.delete(workflowId);
for (const [assignmentId, assignment] of this.assignments.entries()) {
if (assignment.workflowId === workflowId) {
this.assignments.delete(assignmentId);
}
}
for (const [executionId, execution] of this.executions.entries()) {
if (execution.workflowId === workflowId) {
this.executions.delete(executionId);
}
}
}
if (completedWorkflows.length > 0) {
logger.debug(`Cleaned up ${completedWorkflows.length} completed workflows`);
}
}
collectMetrics() {
const stats = {
agents: {
total: this.agents.size,
online: Array.from(this.agents.values()).filter(a => a.status === 'online').length,
busy: Array.from(this.agents.values()).filter(a => a.status === 'busy').length,
idle: Array.from(this.agents.values()).filter(a => a.status === 'idle').length,
offline: Array.from(this.agents.values()).filter(a => a.status === 'offline').length,
averageLoad: this.agents.size > 0 ?
Array.from(this.agents.values()).reduce((sum, a) => sum + a.currentLoad, 0) / this.agents.size : 0
},
workflows: {
active: Array.from(this.workflows.values()).filter(w => w.status === 'running').length,
completed: Array.from(this.workflows.values()).filter(w => w.status === 'completed').length,
failed: Array.from(this.workflows.values()).filter(w => w.status === 'failed').length,
averageDuration: this.workflowCount > 0 ? this.totalExecutionTime / this.workflowCount : 0
},
tasks: {
scheduled: Array.from(this.schedule.values()).filter(s => s.status === 'pending').length,
running: Array.from(this.executions.values()).filter(e => e.status === 'running').length,
completed: Array.from(this.assignments.values()).filter(a => a.status === 'completed').length,
failed: Array.from(this.assignments.values()).filter(a => a.status === 'failed').length,
averageExecutionTime: this.taskExecutionCount > 0 ? this.totalExecutionTime / this.taskExecutionCount : 0
},
performance: {
throughput: this.taskExecutionCount / Math.max(1, (Date.now() - this.orchestrationCount) / 60000),
successRate: this.taskExecutionCount > 0 ?
(this.taskExecutionCount - this.errorCount) / this.taskExecutionCount : 1,
errorRate: this.taskExecutionCount > 0 ? this.errorCount / this.taskExecutionCount : 0,
averageResponseTime: this.taskExecutionCount > 0 ? this.totalExecutionTime / this.taskExecutionCount : 0
}
};
this.emit('metricsCollected', stats);
}
selectAgentByStrategy(agents, task) {
if (agents.length === 0)
return null;
switch (this.config.taskScheduling.strategy) {
case 'round_robin':
return agents[this.orchestrationCount % agents.length];
case 'least_loaded':
return agents.reduce((best, current) => current.currentLoad < best.currentLoad ? current : best);
case 'capability_first': {
const exactMatch = agents.find(agent => task.constraints.requiredCapabilities.every(cap => agent.capabilities.includes(cap)));
return exactMatch || agents[0];
}
case 'performance_based':
return agents.reduce((best, current) => current.performance.successRate > best.performance.successRate ? current : best);
case 'intelligent_hybrid':
default:
return agents.reduce((best, current) => {
const currentScore = this.calculateAgentScore(current, task);
const bestScore = this.calculateAgentScore(best, task);
return currentScore > bestScore ? current : best;
});
}
}
calculateAgentScore(agent, task) {
let score = 0;
score += (1 - agent.currentLoad) * 0.3;
score += agent.performance.successRate * 0.4;
const capabilityMatch = task.constraints.requiredCapabilities.every(cap => agent.capabilities.includes(cap)) ? 1 : 0.5;
score += capabilityMatch * 0.3;
return score;
}
async handleAgentRecovery(agentId) {
const agent = this.agents.get(agentId);
if (!agent)
return;
for (const taskId of agent.currentTasks) {
const assignment = Array.from(this.assignments.values()).find(a => a.taskId === taskId && a.agentId === agentId);
if (assignment && assignment.retryCount < assignment.maxRetries) {
assignment.retryCount++;
assignment.status = 'pending';
const agentsResult = await this.getAvailableAgents();
if (agentsResult.success && agentsResult.data.length > 0) {
const newAgent = agentsResult.data[0];
assignment.agentId = newAgent.id;
logger.info(`Task reassigned from offline agent: ${taskId} ${agentId} -> ${newAgent.id}`);
}
}
}
agent.currentTasks = [];
agent.currentLoad = 0;
}
async handleExecutionTimeout(executionId) {
const execution = this.executions.get(executionId);
if (!execution)
return;
const assignment = Array.from(this.assignments.values()).find(a => a.taskId === execution.taskId && a.agentId === execution.agentId);
if (assignment && assignment.retryCount < assignment.maxRetries) {
assignment.retryCount++;
assignment.status = 'pending';
setTimeout(async () => {
const agentsResult = await this.getAvailableAgents();
if (agentsResult.success && agentsResult.data.length > 0) {
const newAgent = agentsResult.data[0];
assignment.agentId = newAgent.id;
logger.info(`Task retry scheduled after timeout: ${assignment.taskId}`);
}
}, this.config.recovery.retryDelay);
}
}
async fileExists(path) {
try {
await fs.access(path);
return true;
}
catch {
return false;
}
}
getStatistics() {
return {
agents: {
total: this.agents.size,
online: Array.from(this.agents.values()).filter(a => a.status === 'online').length,
busy: Array.from(this.agents.values()).filter(a => a.status === 'busy').length,
idle: Array.from(this.agents.values()).filter(a => a.status === 'idle').length,
offline: Array.from(this.agents.values()).filter(a => a.status === 'offline').length,
averageLoad: this.agents.size > 0 ?
Array.from(this.agents.values()).reduce((sum, a) => sum + a.currentLoad, 0) / this.agents.size : 0
},
workflows: {
active: Array.from(this.workflows.values()).filter(w => w.status === 'running').length,
completed: Array.from(this.workflows.values()).filter(w => w.status === 'completed').length,
failed: Array.from(this.workflows.values()).filter(w => w.status === 'failed').length,
averageDuration: this.workflowCount > 0 ? this.totalExecutionTime / this.workflowCount : 0
},
tasks: {
scheduled: Array.from(this.schedule.values()).filter(s => s.status === 'pending').length,
running: Array.from(this.executions.values()).filter(e => e.status === 'running').length,
completed: Array.from(this.assignments.values()).filter(a => a.status === 'completed').length,
failed: Array.from(this.assignments.values()).filter(a => a.status === 'failed').length,
averageExecutionTime: this.taskExecutionCount > 0 ? this.totalExecutionTime / this.taskExecutionCount : 0
},
performance: {
throughput: this.taskExecutionCount / Math.max(1, (Date.now() - this.orchestrationCount) / 60000),
successRate: this.taskExecutionCount > 0 ?
(this.taskExecutionCount - this.errorCount) / this.taskExecutionCount : 1,
errorRate: this.taskExecutionCount > 0 ? this.errorCount / this.taskExecutionCount : 0,
averageResponseTime: this.taskExecutionCount > 0 ? this.totalExecutionTime / this.taskExecutionCount : 0
}
};
}
dispose() {
if (this.heartbeatTimer) {
clearInterval(this.heartbeatTimer);
this.heartbeatTimer = null;
}
if (this.schedulerTimer) {
clearInterval(this.schedulerTimer);
this.schedulerTimer = null;
}
if (this.watchdogTimer) {
clearInterval(this.watchdogTimer);
this.watchdogTimer = null;
}
if (this.cleanupTimer) {
clearInterval(this.cleanupTimer);
this.cleanupTimer = null;
}
if (this.metricsTimer) {
clearInterval(this.metricsTimer);
this.metricsTimer = null;
}
this.agents.clear();
this.workflows.clear();
this.assignments.clear();
this.executions.clear();
this.schedule.clear();
this.removeAllListeners();
this.initialized = false;
logger.info('Unified Orchestration Engine disposed');
}
}
export function createDefaultOrchestrationConfig() {
return {
enabled: true,
maxConcurrentWorkflows: 10,
maxConcurrentExecutions: 50,
defaultTimeout: 300000,
heartbeatInterval: 30000,
agentManagement: {
maxAgents: 20,
heartbeatTimeout: 90000,
offlineThreshold: 180000,
autoRecovery: true,
loadBalancing: true
},
workflowManagement: {
persistState: true,
stateBackupInterval: 60000,
maxWorkflowDuration: 3600000,
autoCleanup: true,
cleanupInterval: 3600000
},
taskScheduling: {
strategy: 'intelligent_hybrid',
batchSize: 10,
schedulingInterval: 5000,
priorityWeights: {
low: 1,
medium: 2,
high: 3,
critical: 5
},
dependencyResolution: true
},
executionMonitoring: {
watchdogEnabled: true,
watchdogInterval: 10000,
performanceTracking: true,
metricsCollection: true,
alertThresholds: {
errorRate: 0.1,
responseTime: 300000,
memoryUsage: 0.8
}
},
recovery: {
autoRetry: true,
maxRetries: 3,
retryDelay: 5000,
failureEscalation: true,
deadlockDetection: true
}
};
}