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.

338 lines (337 loc) 13.4 kB
import { AgentOrchestrator } from './agent-orchestrator.js'; import { TaskStreamer } from './task-streamer.js'; import { AppError, ValidationError } from '../../../utils/errors.js'; import logger from '../../../logger.js'; export class FeedbackProcessor { static instance = null; agentOrchestrator; taskStreamer; config; performanceMetrics = new Map(); helpRequests = new Map(); blockers = new Map(); stats; constructor(config) { this.config = { enablePerformanceTracking: true, helpRequestTimeout: 3600000, blockerEscalationDelay: 1800000, autoRetryFailedTasks: true, maxHelpRequests: 3, ...config }; this.agentOrchestrator = AgentOrchestrator.getInstance(); this.taskStreamer = TaskStreamer.getInstance(); this.stats = { totalResponsesProcessed: 0, completedTasks: 0, failedTasks: 0, helpRequests: 0, blockers: 0, averageResponseTime: 0 }; logger.info({ config: this.config }, 'Feedback processor initialized'); } static getInstance(config) { if (!FeedbackProcessor.instance) { FeedbackProcessor.instance = new FeedbackProcessor(config); } return FeedbackProcessor.instance; } async processFeedback(responseText, agentId, taskId) { const startTime = Date.now(); try { await this.agentOrchestrator.processAgentResponse(responseText, agentId); this.stats.totalResponsesProcessed++; this.stats.lastProcessedAt = new Date(); const responseTime = Date.now() - startTime; this.stats.averageResponseTime = (this.stats.averageResponseTime + responseTime) / 2; logger.debug({ agentId, taskId, responseTime }, 'Agent feedback processed'); } catch (error) { logger.error({ err: error, agentId, taskId }, 'Failed to process agent feedback'); throw new AppError('Feedback processing failed', { cause: error }); } } async handleTaskCompletion(taskId, agentId, response) { try { if (this.config.enablePerformanceTracking) { await this.updateAgentPerformance(agentId, 'completed', response); } this.stats.completedTasks++; await this.generateNextTaskRecommendations(agentId); logger.info({ taskId, agentId, completionDetails: response.completion_details }, 'Task completion processed'); } catch (error) { logger.error({ err: error, taskId, agentId }, 'Failed to handle task completion'); throw new AppError('Task completion handling failed', { cause: error }); } } async handleHelpRequest(taskId, agentId, response) { try { const helpRequestId = `help_${taskId}_${Date.now()}`; const helpRequest = { id: helpRequestId, taskId, agentId, requestedAt: new Date(), status: 'pending', description: response.help_request?.issue_description || response.message || 'No description provided', attemptedSolutions: response.help_request?.attempted_solutions || [], specificQuestions: response.help_request?.specific_questions || [] }; this.helpRequests.set(helpRequestId, helpRequest); if (this.config.enablePerformanceTracking) { await this.updateAgentPerformance(agentId, 'help_requested', response); } this.stats.helpRequests++; const agentHelpRequests = Array.from(this.helpRequests.values()) .filter(req => req.agentId === agentId && req.status === 'pending').length; if (agentHelpRequests > this.config.maxHelpRequests) { logger.warn({ agentId, helpRequestCount: agentHelpRequests }, 'Agent exceeded help request limit'); await this.escalateAgentIssues(agentId); } setTimeout(() => { this.timeoutHelpRequest(helpRequestId); }, this.config.helpRequestTimeout); logger.info({ helpRequestId, taskId, agentId, description: helpRequest.description }, 'Help request created'); return helpRequest; } catch (error) { logger.error({ err: error, taskId, agentId }, 'Failed to handle help request'); throw new AppError('Help request handling failed', { cause: error }); } } async handleTaskBlocker(taskId, agentId, response) { try { const blockerId = `blocker_${taskId}_${Date.now()}`; const blocker = { id: blockerId, taskId, agentId, reportedAt: new Date(), status: 'active', type: response.blocker_details?.blocker_type || 'technical', description: response.blocker_details?.description || response.message || 'No description provided', suggestedResolution: response.blocker_details?.suggested_resolution || 'Manual intervention required', impact: this.assessBlockerImpact(response) }; this.blockers.set(blockerId, blocker); if (this.config.enablePerformanceTracking) { await this.updateAgentPerformance(agentId, 'blocked', response); } this.stats.blockers++; if (blocker.impact === 'high' || blocker.impact === 'critical') { setTimeout(() => { this.escalateBlocker(blockerId); }, this.config.blockerEscalationDelay); } logger.warn({ blockerId, taskId, agentId, type: blocker.type, impact: blocker.impact }, 'Task blocker reported'); return blocker; } catch (error) { logger.error({ err: error, taskId, agentId }, 'Failed to handle task blocker'); throw new AppError('Task blocker handling failed', { cause: error }); } } async handleTaskFailure(taskId, agentId, response) { try { if (this.config.enablePerformanceTracking) { await this.updateAgentPerformance(agentId, 'failed', response); } this.stats.failedTasks++; if (this.config.autoRetryFailedTasks) { logger.info({ taskId, agentId }, 'Task queued for auto-retry'); } logger.error({ taskId, agentId, message: response.message }, 'Task failure processed'); } catch (error) { logger.error({ err: error, taskId, agentId }, 'Failed to handle task failure'); throw new AppError('Task failure handling failed', { cause: error }); } } async resolveHelpRequest(helpRequestId, resolution, resolutionType = 'human_intervention') { try { const helpRequest = this.helpRequests.get(helpRequestId); if (!helpRequest) { throw new ValidationError(`Help request not found: ${helpRequestId}`); } helpRequest.status = 'resolved'; helpRequest.resolvedAt = new Date(); helpRequest.resolution = resolution; helpRequest.resolutionType = resolutionType; logger.info({ helpRequestId, taskId: helpRequest.taskId, resolutionType }, 'Help request resolved'); } catch (error) { logger.error({ err: error, helpRequestId }, 'Failed to resolve help request'); throw new AppError('Help request resolution failed', { cause: error }); } } async resolveBlocker(blockerId, resolution) { try { const blocker = this.blockers.get(blockerId); if (!blocker) { throw new ValidationError(`Blocker not found: ${blockerId}`); } blocker.status = 'resolved'; blocker.resolvedAt = new Date(); blocker.actualResolution = resolution; logger.info({ blockerId, taskId: blocker.taskId, resolution }, 'Blocker resolved'); } catch (error) { logger.error({ err: error, blockerId }, 'Failed to resolve blocker'); throw new AppError('Blocker resolution failed', { cause: error }); } } getAgentPerformance(agentId) { return this.performanceMetrics.get(agentId) || null; } getAllPerformanceMetrics() { return Array.from(this.performanceMetrics.values()); } getPendingHelpRequests() { return Array.from(this.helpRequests.values()) .filter(req => req.status === 'pending'); } getActiveBlockers() { return Array.from(this.blockers.values()) .filter(blocker => blocker.status === 'active'); } getStats() { return { ...this.stats }; } async updateAgentPerformance(agentId, action, _response) { let metrics = this.performanceMetrics.get(agentId); if (!metrics) { metrics = { agentId, tasksCompleted: 0, tasksFailedCount: 0, averageCompletionTime: 0, successRate: 1.0, helpRequestsCount: 0, blockersEncountered: 0, lastActivityAt: new Date(), performanceScore: 1.0 }; } metrics.lastActivityAt = new Date(); switch (action) { case 'completed': metrics.tasksCompleted++; break; case 'failed': metrics.tasksFailedCount++; break; case 'help_requested': metrics.helpRequestsCount++; break; case 'blocked': metrics.blockersEncountered++; break; } const totalTasks = metrics.tasksCompleted + metrics.tasksFailedCount; if (totalTasks > 0) { metrics.successRate = metrics.tasksCompleted / totalTasks; } metrics.performanceScore = this.calculatePerformanceScore(metrics); this.performanceMetrics.set(agentId, metrics); } calculatePerformanceScore(metrics) { const successWeight = 0.4; const completionWeight = 0.3; const helpWeight = 0.2; const blockerWeight = 0.1; const successScore = metrics.successRate; const completionScore = Math.min(metrics.tasksCompleted / 10, 1.0); const helpScore = Math.max(0, 1.0 - (metrics.helpRequestsCount / 10)); const blockerScore = Math.max(0, 1.0 - (metrics.blockersEncountered / 5)); return (successScore * successWeight + completionScore * completionWeight + helpScore * helpWeight + blockerScore * blockerWeight); } assessBlockerImpact(response) { const description = response.blocker_details?.description || response.message || ''; const lowerDesc = description.toLowerCase(); if (lowerDesc.includes('critical') || lowerDesc.includes('urgent') || lowerDesc.includes('blocking')) { return 'critical'; } else if (lowerDesc.includes('important') || lowerDesc.includes('significant')) { return 'high'; } else if (lowerDesc.includes('minor') || lowerDesc.includes('small')) { return 'low'; } else { return 'medium'; } } async generateNextTaskRecommendations(agentId) { try { logger.debug({ agentId }, 'Generating next task recommendations'); } catch (error) { logger.error({ err: error, agentId }, 'Failed to generate task recommendations'); } } timeoutHelpRequest(helpRequestId) { const helpRequest = this.helpRequests.get(helpRequestId); if (helpRequest && helpRequest.status === 'pending') { helpRequest.status = 'timeout'; logger.warn({ helpRequestId }, 'Help request timed out'); } } escalateBlocker(blockerId) { const blocker = this.blockers.get(blockerId); if (blocker && blocker.status === 'active') { blocker.status = 'escalated'; logger.warn({ blockerId }, 'Blocker escalated'); } } async escalateAgentIssues(agentId) { logger.warn({ agentId }, 'Escalating agent issues - multiple help requests'); } destroy() { this.performanceMetrics.clear(); this.helpRequests.clear(); this.blockers.clear(); FeedbackProcessor.instance = null; logger.info('Feedback processor destroyed'); } }