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.

1,056 lines (1,046 loc) 56.3 kB
import { performResearchQuery } from '../../../utils/researchHelper.js'; import { performFormatAwareLlmCall, performFormatAwareLlmCallWithCentralizedConfig } from '../../../utils/llmHelper.js'; import { OpenRouterConfigManager } from '../../../utils/openrouter-config-manager.js'; import logger from '../../../logger.js'; import { EventEmitter } from 'events'; import crypto from 'crypto'; import { getTimeoutManager } from '../utils/timeout-manager.js'; class ResearchCircuitBreaker { failures = new Map(); lastFailure = new Map(); maxFailures; cooldownPeriod; constructor(maxFailures = 3, cooldownPeriod = 300000) { this.maxFailures = maxFailures; this.cooldownPeriod = cooldownPeriod; } canAttempt(operation) { const failures = this.failures.get(operation) || 0; const lastFailure = this.lastFailure.get(operation) || 0; const now = Date.now(); if (failures < this.maxFailures) { return true; } return now - lastFailure > this.cooldownPeriod; } recordFailure(operation) { const failures = (this.failures.get(operation) || 0) + 1; this.failures.set(operation, failures); this.lastFailure.set(operation, Date.now()); } recordSuccess(operation) { this.failures.delete(operation); this.lastFailure.delete(operation); } getFailureCount(operation) { return this.failures.get(operation) || 0; } getTimeUntilRetry(operation) { const lastFailure = this.lastFailure.get(operation) || 0; const timeSinceFailure = Date.now() - lastFailure; return Math.max(0, this.cooldownPeriod - timeSinceFailure); } } export class ResearchIntegration extends EventEmitter { static instance; config; openRouterConfig; activeRequests = new Map(); researchCache = new Map(); progressSubscriptions = new Map(); completeSubscriptions = new Map(); performanceMetrics = new Map(); cleanupInterval; circuitBreaker = new ResearchCircuitBreaker(); constructor(config) { super(); this.config = { maxConcurrentRequests: 3, defaultCacheTTL: 1800000, qualityThresholds: { minimum: 0.4, good: 0.7, excellent: 0.9 }, performance: { enableCaching: true, enableParallelQueries: true, maxQueryDepth: 3, timeoutMs: 120000 }, integration: { autoEnhanceDecomposition: true, generateSubQueries: true, extractActionItems: true, createKnowledgeBase: true }, enhancement: { temperature: 0.1, format: 'markdown', timeoutComplexity: 'moderate' }, ...config }; this.initializeConfig(); this.startCleanupProcess(); logger.info({ config: this.config }, 'Research Integration initialized'); } static getInstance(config) { if (!ResearchIntegration.instance) { ResearchIntegration.instance = new ResearchIntegration(config); } return ResearchIntegration.instance; } async performEnhancedResearch(request) { const startTime = Date.now(); const requestId = this.generateRequestId(request); try { if (this.activeRequests.has(requestId)) { logger.debug({ requestId }, 'Research request already in progress'); return await this.activeRequests.get(requestId); } if (this.config.performance.enableCaching) { const cachedResult = this.getCachedResearch(requestId, request.optimization.cacheStrategy); if (cachedResult) { logger.debug({ requestId, cacheStrategy: request.optimization.cacheStrategy }, 'Returning cached research result'); const cachedResultWithHit = { ...cachedResult, performance: { ...cachedResult.performance, cacheHit: true } }; return cachedResultWithHit; } } const requestPromise = this.executeResearchRequest(request, requestId, startTime); this.activeRequests.set(requestId, requestPromise); try { const result = await requestPromise; if (this.config.performance.enableCaching) { this.cacheResearchResult(requestId, result, request.optimization.cacheStrategy); } this.emit('research_completed', result); this.notifyCompleteSubscribers(requestId, result); return result; } finally { this.activeRequests.delete(requestId); } } catch (error) { this.activeRequests.delete(requestId); logger.error({ err: error, requestId }, 'Enhanced research failed'); throw new Error(`Failed to perform enhanced research: ${error instanceof Error ? error.message : String(error)}`); } } async enhanceDecompositionWithResearch(decompositionRequest, researchScope) { const startTime = Date.now(); try { const researchQueries = this.generateResearchQueries(decompositionRequest); const researchResults = []; if (this.config.performance.enableParallelQueries && researchQueries.length > 1) { const researchPromises = researchQueries.map((query, index) => { const operationKey = `research_query_${index}`; if (!this.circuitBreaker.canAttempt(operationKey)) { const timeUntilRetry = this.circuitBreaker.getTimeUntilRetry(operationKey); logger.warn({ query, operationKey, timeUntilRetry, failureCount: this.circuitBreaker.getFailureCount(operationKey) }, 'Research query blocked by circuit breaker'); return Promise.reject(new Error(`Circuit breaker open for ${operationKey}. Retry in ${Math.ceil(timeUntilRetry / 1000)}s`)); } return this.performEnhancedResearch({ query, taskContext: { taskDescription: decompositionRequest.taskDescription, projectPath: decompositionRequest.projectPath, domain: decompositionRequest.domain }, scope: { depth: 'medium', focus: 'technical', timeframe: 'current', ...researchScope }, optimization: { cacheStrategy: 'session', qualityThreshold: this.config.qualityThresholds.good, maxQueries: 2, parallelQueries: false, enhanceResults: false }, integration: { includeInDecomposition: true, generateSubQueries: false, extractActionItems: true, createKnowledgeBase: true } }); }); const results = await Promise.allSettled(researchPromises); results.forEach((result, index) => { const operationKey = `research_query_${index}`; if (result.status === 'fulfilled') { researchResults.push(result.value); this.circuitBreaker.recordSuccess(operationKey); } else { this.circuitBreaker.recordFailure(operationKey); const errorDetails = this.extractErrorDetails(result.reason); logger.warn({ query: researchQueries[index], error: errorDetails, queryIndex: index, operation: 'parallel_research_query', circuitBreakerFailures: this.circuitBreaker.getFailureCount(operationKey) }, 'Research query failed'); } }); } else { for (let i = 0; i < researchQueries.length; i++) { const query = researchQueries[i]; const operationKey = `research_query_sequential_${i}`; try { if (!this.circuitBreaker.canAttempt(operationKey)) { const timeUntilRetry = this.circuitBreaker.getTimeUntilRetry(operationKey); logger.warn({ query, operationKey, timeUntilRetry, failureCount: this.circuitBreaker.getFailureCount(operationKey) }, 'Research query blocked by circuit breaker'); continue; } const result = await this.performEnhancedResearch({ query, taskContext: { taskDescription: decompositionRequest.taskDescription, projectPath: decompositionRequest.projectPath, domain: decompositionRequest.domain }, scope: { depth: 'medium', focus: 'technical', timeframe: 'current', ...researchScope }, optimization: { cacheStrategy: 'session', qualityThreshold: this.config.qualityThresholds.good, maxQueries: 2, parallelQueries: false, enhanceResults: false }, integration: { includeInDecomposition: true, generateSubQueries: false, extractActionItems: true, createKnowledgeBase: true } }); researchResults.push(result); this.circuitBreaker.recordSuccess(operationKey); } catch (error) { this.circuitBreaker.recordFailure(operationKey); const errorDetails = this.extractErrorDetails(error); logger.warn({ query, error: errorDetails, operation: 'sequential_research_query', circuitBreakerFailures: this.circuitBreaker.getFailureCount(operationKey) }, 'Research query failed'); } } } if (researchResults.length > 0) { try { const rawAggregatedContent = researchResults .map(result => result.content) .filter(content => content && content.trim().length > 0) .join('\n\n---\n\n'); const maxContentLength = 50000; const aggregatedContent = rawAggregatedContent.length > maxContentLength ? rawAggregatedContent.substring(0, maxContentLength) + '\n\n[Content truncated due to length...]' : rawAggregatedContent; if (aggregatedContent.trim() && aggregatedContent.length > 100) { const enhancementPrompt = this.buildAggregatedEnhancementPrompt(decompositionRequest, aggregatedContent); const timeoutManager = getTimeoutManager(); const complexityAdjustedTimeout = timeoutManager.getComplexityAdjustedTimeout('llmRequest', this.config.enhancement.timeoutComplexity); const complexityAdjustedRetryConfig = timeoutManager.getComplexityAdjustedRetryConfig(this.config.enhancement.timeoutComplexity); const enhancementResult = await timeoutManager.executeWithTimeout('llmRequest', async () => performFormatAwareLlmCallWithCentralizedConfig(enhancementPrompt, this.getAggregatedResearchSystemPrompt(decompositionRequest), 'research_enhancement', this.config.enhancement.format, undefined, this.config.enhancement.temperature), complexityAdjustedTimeout, complexityAdjustedRetryConfig); if (!enhancementResult.success || !enhancementResult.data) { logger.warn({ error: enhancementResult.error, timedOut: enhancementResult.timedOut, researchResultsCount: researchResults.length, aggregatedContentLength: aggregatedContent.length }, 'Research enhancement failed, using fallback strategy'); const fallbackContent = this.createFallbackSynthesis(decompositionRequest, researchResults); researchResults.forEach(result => { result.content = fallbackContent; result.performance.enhancementTime = 0; }); logger.info({ fallbackContentLength: fallbackContent.length, researchResultsCount: researchResults.length }, 'Applied fallback synthesis instead of LLM enhancement'); } else { const enhancedContent = enhancementResult.data; if (!enhancedContent || enhancedContent.trim().length < 50) { logger.warn({ enhancedContentLength: enhancedContent?.length || 0, researchResultsCount: researchResults.length }, 'Enhanced content is too short, using fallback synthesis'); const fallbackContent = this.createFallbackSynthesis(decompositionRequest, researchResults); researchResults.forEach(result => { result.content = fallbackContent; result.performance.enhancementTime = 0; }); } else { researchResults.forEach(result => { result.content = enhancedContent; result.performance.enhancementTime = result.performance.enhancementTime || 0; result.performance.apiCalls = (result.performance.apiCalls || 1) + (1 / researchResults.length); }); logger.info({ aggregatedContentLength: aggregatedContent.length, enhancedContentLength: enhancedContent.length, researchResultsCount: researchResults.length }, 'Applied single aggregated research enhancement'); } } } } catch (error) { logger.warn({ err: error, researchResultsCount: researchResults.length }, 'Aggregated research enhancement failed, proceeding with raw results'); } } const enhancedRequest = researchResults.length > 0 ? this.integrateResearchIntoDecomposition(decompositionRequest, researchResults) : this.createDegradedDecompositionRequest(decompositionRequest, 'All research queries failed'); const integrationMetrics = { researchTime: Date.now() - startTime, queriesExecuted: researchResults.length, insightsGenerated: researchResults.reduce((sum, r) => sum + r.insights.keyFindings.length, 0), tasksEnhanced: researchResults.reduce((sum, r) => sum + r.integrationData.suggestedTasks.length, 0) }; logger.info({ originalTaskDescription: decompositionRequest.taskDescription, researchQueries: researchQueries.length, researchResults: researchResults.length, integrationMetrics }, 'Task decomposition enhanced with research'); return { enhancedRequest, researchResults, integrationMetrics }; } catch (error) { logger.error({ err: error, decompositionRequest }, 'Failed to enhance decomposition with research'); throw new Error(`Failed to enhance decomposition with research: ${error instanceof Error ? error.message : String(error)}`); } } async generateIntelligentResearchQueries(taskDescription, context) { try { if (!this.openRouterConfig) { throw new Error('OpenRouter configuration not initialized'); } const queryGenerationPrompt = ` Generate 2-4 focused research queries to help with the following software development task: Task: ${taskDescription} ${context?.projectPath ? `Project Path: ${context.projectPath}` : ''} ${context?.domain ? `Domain: ${context.domain}` : ''} ${context?.technology ? `Technologies: ${context.technology.join(', ')}` : ''} ${context?.existingKnowledge ? `Existing Knowledge: ${context.existingKnowledge.join(', ')}` : ''} Generate research queries that will help understand: 1. Best practices and implementation approaches 2. Common challenges and solutions 3. Technical requirements and considerations 4. Industry standards and patterns Return only the queries, one per line, without numbering or formatting. `; const systemPrompt = `You are an expert software development researcher. Generate focused, actionable research queries that will provide practical insights for software development tasks. Focus on technical implementation, best practices, and real-world considerations.`; const response = await performFormatAwareLlmCall(queryGenerationPrompt, systemPrompt, this.openRouterConfig, 'research_query_generation', 'text', undefined, 0.3); const queries = response .split('\n') .map(line => line.trim()) .filter(line => line.length > 10 && !line.match(/^\d+\./)) .slice(0, 4); logger.debug({ taskDescription, generatedQueries: queries.length }, 'Generated intelligent research queries'); return queries; } catch (error) { logger.error({ err: error, taskDescription }, 'Failed to generate intelligent research queries'); return [ `Best practices for: ${taskDescription}`, `Common challenges and solutions for: ${taskDescription}`, `Technical implementation approaches for: ${taskDescription}` ]; } } assessResearchQuality(content, query) { const issues = []; let qualityScore = 1.0; let relevanceScore = 1.0; let completenessScore = 1.0; if (content.length < 300) { qualityScore -= 0.3; completenessScore -= 0.4; issues.push('Content is too short for comprehensive research'); } else if (content.length < 600) { qualityScore -= 0.1; completenessScore -= 0.2; issues.push('Content could be more comprehensive'); } const queryKeywords = query.toLowerCase().split(/\s+/).filter(word => word.length > 3); const contentLower = content.toLowerCase(); const keywordMatches = queryKeywords.filter(keyword => contentLower.includes(keyword)); const keywordRelevance = keywordMatches.length / Math.max(queryKeywords.length, 1); if (keywordRelevance < 0.3) { relevanceScore -= 0.5; issues.push('Content has low relevance to the query'); } else if (keywordRelevance < 0.6) { relevanceScore -= 0.2; issues.push('Content relevance could be improved'); } const hasHeaders = /#{1,6}\s/.test(content); const hasBulletPoints = /^\s*[-*+]\s/m.test(content); const hasNumberedLists = /^\s*\d+\.\s/m.test(content); if (!hasHeaders && !hasBulletPoints && !hasNumberedLists) { qualityScore -= 0.1; issues.push('Content could benefit from better structure'); } const technicalIndicators = [ 'implementation', 'architecture', 'design pattern', 'best practice', 'performance', 'security', 'scalability', 'testing', 'deployment', 'technical', 'configure', 'setup', 'authentication', 'api' ]; const technicalMatches = technicalIndicators.filter(indicator => contentLower.includes(indicator)); if (technicalMatches.length < 2) { completenessScore -= 0.2; issues.push('Content could include more technical depth'); } qualityScore = Math.max(0, Math.min(1, qualityScore)); relevanceScore = Math.max(0, Math.min(1, relevanceScore)); completenessScore = Math.max(0, Math.min(1, completenessScore)); return { qualityScore, relevanceScore, completenessScore, issues }; } subscribeToResearchProgress(requestId, callback) { if (!this.progressSubscriptions.has(requestId)) { this.progressSubscriptions.set(requestId, []); } this.progressSubscriptions.get(requestId).push(callback); return () => { const callbacks = this.progressSubscriptions.get(requestId); if (callbacks) { const index = callbacks.indexOf(callback); if (index !== -1) { callbacks.splice(index, 1); } } }; } subscribeToResearchComplete(requestId, callback) { if (!this.completeSubscriptions.has(requestId)) { this.completeSubscriptions.set(requestId, []); } this.completeSubscriptions.get(requestId).push(callback); return () => { const callbacks = this.completeSubscriptions.get(requestId); if (callbacks) { const index = callbacks.indexOf(callback); if (index !== -1) { callbacks.splice(index, 1); } } }; } getResearchStatistics() { const allMetrics = Array.from(this.performanceMetrics.values()); const totalResearch = allMetrics.length; const averageResearchTime = totalResearch > 0 ? allMetrics.reduce((sum, m) => sum + m.totalTime, 0) / totalResearch : 0; const cacheHitRate = totalResearch > 0 ? allMetrics.reduce((sum, m) => sum + (m.cacheHit ? 1 : 0), 0) / totalResearch : 0; const qualityDistribution = { low: 0, medium: 0, high: 0, excellent: 0 }; const queryStats = new Map(); allMetrics.forEach(metric => { const quality = metric.qualityScore || 0; if (quality < this.config.qualityThresholds.minimum) { qualityDistribution.low++; } else if (quality < this.config.qualityThresholds.good) { qualityDistribution.medium++; } else if (quality < this.config.qualityThresholds.excellent) { qualityDistribution.high++; } else { qualityDistribution.excellent++; } const query = metric.query || 'unknown'; if (!queryStats.has(query)) { queryStats.set(query, { count: 0, totalQuality: 0 }); } const stats = queryStats.get(query); stats.count++; stats.totalQuality += quality; }); const topQueries = Array.from(queryStats.entries()) .map(([query, stats]) => ({ query, count: stats.count, avgQuality: stats.totalQuality / stats.count })) .sort((a, b) => b.count - a.count) .slice(0, 10); return { activeRequests: this.activeRequests.size, cacheSize: this.researchCache.size, totalResearchPerformed: totalResearch, averageResearchTime, cacheHitRate, qualityDistribution, topQueries }; } clearResearchCache(pattern) { let clearedCount = 0; if (pattern) { for (const [key] of this.researchCache) { if (key.includes(pattern)) { this.researchCache.delete(key); clearedCount++; } } } else { clearedCount = this.researchCache.size; this.researchCache.clear(); } logger.info({ clearedCount, pattern }, 'Research cache cleared'); this.emit('cache_cleared', { pattern, count: clearedCount }); return clearedCount; } updateConfig(newConfig) { this.config = { ...this.config, ...newConfig }; logger.info({ config: this.config }, 'Research integration configuration updated'); this.emit('config_updated', this.config); } dispose() { if (this.cleanupInterval) { clearInterval(this.cleanupInterval); this.cleanupInterval = undefined; } this.activeRequests.clear(); this.researchCache.clear(); this.progressSubscriptions.clear(); this.completeSubscriptions.clear(); this.performanceMetrics.clear(); this.removeAllListeners(); logger.info('Research Integration disposed'); } async initializeConfig() { try { const configManager = OpenRouterConfigManager.getInstance(); this.openRouterConfig = await configManager.getOpenRouterConfig(); logger.debug({ hasApiKey: Boolean(this.openRouterConfig.apiKey), baseUrl: this.openRouterConfig.baseUrl, mappingCount: Object.keys(this.openRouterConfig.llm_mapping || {}).length }, 'Research integration initialized with centralized OpenRouter config'); } catch (error) { logger.warn({ err: error }, 'Failed to initialize OpenRouter config with centralized manager, using fallback'); this.openRouterConfig = { baseUrl: process.env.OPENROUTER_BASE_URL || 'https://openrouter.ai/api/v1', apiKey: process.env.OPENROUTER_API_KEY || '', geminiModel: process.env.GEMINI_MODEL || process.env.VIBE_DEFAULT_LLM_MODEL || 'google/gemini-2.5-flash-preview-05-20', perplexityModel: process.env.PERPLEXITY_MODEL || 'perplexity/sonar', llm_mapping: {} }; } } generateRequestId(request) { const key = `${request.query}-${request.scope.depth}-${request.scope.focus}-${request.optimization.qualityThreshold}`; return crypto.createHash('md5').update(key).digest('hex').slice(0, 16); } getCachedResearch(requestId, cacheStrategy) { if (cacheStrategy === 'none') { return null; } const cached = this.researchCache.get(requestId); if (!cached) { return null; } const age = Date.now() - cached.metadata.timestamp; if (age > this.config.defaultCacheTTL) { this.researchCache.delete(requestId); return null; } return cached; } cacheResearchResult(requestId, result, cacheStrategy) { if (cacheStrategy === 'none') { return; } if (this.researchCache.size >= 50) { const entries = Array.from(this.researchCache.entries()); entries.sort((a, b) => a[1].metadata.timestamp - b[1].metadata.timestamp); const toRemove = Math.floor(50 * 0.2); for (let i = 0; i < toRemove; i++) { this.researchCache.delete(entries[i][0]); } } this.researchCache.set(requestId, result); } async executeResearchRequest(request, requestId, startTime) { const progressCallbacks = this.progressSubscriptions.get(requestId) || []; try { if (!this.openRouterConfig) { throw new Error('OpenRouter configuration not initialized'); } this.notifyProgress(progressCallbacks, 'performing_research', 20, 'Performing primary research'); const researchContent = await performResearchQuery(request.query, this.openRouterConfig); this.notifyProgress(progressCallbacks, 'research_complete', 40, 'Primary research complete'); let enhancedContent = researchContent; let enhancementTime = 0; if (request.optimization.enhanceResults) { this.notifyProgress(progressCallbacks, 'enhancing_results', 60, 'Enhancing research results'); const enhanceStartTime = Date.now(); const enhancementPrompt = this.buildEnhancementPrompt(request, researchContent); enhancedContent = await performFormatAwareLlmCall(enhancementPrompt, this.getResearchSystemPrompt(request), this.openRouterConfig, 'research_enhancement', 'markdown', undefined, 0.3); enhancementTime = Date.now() - enhanceStartTime; this.notifyProgress(progressCallbacks, 'enhancement_complete', 80, 'Enhancement complete'); } this.notifyProgress(progressCallbacks, 'analyzing_results', 90, 'Analyzing results and extracting insights'); const qualityAssessment = this.assessResearchQuality(enhancedContent, request.query); const insights = await this.extractInsights(enhancedContent, request); const integrationData = await this.generateIntegrationData(enhancedContent, request); this.notifyProgress(progressCallbacks, 'finalizing', 100, 'Finalizing research result'); const result = { content: enhancedContent, metadata: { query: request.query, timestamp: startTime, model: await this.getModelForTask('research_query'), qualityScore: qualityAssessment.qualityScore, relevanceScore: qualityAssessment.relevanceScore, completenessScore: qualityAssessment.completenessScore, sources: this.extractSources(enhancedContent), researchTime: Date.now() - startTime }, insights, integrationData, performance: { cacheHit: false, processingStages: { research: Date.now() - startTime - enhancementTime, enhancement: enhancementTime, analysis: 0, total: Date.now() - startTime }, memoryUsage: this.estimateMemoryUsage(enhancedContent), apiCalls: request.optimization.enhanceResults ? 2 : 1, enhancementTime } }; this.recordPerformanceMetrics(requestId, result); return result; } catch (error) { logger.error({ err: error, requestId, query: request.query }, 'Research request execution failed'); throw error; } } generateResearchQueries(decompositionRequest) { const queries = []; queries.push(`Best practices and implementation approaches for: ${decompositionRequest.taskDescription}`); if (decompositionRequest.domain) { queries.push(`${decompositionRequest.domain} specific considerations for: ${decompositionRequest.taskDescription}`); } else { queries.push(`Technical considerations and requirements for: ${decompositionRequest.taskDescription}`); } if (decompositionRequest.projectPath) { const pathParts = decompositionRequest.projectPath.split('/'); const projectName = pathParts[pathParts.length - 1] || pathParts[pathParts.length - 2]; queries.push(`Common challenges and solutions for ${projectName} projects: ${decompositionRequest.taskDescription}`); } else { queries.push(`Common challenges and solutions for: ${decompositionRequest.taskDescription}`); } queries.push(`Architecture patterns and design considerations for: ${decompositionRequest.taskDescription}`); return queries.slice(0, 4); } integrateResearchIntoDecomposition(originalRequest, researchResults) { const allInsights = researchResults.reduce((acc, result) => { acc.keyFindings.push(...result.insights.keyFindings); acc.actionItems.push(...result.insights.actionItems); acc.recommendations.push(...result.insights.recommendations); acc.technicalConsiderations.push(...result.insights.technicalConsiderations); return acc; }, { keyFindings: [], actionItems: [], recommendations: [], technicalConsiderations: [] }); const enhancedDescription = this.enhanceTaskDescription(originalRequest.taskDescription, allInsights); const researchContext = researchResults.map(result => ({ query: result.metadata.query, keyFindings: result.insights.keyFindings.slice(0, 3), recommendations: result.insights.recommendations.slice(0, 2) })); return { ...originalRequest, taskDescription: enhancedDescription, context: { ...originalRequest.context, researchInsights: allInsights, researchContext } }; } enhanceTaskDescription(originalDescription, insights) { let enhanced = originalDescription; if (insights.technicalConsiderations.length > 0) { const considerations = insights.technicalConsiderations .slice(0, 3) .filter(c => c && c.trim().length > 0) .map(c => c.replace(/^[-*+]\s*/, '').trim()) .filter(c => c.length > 0); if (considerations.length > 0) { enhanced += `\n\nKey Technical Considerations:\n${considerations.map(c => `- ${c}`).join('\n')}`; } } if (insights.recommendations.length > 0) { const recommendations = insights.recommendations .slice(0, 2) .filter(r => r && r.trim().length > 0) .map(r => r.replace(/^[-*+]\s*/, '').trim()) .filter(r => r.length > 0); if (recommendations.length > 0) { enhanced += `\n\nRecommended Approaches:\n${recommendations.map(r => `- ${r}`).join('\n')}`; } } return enhanced; } buildEnhancementPrompt(request, content) { return ` Enhance and structure the following research findings for a software development task: Original Query: ${request.query} Task Context: ${request.taskContext?.taskDescription || 'Not specified'} Focus Area: ${request.scope.focus} Depth Required: ${request.scope.depth} Research Findings: ${content} Please enhance this research by: 1. Structuring the information clearly with headers and bullet points 2. Extracting key technical insights and best practices 3. Identifying potential challenges and solutions 4. Providing actionable recommendations 5. Highlighting important considerations for implementation Format the response as a comprehensive, well-structured research report. `; } getResearchSystemPrompt(request) { const focusInstructions = { technical: 'Focus on technical implementation details, code examples, and engineering best practices.', business: 'Focus on business value, market considerations, and strategic implications.', market: 'Focus on market trends, competitive analysis, and industry standards.', comprehensive: 'Provide a balanced view covering technical, business, and market aspects.' }; return ` You are an expert research analyst specializing in software development. Your task is to enhance and structure research findings to provide maximum value for development teams. ${focusInstructions[request.scope.focus]} Ensure your analysis is: - Accurate and up-to-date - Actionable and practical - Well-structured and easy to understand - Focused on the specific context provided - Comprehensive within the requested scope Always provide clear recommendations and highlight potential risks or challenges. `; } async extractInsights(content, _request) { const lines = content.split('\n').filter(line => line.trim().length > 0); const insights = { keyFindings: [], actionItems: [], recommendations: [], risks: [], opportunities: [], technicalConsiderations: [] }; lines.forEach(line => { const lowerLine = line.toLowerCase(); if (lowerLine.includes('best practice') || lowerLine.includes('recommended') || lowerLine.includes('should')) { insights.recommendations.push(line.trim()); } else if (lowerLine.includes('risk') || lowerLine.includes('challenge') || lowerLine.includes('problem')) { insights.risks.push(line.trim()); } else if (lowerLine.includes('opportunity') || lowerLine.includes('benefit') || lowerLine.includes('advantage')) { insights.opportunities.push(line.trim()); } else if (lowerLine.includes('implement') || lowerLine.includes('configure') || lowerLine.includes('setup')) { insights.actionItems.push(line.trim()); } else if (lowerLine.includes('technical') || lowerLine.includes('architecture') || lowerLine.includes('design') || lowerLine.includes('configure') || lowerLine.includes('cors') || lowerLine.includes('https') || lowerLine.includes('security') || lowerLine.includes('performance')) { insights.technicalConsiderations.push(line.trim()); } else if (line.startsWith('- ') || line.startsWith('* ') || /^\d+\./.test(line)) { insights.keyFindings.push(line.trim()); } }); Object.keys(insights).forEach(key => { insights[key] = insights[key].slice(0, 5); }); return insights; } async generateIntegrationData(content, request) { const integrationData = { suggestedTasks: [], knowledgeEntries: [], contextEnrichment: { additionalQueries: [], relatedTopics: [], expertiseAreas: [] } }; const actionWords = ['implement', 'setup', 'configure', 'create', 'build', 'develop', 'test', 'deploy']; const lines = content.split('\n'); lines.forEach(line => { const lowerLine = line.toLowerCase(); actionWords.forEach(action => { if (lowerLine.includes(action) && line.length > 20 && line.length < 200) { integrationData.suggestedTasks.push({ title: line.trim().replace(/^[-*+]\s*/, ''), description: `Task derived from research: ${line.trim()}`, priority: lowerLine.includes('critical') || lowerLine.includes('important') ? 'high' : 'medium', estimatedHours: this.estimateTaskHours(line), dependencies: [] }); } }); }); const topics = this.extractTopics(content); topics.forEach(topic => { integrationData.knowledgeEntries.push({ topic, summary: `Research findings related to ${topic}`, relevance: this.calculateTopicRelevance(topic, request.query), tags: this.generateTopicTags(topic) }); }); integrationData.contextEnrichment.additionalQueries = [ `Advanced ${request.scope.focus} considerations for: ${request.query}`, `Performance optimization for: ${request.query}`, `Security best practices for: ${request.query}` ]; integrationData.contextEnrichment.relatedTopics = this.extractRelatedTopics(content); integrationData.contextEnrichment.expertiseAreas = this.extractExpertiseAreas(content); return integrationData; } extractSources(content) { const sources = []; const urlRegex = /https?:\/\/[^\s]+/g; const matches = content.match(urlRegex); if (matches) { sources.push(...matches.slice(0, 10)); } return sources; } estimateMemoryUsage(content) { return content.length * 2 + 1024; } recordPerformanceMetrics(requestId, result) { const metrics = { requestId, query: result.metadata.query, totalTime: result.metadata.researchTime, qualityScore: result.metadata.qualityScore, relevanceScore: result.metadata.relevanceScore, completenessScore: result.metadata.completenessScore, cacheHit: result.performance.cacheHit, apiCalls: result.performance.apiCalls, timestamp: result.metadata.timestamp }; this.performanceMetrics.set(requestId, metrics); } notifyProgress(callbacks, stage, progress, message) { callbacks.forEach(callback => { try { callback(stage, progress, message); } catch (error) { logger.error({ err: error, stage }, 'Error in research progress callback'); } }); } notifyCompleteSubscribers(requestId, result) { const callbacks = this.completeSubscriptions.get(requestId) || []; callbacks.forEach(callback => { try { callback(result); } catch (error) { logger.error({ err: error, requestId }, 'Error in research completion callback'); } }); } startCleanupProcess() { this.cleanupInterval = setInterval(() => { this.performCleanup(); }, 300000); } performCleanup() { const now = Date.now(); const maxAge = this.config.defaultCacheTTL * 2; for (const [requestId, result] of this.researchCache.entries()) { if ((now - result.metadata.timestamp) > maxAge) { this.researchCache.delete(requestId); } } for (const [requestId, metrics] of this.performanceMetrics.entries()) { if ((now - metrics.timestamp) > maxAge) { this.performanceMetrics.delete(requestId); } } } estimateTaskHours(taskDescription) { const complexity = taskDescription.toLowerCase(); if (complexity.includes('complex') || complexity.includes('advanced')) return 8; if (complexity.includes('simple') || complexity.includes('basic')) return 2; if (complexity.includes('setup') || complexity.includes('configure')) return 4; return 6; } extractTopics(content) { const topics = new Set(); const words = content.toLowerCase().split(/\s+/); const technicalTerms = ['api', 'database', 'authentication', 'security', 'performance', 'testing', 'deployment']; technicalTerms.forEach(term => { if (words.includes(term)) { topics.add(term); } }); return Array.from(topics).slice(0, 10); } calculateTopicRelevance(topic, query) { const queryWords = query.toLowerCase().split(/\s+/); const topicWords = topic.toLowerCase().split(/\s+/); const matches = topicWords.filter(word => queryWords.includes(word)); return matches.length / Math.max(topicWords.length, 1); } generateTopicTags(topic) { const tags = [topic]; if (topic.includes('api')) tags.push('integration', 'web-services'); if (topic.includes('database')) tags.push('data', 'storage'); if (topic.includes('security')) tags.push('authentication', 'authorization'); return tags.slice(0, 5); } extractRelatedTopics(content) { const topics = new Set(); const lines = content.split('\n'); lines.forEach(line => { if (line.includes('related') || line.includes('similar') || line.includes('also')) { const words = line.split(/\s+/).filter(word => word.length > 4); topics.add(words.slice(0, 3).join(' ')); } }); return Array.from(topics).slice(0, 5); } extractExpertiseAreas(content) { const areas = new Set(); const expertiseKeywords = ['expert', 'specialist', 'professional', 'experienced', 'skilled']; const lines = content.split('\n'); lines.forEach(line => { expertiseKeywords.forEach(keyword => { if (line.toLowerCase().includes(keyword)) { const words = line.split(/\s+/).filter(word => word.length > 4); areas.add(words.slice(0, 2).join(' ')); } }); }); return Array.from(areas).slice(0, 5); } createDegradedDecompositionRequest(originalRequest, reason) { logger.info({ originalTask: originalRequest.taskDescription, reason }, 'Creating degraded decomposition request due to research failure'); const fallbackContext = { ...originalRequest.context, researchStatus: 'unavailable', fallbackReason: reason, degradationApplied: true, suggestedApproach: 'Use standard best practices and conventional patterns', recommendedTechnologies: this.inferTechnologiesFromDescription(originalRequest.taskDescription), estimatedComplexity: this.inferComplexityFromDescription(originalRequest.taskDescription) }; return { ...originalRequest, context: fallbackContext }; } inferTechnologiesFromDescription(description) { const tech = []; const lowerDesc = description.toLowerCase(); const techPatterns = [ { pattern: /react|jsx/i, tech: 'React' }, { pattern: /vue/i, tech: 'Vue.js' }, { pattern: /angular/i, tech: 'Angular' }, { pattern: /node|express/i, tech: 'Node.js' }, { pattern: /python|django|flask/i, tech: 'Python' }, { pattern: /java|spring/i, tech: 'Java' }, { pattern: /typescript|ts/i, tech: 'TypeScript' }, { pattern: /javascript|js/i, tech: 'JavaScript' }, { pattern: /database|sql|mongodb/i, tech: 'Database' }, { pattern: /api|rest|graphql/i, tech: 'API' }, { pattern: /docker|kubernetes/i, tech: 'Container' }, { pattern: /aws|azure|gcp/i, tech: 'Cloud' } ]; techPatterns.forEach(({ pattern, tech: techName }) => { if (pattern.test(lowerDesc)) { tech.push(techName); } }); return tech.length > 0 ? tech : ['Web Development']; } inferComplexityFromDescription(description) { const lowerDesc = description.toLowerCase(); if (lowerDesc.includes('architecture') || lowerDesc.includes('system') || lowerDesc.includes('framework') || lowerDesc.includes('integration') || lowerDesc.includes('migration') || lowerDesc.includes('performance')) { return 'high'; } if (lowerDesc.includes('api') || lowerDe