vibe-coder-mcp
Version:
Production-ready MCP server with complete agent integration, multi-transport support, and comprehensive development automation tools for AI-assisted workflows.
581 lines (580 loc) • 25 kB
JavaScript
import { EventEmitter } from 'events';
import { ErrorFactory, createErrorContext } from '../utils/enhanced-errors.js';
import { createSuccess, createFailure } from './unified-lifecycle-manager.js';
import logger from '../../../logger.js';
export function createTaskId(id) {
if (!id || id.trim().length === 0) {
throw new Error('Task 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 createStreamId(id) {
if (!id || id.trim().length === 0) {
throw new Error('Stream ID cannot be empty');
}
return id;
}
export class UnifiedTaskExecutionEngine extends EventEmitter {
static instance = null;
config;
agents = new Map();
executions = new Map();
monitors = new Map();
streams = new Map();
schedulingQueue = [];
schedulingTimer = null;
isSchedulingActive = false;
streamingQueues = new Map();
streamingTimers = new Map();
watchdogTimer = null;
watchdogConfigs = new Map();
constructor(config) {
super();
this.config = config;
this.initializeDefaultWatchdogConfigs();
this.startScheduler();
if (config.watchdog.enabled) {
this.startWatchdog();
}
logger.info('Unified Task Execution Engine initialized');
}
static getInstance(config) {
if (!UnifiedTaskExecutionEngine.instance) {
if (!config) {
throw new Error('Configuration required for first initialization');
}
UnifiedTaskExecutionEngine.instance = new UnifiedTaskExecutionEngine(config);
}
return UnifiedTaskExecutionEngine.instance;
}
static resetInstance() {
if (UnifiedTaskExecutionEngine.instance) {
UnifiedTaskExecutionEngine.instance.dispose();
UnifiedTaskExecutionEngine.instance = null;
}
}
async registerAgent(agent) {
try {
this.agents.set(agent.id, { ...agent });
this.emit('agentRegistered', agent);
logger.info(`Agent registered: ${agent.id}`);
return createSuccess(undefined);
}
catch (error) {
return createFailure(ErrorFactory.createError('system', `Failed to register agent: ${error instanceof Error ? error.message : 'Unknown error'}`, createErrorContext('UnifiedTaskExecutionEngine', 'registerAgent')
.metadata({ agentId: agent.id })
.build(), { cause: error instanceof Error ? error : undefined }));
}
}
async unregisterAgent(agentId) {
try {
const agent = this.agents.get(agentId);
if (!agent) {
return createFailure(ErrorFactory.createError('validation', `Agent not found: ${agentId}`, createErrorContext('UnifiedTaskExecutionEngine', 'unregisterAgent')
.metadata({ agentId })
.build()));
}
for (const execution of this.executions.values()) {
if (execution.agentId === agentId && execution.status === 'running') {
await this.cancelExecution(execution.executionId);
}
}
this.agents.delete(agentId);
this.emit('agentUnregistered', agent);
logger.info(`Agent unregistered: ${agentId}`);
return createSuccess(undefined);
}
catch (error) {
return createFailure(ErrorFactory.createError('system', `Failed to unregister agent: ${error instanceof Error ? error.message : 'Unknown error'}`, createErrorContext('UnifiedTaskExecutionEngine', 'unregisterAgent')
.metadata({ agentId })
.build(), { cause: error instanceof Error ? error : undefined }));
}
}
async updateAgentStatus(agentId, status, usage) {
try {
const agent = this.agents.get(agentId);
if (!agent) {
return createFailure(ErrorFactory.createError('validation', `Agent not found: ${agentId}`, createErrorContext('UnifiedTaskExecutionEngine', 'updateAgentStatus')
.metadata({ agentId })
.build()));
}
agent.status = status;
agent.metadata.lastHeartbeat = new Date();
if (usage) {
Object.assign(agent.currentUsage, usage);
}
this.emit('agentStatusUpdated', agent);
return createSuccess(undefined);
}
catch (error) {
return createFailure(ErrorFactory.createError('system', `Failed to update agent status: ${error instanceof Error ? error.message : 'Unknown error'}`, createErrorContext('UnifiedTaskExecutionEngine', 'updateAgentStatus')
.metadata({ agentId })
.build(), { cause: error instanceof Error ? error : undefined }));
}
}
async submitTask(task, resourceRequirements) {
try {
const executionId = createExecutionId(`exec_${task.id}_${Date.now()}`);
const taskId = createTaskId(task.id);
const execution = {
executionId,
taskId,
status: 'queued',
priority: task.priority || 'medium',
scheduledAt: new Date(),
retryCount: 0,
maxRetries: this.config.watchdog.maxRetries,
resourceRequirements: {
memoryMB: 256,
cpuWeight: 1,
estimatedDurationMinutes: (task.estimatedHours || 0.5) * 60,
...resourceRequirements
}
};
this.executions.set(executionId, execution);
this.schedulingQueue.push(execution);
this.emit('taskSubmitted', execution);
logger.info(`Task submitted for execution: ${taskId}`);
return createSuccess(executionId);
}
catch (error) {
return createFailure(ErrorFactory.createError('system', `Failed to submit task: ${error instanceof Error ? error.message : 'Unknown error'}`, createErrorContext('UnifiedTaskExecutionEngine', 'submitTask')
.metadata({ taskId: task.id })
.build(), { cause: error instanceof Error ? error : undefined }));
}
}
async cancelExecution(executionId) {
try {
const execution = this.executions.get(executionId);
if (!execution) {
return createFailure(ErrorFactory.createError('validation', `Execution not found: ${executionId}`, createErrorContext('UnifiedTaskExecutionEngine', 'cancelExecution')
.metadata({ executionId })
.build()));
}
if (execution.status === 'completed' || execution.status === 'cancelled') {
return createFailure(ErrorFactory.createError('validation', `Cannot cancel execution in status: ${execution.status}`, createErrorContext('UnifiedTaskExecutionEngine', 'cancelExecution')
.metadata({ executionId, status: execution.status })
.build()));
}
execution.status = 'cancelled';
execution.completedAt = new Date();
const queueIndex = this.schedulingQueue.findIndex(e => e.executionId === executionId);
if (queueIndex !== -1) {
this.schedulingQueue.splice(queueIndex, 1);
}
this.monitors.delete(execution.taskId);
this.emit('executionCancelled', execution);
logger.info(`Execution cancelled: ${executionId}`);
return createSuccess(undefined);
}
catch (error) {
return createFailure(ErrorFactory.createError('system', `Failed to cancel execution: ${error instanceof Error ? error.message : 'Unknown error'}`, createErrorContext('UnifiedTaskExecutionEngine', 'cancelExecution')
.metadata({ executionId })
.build(), { cause: error instanceof Error ? error : undefined }));
}
}
getExecution(executionId) {
return this.executions.get(executionId) || null;
}
getAllExecutions() {
return Array.from(this.executions.values());
}
getExecutionsByStatus(status) {
return Array.from(this.executions.values()).filter(e => e.status === status);
}
startScheduler() {
if (this.schedulingTimer) {
clearInterval(this.schedulingTimer);
}
this.schedulingTimer = setInterval(() => {
this.processSchedulingQueue().catch(error => {
logger.error('Scheduling error:', error);
});
}, this.config.scheduling.schedulingInterval);
logger.info('Task scheduler started');
}
async processSchedulingQueue() {
if (this.isSchedulingActive || this.schedulingQueue.length === 0) {
return;
}
this.isSchedulingActive = true;
try {
const sortedTasks = this.sortTasksByAlgorithm(this.schedulingQueue);
const batchSize = Math.min(this.config.scheduling.batchSize, sortedTasks.length);
const batch = sortedTasks.splice(0, batchSize);
for (const execution of batch) {
const agent = await this.selectOptimalAgent(execution);
if (agent) {
await this.assignTaskToAgent(execution, agent);
}
}
for (const execution of batch) {
const index = this.schedulingQueue.findIndex(e => e.executionId === execution.executionId);
if (index !== -1) {
this.schedulingQueue.splice(index, 1);
}
}
}
finally {
this.isSchedulingActive = false;
}
}
sortTasksByAlgorithm(tasks) {
const sorted = [...tasks];
switch (this.config.scheduling.algorithm) {
case 'priority_first':
return sorted.sort((a, b) => this.comparePriority(b.priority, a.priority));
case 'earliest_deadline':
return sorted.sort((a, b) => {
const aDeadline = a.timeoutAt?.getTime() || Infinity;
const bDeadline = b.timeoutAt?.getTime() || Infinity;
return aDeadline - bDeadline;
});
case 'shortest_job':
return sorted.sort((a, b) => (a.resourceRequirements.estimatedDurationMinutes || 0) -
(b.resourceRequirements.estimatedDurationMinutes || 0));
case 'resource_balanced':
return sorted.sort((a, b) => this.compareResourceRequirements(a, b));
case 'hybrid_optimal':
return sorted.sort((a, b) => this.calculateTaskScore(b) - this.calculateTaskScore(a));
default:
return sorted;
}
}
comparePriority(a, b) {
const priorityOrder = { 'critical': 4, 'high': 3, 'medium': 2, 'low': 1 };
return priorityOrder[a] - priorityOrder[b];
}
compareResourceRequirements(a, b) {
const aScore = a.resourceRequirements.memoryMB + a.resourceRequirements.cpuWeight * 100;
const bScore = b.resourceRequirements.memoryMB + b.resourceRequirements.cpuWeight * 100;
return aScore - bScore;
}
calculateTaskScore(execution) {
const priorityScore = this.comparePriority(execution.priority, 'low') * 25;
const urgencyScore = execution.timeoutAt ?
Math.max(0, 25 - (execution.timeoutAt.getTime() - Date.now()) / (1000 * 60 * 60)) : 0;
const resourceScore = Math.max(0, 25 - execution.resourceRequirements.memoryMB / 100);
const durationScore = Math.max(0, 25 - execution.resourceRequirements.estimatedDurationMinutes);
return priorityScore + urgencyScore + resourceScore + durationScore;
}
async selectOptimalAgent(execution) {
const availableAgents = Array.from(this.agents.values())
.filter(agent => agent.status === 'idle' &&
this.canAgentHandleTask(agent, execution));
if (availableAgents.length === 0) {
return null;
}
return availableAgents.reduce((best, current) => {
const bestScore = this.calculateAgentScore(best, execution);
const currentScore = this.calculateAgentScore(current, execution);
return currentScore > bestScore ? current : best;
});
}
canAgentHandleTask(agent, execution) {
const memoryAvailable = agent.capacity.maxMemoryMB - agent.currentUsage.memoryMB;
const cpuAvailable = agent.capacity.maxCpuWeight - agent.currentUsage.cpuWeight;
const tasksAvailable = agent.capacity.maxConcurrentTasks - agent.currentUsage.activeTasks;
return memoryAvailable >= execution.resourceRequirements.memoryMB &&
cpuAvailable >= execution.resourceRequirements.cpuWeight &&
tasksAvailable > 0;
}
calculateAgentScore(agent, _execution) {
const memoryUtilization = agent.currentUsage.memoryMB / agent.capacity.maxMemoryMB;
const cpuUtilization = agent.currentUsage.cpuWeight / agent.capacity.maxCpuWeight;
const taskUtilization = agent.currentUsage.activeTasks / agent.capacity.maxConcurrentTasks;
const utilizationScore = (1 - (memoryUtilization + cpuUtilization + taskUtilization) / 3) * 50;
const performanceScore = agent.metadata.successRate * 50;
return utilizationScore + performanceScore;
}
async assignTaskToAgent(execution, agent) {
execution.agentId = agent.id;
execution.status = 'running';
execution.startedAt = new Date();
agent.currentUsage.memoryMB += execution.resourceRequirements.memoryMB;
agent.currentUsage.cpuWeight += execution.resourceRequirements.cpuWeight;
agent.currentUsage.activeTasks += 1;
agent.status = 'busy';
await this.startTaskMonitoring(execution);
this.emit('taskAssigned', { execution, agent });
logger.info(`Task assigned: ${execution.taskId} -> ${agent.id}`);
}
initializeDefaultWatchdogConfigs() {
const defaultConfigs = [
{
taskType: 'default',
timeoutMinutes: this.config.watchdog.defaultTimeout,
warningThresholdMinutes: this.config.watchdog.defaultTimeout * 0.8,
maxRetries: this.config.watchdog.maxRetries,
escalationDelayMinutes: 5,
healthCheckIntervalMinutes: this.config.watchdog.healthCheckInterval
},
{
taskType: 'quick',
timeoutMinutes: 15,
warningThresholdMinutes: 10,
maxRetries: 2,
escalationDelayMinutes: 2,
healthCheckIntervalMinutes: 1
},
{
taskType: 'long_running',
timeoutMinutes: 120,
warningThresholdMinutes: 90,
maxRetries: 1,
escalationDelayMinutes: 10,
healthCheckIntervalMinutes: 5
}
];
for (const config of defaultConfigs) {
this.watchdogConfigs.set(config.taskType, config);
}
}
startWatchdog() {
if (this.watchdogTimer) {
clearInterval(this.watchdogTimer);
}
this.watchdogTimer = setInterval(() => {
this.processWatchdogChecks().catch(error => {
logger.error('Watchdog error:', error);
});
}, this.config.watchdog.healthCheckInterval * 60 * 1000);
logger.info('Execution watchdog started');
}
async startTaskMonitoring(execution) {
const config = this.watchdogConfigs.get('default');
const now = new Date();
const monitor = {
taskId: execution.taskId,
agentId: execution.agentId,
startTime: now,
lastHeartbeat: now,
timeoutAt: new Date(now.getTime() + config.timeoutMinutes * 60 * 1000),
warningAt: new Date(now.getTime() + config.warningThresholdMinutes * 60 * 1000),
status: 'monitoring',
retryCount: 0,
escalationLevel: 0,
taskType: 'default',
estimatedDuration: execution.resourceRequirements.estimatedDurationMinutes
};
this.monitors.set(execution.taskId, monitor);
this.emit('monitoringStarted', monitor);
}
async processWatchdogChecks() {
const now = new Date();
for (const monitor of this.monitors.values()) {
const execution = Array.from(this.executions.values())
.find(e => e.taskId === monitor.taskId);
if (!execution || execution.status !== 'running') {
this.monitors.delete(monitor.taskId);
continue;
}
if (now >= monitor.timeoutAt && monitor.status !== 'timeout') {
await this.handleTaskTimeout(execution, monitor);
}
else if (now >= monitor.warningAt && monitor.status === 'monitoring') {
await this.handleTaskWarning(execution, monitor);
}
}
}
async handleTaskTimeout(execution, monitor) {
monitor.status = 'timeout';
execution.status = 'timeout';
execution.completedAt = new Date();
if (execution.agentId) {
const agent = this.agents.get(execution.agentId);
if (agent) {
agent.currentUsage.memoryMB -= execution.resourceRequirements.memoryMB;
agent.currentUsage.cpuWeight -= execution.resourceRequirements.cpuWeight;
agent.currentUsage.activeTasks -= 1;
if (agent.currentUsage.activeTasks === 0) {
agent.status = 'idle';
}
}
}
this.emit('taskTimeout', { execution, monitor });
logger.warn(`Task timeout: ${execution.taskId}`);
if (execution.retryCount < execution.maxRetries) {
await this.retryExecution(execution);
}
}
async handleTaskWarning(execution, monitor) {
monitor.status = 'warning';
this.emit('taskWarning', { execution, monitor });
logger.warn(`Task warning: ${execution.taskId} approaching timeout`);
}
async retryExecution(execution) {
execution.retryCount += 1;
execution.status = 'queued';
execution.agentId = undefined;
execution.startedAt = undefined;
execution.completedAt = undefined;
this.schedulingQueue.push(execution);
this.monitors.delete(execution.taskId);
this.emit('executionRetry', execution);
logger.info(`Retrying execution: ${execution.executionId} (attempt ${execution.retryCount})`);
}
async completeExecution(executionId, result) {
try {
const execution = this.executions.get(executionId);
if (!execution) {
return createFailure(ErrorFactory.createError('validation', `Execution not found: ${executionId}`, createErrorContext('UnifiedTaskExecutionEngine', 'completeExecution')
.metadata({ executionId })
.build()));
}
execution.status = 'completed';
execution.completedAt = new Date();
execution.result = result;
if (execution.startedAt) {
execution.actualDuration = execution.completedAt.getTime() - execution.startedAt.getTime();
}
if (execution.agentId) {
const agent = this.agents.get(execution.agentId);
if (agent) {
agent.currentUsage.memoryMB -= execution.resourceRequirements.memoryMB;
agent.currentUsage.cpuWeight -= execution.resourceRequirements.cpuWeight;
agent.currentUsage.activeTasks -= 1;
agent.metadata.totalTasksExecuted += 1;
if (agent.currentUsage.activeTasks === 0) {
agent.status = 'idle';
}
const successCount = result?.success ? 1 : 0;
agent.metadata.successRate =
(agent.metadata.successRate * (agent.metadata.totalTasksExecuted - 1) + successCount) /
agent.metadata.totalTasksExecuted;
}
}
this.monitors.delete(execution.taskId);
this.emit('executionCompleted', execution);
logger.info(`Execution completed: ${executionId}`);
return createSuccess(undefined);
}
catch (error) {
return createFailure(ErrorFactory.createError('system', `Failed to complete execution: ${error instanceof Error ? error.message : 'Unknown error'}`, createErrorContext('UnifiedTaskExecutionEngine', 'completeExecution')
.metadata({ executionId })
.build(), { cause: error instanceof Error ? error : undefined }));
}
}
getExecutionStatistics() {
const executions = Array.from(this.executions.values());
const total = executions.length;
const byStatus = {
queued: 0,
running: 0,
completed: 0,
failed: 0,
cancelled: 0,
timeout: 0
};
let totalExecutionTime = 0;
let completedCount = 0;
let successCount = 0;
for (const execution of executions) {
byStatus[execution.status]++;
if (execution.actualDuration) {
totalExecutionTime += execution.actualDuration;
completedCount++;
if (execution.result?.success) {
successCount++;
}
}
}
const averageExecutionTime = completedCount > 0 ? totalExecutionTime / completedCount : 0;
const successRate = completedCount > 0 ? successCount / completedCount : 0;
const agentUtilization = {};
for (const agent of this.agents.values()) {
const utilization = agent.currentUsage.activeTasks / agent.capacity.maxConcurrentTasks;
agentUtilization[agent.id] = utilization;
}
return {
total,
byStatus,
averageExecutionTime,
successRate,
agentUtilization
};
}
dispose() {
if (this.schedulingTimer) {
clearInterval(this.schedulingTimer);
this.schedulingTimer = null;
}
if (this.watchdogTimer) {
clearInterval(this.watchdogTimer);
this.watchdogTimer = null;
}
for (const timer of this.streamingTimers.values()) {
clearInterval(timer);
}
this.streamingTimers.clear();
for (const execution of this.executions.values()) {
if (execution.status === 'running' || execution.status === 'queued') {
execution.status = 'cancelled';
}
}
this.agents.clear();
this.executions.clear();
this.monitors.clear();
this.streams.clear();
this.schedulingQueue.length = 0;
this.streamingQueues.clear();
this.removeAllListeners();
logger.info('Unified Task Execution Engine disposed');
}
}
export function createDefaultConfig() {
return {
scheduling: {
algorithm: 'hybrid_optimal',
enableDynamicPriority: true,
resourceConstraints: {
maxMemoryMB: 8192,
maxCpuWeight: 16,
maxConcurrentTasks: 50,
reservedMemoryMB: 1024,
reservedCpuWeight: 2
},
batchSize: 10,
schedulingInterval: 5000
},
streaming: {
batchSize: 5,
streamInterval: 2000,
maxQueueSize: 100,
priorityThreshold: 0.8,
enableRealTimeStreaming: true,
loadBalancingEnabled: true
},
execution: {
maxConcurrentExecutions: 20,
enableLoadBalancing: true,
enableResourceMonitoring: true,
executionTimeout: 3600000
},
watchdog: {
enabled: true,
defaultTimeout: 30,
healthCheckInterval: 1,
maxRetries: 3,
escalationEnabled: true
},
lifecycle: {
enableAutomation: true,
transitionTimeout: 30000,
enableStateHistory: true,
enableDependencyTracking: true
}
};
}