UNPKG

codecrucible-synth

Version:

Production-Ready AI Development Platform with Multi-Voice Synthesis, Smithery MCP Integration, Enterprise Security, and Zero-Timeout Reliability

448 lines (397 loc) 12.4 kB
/** * Enhanced Base Tool with Comprehensive Error Handling * * Extends the basic BaseTool with structured error handling, * input validation, logging, and security measures. */ import { z } from 'zod'; import { BaseTool } from './base-tool.js'; import { ErrorHandler, ErrorFactory, InputValidator, ErrorCategory, ErrorSeverity, ServiceResponse, ErrorResponse, } from '../error-handling/structured-error-system.js'; import { logger } from '../logger.js'; export interface EnhancedToolConfig { name: string; description: string; category: string; parameters: z.ZodTypeAny; requiresAuth?: boolean; rateLimitPerMinute?: number; timeoutMs?: number; securityLevel?: 'low' | 'medium' | 'high'; retryable?: boolean; maxRetries?: number; } export interface ToolExecutionContext { requestId: string; userId?: string; workingDirectory: string; timestamp: number; environment: Record<string, any>; } export interface ToolExecutionResult<T = any> { success: boolean; data?: T; error?: any; executionTime: number; requestId: string; metadata?: Record<string, any>; } /** * Enhanced base tool class with comprehensive error handling and security */ export abstract class EnhancedBaseTool extends BaseTool { protected config: EnhancedToolConfig; private executionCount = 0; private lastExecutionTimes: number[] = []; private readonly maxExecutionHistory = 10; constructor(config: EnhancedToolConfig) { super({ name: config.name, description: config.description, category: config.category, parameters: config.parameters as z.ZodObject<any>, }); this.config = { requiresAuth: false, rateLimitPerMinute: 60, timeoutMs: 30000, securityLevel: 'medium', retryable: true, maxRetries: 3, ...config, }; } /** * Enhanced execute method with error handling and security */ async execute( params: any, context?: Partial<ToolExecutionContext> ): Promise<ToolExecutionResult> { const executionContext: ToolExecutionContext = { requestId: `req_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`, workingDirectory: process.cwd(), timestamp: Date.now(), environment: {}, ...context, }; const startTime = Date.now(); try { // Pre-execution checks await this.preExecutionChecks(params, executionContext); // Validate and sanitize input const validationResult = await this.validateAndSanitizeInput(params); if (!validationResult.success) { return this.createErrorResult( (validationResult as ErrorResponse).error, executionContext, startTime ); } // Execute with timeout and retry logic const result = await this.executeWithTimeoutAndRetry(validationResult.data, executionContext); // Post-execution processing const finalResult = await this.postExecutionProcessing(result, executionContext); this.recordExecution(Date.now() - startTime); return { success: true, data: finalResult, executionTime: Date.now() - startTime, requestId: executionContext.requestId, metadata: { tool: this.config.name, category: this.config.category, execution_count: this.executionCount, }, }; } catch (error) { const structuredError = await ErrorHandler.handleError(error as Error, { tool: this.config.name, requestId: executionContext.requestId, params: this.sanitizeParamsForLogging(params), }); return this.createErrorResult(structuredError, executionContext, startTime); } } /** * Abstract method that concrete tools must implement */ protected abstract executeCore(params: any, context: ToolExecutionContext): Promise<any>; /** * Pre-execution security and validation checks */ protected async preExecutionChecks(params: any, context: ToolExecutionContext): Promise<void> { // Rate limiting check if (this.isRateLimited()) { throw ErrorFactory.createError( 'Rate limit exceeded', ErrorCategory.SYSTEM, ErrorSeverity.MEDIUM, { userMessage: 'Too many requests, please wait before trying again', suggestedActions: ['Wait a moment and try again'], retryable: true, metadata: { rate_limit: this.config.rateLimitPerMinute }, } ); } // Authentication check if (this.config.requiresAuth && !context.userId) { throw ErrorFactory.createError( 'Authentication required', ErrorCategory.AUTHENTICATION, ErrorSeverity.HIGH, { userMessage: 'This operation requires authentication', suggestedActions: ['Please authenticate and try again'], recoverable: false, } ); } // Security level checks if (this.config.securityLevel === 'high') { await this.performHighSecurityChecks(params, context); } } /** * Validate and sanitize input parameters */ protected async validateAndSanitizeInput(params: any): Promise<ServiceResponse<any>> { try { // Zod schema validation const validatedParams = this.config.parameters.parse(params); // Additional security sanitization const sanitizedParams = this.sanitizeInput(validatedParams); return ErrorHandler.createSuccessResponse(sanitizedParams); } catch (error) { if (error instanceof z.ZodError) { const errorMessage = error.errors.map(e => `${e.path.join('.')}: ${e.message}`).join(', '); return ErrorHandler.createErrorResponse( ErrorFactory.createError( `Validation failed: ${errorMessage}`, ErrorCategory.VALIDATION, ErrorSeverity.MEDIUM, { context: { validation_errors: error.errors }, userMessage: 'Invalid input provided', suggestedActions: [ 'Check parameter types and required fields', 'Review input format', ], } ) ); } return ErrorHandler.createErrorResponse(error as Error); } } /** * Execute with timeout and retry logic */ protected async executeWithTimeoutAndRetry( params: any, context: ToolExecutionContext ): Promise<any> { if (this.config.retryable && this.config.maxRetries! > 1) { const result = await ErrorHandler.retryWithBackoff( () => this.executeWithTimeout(params, context), this.config.maxRetries!, 1000, { tool: this.config.name, requestId: context.requestId } ); if (!result.success) { throw (result as ErrorResponse).error; } return result.data; } else { return await this.executeWithTimeout(params, context); } } /** * Execute with timeout protection */ protected async executeWithTimeout(params: any, context: ToolExecutionContext): Promise<any> { return Promise.race([ this.executeCore(params, context), new Promise((_, reject) => { setTimeout(() => { reject( ErrorFactory.createError( `Tool execution timeout after ${this.config.timeoutMs}ms`, ErrorCategory.SYSTEM, ErrorSeverity.HIGH, { context: { timeout: this.config.timeoutMs, tool: this.config.name }, userMessage: 'Operation timed out', suggestedActions: ['Try again with simpler input', 'Check system performance'], retryable: true, } ) ); }, this.config.timeoutMs!); }), ]); } /** * Post-execution processing and cleanup */ protected async postExecutionProcessing( result: any, context: ToolExecutionContext ): Promise<any> { // Log successful execution logger.info(`Tool executed successfully: ${this.config.name}`, { requestId: context.requestId, tool: this.config.name, executionTime: Date.now() - context.timestamp, }); // Override in subclasses for custom post-processing return result; } /** * Sanitize input for security */ protected sanitizeInput(params: any): any { if (typeof params === 'string') { return InputValidator.sanitizeInput(params); } if (Array.isArray(params)) { return params.map(item => this.sanitizeInput(item)); } if (params && typeof params === 'object') { const sanitized: any = {}; for (const [key, value] of Object.entries(params)) { sanitized[key] = this.sanitizeInput(value); } return sanitized; } return params; } /** * High security checks for sensitive operations */ protected async performHighSecurityChecks( params: any, context: ToolExecutionContext ): Promise<void> { // Check for suspicious patterns const paramString = JSON.stringify(params); const suspiciousPatterns = [ /rm\s+-rf/, /del\s+\/[sf]/, /sudo\s+/, /eval\s*\(/, /exec\s*\(/, /system\s*\(/, /require\s*\(/, /import\s+os/, /__import__/, ]; for (const pattern of suspiciousPatterns) { if (pattern.test(paramString)) { throw ErrorFactory.createError( 'Potentially dangerous input detected', ErrorCategory.AUTHORIZATION, ErrorSeverity.CRITICAL, { context: { suspicious_pattern: pattern.source }, userMessage: 'Input contains potentially dangerous commands', suggestedActions: ['Review input for security concerns', 'Use safer alternatives'], recoverable: false, } ); } } } /** * Check rate limiting */ protected isRateLimited(): boolean { const now = Date.now(); const oneMinuteAgo = now - 60000; // Clean old executions this.lastExecutionTimes = this.lastExecutionTimes.filter(time => time > oneMinuteAgo); if (this.lastExecutionTimes.length >= this.config.rateLimitPerMinute!) { return true; } this.lastExecutionTimes.push(now); return false; } /** * Record execution for performance monitoring */ protected recordExecution(executionTime: number): void { this.executionCount++; // Keep track of recent execution times for performance analysis if (this.lastExecutionTimes.length >= this.maxExecutionHistory) { this.lastExecutionTimes.shift(); } } /** * Create error result */ protected createErrorResult( error: any, context: ToolExecutionContext, startTime: number ): ToolExecutionResult { return { success: false, error, executionTime: Date.now() - startTime, requestId: context.requestId, metadata: { tool: this.config.name, category: this.config.category, error_category: error.category || 'unknown', }, }; } /** * Sanitize parameters for logging (remove sensitive data) */ protected sanitizeParamsForLogging(params: any): any { const sensitiveKeys = ['password', 'token', 'key', 'secret', 'api_key', 'auth']; if (typeof params === 'object' && params !== null) { const sanitized = { ...params }; for (const key of Object.keys(sanitized)) { if (sensitiveKeys.some(sensitive => key.toLowerCase().includes(sensitive))) { sanitized[key] = '[REDACTED]'; } } return sanitized; } return params; } /** * Get tool performance statistics */ getPerformanceStats(): { total_executions: number; average_execution_time: number; last_execution_times: number[]; rate_limit: number; security_level: string; } { const avgTime = this.lastExecutionTimes.length > 0 ? this.lastExecutionTimes.reduce((sum, time) => sum + time, 0) / this.lastExecutionTimes.length : 0; return { total_executions: this.executionCount, average_execution_time: avgTime, last_execution_times: [...this.lastExecutionTimes], rate_limit: this.config.rateLimitPerMinute!, security_level: this.config.securityLevel!, }; } } export default EnhancedBaseTool;