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
JavaScript
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');
}
}