UNPKG

vibe-coder-mcp

Version:

Production-ready MCP server with complete agent integration, multi-transport support, and comprehensive development automation tools for AI-assisted workflows.

273 lines (272 loc) 10.5 kB
import logger from '../../../logger.js'; export class TimeoutManager { static instance = null; config = null; constructor() { } static getInstance() { if (!TimeoutManager.instance) { TimeoutManager.instance = new TimeoutManager(); } return TimeoutManager.instance; } static isInstanceInitialized() { return TimeoutManager.instance !== null && TimeoutManager.instance.isInitialized(); } initialize(config) { this.config = config; logger.debug('TimeoutManager initialized with configuration'); } isInitialized() { return this.config !== null; } getTimeout(operation) { if (!this.config) { const fallbacks = { taskExecution: 300000, taskDecomposition: 600000, recursiveTaskDecomposition: 720000, taskRefinement: 180000, agentCommunication: 30000, llmRequest: 60000, fileOperations: 10000, databaseOperations: 15000, networkOperations: 20000 }; logger.warn({ operation }, 'Using fallback timeout value - config not initialized'); return fallbacks[operation]; } return this.config.timeouts[operation]; } getComplexityAdjustedTimeout(operation, complexity, estimatedHours) { const baseTimeout = this.getTimeout(operation); const complexityMultipliers = { simple: 1.0, moderate: 1.5, complex: 2.0, critical: 3.0 }; let adjustedTimeout = baseTimeout * complexityMultipliers[complexity]; if (operation === 'taskExecution' && estimatedHours) { const hourMultiplier = Math.max(1.0, estimatedHours / 2); adjustedTimeout = Math.max(adjustedTimeout, baseTimeout * hourMultiplier); } const maxTimeout = operation === 'taskExecution' ? 14400000 : baseTimeout * 5; return Math.min(adjustedTimeout, maxTimeout); } getRetryConfig() { if (!this.config) { logger.warn('Using fallback retry config - config not initialized'); return { maxRetries: 3, backoffMultiplier: 2.0, initialDelayMs: 1000, maxDelayMs: 30000, enableExponentialBackoff: true }; } return this.config.retryPolicy; } getComplexityAdjustedRetryConfig(complexity) { const baseConfig = this.getRetryConfig(); const complexityAdjustments = { simple: { maxRetries: baseConfig.maxRetries, backoffMultiplier: baseConfig.backoffMultiplier }, moderate: { maxRetries: baseConfig.maxRetries + 1, backoffMultiplier: baseConfig.backoffMultiplier * 0.9 }, complex: { maxRetries: baseConfig.maxRetries + 2, backoffMultiplier: baseConfig.backoffMultiplier * 0.8 }, critical: { maxRetries: baseConfig.maxRetries + 3, backoffMultiplier: baseConfig.backoffMultiplier * 0.7 } }; const adjustment = complexityAdjustments[complexity]; return { ...baseConfig, maxRetries: Math.min(adjustment.maxRetries, 10), backoffMultiplier: Math.max(adjustment.backoffMultiplier, 1.2), maxDelayMs: Math.min(baseConfig.maxDelayMs * 2, 120000) }; } async executeWithTimeout(operation, operationFn, customTimeout, customRetryConfig) { const timeout = customTimeout || this.getTimeout(operation); const retryConfig = { ...this.getRetryConfig(), ...customRetryConfig }; let retryCount = 0; let lastError; const startTime = Date.now(); while (retryCount <= retryConfig.maxRetries) { try { const result = await this.executeWithTimeoutOnce(operationFn, timeout); if (result.success) { const duration = Date.now() - startTime; logger.debug({ operation, duration, retryCount, timeout }, 'Operation completed successfully'); return { success: true, data: result.data, timedOut: false, duration, retryCount }; } lastError = result.error; if (result.timedOut && retryCount < retryConfig.maxRetries) { const delay = this.calculateDelay(retryCount, retryConfig); logger.warn({ operation, retryCount: retryCount + 1, delay, timeout, error: lastError }, 'Operation timed out, retrying'); await this.delay(delay); } } catch (error) { lastError = error instanceof Error ? error.message : 'Unknown error'; if (retryCount < retryConfig.maxRetries) { const delay = this.calculateDelay(retryCount, retryConfig); logger.warn({ operation, retryCount: retryCount + 1, delay, error: lastError }, 'Operation failed, retrying'); await this.delay(delay); } } retryCount++; } const duration = Date.now() - startTime; logger.error({ operation, retryCount, duration, timeout, error: lastError }, 'Operation failed after all retries'); return { success: false, error: lastError || 'Operation failed after maximum retries', timedOut: true, duration, retryCount }; } async executeWithTimeoutOnce(operationFn, timeout) { return new Promise((resolve) => { let completed = false; const timeoutHandle = setTimeout(() => { if (!completed) { completed = true; resolve({ success: false, error: `Operation timed out after ${timeout}ms`, timedOut: true }); } }, timeout); operationFn() .then((result) => { if (!completed) { completed = true; clearTimeout(timeoutHandle); resolve({ success: true, data: result, timedOut: false }); } }) .catch((error) => { if (!completed) { completed = true; clearTimeout(timeoutHandle); resolve({ success: false, error: error instanceof Error ? error.message : 'Unknown error', timedOut: false }); } }); }); } calculateDelay(retryCount, config) { if (!config.enableExponentialBackoff) { return config.initialDelayMs; } const delay = config.initialDelayMs * Math.pow(config.backoffMultiplier, retryCount); return Math.min(delay, config.maxDelayMs); } delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } createTimeoutPromise(operation, customTimeout) { const timeout = customTimeout || this.getTimeout(operation); return new Promise((_, reject) => { setTimeout(() => { reject(new Error(`${operation} operation timed out after ${timeout}ms`)); }, timeout); }); } async raceWithTimeout(operation, operationPromise, customTimeout) { const timeoutPromise = this.createTimeoutPromise(operation, customTimeout); return Promise.race([operationPromise, timeoutPromise]); } getTimeoutSummary() { const operations = [ 'taskExecution', 'taskDecomposition', 'recursiveTaskDecomposition', 'taskRefinement', 'agentCommunication', 'llmRequest', 'fileOperations', 'databaseOperations', 'networkOperations' ]; const summary = {}; for (const operation of operations) { summary[operation] = this.getTimeout(operation); } return summary; } validateTimeouts() { const issues = []; if (!this.config) { issues.push('Timeout configuration not initialized'); return { valid: false, issues }; } const timeouts = this.config.timeouts; if (timeouts.taskExecution < 10000) { issues.push('Task execution timeout is too low (< 10 seconds)'); } if (timeouts.taskExecution > 3600000) { issues.push('Task execution timeout is too high (> 1 hour)'); } if (timeouts.llmRequest < 5000) { issues.push('LLM request timeout is too low (< 5 seconds)'); } if (timeouts.fileOperations < 1000) { issues.push('File operations timeout is too low (< 1 second)'); } const retry = this.config.retryPolicy; if (retry.maxRetries < 0 || retry.maxRetries > 10) { issues.push('Max retries should be between 0 and 10'); } if (retry.backoffMultiplier < 1.0 || retry.backoffMultiplier > 5.0) { issues.push('Backoff multiplier should be between 1.0 and 5.0'); } if (retry.initialDelayMs < 100 || retry.initialDelayMs > 10000) { issues.push('Initial delay should be between 100ms and 10 seconds'); } return { valid: issues.length === 0, issues }; } } export function isTimeoutManagerInitialized() { return TimeoutManager.isInstanceInitialized(); } export function getTimeoutManager() { return TimeoutManager.getInstance(); }