From 8daf9470de23109be5528fbc5a814ea2b68c55e3 Mon Sep 17 00:00:00 2001 From: htafolla Date: Fri, 15 May 2026 16:22:47 -0500 Subject: [PATCH] Codify Extract Method pattern --- .strray/state/state.json | 476 +++++++++++++++++++++- src/mcps/connection/connection-pool.ts | 13 + src/mcps/mcp-client.ts | 136 ++++--- src/mcps/simulation/server-simulations.ts | 57 --- 4 files changed, 555 insertions(+), 127 deletions(-) diff --git a/.strray/state/state.json b/.strray/state/state.json index 840bc4d532..6a4933d8f2 100644 --- a/.strray/state/state.json +++ b/.strray/state/state.json @@ -1,17 +1,465 @@ { - "votingHistory": [], - "metrics": { - "totalVotes": 0, - "successfulVotes": 0, - "failedVotes": 0, - "averageConfidence": 0, - "strategyUsage": { - "majority_vote": 0, - "consensus": 0, - "expert_priority": 0 - }, - "agentParticipation": {}, - "averageVoterTurnout": 0 + "coordination:main_coordinator": { + "strRayOrchestrator": { + "taskQueue": {}, + "activeTasks": {}, + "totalProcessed": 0, + "config": { + "maxConcurrentTasks": 3, + "taskTimeout": 10000, + "conflictResolutionStrategy": "majority_vote" + }, + "kernel": { + "config": { + "enabled": true, + "confidenceThreshold": 0.75, + "maxPatternsPerAnalysis": 10, + "enableLearning": true, + "autoPrevention": true + }, + "patterns": {}, + "assumptions": {}, + "cascades": {} + } + }, + "enhancedOrchestrator": { + "state": { + "activeAgents": {}, + "pendingSpawns": [], + "completedAgents": {}, + "failedAgents": {}, + "agentDependencies": {}, + "monitoringEnabled": true, + "cleanupInterval": 30000, + "isMainOrchestrator": true + }, + "stateManager": { + "store": {}, + "persistencePath": "/Users/blaze/dev/stringray/.strray/state/state.json", + "persistenceEnabled": true, + "writeQueue": {}, + "initialized": true, + "earlyOperationsQueue": [] + }, + "complexityAnalyzer": { + "thresholds": { + "simple": 15, + "moderate": 25, + "complex": 50, + "enterprise": 75 + }, + "operationWeights": { + "create": 1, + "modify": 1.2, + "refactor": 1.8, + "analyze": 1.5, + "debug": 2, + "test": 1.3 + }, + "riskMultipliers": { + "low": 0.8, + "medium": 1, + "high": 1.3, + "critical": 1.6 + }, + "calibrationHistory": [] + }, + "agentDelegator": { + "complexityAnalyzer": { + "thresholds": { + "simple": 15, + "moderate": 25, + "complex": 50, + "enterprise": 75 + }, + "operationWeights": { + "create": 1, + "modify": 1.2, + "refactor": 1.8, + "analyze": 1.5, + "debug": 2, + "test": 1.3 + }, + "riskMultipliers": { + "low": 0.8, + "medium": 1, + "high": 1.3, + "critical": 1.6 + }, + "calibrationHistory": [] + }, + "stateManager": { + "store": {}, + "persistencePath": "/Users/blaze/dev/stringray/.strray/state/state.json", + "persistenceEnabled": true, + "writeQueue": {}, + "initialized": true, + "earlyOperationsQueue": [] + }, + "configLoader": { + "configPath": "/Users/blaze/dev/stringray/.strray/config.json", + "cachedConfig": null, + "cacheExpiry": 30000, + "lastLoadTime": 0 + }, + "kernel": { + "config": { + "enabled": true, + "confidenceThreshold": 0.75, + "maxPatternsPerAnalysis": 10, + "enableLearning": true, + "autoPrevention": true + }, + "patterns": {}, + "assumptions": {}, + "cascades": {} + }, + "agentMetrics": { + "stateManager": { + "store": {}, + "persistencePath": "/Users/blaze/dev/stringray/.strray/state/state.json", + "persistenceEnabled": true, + "writeQueue": {}, + "initialized": true, + "earlyOperationsQueue": [] + }, + "retentionConfig": { + "maxEntries": 10000, + "maxAgeMs": 2592000000, + "enableAutoCleanup": true, + "cleanupIntervalMs": 3600000 + }, + "initialized": false + } + }, + "executionContext": { + "isExecutingAsSubagent": false, + "currentAgentId": null, + "spawnStack": [] + }, + "cleanupTimer": null + }, + "agentDelegator": { + "complexityAnalyzer": { + "thresholds": { + "simple": 15, + "moderate": 25, + "complex": 50, + "enterprise": 75 + }, + "operationWeights": { + "create": 1, + "modify": 1.2, + "refactor": 1.8, + "analyze": 1.5, + "debug": 2, + "test": 1.3 + }, + "riskMultipliers": { + "low": 0.8, + "medium": 1, + "high": 1.3, + "critical": 1.6 + }, + "calibrationHistory": [] + }, + "stateManager": { + "store": {}, + "persistencePath": "/Users/blaze/dev/stringray/.strray/state/state.json", + "persistenceEnabled": true, + "writeQueue": {}, + "initialized": true, + "earlyOperationsQueue": [] + }, + "configLoader": { + "configPath": "/Users/blaze/dev/stringray/.strray/config.json", + "cachedConfig": null, + "cacheExpiry": 30000, + "lastLoadTime": 0 + }, + "kernel": { + "config": { + "enabled": true, + "confidenceThreshold": 0.75, + "maxPatternsPerAnalysis": 10, + "enableLearning": true, + "autoPrevention": true + }, + "patterns": {}, + "assumptions": {}, + "cascades": {} + }, + "agentMetrics": { + "stateManager": { + "store": {}, + "persistencePath": "/Users/blaze/dev/stringray/.strray/state/state.json", + "persistenceEnabled": true, + "writeQueue": {}, + "initialized": true, + "earlyOperationsQueue": [] + }, + "retentionConfig": { + "maxEntries": 10000, + "maxAgeMs": 2592000000, + "enableAutoCleanup": true, + "cleanupIntervalMs": 3600000 + }, + "initialized": false + } + }, + "stateManager": { + "store": {}, + "persistencePath": "/Users/blaze/dev/stringray/.strray/state/state.json", + "persistenceEnabled": true, + "writeQueue": {}, + "initialized": true, + "earlyOperationsQueue": [] + }, + "complexityAnalyzer": { + "thresholds": { + "simple": 15, + "moderate": 25, + "complex": 50, + "enterprise": 75 + }, + "operationWeights": { + "create": 1, + "modify": 1.2, + "refactor": 1.8, + "analyze": 1.5, + "debug": 2, + "test": 1.3 + }, + "riskMultipliers": { + "low": 0.8, + "medium": 1, + "high": 1.3, + "critical": 1.6 + }, + "calibrationHistory": [] + }, + "coordinationMetrics": { + "totalWorkflows": 0, + "successfulWorkflows": 0, + "failedWorkflows": 0, + "averageDuration": 0, + "agentUtilization": {}, + "coordinationEfficiency": 0 + } + }, + "coordination:strray_orchestrator": { + "taskQueue": {}, + "activeTasks": {}, + "totalProcessed": 0, + "config": { + "maxConcurrentTasks": 3, + "taskTimeout": 10000, + "conflictResolutionStrategy": "majority_vote" + }, + "kernel": { + "config": { + "enabled": true, + "confidenceThreshold": 0.75, + "maxPatternsPerAnalysis": 10, + "enableLearning": true, + "autoPrevention": true + }, + "patterns": {}, + "assumptions": {}, + "cascades": {} + } + }, + "coordination:enhanced_orchestrator": { + "state": { + "activeAgents": {}, + "pendingSpawns": [], + "completedAgents": {}, + "failedAgents": {}, + "agentDependencies": {}, + "monitoringEnabled": true, + "cleanupInterval": 30000, + "isMainOrchestrator": true + }, + "stateManager": { + "store": {}, + "persistencePath": "/Users/blaze/dev/stringray/.strray/state/state.json", + "persistenceEnabled": true, + "writeQueue": {}, + "initialized": true, + "earlyOperationsQueue": [] + }, + "complexityAnalyzer": { + "thresholds": { + "simple": 15, + "moderate": 25, + "complex": 50, + "enterprise": 75 + }, + "operationWeights": { + "create": 1, + "modify": 1.2, + "refactor": 1.8, + "analyze": 1.5, + "debug": 2, + "test": 1.3 + }, + "riskMultipliers": { + "low": 0.8, + "medium": 1, + "high": 1.3, + "critical": 1.6 + }, + "calibrationHistory": [] + }, + "agentDelegator": { + "complexityAnalyzer": { + "thresholds": { + "simple": 15, + "moderate": 25, + "complex": 50, + "enterprise": 75 + }, + "operationWeights": { + "create": 1, + "modify": 1.2, + "refactor": 1.8, + "analyze": 1.5, + "debug": 2, + "test": 1.3 + }, + "riskMultipliers": { + "low": 0.8, + "medium": 1, + "high": 1.3, + "critical": 1.6 + }, + "calibrationHistory": [] + }, + "stateManager": { + "store": {}, + "persistencePath": "/Users/blaze/dev/stringray/.strray/state/state.json", + "persistenceEnabled": true, + "writeQueue": {}, + "initialized": true, + "earlyOperationsQueue": [] + }, + "configLoader": { + "configPath": "/Users/blaze/dev/stringray/.strray/config.json", + "cachedConfig": null, + "cacheExpiry": 30000, + "lastLoadTime": 0 + }, + "kernel": { + "config": { + "enabled": true, + "confidenceThreshold": 0.75, + "maxPatternsPerAnalysis": 10, + "enableLearning": true, + "autoPrevention": true + }, + "patterns": {}, + "assumptions": {}, + "cascades": {} + }, + "agentMetrics": { + "stateManager": { + "store": {}, + "persistencePath": "/Users/blaze/dev/stringray/.strray/state/state.json", + "persistenceEnabled": true, + "writeQueue": {}, + "initialized": true, + "earlyOperationsQueue": [] + }, + "retentionConfig": { + "maxEntries": 10000, + "maxAgeMs": 2592000000, + "enableAutoCleanup": true, + "cleanupIntervalMs": 3600000 + }, + "initialized": false + } + }, + "executionContext": { + "isExecutingAsSubagent": false, + "currentAgentId": null, + "spawnStack": [] + }, + "cleanupTimer": null + }, + "coordination:agent_delegator": { + "complexityAnalyzer": { + "thresholds": { + "simple": 15, + "moderate": 25, + "complex": 50, + "enterprise": 75 + }, + "operationWeights": { + "create": 1, + "modify": 1.2, + "refactor": 1.8, + "analyze": 1.5, + "debug": 2, + "test": 1.3 + }, + "riskMultipliers": { + "low": 0.8, + "medium": 1, + "high": 1.3, + "critical": 1.6 + }, + "calibrationHistory": [] + }, + "stateManager": { + "store": {}, + "persistencePath": "/Users/blaze/dev/stringray/.strray/state/state.json", + "persistenceEnabled": true, + "writeQueue": {}, + "initialized": true, + "earlyOperationsQueue": [] + }, + "configLoader": { + "configPath": "/Users/blaze/dev/stringray/.strray/config.json", + "cachedConfig": null, + "cacheExpiry": 30000, + "lastLoadTime": 0 + }, + "kernel": { + "config": { + "enabled": true, + "confidenceThreshold": 0.75, + "maxPatternsPerAnalysis": 10, + "enableLearning": true, + "autoPrevention": true + }, + "patterns": {}, + "assumptions": {}, + "cascades": {} + }, + "agentMetrics": { + "stateManager": { + "store": {}, + "persistencePath": "/Users/blaze/dev/stringray/.strray/state/state.json", + "persistenceEnabled": true, + "writeQueue": {}, + "initialized": true, + "earlyOperationsQueue": [] + }, + "retentionConfig": { + "maxEntries": 10000, + "maxAgeMs": 2592000000, + "enableAutoCleanup": true, + "cleanupIntervalMs": 3600000 + }, + "initialized": false + } }, - "exportedAt": "2026-05-15T14:18:59.026Z" + "coordination:metrics": { + "totalWorkflows": 0, + "successfulWorkflows": 0, + "failedWorkflows": 0, + "averageDuration": 0, + "agentUtilization": {}, + "coordinationEfficiency": 0 + } } \ No newline at end of file diff --git a/src/mcps/connection/connection-pool.ts b/src/mcps/connection/connection-pool.ts index 6ee7d30386..13891fb443 100644 --- a/src/mcps/connection/connection-pool.ts +++ b/src/mcps/connection/connection-pool.ts @@ -201,3 +201,16 @@ export class ConnectionPool implements IConnectionPoolExtended { } } } + +// Shared singleton for real MCP transport (production path) +let sharedConnectionPool: ConnectionPool | null = null; + +export function getConnectionPool(): ConnectionPool { + if (!sharedConnectionPool) { + sharedConnectionPool = new ConnectionPool({ + maxPoolSize: 10, + maxIdleTimeMs: 5 * 60 * 1000, + }); + } + return sharedConnectionPool; +} diff --git a/src/mcps/mcp-client.ts b/src/mcps/mcp-client.ts index 43b4acb2d7..83dafba8ae 100644 --- a/src/mcps/mcp-client.ts +++ b/src/mcps/mcp-client.ts @@ -31,6 +31,7 @@ import { SimulationEngine, getAllServerSimulations, } from './simulation/index.js'; +import { getConnectionPool } from './connection/connection-pool.js'; import { ConnectionPool } from './connection/connection-pool.js'; /** @@ -129,6 +130,29 @@ export class MCPClient extends EventEmitter { throw lastError || new Error(`Operation ${operationName} failed after ${this.retryConfig.maxRetries} retries`); } + /** + * Execute a tool using the real MCP transport (ConnectionPool + ToolExecutor). + * This is the primary production path. + */ + private async executeRealTool(toolName: string, args: unknown): Promise { + const pool = getConnectionPool(); + const serverConfig = this.config as unknown as IServerConfig; + + const connection = await pool.acquire(this.config.serverName, serverConfig); + try { + return await this.toolExecutor.executeTool(connection, toolName, args); + } finally { + pool.release(connection); + } + } + + /** + * Pure MCP mode — simulation and generic fallbacks are disabled. + */ + private get isPureMcpMode(): boolean { + return process.env.STRRAY_FORCE_MCP_GOVERNANCE === 'true'; + } + /** * Register default simulation implementations */ @@ -329,7 +353,6 @@ export class MCPClient extends EventEmitter { async callTool(toolName: string, args: unknown = {}): Promise { const startTime = Date.now(); - // Emit tool.before event const beforeEvent: ToolBeforeEvent = { toolName, serverName: this.config.serverName, @@ -338,18 +361,26 @@ export class MCPClient extends EventEmitter { }; this.emit('tool.before', beforeEvent); - try { - // In pure MCP governance mode, never use simulation. - const isPureMcp = process.env.STRRAY_FORCE_MCP_GOVERNANCE === 'true'; + const serverName = this.config.serverName; + const isGovernanceServer = ['code-review', 'security-audit', 'researcher'].includes(serverName); + const isGovernanceTool = toolName === 'analyze_proposal'; + const preferReal = this.isPureMcpMode || isGovernanceServer || isGovernanceTool; - // Wrap with retry for simulation (skipped in pure mode) - if (!isPureMcp && this.simulationEngine.canSimulate(this.config.serverName, toolName)) { + try { + // === PRIMARY PATH: Real MCP transport === + if (preferReal) { try { const result = await this.executeWithRetry( - () => this.simulationEngine.simulate(this.config.serverName, toolName, args), - `simulate:${toolName}` + () => this.executeRealTool(toolName, args), + `real:${toolName}` ); + frameworkLogger.log('mcp-client', 'real-transport-success', 'info', { + server: serverName, + tool: toolName, + pureMode: this.isPureMcpMode, + }); + const afterEvent: ToolAfterEvent = { ...beforeEvent, result, @@ -357,61 +388,57 @@ export class MCPClient extends EventEmitter { success: true, }; this.emit('tool.after', afterEvent); - return result; - } catch (error) { - frameworkLogger.log( - 'mcp-client', - `Simulation failed for ${toolName}: ${error instanceof Error ? error.message : String(error)}`, - 'info', - { toolName } - ); + } catch (realError) { + const errMsg = realError instanceof Error ? realError.message : String(realError); + + frameworkLogger.log('mcp-client', 'real-transport-failed', 'error', { + server: serverName, + tool: toolName, + error: errMsg, + }); + + if (this.isPureMcpMode) { + throw new Error( + `[PURE MCP] Real transport failed for ${serverName}/${toolName}: ${errMsg}. ` + + `Simulation and generic fallbacks are disabled in pure governance mode.` + ); + } + // Non-pure mode can fall through to simulation/generic } } - // === REAL MCP TRANSPORT (ConnectionPool + ToolExecutor) === - try { - const serverConfig: IServerConfig | undefined = defaultServerRegistry.get(this.config.serverName); - if (serverConfig) { - const pool = this.getConnectionPool(); - const connection = await pool.acquire(this.config.serverName, serverConfig); - try { - const realResult = await this.toolExecutor.executeTool(connection, toolName, args); - - const afterEvent: ToolAfterEvent = { - ...beforeEvent, - result: realResult, - duration: Date.now() - startTime, - success: true, - }; - this.emit('tool.after', afterEvent); - - return realResult; - } finally { - pool.release(connection); - } - } - } catch (realMcpError) { - frameworkLogger.log('mcp-client', 'real-mcp-call-failed', 'warning', { - serverName: this.config.serverName, - toolName, - error: String(realMcpError), - }); - - if (isPureMcp) { - throw new Error( - `[PURE MCP] Real transport failed for "${toolName}" on "${this.config.serverName}": ${realMcpError}` + // === FALLBACK: Simulation only when NOT in pure mode and not a governance tool === + if (!this.isPureMcpMode && this.simulationEngine.canSimulate(serverName, toolName)) { + try { + const result = await this.executeWithRetry( + () => this.simulationEngine.simulate(serverName, toolName, args), + `simulate:${toolName}` ); + const afterEvent: ToolAfterEvent = { + ...beforeEvent, + result, + duration: Date.now() - startTime, + success: true, + }; + this.emit('tool.after', afterEvent); + return result; + } catch (error) { + frameworkLogger.log('mcp-client', `Simulation failed for ${toolName}`, 'warning', { server: serverName }); } } - // Legacy generic fallback (only for non-pure-MCP mode) + // === LAST RESORT: Generic fallback (disabled in pure mode) === + if (this.isPureMcpMode) { + throw new Error( + `[PURE MCP] No real response for ${serverName}/${toolName}. ` + + `All fallbacks are disabled when STRRAY_FORCE_MCP_GOVERNANCE=true.` + ); + } + const fallbackResult = { content: [ - { - type: 'text', - text: `Tool ${toolName} executed on ${this.config.serverName} server`, - }, + { type: 'text' as const, text: `Tool ${toolName} executed on ${serverName} server` }, ], }; @@ -422,10 +449,8 @@ export class MCPClient extends EventEmitter { success: true, }; this.emit('tool.after', afterEvent); - return fallbackResult; } catch (error) { - // Emit tool.after event (error) const errorMessage = error instanceof Error ? error.message : String(error); const afterEvent: ToolAfterEvent = { ...beforeEvent, @@ -434,7 +459,6 @@ export class MCPClient extends EventEmitter { success: false, }; this.emit('tool.after', afterEvent); - throw error; } } diff --git a/src/mcps/simulation/server-simulations.ts b/src/mcps/simulation/server-simulations.ts index 97bc4ebea9..be5c01fc7a 100644 --- a/src/mcps/simulation/server-simulations.ts +++ b/src/mcps/simulation/server-simulations.ts @@ -24,23 +24,6 @@ export const codeReviewSimulations: Record = { }, ], }), - analyze_proposal: (args: any = {}): MCPToolResult => { - const { proposalTitle = "", proposalDescription = "", evidence = [], proposalType = "" } = args; - const text = `${proposalTitle} ${proposalDescription} ${(evidence || []).join(" ")}`.toLowerCase(); - let decision: "approve" | "reject" | "abstain" = "approve"; - let confidence = 0.82; - let reasoning = "The proposal appears reasonable from a code quality and maintainability perspective."; - if (text.includes("extract method")) { - decision = "approve"; confidence = 0.93; reasoning = "Extract Method is a well-established refactoring pattern that improves readability and reduces cognitive load when applied consistently."; - } else if (text.includes("test coverage")) { - decision = "approve"; confidence = 0.90; reasoning = "Expanding automated test coverage generally improves long-term code health and reduces regression risk."; - } else if (text.includes("technical debt")) { - decision = "approve"; confidence = 0.78; reasoning = "Addressing accumulated technical debt systematically improves long-term maintainability and reduces future bug rates."; - } - return { - content: [{ type: "text", text: `DECISION: ${decision}\nCONFIDENCE: ${confidence.toFixed(2)}\nREASONING: ${reasoning}` }], - }; - }, }; /** @@ -55,26 +38,6 @@ export const securityAuditSimulations: Record = { }, ], }), - analyze_proposal: (args: any = {}): MCPToolResult => { - const { proposalTitle = "", proposalDescription = "", evidence = [], proposalType = "" } = args; - const text = `${proposalTitle} ${proposalDescription} ${(evidence || []).join(" ")}`.toLowerCase(); - let decision: "approve" | "reject" | "abstain" = "approve"; - let confidence = 0.82; - let reasoning = "The proposal does not appear to introduce significant new security surface area."; - if (text.includes("extract method")) { - decision = "approve"; confidence = 0.88; reasoning = "Extract Method refactoring improves security posture by reducing attack surface in large monolithic files and enabling better isolation of sensitive logic."; - } else if (text.includes("test coverage")) { - decision = "approve"; confidence = 0.91; reasoning = "Expanding test coverage is one of the highest-ROI security controls available — more tests surface regressions and boundary condition vulnerabilities earlier."; - } else if (text.includes("technical debt")) { - decision = "approve"; confidence = 0.79; reasoning = "Paying down technical debt reduces the likelihood of security vulnerabilities that accumulate in unmaintained code paths."; - } - if (proposalType === "fix" && text.includes("timeout")) { - confidence = Math.max(0.65, confidence - 0.10); - } - return { - content: [{ type: "text", text: `DECISION: ${decision}\nCONFIDENCE: ${confidence.toFixed(2)}\nREASONING: ${reasoning}` }], - }; - }, }; /** @@ -117,26 +80,6 @@ export const researcherSimulations: Record = { }, ], }), - analyze_proposal: (args: any = {}): MCPToolResult => { - const { proposalTitle = "", proposalDescription = "", evidence = [], proposalType = "" } = args; - const text = `${proposalTitle} ${proposalDescription} ${(evidence || []).join(" ")}`.toLowerCase(); - let decision: "approve" | "reject" | "abstain" = "approve"; - let confidence = 0.80; - let reasoning = "From a project-wide analysis perspective, the proposal aligns with observed recurring patterns and has supporting evidence in the corpus."; - if (text.includes("extract method")) { - decision = "approve"; confidence = 0.89; reasoning = "The Extract Method pattern is a core refactoring technique that improves modularity; the corpus shows consistent positive outcomes when applied to repeated logic across many sessions."; - } else if (text.includes("test coverage")) { - decision = "approve"; confidence = 0.94; reasoning = "Test coverage expansion is one of the highest-leverage improvements for long-term project health, directly reducing regression incidents across 100+ sessions in the historical data."; - } else if (text.includes("technical debt")) { - decision = "approve"; confidence = 0.85; reasoning = "Systematic technical debt reduction is strongly supported by historical data showing fewer critical violations and faster feature delivery in low-debt modules."; - } - if (proposalType === "fix" && !text.includes("pattern") && !text.includes("recurring")) { - confidence = Math.max(0.68, confidence - 0.10); - } - return { - content: [{ type: "text", text: `DECISION: ${decision}\nCONFIDENCE: ${confidence.toFixed(2)}\nREASONING: ${reasoning}` }], - }; - }, }; /**