UNPKG

@juspay/neurolink

Version:

Universal AI Development Platform with working MCP integration, multi-provider support, and professional CLI. Built-in tools operational, 58+ external MCP servers discoverable. Connect to filesystem, GitHub, database operations, and more. Build, test, and

960 lines (959 loc) 167 kB
/** * NeuroLink - Unified AI Interface with Real MCP Tool Integration * * REDESIGNED FALLBACK CHAIN - NO CIRCULAR DEPENDENCIES * Enhanced AI provider system with natural MCP tool access. * Uses real MCP infrastructure for tool discovery and execution. */ // Load environment variables from .env file (critical for SDK usage) import { config as dotenvConfig } from "dotenv"; try { dotenvConfig(); // Load .env from current working directory } catch { // Environment variables should be set externally in production } import { AIProviderFactory } from "./core/factory.js"; import { mcpLogger } from "./utils/logger.js"; import { SYSTEM_LIMITS } from "./core/constants.js"; import pLimit from "p-limit"; import { toolRegistry } from "./mcp/toolRegistry.js"; import { logger } from "./utils/logger.js"; import { getBestProvider } from "./utils/providerUtils.js"; import { ProviderRegistry } from "./factories/providerRegistry.js"; import { createCustomToolServerInfo, detectCategory, } from "./utils/mcpDefaults.js"; // Factory processing imports import { processFactoryOptions, enhanceTextGenerationOptions, validateFactoryConfig, processStreamingFactoryOptions, createCleanStreamOptions, } from "./utils/factoryProcessing.js"; // Tool detection and execution imports // Transformation utilities import { transformToolExecutions, transformToolExecutionsForMCP, transformAvailableTools, transformToolsForMCP, transformToolsToExpectedFormat, transformToolsToDescriptions, extractToolNames, transformParamsForLogging, optimizeToolForCollection, } from "./utils/transformationUtils.js"; // Enhanced error handling imports import { ErrorFactory, NeuroLinkError, withTimeout, withRetry, isRetriableError, logStructuredError, CircuitBreaker, } from "./utils/errorHandling.js"; import { EventEmitter } from "events"; import { ConversationMemoryManager } from "./core/conversationMemoryManager.js"; import { applyConversationMemoryDefaults, getConversationMessages, storeConversationTurn, } from "./utils/conversationMemoryUtils.js"; import { ExternalServerManager } from "./mcp/externalServerManager.js"; // Import direct tools server for automatic registration import { directToolsServer } from "./mcp/servers/agent/directToolsServer.js"; import { isNonNullObject } from "./utils/typeUtils.js"; // Core types imported from core/types.js export class NeuroLink { mcpInitialized = false; emitter = new EventEmitter(); autoDiscoveredServerInfos = []; // External MCP server management externalServerManager; // Enhanced error handling support toolCircuitBreakers = new Map(); toolExecutionMetrics = new Map(); /** * Helper method to emit tool end event in a consistent way * Used by executeTool in both success and error paths * @param toolName - Name of the tool * @param startTime - Timestamp when tool execution started * @param success - Whether the tool execution was successful * @param result - The result of the tool execution (optional) * @param error - The error if execution failed (optional) */ emitToolEndEvent(toolName, startTime, success, result, error) { // Emit tool end event (NeuroLink format - enhanced with result/error) this.emitter.emit("tool:end", { toolName, responseTime: Date.now() - startTime, success, timestamp: Date.now(), result: result, // Enhanced: include actual result error: error, // Enhanced: include error if present }); // ADD: Bedrock-compatible tool:end event (positional parameters) this.emitter.emit("tool:end", toolName, success ? result : error); } // Conversation memory support conversationMemory; /** * Creates a new NeuroLink instance for AI text generation with MCP tool integration. * * @param config - Optional configuration object * @param config.conversationMemory - Configuration for conversation memory features * @param config.conversationMemory.enabled - Whether to enable conversation memory (default: false) * @param config.conversationMemory.maxSessions - Maximum number of concurrent sessions (default: 100) * @param config.conversationMemory.maxTurnsPerSession - Maximum conversation turns per session (default: 50) * * @example * ```typescript * // Basic usage * const neurolink = new NeuroLink(); * * // With conversation memory * const neurolink = new NeuroLink({ * conversationMemory: { * enabled: true, * maxSessions: 50, * maxTurnsPerSession: 20 * } * }); * ``` * * @throws {Error} When provider registry setup fails * @throws {Error} When conversation memory initialization fails (if enabled) * @throws {Error} When external server manager initialization fails */ constructor(config) { const constructorStartTime = Date.now(); const constructorHrTimeStart = process.hrtime.bigint(); const constructorId = `neurolink-constructor-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; this.logConstructorStart(constructorId, constructorStartTime, constructorHrTimeStart, config); this.initializeProviderRegistry(constructorId, constructorStartTime, constructorHrTimeStart); this.initializeConversationMemory(config, constructorId, constructorStartTime, constructorHrTimeStart); this.initializeExternalServerManager(constructorId, constructorStartTime, constructorHrTimeStart); this.logConstructorComplete(constructorId, constructorStartTime, constructorHrTimeStart); } /** * Log constructor start with comprehensive environment analysis */ logConstructorStart(constructorId, constructorStartTime, constructorHrTimeStart, config) { logger.debug(`[NeuroLink] 🏗️ LOG_POINT_C001_CONSTRUCTOR_START`, { logPoint: "C001_CONSTRUCTOR_START", constructorId, timestamp: new Date().toISOString(), constructorStartTime, constructorHrTimeStart: constructorHrTimeStart.toString(), hasConfig: !!config, configType: typeof config, configKeys: config ? Object.keys(config) : [], configSize: config ? JSON.stringify(config).length : 0, hasConversationMemoryConfig: !!config?.conversationMemory, conversationMemoryEnabled: config?.conversationMemory?.enabled || false, conversationMemoryKeys: config?.conversationMemory ? Object.keys(config.conversationMemory) : [], nodeVersion: process.version, platform: process.platform, arch: process.arch, nodeEnv: process.env.NODE_ENV || "UNKNOWN", memoryUsage: process.memoryUsage(), cpuUsage: process.cpuUsage(), uptime: process.uptime(), pid: process.pid, ppid: process.ppid, message: "NeuroLink constructor initialization starting with comprehensive environment analysis", }); } /** * Initialize provider registry with security settings */ initializeProviderRegistry(constructorId, constructorStartTime, constructorHrTimeStart) { const registrySetupStartTime = process.hrtime.bigint(); logger.debug(`[NeuroLink] 🏗️ LOG_POINT_C002_PROVIDER_REGISTRY_SETUP_START`, { logPoint: "C002_PROVIDER_REGISTRY_SETUP_START", constructorId, timestamp: new Date().toISOString(), elapsedMs: Date.now() - constructorStartTime, elapsedNs: (process.hrtime.bigint() - constructorHrTimeStart).toString(), registrySetupStartTimeNs: registrySetupStartTime.toString(), message: "Starting ProviderRegistry configuration for security", }); try { ProviderRegistry.setOptions({ enableManualMCP: false }); const registrySetupEndTime = process.hrtime.bigint(); const registrySetupDurationNs = registrySetupEndTime - registrySetupStartTime; logger.debug(`[NeuroLink] ✅ LOG_POINT_C003_PROVIDER_REGISTRY_SETUP_SUCCESS`, { logPoint: "C003_PROVIDER_REGISTRY_SETUP_SUCCESS", constructorId, timestamp: new Date().toISOString(), elapsedMs: Date.now() - constructorStartTime, elapsedNs: (process.hrtime.bigint() - constructorHrTimeStart).toString(), registrySetupDurationNs: registrySetupDurationNs.toString(), registrySetupDurationMs: Number(registrySetupDurationNs) / 1000000, enableManualMCP: false, message: "ProviderRegistry configured successfully with security settings", }); } catch (error) { const registrySetupErrorTime = process.hrtime.bigint(); const registrySetupDurationNs = registrySetupErrorTime - registrySetupStartTime; logger.error(`[NeuroLink] ❌ LOG_POINT_C004_PROVIDER_REGISTRY_SETUP_ERROR`, { logPoint: "C004_PROVIDER_REGISTRY_SETUP_ERROR", constructorId, timestamp: new Date().toISOString(), elapsedMs: Date.now() - constructorStartTime, elapsedNs: (process.hrtime.bigint() - constructorHrTimeStart).toString(), registrySetupDurationNs: registrySetupDurationNs.toString(), registrySetupDurationMs: Number(registrySetupDurationNs) / 1000000, error: error instanceof Error ? error.message : String(error), errorName: error instanceof Error ? error.name : "UnknownError", errorStack: error instanceof Error ? error.stack : undefined, message: "ProviderRegistry setup failed - critical initialization error", }); throw error; } } /** * Initialize conversation memory if enabled */ initializeConversationMemory(config, constructorId, constructorStartTime, constructorHrTimeStart) { if (config?.conversationMemory?.enabled) { const memoryInitStartTime = process.hrtime.bigint(); logger.debug(`[NeuroLink] 🧠 LOG_POINT_C005_MEMORY_INIT_START`, { logPoint: "C005_MEMORY_INIT_START", constructorId, timestamp: new Date().toISOString(), elapsedMs: Date.now() - constructorStartTime, elapsedNs: (process.hrtime.bigint() - constructorHrTimeStart).toString(), memoryInitStartTimeNs: memoryInitStartTime.toString(), memoryConfig: { enabled: config.conversationMemory.enabled, maxSessions: config.conversationMemory.maxSessions, maxTurnsPerSession: config.conversationMemory.maxTurnsPerSession, keys: Object.keys(config.conversationMemory), }, message: "Starting conversation memory initialization", }); try { const memoryConfig = applyConversationMemoryDefaults(config.conversationMemory); const memoryManagerCreateStartTime = process.hrtime.bigint(); this.conversationMemory = new ConversationMemoryManager(memoryConfig); const memoryManagerCreateEndTime = process.hrtime.bigint(); const memoryManagerCreateDurationNs = memoryManagerCreateEndTime - memoryManagerCreateStartTime; const memoryInitEndTime = process.hrtime.bigint(); const memoryInitDurationNs = memoryInitEndTime - memoryInitStartTime; logger.info(`[NeuroLink] ✅ LOG_POINT_C006_MEMORY_INIT_SUCCESS`, { logPoint: "C006_MEMORY_INIT_SUCCESS", constructorId, timestamp: new Date().toISOString(), elapsedMs: Date.now() - constructorStartTime, elapsedNs: (process.hrtime.bigint() - constructorHrTimeStart).toString(), memoryInitDurationNs: memoryInitDurationNs.toString(), memoryInitDurationMs: Number(memoryInitDurationNs) / 1000000, memoryManagerCreateDurationNs: memoryManagerCreateDurationNs.toString(), memoryManagerCreateDurationMs: Number(memoryManagerCreateDurationNs) / 1000000, finalMemoryConfig: { maxSessions: memoryConfig.maxSessions, maxTurnsPerSession: memoryConfig.maxTurnsPerSession, }, memoryUsageAfterInit: process.memoryUsage(), message: "NeuroLink initialized with conversation memory successfully", }); } catch (error) { const memoryInitErrorTime = process.hrtime.bigint(); const memoryInitDurationNs = memoryInitErrorTime - memoryInitStartTime; logger.error(`[NeuroLink] ❌ LOG_POINT_C007_MEMORY_INIT_ERROR`, { logPoint: "C007_MEMORY_INIT_ERROR", constructorId, timestamp: new Date().toISOString(), elapsedMs: Date.now() - constructorStartTime, elapsedNs: (process.hrtime.bigint() - constructorHrTimeStart).toString(), memoryInitDurationNs: memoryInitDurationNs.toString(), memoryInitDurationMs: Number(memoryInitDurationNs) / 1000000, error: error instanceof Error ? error.message : String(error), errorName: error instanceof Error ? error.name : "UnknownError", errorStack: error instanceof Error ? error.stack : undefined, memoryConfig: config.conversationMemory, message: "Conversation memory initialization failed", }); throw error; } } else { logger.debug(`[NeuroLink] 🚫 LOG_POINT_C008_MEMORY_DISABLED`, { logPoint: "C008_MEMORY_DISABLED", constructorId, timestamp: new Date().toISOString(), elapsedMs: Date.now() - constructorStartTime, elapsedNs: (process.hrtime.bigint() - constructorHrTimeStart).toString(), hasConfig: !!config, hasMemoryConfig: !!config?.conversationMemory, memoryEnabled: config?.conversationMemory?.enabled || false, reason: !config ? "NO_CONFIG" : !config.conversationMemory ? "NO_MEMORY_CONFIG" : !config.conversationMemory.enabled ? "MEMORY_DISABLED" : "UNKNOWN", message: "Conversation memory not enabled - skipping initialization", }); } } /** * Initialize external server manager with event handlers */ initializeExternalServerManager(constructorId, constructorStartTime, constructorHrTimeStart) { const externalServerInitStartTime = process.hrtime.bigint(); logger.debug(`[NeuroLink] 🌐 LOG_POINT_C009_EXTERNAL_SERVER_INIT_START`, { logPoint: "C009_EXTERNAL_SERVER_INIT_START", constructorId, timestamp: new Date().toISOString(), elapsedMs: Date.now() - constructorStartTime, elapsedNs: (process.hrtime.bigint() - constructorHrTimeStart).toString(), externalServerInitStartTimeNs: externalServerInitStartTime.toString(), serverManagerConfig: { maxServers: 20, defaultTimeout: 15000, enableAutoRestart: true, enablePerformanceMonitoring: true, }, registryIntegrationConfig: { enableMainRegistryIntegration: true, }, message: "Starting external server manager initialization", }); try { this.externalServerManager = new ExternalServerManager({ maxServers: 20, defaultTimeout: 15000, enableAutoRestart: true, enablePerformanceMonitoring: true, }, { enableMainRegistryIntegration: true, }); const externalServerInitEndTime = process.hrtime.bigint(); const externalServerInitDurationNs = externalServerInitEndTime - externalServerInitStartTime; logger.debug(`[NeuroLink] ✅ LOG_POINT_C010_EXTERNAL_SERVER_INIT_SUCCESS`, { logPoint: "C010_EXTERNAL_SERVER_INIT_SUCCESS", constructorId, timestamp: new Date().toISOString(), elapsedMs: Date.now() - constructorStartTime, elapsedNs: (process.hrtime.bigint() - constructorHrTimeStart).toString(), externalServerInitDurationNs: externalServerInitDurationNs.toString(), externalServerInitDurationMs: Number(externalServerInitDurationNs) / 1000000, hasExternalServerManager: !!this.externalServerManager, message: "External server manager initialized successfully", }); this.setupExternalServerEventHandlers(constructorId, constructorStartTime, constructorHrTimeStart); } catch (error) { const externalServerInitErrorTime = process.hrtime.bigint(); const externalServerInitDurationNs = externalServerInitErrorTime - externalServerInitStartTime; logger.error(`[NeuroLink] ❌ LOG_POINT_C013_EXTERNAL_SERVER_INIT_ERROR`, { logPoint: "C013_EXTERNAL_SERVER_INIT_ERROR", constructorId, timestamp: new Date().toISOString(), elapsedMs: Date.now() - constructorStartTime, elapsedNs: (process.hrtime.bigint() - constructorHrTimeStart).toString(), externalServerInitDurationNs: externalServerInitDurationNs.toString(), externalServerInitDurationMs: Number(externalServerInitDurationNs) / 1000000, error: error instanceof Error ? error.message : String(error), errorName: error instanceof Error ? error.name : "UnknownError", errorStack: error instanceof Error ? error.stack : undefined, message: "External server manager initialization failed", }); throw error; } } /** * Setup event handlers for external server manager */ setupExternalServerEventHandlers(constructorId, constructorStartTime, constructorHrTimeStart) { const eventHandlerSetupStartTime = process.hrtime.bigint(); logger.debug(`[NeuroLink] 🔗 LOG_POINT_C011_EVENT_HANDLER_SETUP_START`, { logPoint: "C011_EVENT_HANDLER_SETUP_START", constructorId, timestamp: new Date().toISOString(), elapsedMs: Date.now() - constructorStartTime, elapsedNs: (process.hrtime.bigint() - constructorHrTimeStart).toString(), eventHandlerSetupStartTimeNs: eventHandlerSetupStartTime.toString(), message: "Setting up external server event handlers", }); this.externalServerManager.on("connected", (event) => { logger.debug(`[NeuroLink] 🔗 EXTERNAL_SERVER_EVENT_CONNECTED`, { constructorId, eventType: "connected", event, timestamp: new Date().toISOString(), message: "External MCP server connected event received", }); this.emitter.emit("externalMCP:serverConnected", event); }); this.externalServerManager.on("disconnected", (event) => { logger.debug(`[NeuroLink] 🔗 EXTERNAL_SERVER_EVENT_DISCONNECTED`, { constructorId, eventType: "disconnected", event, timestamp: new Date().toISOString(), message: "External MCP server disconnected event received", }); this.emitter.emit("externalMCP:serverDisconnected", event); }); this.externalServerManager.on("failed", (event) => { logger.warn(`[NeuroLink] 🔗 EXTERNAL_SERVER_EVENT_FAILED`, { constructorId, eventType: "failed", event, timestamp: new Date().toISOString(), message: "External MCP server failed event received", }); this.emitter.emit("externalMCP:serverFailed", event); }); this.externalServerManager.on("toolDiscovered", (event) => { logger.debug(`[NeuroLink] 🔗 EXTERNAL_SERVER_EVENT_TOOL_DISCOVERED`, { constructorId, eventType: "toolDiscovered", toolName: event.toolName, serverId: event.serverId, timestamp: new Date().toISOString(), message: "External MCP tool discovered event received", }); this.emitter.emit("externalMCP:toolDiscovered", event); }); this.externalServerManager.on("toolRemoved", (event) => { logger.debug(`[NeuroLink] 🔗 EXTERNAL_SERVER_EVENT_TOOL_REMOVED`, { constructorId, eventType: "toolRemoved", toolName: event.toolName, serverId: event.serverId, timestamp: new Date().toISOString(), message: "External MCP tool removed event received", }); this.emitter.emit("externalMCP:toolRemoved", event); this.unregisterExternalMCPToolFromRegistry(event.toolName); }); const eventHandlerSetupEndTime = process.hrtime.bigint(); const eventHandlerSetupDurationNs = eventHandlerSetupEndTime - eventHandlerSetupStartTime; logger.debug(`[NeuroLink] ✅ LOG_POINT_C012_EVENT_HANDLER_SETUP_SUCCESS`, { logPoint: "C012_EVENT_HANDLER_SETUP_SUCCESS", constructorId, timestamp: new Date().toISOString(), elapsedMs: Date.now() - constructorStartTime, elapsedNs: (process.hrtime.bigint() - constructorHrTimeStart).toString(), eventHandlerSetupDurationNs: eventHandlerSetupDurationNs.toString(), eventHandlerSetupDurationMs: Number(eventHandlerSetupDurationNs) / 1000000, eventHandlersCount: 5, eventHandlerTypes: [ "connected", "disconnected", "failed", "toolDiscovered", "toolRemoved", ], message: "Event handlers set up successfully", }); } /** * Log constructor completion with final state summary */ logConstructorComplete(constructorId, constructorStartTime, constructorHrTimeStart) { const constructorEndTime = process.hrtime.bigint(); const constructorDurationNs = constructorEndTime - constructorHrTimeStart; logger.info(`[NeuroLink] 🏁 LOG_POINT_C014_CONSTRUCTOR_COMPLETE`, { logPoint: "C014_CONSTRUCTOR_COMPLETE", constructorId, timestamp: new Date().toISOString(), constructorDurationNs: constructorDurationNs.toString(), constructorDurationMs: Number(constructorDurationNs) / 1000000, totalElapsedMs: Date.now() - constructorStartTime, finalState: { hasConversationMemory: !!this.conversationMemory, hasExternalServerManager: !!this.externalServerManager, hasEmitter: !!this.emitter, mcpInitialized: this.mcpInitialized, toolCircuitBreakersCount: this.toolCircuitBreakers.size, toolExecutionMetricsCount: this.toolExecutionMetrics.size, }, finalMemoryUsage: process.memoryUsage(), finalCpuUsage: process.cpuUsage(), message: "NeuroLink constructor completed successfully with all components initialized", }); } /** * Initialize MCP registry with enhanced error handling and resource cleanup * Uses isolated async context to prevent hanging */ async initializeMCP() { const mcpInitId = `mcp-init-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; const mcpInitStartTime = Date.now(); const mcpInitHrTimeStart = process.hrtime.bigint(); this.logMCPInitStart(mcpInitId, mcpInitStartTime, mcpInitHrTimeStart); if (this.mcpInitialized) { this.logMCPAlreadyInitialized(mcpInitId, mcpInitStartTime, mcpInitHrTimeStart); return; } const MemoryManager = await this.importPerformanceManager(mcpInitId, mcpInitStartTime, mcpInitHrTimeStart); const startMemory = MemoryManager ? MemoryManager.getMemoryUsageMB() : { heapUsed: 0, heapTotal: 0, rss: 0, external: 0 }; try { await this.performMCPInitialization(mcpInitId, mcpInitStartTime, mcpInitHrTimeStart, startMemory); this.mcpInitialized = true; this.logMCPInitComplete(startMemory, MemoryManager, mcpInitStartTime); } catch (error) { mcpLogger.warn("[NeuroLink] MCP initialization failed", { error: error instanceof Error ? error.message : String(error), }); // Continue without MCP - graceful degradation } } /** * Log MCP initialization start */ logMCPInitStart(mcpInitId, mcpInitStartTime, mcpInitHrTimeStart) { logger.debug(`[NeuroLink] 🔧 LOG_POINT_M001_MCP_INIT_ENTRY`, { logPoint: "M001_MCP_INIT_ENTRY", mcpInitId, timestamp: new Date().toISOString(), mcpInitStartTime, mcpInitHrTimeStart: mcpInitHrTimeStart.toString(), mcpInitialized: this.mcpInitialized, hasExternalServerManager: !!this.externalServerManager, memoryUsage: process.memoryUsage(), cpuUsage: process.cpuUsage(), message: "MCP initialization entry point - checking if already initialized", }); } /** * Log MCP already initialized */ logMCPAlreadyInitialized(mcpInitId, mcpInitStartTime, mcpInitHrTimeStart) { logger.debug(`[NeuroLink] ✅ LOG_POINT_M002_MCP_ALREADY_INITIALIZED`, { logPoint: "M002_MCP_ALREADY_INITIALIZED", mcpInitId, timestamp: new Date().toISOString(), elapsedMs: Date.now() - mcpInitStartTime, elapsedNs: (process.hrtime.bigint() - mcpInitHrTimeStart).toString(), mcpInitialized: this.mcpInitialized, message: "MCP already initialized - skipping initialization", }); } /** * Import performance manager with error handling */ async importPerformanceManager(mcpInitId, mcpInitStartTime, mcpInitHrTimeStart) { const performanceImportStartTime = process.hrtime.bigint(); logger.debug(`[NeuroLink] 📊 LOG_POINT_M003_PERFORMANCE_IMPORT_START`, { logPoint: "M003_PERFORMANCE_IMPORT_START", mcpInitId, timestamp: new Date().toISOString(), elapsedMs: Date.now() - mcpInitStartTime, elapsedNs: (process.hrtime.bigint() - mcpInitHrTimeStart).toString(), performanceImportStartTimeNs: performanceImportStartTime.toString(), message: "Starting MemoryManager import for performance tracking", }); try { const moduleImport = await import("./utils/performance.js"); const MemoryManager = moduleImport.MemoryManager; const performanceImportEndTime = process.hrtime.bigint(); const performanceImportDurationNs = performanceImportEndTime - performanceImportStartTime; logger.debug(`[NeuroLink] ✅ LOG_POINT_M004_PERFORMANCE_IMPORT_SUCCESS`, { logPoint: "M004_PERFORMANCE_IMPORT_SUCCESS", mcpInitId, timestamp: new Date().toISOString(), elapsedMs: Date.now() - mcpInitStartTime, elapsedNs: (process.hrtime.bigint() - mcpInitHrTimeStart).toString(), performanceImportDurationNs: performanceImportDurationNs.toString(), performanceImportDurationMs: Number(performanceImportDurationNs) / 1000000, hasMemoryManager: !!MemoryManager, message: "MemoryManager imported successfully", }); return MemoryManager; } catch (error) { const performanceImportErrorTime = process.hrtime.bigint(); const performanceImportDurationNs = performanceImportErrorTime - performanceImportStartTime; logger.warn(`[NeuroLink] ⚠️ LOG_POINT_M005_PERFORMANCE_IMPORT_ERROR`, { logPoint: "M005_PERFORMANCE_IMPORT_ERROR", mcpInitId, timestamp: new Date().toISOString(), elapsedMs: Date.now() - mcpInitStartTime, elapsedNs: (process.hrtime.bigint() - mcpInitHrTimeStart).toString(), performanceImportDurationNs: performanceImportDurationNs.toString(), performanceImportDurationMs: Number(performanceImportDurationNs) / 1000000, error: error instanceof Error ? error.message : String(error), errorName: error instanceof Error ? error.name : "UnknownError", message: "MemoryManager import failed - continuing without performance tracking", }); return undefined; } } /** * Perform main MCP initialization logic */ async performMCPInitialization(mcpInitId, mcpInitStartTime, mcpInitHrTimeStart, startMemory) { logger.info(`[NeuroLink] 🚀 LOG_POINT_M006_MCP_MAIN_INIT_START`, { logPoint: "M006_MCP_MAIN_INIT_START", mcpInitId, timestamp: new Date().toISOString(), elapsedMs: Date.now() - mcpInitStartTime, elapsedNs: (process.hrtime.bigint() - mcpInitHrTimeStart).toString(), startMemory, message: "Starting isolated MCP initialization process", }); mcpLogger.debug("[NeuroLink] Starting isolated MCP initialization..."); await this.initializeToolRegistryInternal(mcpInitId, mcpInitStartTime, mcpInitHrTimeStart); await this.initializeProviderRegistryInternal(mcpInitId, mcpInitStartTime, mcpInitHrTimeStart); await this.registerDirectToolsServerInternal(mcpInitId, mcpInitStartTime, mcpInitHrTimeStart); await this.loadMCPConfigurationInternal(mcpInitId, mcpInitStartTime, mcpInitHrTimeStart); } /** * Initialize tool registry with timeout protection */ async initializeToolRegistryInternal(mcpInitId, mcpInitStartTime, mcpInitHrTimeStart) { const toolRegistryStartTime = process.hrtime.bigint(); const initTimeout = 3000; logger.debug(`[NeuroLink] ⏱️ LOG_POINT_M007_TOOL_REGISTRY_TIMEOUT_SETUP`, { logPoint: "M007_TOOL_REGISTRY_TIMEOUT_SETUP", mcpInitId, timestamp: new Date().toISOString(), elapsedMs: Date.now() - mcpInitStartTime, elapsedNs: (process.hrtime.bigint() - mcpInitHrTimeStart).toString(), toolRegistryStartTimeNs: toolRegistryStartTime.toString(), initTimeoutMs: initTimeout, message: "Setting up tool registry initialization with timeout protection", }); await Promise.race([ Promise.resolve(), new Promise((_, reject) => { setTimeout(() => reject(new Error("MCP initialization timeout")), initTimeout); }), ]); const toolRegistryEndTime = process.hrtime.bigint(); const toolRegistryDurationNs = toolRegistryEndTime - toolRegistryStartTime; logger.debug(`[NeuroLink] ✅ LOG_POINT_M008_TOOL_REGISTRY_SUCCESS`, { logPoint: "M008_TOOL_REGISTRY_SUCCESS", mcpInitId, timestamp: new Date().toISOString(), elapsedMs: Date.now() - mcpInitStartTime, elapsedNs: (process.hrtime.bigint() - mcpInitHrTimeStart).toString(), toolRegistryDurationNs: toolRegistryDurationNs.toString(), toolRegistryDurationMs: Number(toolRegistryDurationNs) / 1000000, message: "Tool registry initialization completed within timeout", }); } /** * Initialize provider registry */ async initializeProviderRegistryInternal(mcpInitId, mcpInitStartTime, mcpInitHrTimeStart) { const providerRegistryStartTime = process.hrtime.bigint(); logger.debug(`[NeuroLink] 🏭 LOG_POINT_M009_PROVIDER_REGISTRY_START`, { logPoint: "M009_PROVIDER_REGISTRY_START", mcpInitId, timestamp: new Date().toISOString(), elapsedMs: Date.now() - mcpInitStartTime, elapsedNs: (process.hrtime.bigint() - mcpInitHrTimeStart).toString(), providerRegistryStartTimeNs: providerRegistryStartTime.toString(), message: "Starting provider registry registration with lazy loading", }); await ProviderRegistry.registerAllProviders(); const providerRegistryEndTime = process.hrtime.bigint(); const providerRegistryDurationNs = providerRegistryEndTime - providerRegistryStartTime; logger.debug(`[NeuroLink] ✅ LOG_POINT_M010_PROVIDER_REGISTRY_SUCCESS`, { logPoint: "M010_PROVIDER_REGISTRY_SUCCESS", mcpInitId, timestamp: new Date().toISOString(), elapsedMs: Date.now() - mcpInitStartTime, elapsedNs: (process.hrtime.bigint() - mcpInitHrTimeStart).toString(), providerRegistryDurationNs: providerRegistryDurationNs.toString(), providerRegistryDurationMs: Number(providerRegistryDurationNs) / 1000000, message: "Provider registry registration completed successfully", }); } /** * Register direct tools server */ async registerDirectToolsServerInternal(mcpInitId, mcpInitStartTime, mcpInitHrTimeStart) { const directToolsStartTime = process.hrtime.bigint(); logger.debug(`[NeuroLink] 🛠️ LOG_POINT_M011_DIRECT_TOOLS_START`, { logPoint: "M011_DIRECT_TOOLS_START", mcpInitId, timestamp: new Date().toISOString(), elapsedMs: Date.now() - mcpInitStartTime, elapsedNs: (process.hrtime.bigint() - mcpInitHrTimeStart).toString(), directToolsStartTimeNs: directToolsStartTime.toString(), serverId: "neurolink-direct", message: "Starting direct tools server registration", }); try { await toolRegistry.registerServer("neurolink-direct", directToolsServer); const directToolsSuccessTime = process.hrtime.bigint(); const directToolsDurationNs = directToolsSuccessTime - directToolsStartTime; logger.debug(`[NeuroLink] ✅ LOG_POINT_M012_DIRECT_TOOLS_SUCCESS`, { logPoint: "M012_DIRECT_TOOLS_SUCCESS", mcpInitId, timestamp: new Date().toISOString(), elapsedMs: Date.now() - mcpInitStartTime, elapsedNs: (process.hrtime.bigint() - mcpInitHrTimeStart).toString(), directToolsDurationNs: directToolsDurationNs.toString(), directToolsDurationMs: Number(directToolsDurationNs) / 1000000, serverId: "neurolink-direct", message: "Direct tools server registered successfully", }); mcpLogger.debug("[NeuroLink] Direct tools server registered successfully", { serverId: "neurolink-direct", }); } catch (error) { const directToolsErrorTime = process.hrtime.bigint(); const directToolsDurationNs = directToolsErrorTime - directToolsStartTime; logger.warn(`[NeuroLink] ⚠️ LOG_POINT_M013_DIRECT_TOOLS_ERROR`, { logPoint: "M013_DIRECT_TOOLS_ERROR", mcpInitId, timestamp: new Date().toISOString(), elapsedMs: Date.now() - mcpInitStartTime, elapsedNs: (process.hrtime.bigint() - mcpInitHrTimeStart).toString(), directToolsDurationNs: directToolsDurationNs.toString(), directToolsDurationMs: Number(directToolsDurationNs) / 1000000, error: error instanceof Error ? error.message : String(error), errorName: error instanceof Error ? error.name : "UnknownError", errorStack: error instanceof Error ? error.stack : undefined, serverId: "neurolink-direct", message: "Direct tools server registration failed but continuing", }); mcpLogger.warn("[NeuroLink] Failed to register direct tools server", { error: error instanceof Error ? error.message : String(error), }); } } /** * Load MCP configuration from .mcp-config.json */ async loadMCPConfigurationInternal(mcpInitId, mcpInitStartTime, mcpInitHrTimeStart) { const mcpConfigStartTime = process.hrtime.bigint(); logger.debug(`[NeuroLink] 📄 LOG_POINT_M014_MCP_CONFIG_START`, { logPoint: "M014_MCP_CONFIG_START", mcpInitId, timestamp: new Date().toISOString(), elapsedMs: Date.now() - mcpInitStartTime, elapsedNs: (process.hrtime.bigint() - mcpInitHrTimeStart).toString(), mcpConfigStartTimeNs: mcpConfigStartTime.toString(), hasExternalServerManager: !!this.externalServerManager, message: "Starting MCP configuration loading from .mcp-config.json", }); try { const configResult = await this.externalServerManager.loadMCPConfiguration(); const mcpConfigSuccessTime = process.hrtime.bigint(); const mcpConfigDurationNs = mcpConfigSuccessTime - mcpConfigStartTime; logger.debug(`[NeuroLink] ✅ LOG_POINT_M015_MCP_CONFIG_SUCCESS`, { logPoint: "M015_MCP_CONFIG_SUCCESS", mcpInitId, timestamp: new Date().toISOString(), elapsedMs: Date.now() - mcpInitStartTime, elapsedNs: (process.hrtime.bigint() - mcpInitHrTimeStart).toString(), mcpConfigDurationNs: mcpConfigDurationNs.toString(), mcpConfigDurationMs: Number(mcpConfigDurationNs) / 1000000, serversLoaded: configResult.serversLoaded, errorsCount: configResult.errors.length, configResult: { serversLoaded: configResult.serversLoaded, errors: configResult.errors.map((err) => ({ message: err instanceof Error ? err.message : String(err), name: err instanceof Error ? err.name : "UnknownError", })), }, message: "MCP configuration loaded successfully", }); mcpLogger.debug("[NeuroLink] MCP configuration loaded successfully", { serversLoaded: configResult.serversLoaded, errors: configResult.errors.length, }); if (configResult.errors.length > 0) { mcpLogger.warn("[NeuroLink] Some MCP servers failed to load", { errors: configResult.errors, }); } } catch (configError) { mcpLogger.warn("[NeuroLink] MCP configuration loading failed", { error: configError instanceof Error ? configError.message : String(configError), }); } } /** * Log MCP initialization completion */ logMCPInitComplete(startMemory, MemoryManager, mcpInitStartTime) { const endMemory = MemoryManager ? MemoryManager.getMemoryUsageMB() : { heapUsed: 0, heapTotal: 0, rss: 0, external: 0 }; const memoryDelta = endMemory.heapUsed - startMemory.heapUsed; const initTime = Date.now() - mcpInitStartTime; mcpLogger.debug("[NeuroLink] MCP initialization completed successfully", { initTime: `${initTime}ms`, memoryUsed: `${memoryDelta}MB`, }); if (memoryDelta > 30) { mcpLogger.debug("💡 Memory cleanup suggestion: MCP initialization used significant memory. Consider calling MemoryManager.forceGC() after heavy operations."); } } /** * MAIN ENTRY POINT: Enhanced generate method with new function signature * Replaces both generateText and legacy methods */ /** * Extracts the original prompt text from the provided input. * If a string is provided, it returns the string directly. * If a GenerateOptions object is provided, it returns the input text from the object. * @param optionsOrPrompt The prompt input, either as a string or a GenerateOptions object. * @returns The original prompt text as a string. */ _extractOriginalPrompt(optionsOrPrompt) { return typeof optionsOrPrompt === "string" ? optionsOrPrompt : optionsOrPrompt.input.text; } /** * Generate AI content using the best available provider with MCP tool integration. * This is the primary method for text generation with full feature support. * * @param optionsOrPrompt - Either a string prompt or a comprehensive GenerateOptions object * @param optionsOrPrompt.input - Input configuration object * @param optionsOrPrompt.input.text - The text prompt to send to the AI (required) * @param optionsOrPrompt.provider - AI provider to use ('auto', 'openai', 'anthropic', etc.) * @param optionsOrPrompt.model - Specific model to use (e.g., 'gpt-4', 'claude-3-opus') * @param optionsOrPrompt.temperature - Randomness in response (0.0 = deterministic, 2.0 = very random) * @param optionsOrPrompt.maxTokens - Maximum tokens in response * @param optionsOrPrompt.systemPrompt - System message to set AI behavior * @param optionsOrPrompt.disableTools - Whether to disable MCP tool usage * @param optionsOrPrompt.enableAnalytics - Whether to include usage analytics * @param optionsOrPrompt.enableEvaluation - Whether to include response quality evaluation * @param optionsOrPrompt.context - Additional context for the request * @param optionsOrPrompt.evaluationDomain - Domain for specialized evaluation * @param optionsOrPrompt.toolUsageContext - Context for tool usage decisions * * @returns Promise resolving to GenerateResult with content, usage data, and optional analytics * * @example * ```typescript * // Simple usage with string prompt * const result = await neurolink.generate("What is artificial intelligence?"); * console.log(result.content); * * // Advanced usage with options * const result = await neurolink.generate({ * input: { text: "Explain quantum computing" }, * provider: "openai", * model: "gpt-4", * temperature: 0.7, * maxTokens: 500, * enableAnalytics: true, * enableEvaluation: true, * context: { domain: "science", level: "intermediate" } * }); * * // Access analytics and evaluation data * console.log(result.analytics?.usage); * console.log(result.evaluation?.relevance); * ``` * * @throws {Error} When input text is missing or invalid * @throws {Error} When all providers fail to generate content * @throws {Error} When conversation memory operations fail (if enabled) */ async generate(optionsOrPrompt) { const originalPrompt = this._extractOriginalPrompt(optionsOrPrompt); // Convert string prompt to full options const options = typeof optionsOrPrompt === "string" ? { input: { text: optionsOrPrompt } } : optionsOrPrompt; // Validate prompt if (!options.input?.text || typeof options.input.text !== "string") { throw new Error("Input text is required and must be a non-empty string"); } const startTime = Date.now(); // Emit generation start event (NeuroLink format - keep existing) this.emitter.emit("generation:start", { provider: options.provider || "auto", timestamp: startTime, }); // ADD: Bedrock-compatible response:start event this.emitter.emit("response:start"); // ADD: Bedrock-compatible message event this.emitter.emit("message", `Starting ${options.provider || "auto"} text generation...`); // Process factory configuration const factoryResult = processFactoryOptions(options); // Validate factory configuration if present if (factoryResult.hasFactoryConfig && options.factoryConfig) { const validation = validateFactoryConfig(options.factoryConfig); if (!validation.isValid) { logger.warn("Invalid factory configuration detected", { errors: validation.errors, }); // Continue with warning rather than throwing - graceful degradation } } // Convert to TextGenerationOptions using factory utilities const baseOptions = { prompt: options.input.text, provider: options.provider, model: options.model, temperature: options.temperature, maxTokens: options.maxTokens, systemPrompt: options.systemPrompt, disableTools: options.disableTools, enableAnalytics: options.enableAnalytics, enableEvaluation: options.enableEvaluation, context: options.context, evaluationDomain: options.evaluationDomain, toolUsageContext: options.toolUsageContext, }; // Apply factory enhancement using centralized utilities const textOptions = enhanceTextGenerationOptions(baseOptions, factoryResult); // Pass conversation memory config if available if (this.conversationMemory) { textOptions.conversationMemoryConfig = this.conversationMemory.config; // Include original prompt for context summarization textOptions.originalPrompt = originalPrompt; } // Detect and execute domain-specific tools const { toolResults, enhancedPrompt } = await this.detectAndExecuteTools(textOptions.prompt || options.input.text, factoryResult.domainType); // Update prompt with tool results if available if (enhancedPrompt !== textOptions.prompt) { textOptions.prompt = enhancedPrompt; logger.debug("Enhanced prompt with tool results", { originalLength: options.input.text.length, enhancedLength: enhancedPrompt.length, toolResults: toolResults.length, }); } // Use redesigned generation logic const textResult = await this.generateTextInternal(textOptions); // Emit generation completion event (NeuroLink format - enhanced with content) this.emitter.emit("generation:end", { provider: textResult.provider, responseTime: Date.now() - startTime, toolsUsed: textResult.toolsUsed, timestamp: Date.now(), result: textResult, // Enhanced: include full result }); // ADD: Bedrock-compatible response:end event with content this.emitter.emit("response:end", textResult.content || ""); // ADD: Bedrock-compatible message event this.emitter.emit("message", `Generation completed in ${Date.now() - startTime}ms`); // Convert back to GenerateResult const generateResult = { content: textResult.content, provider: textResult.provider, model: textResult.model, usage: textResult.usage ? { input: textResult.usage.input || 0, output: textResult.usage.output || 0, total: textResult.usage.total || 0, } : undefined, responseTime: textResult.responseTime, toolsUsed: textResult.toolsUsed, toolExecutions: transformToolExecutions(textResult.toolExecutions), enhancedWithTools: textResult.enhancedWithTools, availableTools: transformAvailableTools(textResult.availableTools), analytics: textResult.analytics, evaluation: textResult.evaluation ? { ...textResult.evaluation, isOffTopic: textResult.evaluation .isOffTopic ?? false, alertSeverity: textResult.evaluation .alertSeverity ?? "none", reasoning: textResult.evaluation .reasoning ?? "No evaluation provided", evaluationModel: textResult.evaluation .evaluationModel ?? "unknown", evaluationTime: textResult.evaluation .evaluationTime ?? Date.now(), // Include evaluationDomain from original options evaluationDomain: textResult.evaluation .evaluationDomain ?? textOptions.evaluationDomain ??