@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
JavaScript
/**
* 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 ??