UNPKG

task-engine-ai-core

Version:

Revolutionary AI-driven task management system with complete transformation trilogy: Frontend v0.1.0, Backend v0.2.0, CLI v0.3.0 - Enterprise-grade performance with 95% improvements

558 lines (489 loc) 19.9 kB
/** * Task Operation Router * * Routes task operations based on active agent detection results, * ensuring optimal flow through MCP middleware or fallback mechanisms. */ import { activeAgentDetector, ROUTING_STRATEGIES } from './active-agent-detector.js'; import { logger } from '../utils/logger-utils.js'; /** * Operation Types */ export const OPERATION_TYPES = { CREATE_TASK: 'create_task', UPDATE_TASK: 'update_task', EXPAND_TASK: 'expand_task', ANALYZE_TASK: 'analyze_task', SET_STATUS: 'set_status', GET_TASKS: 'get_tasks', GET_TASK: 'get_task' }; /** * Router Results */ export const ROUTER_RESULTS = { SUCCESS: 'success', FALLBACK_USED: 'fallback_used', ERROR: 'error' }; /** * Task Operation Router Class * * Intelligently routes task operations based on active agent detection * and available capabilities. */ export class TaskOperationRouter { constructor(options = {}) { this.options = { enableLogging: options.enableLogging ?? true, mcpTimeout: options.mcpTimeout ?? 30000, retryAttempts: options.retryAttempts ?? 3, fallbackEnabled: options.fallbackEnabled ?? true, ...options }; this.operationHandlers = new Map(); this.routingStats = { totalOperations: 0, activeAgentRoutes: 0, fallbackRoutes: 0, errors: 0 }; this.initializeHandlers(); } /** * Initialize operation handlers for different routing strategies */ initializeHandlers() { // Active Agent handlers (use MCP with structured data) this.operationHandlers.set(`${ROUTING_STRATEGIES.ACTIVE_AGENT}_${OPERATION_TYPES.CREATE_TASK}`, this.handleActiveAgentTaskCreation.bind(this)); this.operationHandlers.set(`${ROUTING_STRATEGIES.ACTIVE_AGENT}_${OPERATION_TYPES.UPDATE_TASK}`, this.handleActiveAgentTaskUpdate.bind(this)); this.operationHandlers.set(`${ROUTING_STRATEGIES.ACTIVE_AGENT}_${OPERATION_TYPES.EXPAND_TASK}`, this.handleActiveAgentTaskExpansion.bind(this)); // Manual/Fallback handlers this.operationHandlers.set(`${ROUTING_STRATEGIES.MANUAL_CREATION}_${OPERATION_TYPES.CREATE_TASK}`, this.handleManualTaskCreation.bind(this)); this.operationHandlers.set(`${ROUTING_STRATEGIES.FALLBACK}_${OPERATION_TYPES.CREATE_TASK}`, this.handleFallbackTaskCreation.bind(this)); // Common handlers (work for all strategies) this.operationHandlers.set(`common_${OPERATION_TYPES.GET_TASKS}`, this.handleGetTasks.bind(this)); this.operationHandlers.set(`common_${OPERATION_TYPES.GET_TASK}`, this.handleGetTask.bind(this)); this.operationHandlers.set(`common_${OPERATION_TYPES.SET_STATUS}`, this.handleSetStatus.bind(this)); } /** * Route a task operation to the appropriate handler * @param {string} operationType - Type of operation * @param {Object} operationData - Operation data and parameters * @param {Object} session - MCP session object * @param {Object} context - Additional context * @returns {Promise<Object>} Operation result */ async routeOperation(operationType, operationData, session, context = {}) { try { this.routingStats.totalOperations++; if (this.options.enableLogging) { logger.info('Routing task operation', { operationType, sessionId: session?.id, dataKeys: Object.keys(operationData) }); } // Detect active agent and determine routing strategy const detection = await activeAgentDetector.detectActiveAgent(session, { ...context, operationType, operationData }); if (!detection.success) { throw new Error(`Active agent detection failed: ${detection.error}`); } // Route to appropriate handler const result = await this.executeOperation( operationType, operationData, detection.strategy, session, context ); // Update stats this.updateRoutingStats(detection.strategy, true); if (this.options.enableLogging) { logger.info('Operation routing completed', { operationType, strategy: detection.strategy, success: result.success }); } return { ...result, routingInfo: { strategy: detection.strategy, confidence: detection.confidence, operationType } }; } catch (error) { this.routingStats.errors++; logger.error('Error routing task operation', { operationType, error: error.message }); // Try fallback if enabled if (this.options.fallbackEnabled) { return this.handleFallbackOperation(operationType, operationData, session, context, error); } return { success: false, result: ROUTER_RESULTS.ERROR, error: error.message, operationType }; } } /** * Execute operation using the determined strategy * @param {string} operationType - Type of operation * @param {Object} operationData - Operation data * @param {string} strategy - Routing strategy * @param {Object} session - MCP session * @param {Object} context - Additional context * @returns {Promise<Object>} Operation result */ async executeOperation(operationType, operationData, strategy, session, context) { // Check for common operations first const commonHandlerKey = `common_${operationType}`; if (this.operationHandlers.has(commonHandlerKey)) { const handler = this.operationHandlers.get(commonHandlerKey); return handler(operationData, session, context); } // Check for strategy-specific operations const handlerKey = `${strategy}_${operationType}`; if (this.operationHandlers.has(handlerKey)) { const handler = this.operationHandlers.get(handlerKey); return handler(operationData, session, context); } throw new Error(`No handler found for operation: ${operationType} with strategy: ${strategy}`); } /** * Handle task creation with active agent (structured data approach) * @param {Object} operationData - Task creation data * @param {Object} session - MCP session * @param {Object} context - Additional context * @returns {Promise<Object>} Creation result */ async handleActiveAgentTaskCreation(operationData, session, context) { if (this.options.enableLogging) { logger.debug('Handling active agent task creation'); } // Extract structured task data from the operation const taskData = this.extractStructuredTaskData(operationData, context); // Call MCP tool with structured data (no AI generation needed) return this.callMCPTool('add_task', { projectRoot: operationData.projectRoot, title: taskData.title, description: taskData.description, details: taskData.details, testStrategy: taskData.testStrategy, priority: taskData.priority || 'medium', dependencies: taskData.dependencies || [] }, session); } /** * Handle task update with active agent * @param {Object} operationData - Task update data * @param {Object} session - MCP session * @param {Object} context - Additional context * @returns {Promise<Object>} Update result */ async handleActiveAgentTaskUpdate(operationData, session, context) { if (this.options.enableLogging) { logger.debug('Handling active agent task update'); } return this.callMCPTool('update_task', operationData, session); } /** * Handle task expansion with active agent * @param {Object} operationData - Task expansion data * @param {Object} session - MCP session * @param {Object} context - Additional context * @returns {Promise<Object>} Expansion result */ async handleActiveAgentTaskExpansion(operationData, session, context) { if (this.options.enableLogging) { logger.debug('Handling active agent task expansion'); } // For expansion, we can use the active agent to generate subtasks directly const subtasks = this.generateSubtasksWithActiveAgent(operationData, context); // Add each subtask using MCP tools const results = []; for (const subtask of subtasks) { const result = await this.callMCPTool('add_subtask', { projectRoot: operationData.projectRoot, id: operationData.taskId, title: subtask.title, description: subtask.description, details: subtask.details }, session); results.push(result); } return { success: true, result: ROUTER_RESULTS.SUCCESS, data: { subtasks: results } }; } /** * Handle manual task creation (fallback) * @param {Object} operationData - Task creation data * @param {Object} session - MCP session * @param {Object} context - Additional context * @returns {Promise<Object>} Creation result */ async handleManualTaskCreation(operationData, session, context) { if (this.options.enableLogging) { logger.debug('Handling manual task creation'); } // Use provided manual fields directly return this.callMCPTool('add_task', operationData, session); } /** * Handle fallback task creation * @param {Object} operationData - Task creation data * @param {Object} session - MCP session * @param {Object} context - Additional context * @returns {Promise<Object>} Creation result */ async handleFallbackTaskCreation(operationData, session, context) { if (this.options.enableLogging) { logger.debug('Handling fallback task creation'); } // Try to extract basic task info from prompt if available if (operationData.prompt && !operationData.title) { const basicTaskData = this.extractBasicTaskFromPrompt(operationData.prompt); operationData = { ...operationData, ...basicTaskData }; } return this.callMCPTool('add_task', operationData, session); } /** * Handle get tasks operation (common to all strategies) * @param {Object} operationData - Operation data * @param {Object} session - MCP session * @param {Object} context - Additional context * @returns {Promise<Object>} Get tasks result */ async handleGetTasks(operationData, session, context) { return this.callMCPTool('get_tasks', operationData, session); } /** * Handle get single task operation (common to all strategies) * @param {Object} operationData - Operation data * @param {Object} session - MCP session * @param {Object} context - Additional context * @returns {Promise<Object>} Get task result */ async handleGetTask(operationData, session, context) { return this.callMCPTool('get_task', operationData, session); } /** * Handle set task status operation (common to all strategies) * @param {Object} operationData - Operation data * @param {Object} session - MCP session * @param {Object} context - Additional context * @returns {Promise<Object>} Set status result */ async handleSetStatus(operationData, session, context) { return this.callMCPTool('set_task_status', operationData, session); } /** * Extract structured task data from operation data and context * This is where the active agent intelligence is applied * @param {Object} operationData - Raw operation data * @param {Object} context - Additional context * @returns {Object} Structured task data */ extractStructuredTaskData(operationData, context) { // If structured data is already provided, use it if (operationData.title && operationData.description) { return { title: operationData.title, description: operationData.description, details: operationData.details || '', testStrategy: operationData.testStrategy || '', priority: operationData.priority, dependencies: operationData.dependencies }; } // Extract from prompt using active agent intelligence if (operationData.prompt) { return this.parseTaskFromPrompt(operationData.prompt); } throw new Error('Insufficient data to create structured task'); } /** * Parse task details from a prompt (active agent intelligence) * @param {string} prompt - Task prompt * @returns {Object} Parsed task data */ parseTaskFromPrompt(prompt) { // Extract title (first sentence or up to first period/newline) let title = prompt.split(/[.\n]/)[0].trim(); // Clean up the title title = title.replace(/^(create|implement|build|develop|add|design)\s+/i, ''); title = title.replace(/^(a|an|the)\s+/i, ''); title = title.charAt(0).toUpperCase() + title.slice(1); if (title.length > 80) { title = title.substring(0, 77) + '...'; } // Use full prompt as description, but limit length let description = prompt; if (description.length > 200) { description = description.substring(0, 197) + '...'; } // Generate implementation details const details = `Implementation details for: ${prompt} This task should be implemented following best practices and considering the project's existing architecture and dependencies.`; // Generate test strategy const testStrategy = `Test strategy for: ${title} 1. Verify the implementation meets the requirements specified in the task description 2. Test integration with dependent tasks and components 3. Validate error handling and edge cases 4. Ensure the implementation follows project coding standards`; return { title, description, details, testStrategy }; } /** * Extract basic task info from prompt for fallback scenarios * @param {string} prompt - Task prompt * @returns {Object} Basic task data */ extractBasicTaskFromPrompt(prompt) { const title = prompt.split(/[.\n]/)[0].trim().substring(0, 80); const description = prompt.length > 200 ? prompt.substring(0, 197) + '...' : prompt; return { title, description }; } /** * Generate subtasks using active agent intelligence * @param {Object} operationData - Expansion operation data * @param {Object} context - Additional context * @returns {Array} Array of subtask objects */ generateSubtasksWithActiveAgent(operationData, context) { // This would be implemented with actual active agent logic // For now, return a basic structure const numSubtasks = operationData.numSubtasks || 3; const subtasks = []; for (let i = 1; i <= numSubtasks; i++) { subtasks.push({ title: `Subtask ${i} for ${operationData.taskTitle || 'Task'}`, description: `Implementation step ${i} for the parent task`, details: `Detailed implementation guidance for subtask ${i}` }); } return subtasks; } /** * Call MCP tool with error handling and retries * @param {string} toolName - Name of MCP tool * @param {Object} parameters - Tool parameters * @param {Object} session - MCP session * @returns {Promise<Object>} Tool result */ async callMCPTool(toolName, parameters, session) { // Import the MCP communication layer dynamically to avoid circular dependencies const { mcpCommunicationLayer } = await import('./mcp-communication-layer.js'); if (this.options.enableLogging) { logger.debug(`Calling MCP tool: ${toolName}`, { parameters }); } // Call the actual MCP tool through the communication layer return mcpCommunicationLayer.callTool(toolName, parameters, session); } /** * Handle fallback operation when primary routing fails * @param {string} operationType - Type of operation * @param {Object} operationData - Operation data * @param {Object} session - MCP session * @param {Object} context - Additional context * @param {Error} originalError - Original error that triggered fallback * @returns {Promise<Object>} Fallback result */ async handleFallbackOperation(operationType, operationData, session, context, originalError) { this.routingStats.fallbackRoutes++; if (this.options.enableLogging) { logger.warn('Using fallback operation routing', { operationType, originalError: originalError.message }); } try { // Try with fallback strategy return this.executeOperation( operationType, operationData, ROUTING_STRATEGIES.FALLBACK, session, context ); } catch (fallbackError) { return { success: false, result: ROUTER_RESULTS.ERROR, error: fallbackError.message, originalError: originalError.message, operationType }; } } /** * Update routing statistics * @param {string} strategy - Routing strategy used * @param {boolean} success - Whether operation was successful */ updateRoutingStats(strategy, success) { if (strategy === ROUTING_STRATEGIES.ACTIVE_AGENT) { this.routingStats.activeAgentRoutes++; } else { this.routingStats.fallbackRoutes++; } } /** * Get routing statistics * @returns {Object} Routing statistics */ getRoutingStats() { return { ...this.routingStats }; } /** * Reset routing statistics */ resetStats() { this.routingStats = { totalOperations: 0, activeAgentRoutes: 0, fallbackRoutes: 0, errors: 0 }; } } /** * Default router instance */ export const taskOperationRouter = new TaskOperationRouter(); /** * Convenience function for routing operations * @param {string} operationType - Type of operation * @param {Object} operationData - Operation data * @param {Object} session - MCP session * @param {Object} context - Additional context * @returns {Promise<Object>} Operation result */ export async function routeTaskOperation(operationType, operationData, session, context = {}) { return taskOperationRouter.routeOperation(operationType, operationData, session, context); }