@juspay/neurolink 7.37.0 → 7.38.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/cli/commands/config.d.ts +18 -18
  3. package/dist/cli/factories/commandFactory.d.ts +24 -0
  4. package/dist/cli/factories/commandFactory.js +297 -245
  5. package/dist/core/baseProvider.d.ts +44 -3
  6. package/dist/core/baseProvider.js +729 -352
  7. package/dist/core/constants.d.ts +2 -30
  8. package/dist/core/constants.js +15 -43
  9. package/dist/core/redisConversationMemoryManager.d.ts +98 -15
  10. package/dist/core/redisConversationMemoryManager.js +665 -203
  11. package/dist/factories/providerFactory.js +23 -6
  12. package/dist/index.d.ts +3 -2
  13. package/dist/index.js +4 -3
  14. package/dist/lib/core/baseProvider.d.ts +44 -3
  15. package/dist/lib/core/baseProvider.js +729 -352
  16. package/dist/lib/core/constants.d.ts +2 -30
  17. package/dist/lib/core/constants.js +15 -43
  18. package/dist/lib/core/redisConversationMemoryManager.d.ts +98 -15
  19. package/dist/lib/core/redisConversationMemoryManager.js +665 -203
  20. package/dist/lib/factories/providerFactory.js +23 -6
  21. package/dist/lib/index.d.ts +3 -2
  22. package/dist/lib/index.js +4 -3
  23. package/dist/lib/mcp/externalServerManager.js +2 -2
  24. package/dist/lib/mcp/registry.js +2 -2
  25. package/dist/lib/mcp/servers/agent/directToolsServer.js +19 -10
  26. package/dist/lib/mcp/toolRegistry.js +4 -8
  27. package/dist/lib/neurolink.d.ts +95 -28
  28. package/dist/lib/neurolink.js +479 -719
  29. package/dist/lib/providers/amazonBedrock.js +2 -2
  30. package/dist/lib/providers/anthropic.js +8 -0
  31. package/dist/lib/providers/anthropicBaseProvider.js +8 -0
  32. package/dist/lib/providers/azureOpenai.js +8 -0
  33. package/dist/lib/providers/googleAiStudio.js +8 -0
  34. package/dist/lib/providers/googleVertex.d.ts +3 -23
  35. package/dist/lib/providers/googleVertex.js +24 -342
  36. package/dist/lib/providers/huggingFace.js +8 -0
  37. package/dist/lib/providers/litellm.js +8 -0
  38. package/dist/lib/providers/mistral.js +8 -0
  39. package/dist/lib/providers/openAI.d.ts +23 -0
  40. package/dist/lib/providers/openAI.js +323 -6
  41. package/dist/lib/providers/openaiCompatible.js +8 -0
  42. package/dist/lib/providers/sagemaker/language-model.d.ts +2 -2
  43. package/dist/lib/sdk/toolRegistration.js +18 -1
  44. package/dist/lib/types/common.d.ts +98 -0
  45. package/dist/lib/types/conversation.d.ts +52 -2
  46. package/dist/lib/types/streamTypes.d.ts +13 -6
  47. package/dist/lib/types/typeAliases.d.ts +3 -2
  48. package/dist/lib/utils/conversationMemory.js +3 -1
  49. package/dist/lib/utils/messageBuilder.d.ts +10 -2
  50. package/dist/lib/utils/messageBuilder.js +22 -1
  51. package/dist/lib/utils/parameterValidation.js +6 -25
  52. package/dist/lib/utils/promptRedaction.js +4 -4
  53. package/dist/lib/utils/redis.d.ts +10 -6
  54. package/dist/lib/utils/redis.js +71 -70
  55. package/dist/lib/utils/schemaConversion.d.ts +14 -0
  56. package/dist/lib/utils/schemaConversion.js +140 -0
  57. package/dist/lib/utils/transformationUtils.js +143 -5
  58. package/dist/mcp/externalServerManager.js +2 -2
  59. package/dist/mcp/registry.js +2 -2
  60. package/dist/mcp/servers/agent/directToolsServer.js +19 -10
  61. package/dist/mcp/toolRegistry.js +4 -8
  62. package/dist/neurolink.d.ts +95 -28
  63. package/dist/neurolink.js +479 -719
  64. package/dist/providers/amazonBedrock.js +2 -2
  65. package/dist/providers/anthropic.js +8 -0
  66. package/dist/providers/anthropicBaseProvider.js +8 -0
  67. package/dist/providers/azureOpenai.js +8 -0
  68. package/dist/providers/googleAiStudio.js +8 -0
  69. package/dist/providers/googleVertex.d.ts +3 -23
  70. package/dist/providers/googleVertex.js +24 -342
  71. package/dist/providers/huggingFace.js +8 -0
  72. package/dist/providers/litellm.js +8 -0
  73. package/dist/providers/mistral.js +8 -0
  74. package/dist/providers/openAI.d.ts +23 -0
  75. package/dist/providers/openAI.js +323 -6
  76. package/dist/providers/openaiCompatible.js +8 -0
  77. package/dist/providers/sagemaker/language-model.d.ts +2 -2
  78. package/dist/sdk/toolRegistration.js +18 -1
  79. package/dist/types/common.d.ts +98 -0
  80. package/dist/types/conversation.d.ts +52 -2
  81. package/dist/types/streamTypes.d.ts +13 -6
  82. package/dist/types/typeAliases.d.ts +3 -2
  83. package/dist/utils/conversationMemory.js +3 -1
  84. package/dist/utils/messageBuilder.d.ts +10 -2
  85. package/dist/utils/messageBuilder.js +22 -1
  86. package/dist/utils/parameterValidation.js +6 -25
  87. package/dist/utils/promptRedaction.js +4 -4
  88. package/dist/utils/redis.d.ts +10 -6
  89. package/dist/utils/redis.js +71 -70
  90. package/dist/utils/schemaConversion.d.ts +14 -0
  91. package/dist/utils/schemaConversion.js +140 -0
  92. package/dist/utils/transformationUtils.js +143 -5
  93. package/package.json +3 -2
package/dist/neurolink.js CHANGED
@@ -16,7 +16,7 @@ catch {
16
16
  import { AIProviderFactory } from "./core/factory.js";
17
17
  import { mcpLogger } from "./utils/logger.js";
18
18
  import { SYSTEM_LIMITS } from "./core/constants.js";
19
- import { NANOSECOND_TO_MS_DIVISOR, MCP_TIMEOUTS, SERVER_CONFIG, TOOL_TIMEOUTS, RETRY_ATTEMPTS, RETRY_DELAYS, CIRCUIT_BREAKER, CIRCUIT_BREAKER_RESET_MS, MEMORY_THRESHOLDS, PROVIDER_TIMEOUTS, PERFORMANCE_THRESHOLDS, } from "./constants/index.js";
19
+ import { NANOSECOND_TO_MS_DIVISOR, TOOL_TIMEOUTS, RETRY_ATTEMPTS, RETRY_DELAYS, CIRCUIT_BREAKER, CIRCUIT_BREAKER_RESET_MS, MEMORY_THRESHOLDS, PROVIDER_TIMEOUTS, PERFORMANCE_THRESHOLDS, } from "./constants/index.js";
20
20
  import pLimit from "p-limit";
21
21
  import { toolRegistry } from "./mcp/toolRegistry.js";
22
22
  import { logger } from "./utils/logger.js";
@@ -39,6 +39,7 @@ import { directToolsServer } from "./mcp/servers/agent/directToolsServer.js";
39
39
  import { ModelRouter } from "./utils/modelRouter.js";
40
40
  import { BinaryTaskClassifier } from "./utils/taskClassifier.js";
41
41
  import { isNonNullObject } from "./utils/typeUtils.js";
42
+ import { isZodSchema } from "./utils/schemaConversion.js";
42
43
  // Core types imported from "./types/index.js"
43
44
  export class NeuroLink {
44
45
  mcpInitialized = false;
@@ -52,6 +53,9 @@ export class NeuroLink {
52
53
  // Enhanced error handling support
53
54
  toolCircuitBreakers = new Map();
54
55
  toolExecutionMetrics = new Map();
56
+ currentStreamToolExecutions = [];
57
+ toolExecutionHistory = [];
58
+ activeToolExecutions = new Map();
55
59
  /**
56
60
  * Helper method to emit tool end event in a consistent way
57
61
  * Used by executeTool in both success and error paths
@@ -80,6 +84,11 @@ export class NeuroLink {
80
84
  conversationMemoryConfig;
81
85
  // Add orchestration property
82
86
  enableOrchestration;
87
+ /**
88
+ * Context storage for tool execution
89
+ * This context will be merged with any runtime context passed by the AI model
90
+ */
91
+ toolExecutionContext;
83
92
  /**
84
93
  * Creates a new NeuroLink instance for AI text generation with MCP tool integration.
85
94
  *
@@ -115,6 +124,8 @@ export class NeuroLink {
115
124
  * @throws {Error} When external server manager initialization fails
116
125
  */
117
126
  constructor(config) {
127
+ // Initialize orchestration setting
128
+ this.enableOrchestration = config?.enableOrchestration ?? false;
118
129
  // Read tool cache duration from environment variables, with a default
119
130
  const cacheDurationEnv = process.env.NEUROLINK_TOOL_CACHE_DURATION;
120
131
  this.toolCacheDuration = cacheDurationEnv
@@ -123,45 +134,11 @@ export class NeuroLink {
123
134
  const constructorStartTime = Date.now();
124
135
  const constructorHrTimeStart = process.hrtime.bigint();
125
136
  const constructorId = `neurolink-constructor-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
126
- // Initialize orchestration setting
127
- this.enableOrchestration = config?.enableOrchestration ?? false;
128
- this.logConstructorStart(constructorId, constructorStartTime, constructorHrTimeStart, config);
129
137
  this.initializeProviderRegistry(constructorId, constructorStartTime, constructorHrTimeStart);
130
138
  this.initializeConversationMemory(config, constructorId, constructorStartTime, constructorHrTimeStart);
131
139
  this.initializeExternalServerManager(constructorId, constructorStartTime, constructorHrTimeStart);
132
140
  this.logConstructorComplete(constructorId, constructorStartTime, constructorHrTimeStart);
133
141
  }
134
- /**
135
- * Log constructor start with comprehensive environment analysis
136
- */
137
- logConstructorStart(constructorId, constructorStartTime, constructorHrTimeStart, config) {
138
- logger.debug(`[NeuroLink] 🏗️ LOG_POINT_C001_CONSTRUCTOR_START`, {
139
- logPoint: "C001_CONSTRUCTOR_START",
140
- constructorId,
141
- timestamp: new Date().toISOString(),
142
- constructorStartTime,
143
- constructorHrTimeStart: constructorHrTimeStart.toString(),
144
- hasConfig: !!config,
145
- configType: typeof config,
146
- configKeys: config ? Object.keys(config) : [],
147
- configSize: config ? JSON.stringify(config).length : 0,
148
- hasConversationMemoryConfig: !!config?.conversationMemory,
149
- conversationMemoryEnabled: config?.conversationMemory?.enabled || false,
150
- conversationMemoryKeys: config?.conversationMemory
151
- ? Object.keys(config.conversationMemory)
152
- : [],
153
- nodeVersion: process.version,
154
- platform: process.platform,
155
- arch: process.arch,
156
- nodeEnv: process.env.NODE_ENV || "UNKNOWN",
157
- memoryUsage: process.memoryUsage(),
158
- cpuUsage: process.cpuUsage(),
159
- uptime: process.uptime(),
160
- pid: process.pid,
161
- ppid: process.ppid,
162
- message: "NeuroLink constructor initialization starting with comprehensive environment analysis",
163
- });
164
- }
165
142
  /**
166
143
  * Initialize provider registry with security settings
167
144
  */
@@ -176,40 +153,7 @@ export class NeuroLink {
176
153
  registrySetupStartTimeNs: registrySetupStartTime.toString(),
177
154
  message: "Starting ProviderRegistry configuration for security",
178
155
  });
179
- try {
180
- ProviderRegistry.setOptions({ enableManualMCP: false });
181
- const registrySetupEndTime = process.hrtime.bigint();
182
- const registrySetupDurationNs = registrySetupEndTime - registrySetupStartTime;
183
- logger.debug(`[NeuroLink] ✅ LOG_POINT_C003_PROVIDER_REGISTRY_SETUP_SUCCESS`, {
184
- logPoint: "C003_PROVIDER_REGISTRY_SETUP_SUCCESS",
185
- constructorId,
186
- timestamp: new Date().toISOString(),
187
- elapsedMs: Date.now() - constructorStartTime,
188
- elapsedNs: (process.hrtime.bigint() - constructorHrTimeStart).toString(),
189
- registrySetupDurationNs: registrySetupDurationNs.toString(),
190
- registrySetupDurationMs: Number(registrySetupDurationNs) / NANOSECOND_TO_MS_DIVISOR,
191
- enableManualMCP: false,
192
- message: "ProviderRegistry configured successfully with security settings",
193
- });
194
- }
195
- catch (error) {
196
- const registrySetupErrorTime = process.hrtime.bigint();
197
- const registrySetupDurationNs = registrySetupErrorTime - registrySetupStartTime;
198
- logger.error(`[NeuroLink] ❌ LOG_POINT_C004_PROVIDER_REGISTRY_SETUP_ERROR`, {
199
- logPoint: "C004_PROVIDER_REGISTRY_SETUP_ERROR",
200
- constructorId,
201
- timestamp: new Date().toISOString(),
202
- elapsedMs: Date.now() - constructorStartTime,
203
- elapsedNs: (process.hrtime.bigint() - constructorHrTimeStart).toString(),
204
- registrySetupDurationNs: registrySetupDurationNs.toString(),
205
- registrySetupDurationMs: Number(registrySetupDurationNs) / NANOSECOND_TO_MS_DIVISOR,
206
- error: error instanceof Error ? error.message : String(error),
207
- errorName: error instanceof Error ? error.name : "UnknownError",
208
- errorStack: error instanceof Error ? error.stack : undefined,
209
- message: "ProviderRegistry setup failed - critical initialization error",
210
- });
211
- throw error;
212
- }
156
+ ProviderRegistry.setOptions({ enableManualMCP: false });
213
157
  }
214
158
  /**
215
159
  * Initialize conversation memory if enabled
@@ -217,21 +161,6 @@ export class NeuroLink {
217
161
  initializeConversationMemory(config, constructorId, constructorStartTime, constructorHrTimeStart) {
218
162
  if (config?.conversationMemory?.enabled) {
219
163
  const memoryInitStartTime = process.hrtime.bigint();
220
- logger.debug(`[NeuroLink] 🧠 LOG_POINT_C005_MEMORY_INIT_START`, {
221
- logPoint: "C005_MEMORY_INIT_START",
222
- constructorId,
223
- timestamp: new Date().toISOString(),
224
- elapsedMs: Date.now() - constructorStartTime,
225
- elapsedNs: (process.hrtime.bigint() - constructorHrTimeStart).toString(),
226
- memoryInitStartTimeNs: memoryInitStartTime.toString(),
227
- memoryConfig: {
228
- enabled: config.conversationMemory.enabled,
229
- maxSessions: config.conversationMemory.maxSessions,
230
- maxTurnsPerSession: config.conversationMemory.maxTurnsPerSession,
231
- keys: Object.keys(config.conversationMemory),
232
- },
233
- message: "Conversation memory initialization flag set for lazy loading",
234
- });
235
164
  // Store config for later use and set flag for lazy initialization
236
165
  this.conversationMemoryConfig = config;
237
166
  this.conversationMemoryNeedsInit = true;
@@ -274,24 +203,6 @@ export class NeuroLink {
274
203
  */
275
204
  initializeExternalServerManager(constructorId, constructorStartTime, constructorHrTimeStart) {
276
205
  const externalServerInitStartTime = process.hrtime.bigint();
277
- logger.debug(`[NeuroLink] 🌐 LOG_POINT_C009_EXTERNAL_SERVER_INIT_START`, {
278
- logPoint: "C009_EXTERNAL_SERVER_INIT_START",
279
- constructorId,
280
- timestamp: new Date().toISOString(),
281
- elapsedMs: Date.now() - constructorStartTime,
282
- elapsedNs: (process.hrtime.bigint() - constructorHrTimeStart).toString(),
283
- externalServerInitStartTimeNs: externalServerInitStartTime.toString(),
284
- serverManagerConfig: {
285
- maxServers: SERVER_CONFIG.MAX_MCP_SERVERS,
286
- defaultTimeout: MCP_TIMEOUTS.EXTERNAL_SERVER_STARTUP_MS,
287
- enableAutoRestart: true,
288
- enablePerformanceMonitoring: true,
289
- },
290
- registryIntegrationConfig: {
291
- enableMainRegistryIntegration: true,
292
- },
293
- message: "Starting external server manager initialization",
294
- });
295
206
  try {
296
207
  this.externalServerManager = new ExternalServerManager({
297
208
  maxServers: 20,
@@ -314,7 +225,7 @@ export class NeuroLink {
314
225
  hasExternalServerManager: !!this.externalServerManager,
315
226
  message: "External server manager initialized successfully",
316
227
  });
317
- this.setupExternalServerEventHandlers(constructorId, constructorStartTime, constructorHrTimeStart);
228
+ this.setupExternalServerEventHandlers(constructorId);
318
229
  }
319
230
  catch (error) {
320
231
  const externalServerInitErrorTime = process.hrtime.bigint();
@@ -338,17 +249,7 @@ export class NeuroLink {
338
249
  /**
339
250
  * Setup event handlers for external server manager
340
251
  */
341
- setupExternalServerEventHandlers(constructorId, constructorStartTime, constructorHrTimeStart) {
342
- const eventHandlerSetupStartTime = process.hrtime.bigint();
343
- logger.debug(`[NeuroLink] 🔗 LOG_POINT_C011_EVENT_HANDLER_SETUP_START`, {
344
- logPoint: "C011_EVENT_HANDLER_SETUP_START",
345
- constructorId,
346
- timestamp: new Date().toISOString(),
347
- elapsedMs: Date.now() - constructorStartTime,
348
- elapsedNs: (process.hrtime.bigint() - constructorHrTimeStart).toString(),
349
- eventHandlerSetupStartTimeNs: eventHandlerSetupStartTime.toString(),
350
- message: "Setting up external server event handlers",
351
- });
252
+ setupExternalServerEventHandlers(constructorId) {
352
253
  this.externalServerManager.on("connected", (event) => {
353
254
  logger.debug(`[NeuroLink] 🔗 EXTERNAL_SERVER_EVENT_CONNECTED`, {
354
255
  constructorId,
@@ -402,26 +303,6 @@ export class NeuroLink {
402
303
  this.emitter.emit("externalMCP:toolRemoved", event);
403
304
  this.unregisterExternalMCPToolFromRegistry(event.toolName);
404
305
  });
405
- const eventHandlerSetupEndTime = process.hrtime.bigint();
406
- const eventHandlerSetupDurationNs = eventHandlerSetupEndTime - eventHandlerSetupStartTime;
407
- logger.debug(`[NeuroLink] ✅ LOG_POINT_C012_EVENT_HANDLER_SETUP_SUCCESS`, {
408
- logPoint: "C012_EVENT_HANDLER_SETUP_SUCCESS",
409
- constructorId,
410
- timestamp: new Date().toISOString(),
411
- elapsedMs: Date.now() - constructorStartTime,
412
- elapsedNs: (process.hrtime.bigint() - constructorHrTimeStart).toString(),
413
- eventHandlerSetupDurationNs: eventHandlerSetupDurationNs.toString(),
414
- eventHandlerSetupDurationMs: Number(eventHandlerSetupDurationNs) / NANOSECOND_TO_MS_DIVISOR,
415
- eventHandlersCount: 5,
416
- eventHandlerTypes: [
417
- "connected",
418
- "disconnected",
419
- "failed",
420
- "toolDiscovered",
421
- "toolRemoved",
422
- ],
423
- message: "Event handlers set up successfully",
424
- });
425
306
  }
426
307
  /**
427
308
  * Log constructor completion with final state summary
@@ -429,7 +310,7 @@ export class NeuroLink {
429
310
  logConstructorComplete(constructorId, constructorStartTime, constructorHrTimeStart) {
430
311
  const constructorEndTime = process.hrtime.bigint();
431
312
  const constructorDurationNs = constructorEndTime - constructorHrTimeStart;
432
- logger.info(`[NeuroLink] 🏁 LOG_POINT_C014_CONSTRUCTOR_COMPLETE`, {
313
+ logger.debug(`🏁 LOG_POINT_C014_CONSTRUCTOR_COMPLETE`, {
433
314
  logPoint: "C014_CONSTRUCTOR_COMPLETE",
434
315
  constructorId,
435
316
  timestamp: new Date().toISOString(),
@@ -457,11 +338,6 @@ export class NeuroLink {
457
338
  const mcpInitId = `mcp-init-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
458
339
  const mcpInitStartTime = Date.now();
459
340
  const mcpInitHrTimeStart = process.hrtime.bigint();
460
- this.logMCPInitStart(mcpInitId, mcpInitStartTime, mcpInitHrTimeStart);
461
- if (this.mcpInitialized) {
462
- this.logMCPAlreadyInitialized(mcpInitId, mcpInitStartTime, mcpInitHrTimeStart);
463
- return;
464
- }
465
341
  const MemoryManager = await this.importPerformanceManager(mcpInitId, mcpInitStartTime, mcpInitHrTimeStart);
466
342
  const startMemory = MemoryManager
467
343
  ? MemoryManager.getMemoryUsageMB()
@@ -472,73 +348,31 @@ export class NeuroLink {
472
348
  this.logMCPInitComplete(startMemory, MemoryManager, mcpInitStartTime);
473
349
  }
474
350
  catch (error) {
351
+ const initializationTime = Date.now() - mcpInitStartTime;
352
+ const initializationTimeNs = process.hrtime.bigint() - mcpInitHrTimeStart;
475
353
  mcpLogger.warn("[NeuroLink] MCP initialization failed", {
354
+ mcpInitId,
476
355
  error: error instanceof Error ? error.message : String(error),
356
+ errorName: error instanceof Error ? error.name : "UnknownError",
357
+ errorStack: error instanceof Error ? error.stack : undefined,
358
+ initializationTime,
359
+ initializationTimeNs: initializationTimeNs.toString(),
360
+ initializationPhase: "performMCPInitialization",
361
+ memoryUsage: process.memoryUsage(),
362
+ timestamp: new Date().toISOString(),
363
+ gracefulDegradation: true,
477
364
  });
478
365
  // Continue without MCP - graceful degradation
479
366
  }
480
367
  }
481
- /**
482
- * Log MCP initialization start
483
- */
484
- logMCPInitStart(mcpInitId, mcpInitStartTime, mcpInitHrTimeStart) {
485
- logger.debug(`[NeuroLink] 🔧 LOG_POINT_M001_MCP_INIT_ENTRY`, {
486
- logPoint: "M001_MCP_INIT_ENTRY",
487
- mcpInitId,
488
- timestamp: new Date().toISOString(),
489
- mcpInitStartTime,
490
- mcpInitHrTimeStart: mcpInitHrTimeStart.toString(),
491
- mcpInitialized: this.mcpInitialized,
492
- hasExternalServerManager: !!this.externalServerManager,
493
- memoryUsage: process.memoryUsage(),
494
- cpuUsage: process.cpuUsage(),
495
- message: "MCP initialization entry point - checking if already initialized",
496
- });
497
- }
498
- /**
499
- * Log MCP already initialized
500
- */
501
- logMCPAlreadyInitialized(mcpInitId, mcpInitStartTime, mcpInitHrTimeStart) {
502
- logger.debug(`[NeuroLink] ✅ LOG_POINT_M002_MCP_ALREADY_INITIALIZED`, {
503
- logPoint: "M002_MCP_ALREADY_INITIALIZED",
504
- mcpInitId,
505
- timestamp: new Date().toISOString(),
506
- elapsedMs: Date.now() - mcpInitStartTime,
507
- elapsedNs: (process.hrtime.bigint() - mcpInitHrTimeStart).toString(),
508
- mcpInitialized: this.mcpInitialized,
509
- message: "MCP already initialized - skipping initialization",
510
- });
511
- }
512
368
  /**
513
369
  * Import performance manager with error handling
514
370
  */
515
371
  async importPerformanceManager(mcpInitId, mcpInitStartTime, mcpInitHrTimeStart) {
516
372
  const performanceImportStartTime = process.hrtime.bigint();
517
- logger.debug(`[NeuroLink] 📊 LOG_POINT_M003_PERFORMANCE_IMPORT_START`, {
518
- logPoint: "M003_PERFORMANCE_IMPORT_START",
519
- mcpInitId,
520
- timestamp: new Date().toISOString(),
521
- elapsedMs: Date.now() - mcpInitStartTime,
522
- elapsedNs: (process.hrtime.bigint() - mcpInitHrTimeStart).toString(),
523
- performanceImportStartTimeNs: performanceImportStartTime.toString(),
524
- message: "Starting MemoryManager import for performance tracking",
525
- });
526
373
  try {
527
374
  const moduleImport = await import("./utils/performance.js");
528
375
  const MemoryManager = moduleImport.MemoryManager;
529
- const performanceImportEndTime = process.hrtime.bigint();
530
- const performanceImportDurationNs = performanceImportEndTime - performanceImportStartTime;
531
- logger.debug(`[NeuroLink] ✅ LOG_POINT_M004_PERFORMANCE_IMPORT_SUCCESS`, {
532
- logPoint: "M004_PERFORMANCE_IMPORT_SUCCESS",
533
- mcpInitId,
534
- timestamp: new Date().toISOString(),
535
- elapsedMs: Date.now() - mcpInitStartTime,
536
- elapsedNs: (process.hrtime.bigint() - mcpInitHrTimeStart).toString(),
537
- performanceImportDurationNs: performanceImportDurationNs.toString(),
538
- performanceImportDurationMs: Number(performanceImportDurationNs) / NANOSECOND_TO_MS_DIVISOR,
539
- hasMemoryManager: !!MemoryManager,
540
- message: "MemoryManager imported successfully",
541
- });
542
376
  return MemoryManager;
543
377
  }
544
378
  catch (error) {
@@ -573,107 +407,44 @@ export class NeuroLink {
573
407
  message: "Starting isolated MCP initialization process",
574
408
  });
575
409
  mcpLogger.debug("[NeuroLink] Starting isolated MCP initialization...");
576
- await this.initializeToolRegistryInternal(mcpInitId, mcpInitStartTime, mcpInitHrTimeStart);
577
- await this.initializeProviderRegistryInternal(mcpInitId, mcpInitStartTime, mcpInitHrTimeStart);
410
+ await this.initializeToolRegistryInternal();
411
+ await this.initializeProviderRegistryInternal();
578
412
  await this.registerDirectToolsServerInternal(mcpInitId, mcpInitStartTime, mcpInitHrTimeStart);
579
- await this.loadMCPConfigurationInternal(mcpInitId, mcpInitStartTime, mcpInitHrTimeStart);
413
+ await this.loadMCPConfigurationInternal();
580
414
  }
581
415
  /**
582
416
  * Initialize tool registry with timeout protection
583
417
  */
584
- async initializeToolRegistryInternal(mcpInitId, mcpInitStartTime, mcpInitHrTimeStart) {
585
- const toolRegistryStartTime = process.hrtime.bigint();
586
- const initTimeout = MCP_TIMEOUTS.INITIALIZATION_MS;
587
- logger.debug(`[NeuroLink] ⏱️ LOG_POINT_M007_TOOL_REGISTRY_TIMEOUT_SETUP`, {
588
- logPoint: "M007_TOOL_REGISTRY_TIMEOUT_SETUP",
589
- mcpInitId,
590
- timestamp: new Date().toISOString(),
591
- elapsedMs: Date.now() - mcpInitStartTime,
592
- elapsedNs: (process.hrtime.bigint() - mcpInitHrTimeStart).toString(),
593
- toolRegistryStartTimeNs: toolRegistryStartTime.toString(),
594
- initTimeoutMs: initTimeout,
595
- message: "Setting up tool registry initialization with timeout protection",
596
- });
418
+ async initializeToolRegistryInternal() {
419
+ const initTimeout = 3000;
597
420
  await Promise.race([
598
421
  Promise.resolve(),
599
422
  new Promise((_, reject) => {
600
423
  setTimeout(() => reject(new Error("MCP initialization timeout")), initTimeout);
601
424
  }),
602
425
  ]);
603
- const toolRegistryEndTime = process.hrtime.bigint();
604
- const toolRegistryDurationNs = toolRegistryEndTime - toolRegistryStartTime;
605
- logger.debug(`[NeuroLink] ✅ LOG_POINT_M008_TOOL_REGISTRY_SUCCESS`, {
606
- logPoint: "M008_TOOL_REGISTRY_SUCCESS",
607
- mcpInitId,
608
- timestamp: new Date().toISOString(),
609
- elapsedMs: Date.now() - mcpInitStartTime,
610
- elapsedNs: (process.hrtime.bigint() - mcpInitHrTimeStart).toString(),
611
- toolRegistryDurationNs: toolRegistryDurationNs.toString(),
612
- toolRegistryDurationMs: Number(toolRegistryDurationNs) / NANOSECOND_TO_MS_DIVISOR,
613
- message: "Tool registry initialization completed within timeout",
614
- });
615
426
  }
616
427
  /**
617
428
  * Initialize provider registry
618
429
  */
619
- async initializeProviderRegistryInternal(mcpInitId, mcpInitStartTime, mcpInitHrTimeStart) {
620
- const providerRegistryStartTime = process.hrtime.bigint();
621
- logger.debug(`[NeuroLink] 🏭 LOG_POINT_M009_PROVIDER_REGISTRY_START`, {
622
- logPoint: "M009_PROVIDER_REGISTRY_START",
623
- mcpInitId,
624
- timestamp: new Date().toISOString(),
625
- elapsedMs: Date.now() - mcpInitStartTime,
626
- elapsedNs: (process.hrtime.bigint() - mcpInitHrTimeStart).toString(),
627
- providerRegistryStartTimeNs: providerRegistryStartTime.toString(),
628
- message: "Starting provider registry registration with lazy loading",
629
- });
430
+ async initializeProviderRegistryInternal() {
630
431
  await ProviderRegistry.registerAllProviders();
631
- const providerRegistryEndTime = process.hrtime.bigint();
632
- const providerRegistryDurationNs = providerRegistryEndTime - providerRegistryStartTime;
633
- logger.debug(`[NeuroLink] ✅ LOG_POINT_M010_PROVIDER_REGISTRY_SUCCESS`, {
634
- logPoint: "M010_PROVIDER_REGISTRY_SUCCESS",
635
- mcpInitId,
636
- timestamp: new Date().toISOString(),
637
- elapsedMs: Date.now() - mcpInitStartTime,
638
- elapsedNs: (process.hrtime.bigint() - mcpInitHrTimeStart).toString(),
639
- providerRegistryDurationNs: providerRegistryDurationNs.toString(),
640
- providerRegistryDurationMs: Number(providerRegistryDurationNs) / NANOSECOND_TO_MS_DIVISOR,
641
- message: "Provider registry registration completed successfully",
642
- });
643
432
  }
644
433
  /**
645
434
  * Register direct tools server
646
435
  */
647
436
  async registerDirectToolsServerInternal(mcpInitId, mcpInitStartTime, mcpInitHrTimeStart) {
648
437
  const directToolsStartTime = process.hrtime.bigint();
649
- logger.debug(`[NeuroLink] 🛠️ LOG_POINT_M011_DIRECT_TOOLS_START`, {
650
- logPoint: "M011_DIRECT_TOOLS_START",
651
- mcpInitId,
652
- timestamp: new Date().toISOString(),
653
- elapsedMs: Date.now() - mcpInitStartTime,
654
- elapsedNs: (process.hrtime.bigint() - mcpInitHrTimeStart).toString(),
655
- directToolsStartTimeNs: directToolsStartTime.toString(),
656
- serverId: "neurolink-direct",
657
- message: "Starting direct tools server registration",
658
- });
659
438
  try {
660
- await toolRegistry.registerServer("neurolink-direct", directToolsServer);
661
- const directToolsSuccessTime = process.hrtime.bigint();
662
- const directToolsDurationNs = directToolsSuccessTime - directToolsStartTime;
663
- logger.debug(`[NeuroLink] ✅ LOG_POINT_M012_DIRECT_TOOLS_SUCCESS`, {
664
- logPoint: "M012_DIRECT_TOOLS_SUCCESS",
665
- mcpInitId,
666
- timestamp: new Date().toISOString(),
667
- elapsedMs: Date.now() - mcpInitStartTime,
668
- elapsedNs: (process.hrtime.bigint() - mcpInitHrTimeStart).toString(),
669
- directToolsDurationNs: directToolsDurationNs.toString(),
670
- directToolsDurationMs: Number(directToolsDurationNs) / NANOSECOND_TO_MS_DIVISOR,
671
- serverId: "neurolink-direct",
672
- message: "Direct tools server registered successfully",
673
- });
674
- mcpLogger.debug("[NeuroLink] Direct tools server registered successfully", {
675
- serverId: "neurolink-direct",
676
- });
439
+ if (process.env.NEUROLINK_DISABLE_DIRECT_TOOLS === "true") {
440
+ mcpLogger.debug("Direct tools server are disabled via environment variable.");
441
+ }
442
+ else {
443
+ await toolRegistry.registerServer("neurolink-direct", directToolsServer);
444
+ mcpLogger.debug("[NeuroLink] Direct tools server registered successfully", {
445
+ serverId: "neurolink-direct",
446
+ });
447
+ }
677
448
  }
678
449
  catch (error) {
679
450
  const directToolsErrorTime = process.hrtime.bigint();
@@ -700,42 +471,10 @@ export class NeuroLink {
700
471
  /**
701
472
  * Load MCP configuration from .mcp-config.json with parallel loading for improved performance
702
473
  */
703
- async loadMCPConfigurationInternal(mcpInitId, mcpInitStartTime, mcpInitHrTimeStart) {
704
- const mcpConfigStartTime = process.hrtime.bigint();
705
- logger.debug(`[NeuroLink] 📄 LOG_POINT_M014_MCP_CONFIG_START`, {
706
- logPoint: "M014_MCP_CONFIG_START",
707
- mcpInitId,
708
- timestamp: new Date().toISOString(),
709
- elapsedMs: Date.now() - mcpInitStartTime,
710
- elapsedNs: (process.hrtime.bigint() - mcpInitHrTimeStart).toString(),
711
- mcpConfigStartTimeNs: mcpConfigStartTime.toString(),
712
- hasExternalServerManager: !!this.externalServerManager,
713
- message: "Starting MCP configuration loading from .mcp-config.json",
714
- });
474
+ async loadMCPConfigurationInternal() {
715
475
  try {
716
476
  const configResult = await this.externalServerManager.loadMCPConfiguration(undefined, // Use default config path
717
477
  { parallel: true });
718
- const mcpConfigSuccessTime = process.hrtime.bigint();
719
- const mcpConfigDurationNs = mcpConfigSuccessTime - mcpConfigStartTime;
720
- logger.debug(`[NeuroLink] ✅ LOG_POINT_M015_MCP_CONFIG_SUCCESS`, {
721
- logPoint: "M015_MCP_CONFIG_SUCCESS",
722
- mcpInitId,
723
- timestamp: new Date().toISOString(),
724
- elapsedMs: Date.now() - mcpInitStartTime,
725
- elapsedNs: (process.hrtime.bigint() - mcpInitHrTimeStart).toString(),
726
- mcpConfigDurationNs: mcpConfigDurationNs.toString(),
727
- mcpConfigDurationMs: Number(mcpConfigDurationNs) / NANOSECOND_TO_MS_DIVISOR,
728
- serversLoaded: configResult.serversLoaded,
729
- errorsCount: configResult.errors.length,
730
- configResult: {
731
- serversLoaded: configResult.serversLoaded,
732
- errors: configResult.errors.map((err) => ({
733
- message: err instanceof Error ? err.message : String(err),
734
- name: err instanceof Error ? err.name : "UnknownError",
735
- })),
736
- },
737
- message: "MCP configuration loaded successfully",
738
- });
739
478
  mcpLogger.debug("[NeuroLink] MCP configuration loaded successfully", {
740
479
  serversLoaded: configResult.serversLoaded,
741
480
  errors: configResult.errors.length,
@@ -821,8 +560,20 @@ export class NeuroLink {
821
560
  });
822
561
  return {}; // Return empty object to preserve existing fallback behavior
823
562
  }
824
- const { models } = await response.json();
825
- const modelIsAvailable = models.some((m) => m.name === (route.model || "llama3.2:latest"));
563
+ const responseData = await response.json();
564
+ const models = responseData?.models;
565
+ // Runtime-safe guard: ensure models is an array with valid objects
566
+ if (!Array.isArray(models)) {
567
+ logger.warn("Ollama API returned invalid models format", {
568
+ responseData,
569
+ modelsType: typeof models,
570
+ });
571
+ return {}; // Return empty object for fallback behavior
572
+ }
573
+ // Filter and validate models before comparison
574
+ const validModels = models.filter((m) => m && typeof m === "object" && typeof m.name === "string");
575
+ const targetModel = route.model || "llama3.2:latest";
576
+ const modelIsAvailable = validModels.some((m) => m.name === targetModel);
826
577
  if (!modelIsAvailable) {
827
578
  logger.debug("Orchestration provider validation failed", {
828
579
  taskType: classification.type,
@@ -839,7 +590,9 @@ export class NeuroLink {
839
590
  taskType: classification.type,
840
591
  routedProvider: route.provider,
841
592
  routedModel: route.model,
842
- reason: error instanceof Error ? error.message : "Ollama service check failed",
593
+ reason: error instanceof Error
594
+ ? error.message
595
+ : "Ollama service check failed",
843
596
  orchestrationTime: `${Date.now() - startTime}ms`,
844
597
  });
845
598
  return {}; // Return empty object to preserve existing fallback behavior
@@ -921,8 +674,20 @@ export class NeuroLink {
921
674
  });
922
675
  return {}; // Return empty object to preserve existing fallback behavior
923
676
  }
924
- const { models } = await response.json();
925
- const modelIsAvailable = models.some((m) => m.name === (route.model || "llama3.2:latest"));
677
+ const responseData = await response.json();
678
+ const models = responseData?.models;
679
+ // Runtime-safe guard: ensure models is an array with valid objects
680
+ if (!Array.isArray(models)) {
681
+ logger.warn("Ollama API returned invalid models format in stream", {
682
+ responseData,
683
+ modelsType: typeof models,
684
+ });
685
+ return {}; // Return empty object for fallback behavior
686
+ }
687
+ // Filter and validate models before comparison
688
+ const validModels = models.filter((m) => m && typeof m === "object" && typeof m.name === "string");
689
+ const targetModel = route.model || "llama3.2:latest";
690
+ const modelIsAvailable = validModels.some((m) => m.name === targetModel);
926
691
  if (!modelIsAvailable) {
927
692
  logger.debug("Stream orchestration provider validation failed", {
928
693
  taskType: classification.type,
@@ -939,7 +704,9 @@ export class NeuroLink {
939
704
  taskType: classification.type,
940
705
  routedProvider: route.provider,
941
706
  routedModel: route.model,
942
- reason: error instanceof Error ? error.message : "Ollama service check failed",
707
+ reason: error instanceof Error
708
+ ? error.message
709
+ : "Ollama service check failed",
943
710
  orchestrationTime: `${Date.now() - startTime}ms`,
944
711
  });
945
712
  return {}; // Return empty object to preserve existing fallback behavior
@@ -1238,54 +1005,6 @@ export class NeuroLink {
1238
1005
  * Log generateTextInternal start with comprehensive analysis
1239
1006
  */
1240
1007
  logGenerateTextInternalStart(generateInternalId, generateInternalStartTime, generateInternalHrTimeStart, options, functionTag) {
1241
- logger.debug(`[NeuroLink] 🎯 LOG_POINT_G001_GENERATE_INTERNAL_START`, {
1242
- logPoint: "G001_GENERATE_INTERNAL_START",
1243
- generateInternalId,
1244
- timestamp: new Date().toISOString(),
1245
- generateInternalStartTime,
1246
- generateInternalHrTimeStart: generateInternalHrTimeStart.toString(),
1247
- inputAnalysis: {
1248
- provider: options.provider || "auto",
1249
- providerType: typeof options.provider,
1250
- isAutoProvider: options.provider === "auto" || !options.provider,
1251
- model: options.model || "NOT_SET",
1252
- modelType: typeof options.model,
1253
- temperature: options.temperature,
1254
- temperatureType: typeof options.temperature,
1255
- maxTokens: options.maxTokens,
1256
- maxTokensType: typeof options.maxTokens,
1257
- promptLength: options.prompt?.length || 0,
1258
- promptPreview: options.prompt?.substring(0, 200) || "NO_PROMPT",
1259
- hasSystemPrompt: !!options.systemPrompt,
1260
- systemPromptLength: options.systemPrompt?.length || 0,
1261
- disableTools: options.disableTools || false,
1262
- enableAnalytics: options.enableAnalytics || false,
1263
- enableEvaluation: options.enableEvaluation || false,
1264
- hasContext: !!options.context,
1265
- contextKeys: options.context ? Object.keys(options.context) : [],
1266
- evaluationDomain: options.evaluationDomain || "NOT_SET",
1267
- toolUsageContext: options.toolUsageContext || "NOT_SET",
1268
- },
1269
- instanceState: {
1270
- hasConversationMemory: !!this.conversationMemory,
1271
- conversationMemoryType: this.conversationMemory?.constructor?.name || "NOT_SET",
1272
- mcpInitialized: this.mcpInitialized,
1273
- hasProviderRegistry: !!AIProviderFactory,
1274
- providerRegistrySize: 0,
1275
- hasToolRegistry: !!toolRegistry,
1276
- toolRegistrySize: 0,
1277
- hasExternalServerManager: !!this.externalServerManager,
1278
- },
1279
- environmentContext: {
1280
- nodeVersion: process.version,
1281
- platform: process.platform,
1282
- arch: process.arch,
1283
- memoryUsage: process.memoryUsage(),
1284
- cpuUsage: process.cpuUsage(),
1285
- uptime: process.uptime(),
1286
- },
1287
- message: "Starting generateTextInternal with comprehensive input analysis",
1288
- });
1289
1008
  logger.debug(`[${functionTag}] Starting generation`, {
1290
1009
  provider: options.provider || "auto",
1291
1010
  promptLength: options.prompt?.length || 0,
@@ -1305,18 +1024,6 @@ export class NeuroLink {
1305
1024
  */
1306
1025
  async initializeConversationMemoryForGeneration(generateInternalId, generateInternalStartTime, generateInternalHrTimeStart) {
1307
1026
  const conversationMemoryStartTime = process.hrtime.bigint();
1308
- logger.debug(`[NeuroLink] 🧠 LOG_POINT_G002_CONVERSATION_MEMORY_CHECK`, {
1309
- logPoint: "G002_CONVERSATION_MEMORY_CHECK",
1310
- generateInternalId,
1311
- timestamp: new Date().toISOString(),
1312
- elapsedMs: Date.now() - generateInternalStartTime,
1313
- elapsedNs: (process.hrtime.bigint() - generateInternalHrTimeStart).toString(),
1314
- conversationMemoryStartTimeNs: conversationMemoryStartTime.toString(),
1315
- hasConversationMemory: !!this.conversationMemory,
1316
- needsLazyInit: this.conversationMemoryNeedsInit,
1317
- hasConfig: !!this.conversationMemoryConfig,
1318
- message: "Checking conversation memory initialization requirement",
1319
- });
1320
1027
  // Handle lazy initialization if needed
1321
1028
  if (this.conversationMemoryNeedsInit && this.conversationMemoryConfig) {
1322
1029
  await this.lazyInitializeConversationMemory(generateInternalId, generateInternalStartTime, generateInternalHrTimeStart);
@@ -1350,33 +1057,6 @@ export class NeuroLink {
1350
1057
  * Attempt MCP generation with retry logic
1351
1058
  */
1352
1059
  async attemptMCPGeneration(options, generateInternalId, generateInternalStartTime, generateInternalHrTimeStart, functionTag) {
1353
- const mcpDecisionStartTime = process.hrtime.bigint();
1354
- logger.debug(`[NeuroLink] 🔧 LOG_POINT_G005_MCP_DECISION_CHECK`, {
1355
- logPoint: "G005_MCP_DECISION_CHECK",
1356
- generateInternalId,
1357
- timestamp: new Date().toISOString(),
1358
- elapsedMs: Date.now() - generateInternalStartTime,
1359
- elapsedNs: (process.hrtime.bigint() - generateInternalHrTimeStart).toString(),
1360
- mcpDecisionStartTimeNs: mcpDecisionStartTime.toString(),
1361
- mcpDecisionFactors: {
1362
- disableTools: options.disableTools || false,
1363
- toolsEnabled: !options.disableTools,
1364
- mcpInitialized: this.mcpInitialized,
1365
- hasExternalServerManager: !!this.externalServerManager,
1366
- hasToolRegistry: !!toolRegistry,
1367
- toolRegistrySize: 0,
1368
- shouldTryMCP: !options.disableTools,
1369
- },
1370
- mcpReadinessAnalysis: {
1371
- mcpAvailable: !options.disableTools && this.mcpInitialized,
1372
- componentsReady: {
1373
- externalServerManager: !!this.externalServerManager,
1374
- toolRegistry: !!toolRegistry,
1375
- providerRegistry: !!AIProviderFactory,
1376
- },
1377
- },
1378
- message: "Analyzing MCP generation eligibility and readiness",
1379
- });
1380
1060
  if (!options.disableTools) {
1381
1061
  return await this.performMCPGenerationRetries(options, generateInternalId, generateInternalStartTime, generateInternalHrTimeStart, functionTag);
1382
1062
  }
@@ -1387,66 +1067,11 @@ export class NeuroLink {
1387
1067
  */
1388
1068
  async performMCPGenerationRetries(options, generateInternalId, generateInternalStartTime, generateInternalHrTimeStart, functionTag) {
1389
1069
  const maxMcpRetries = RETRY_ATTEMPTS.QUICK;
1390
- const mcpRetryLoopStartTime = process.hrtime.bigint();
1391
- logger.debug(`[NeuroLink] 🔄 LOG_POINT_G006_MCP_RETRY_LOOP_START`, {
1392
- logPoint: "G006_MCP_RETRY_LOOP_START",
1393
- generateInternalId,
1394
- timestamp: new Date().toISOString(),
1395
- elapsedMs: Date.now() - generateInternalStartTime,
1396
- elapsedNs: (process.hrtime.bigint() - generateInternalHrTimeStart).toString(),
1397
- mcpRetryLoopStartTimeNs: mcpRetryLoopStartTime.toString(),
1398
- maxMcpRetries,
1399
- totalPossibleAttempts: maxMcpRetries + 1,
1400
- message: "Starting MCP generation retry loop with failure tolerance",
1401
- });
1402
1070
  const maxAttempts = maxMcpRetries + 1;
1403
1071
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
1404
1072
  try {
1405
- const mcpAttemptStartTime = process.hrtime.bigint();
1406
- logger.debug(`[NeuroLink] 🎯 LOG_POINT_G007_MCP_ATTEMPT_START`, {
1407
- logPoint: "G007_MCP_ATTEMPT_START",
1408
- generateInternalId,
1409
- timestamp: new Date().toISOString(),
1410
- elapsedMs: Date.now() - generateInternalStartTime,
1411
- elapsedNs: (process.hrtime.bigint() - generateInternalHrTimeStart).toString(),
1412
- mcpAttemptStartTimeNs: mcpAttemptStartTime.toString(),
1413
- currentAttempt: attempt,
1414
- maxAttempts,
1415
- isFirstAttempt: attempt === 1,
1416
- isLastAttempt: attempt === maxAttempts,
1417
- attemptType: attempt === 1 ? "INITIAL" : "RETRY",
1418
- message: `Attempting MCP generation (attempt ${attempt}/${maxAttempts})`,
1419
- });
1420
1073
  logger.debug(`[${functionTag}] Attempting MCP generation (attempt ${attempt}/${maxAttempts})...`);
1421
1074
  const mcpResult = await this.tryMCPGeneration(options);
1422
- const mcpAttemptEndTime = process.hrtime.bigint();
1423
- const mcpAttemptDurationNs = mcpAttemptEndTime - mcpAttemptStartTime;
1424
- logger.debug(`[NeuroLink] 📊 LOG_POINT_G008_MCP_ATTEMPT_RESULT`, {
1425
- logPoint: "G008_MCP_ATTEMPT_RESULT",
1426
- generateInternalId,
1427
- timestamp: new Date().toISOString(),
1428
- elapsedMs: Date.now() - generateInternalStartTime,
1429
- elapsedNs: (process.hrtime.bigint() - generateInternalHrTimeStart).toString(),
1430
- mcpAttemptDurationNs: mcpAttemptDurationNs.toString(),
1431
- mcpAttemptDurationMs: Number(mcpAttemptDurationNs) / NANOSECOND_TO_MS_DIVISOR,
1432
- currentAttempt: attempt,
1433
- resultAnalysis: {
1434
- hasResult: !!mcpResult,
1435
- resultType: typeof mcpResult,
1436
- hasContent: !!(mcpResult && mcpResult.content),
1437
- contentLength: mcpResult?.content?.length || 0,
1438
- contentPreview: mcpResult?.content?.substring(0, 200) || "NO_CONTENT",
1439
- hasToolExecutions: !!(mcpResult &&
1440
- mcpResult.toolExecutions &&
1441
- mcpResult.toolExecutions.length > 0),
1442
- toolExecutionsCount: mcpResult?.toolExecutions?.length || 0,
1443
- toolsUsedCount: mcpResult?.toolsUsed?.length || 0,
1444
- provider: mcpResult?.provider || "NOT_SET",
1445
- responseTime: mcpResult?.responseTime || 0,
1446
- enhancedWithTools: mcpResult?.enhancedWithTools || false,
1447
- },
1448
- message: `MCP generation attempt ${attempt} completed - analyzing result`,
1449
- });
1450
1075
  if (mcpResult &&
1451
1076
  (mcpResult.content ||
1452
1077
  (mcpResult.toolExecutions && mcpResult.toolExecutions.length > 0))) {
@@ -1489,69 +1114,9 @@ export class NeuroLink {
1489
1114
  const tryMCPStartTime = Date.now();
1490
1115
  const tryMCPHrTimeStart = process.hrtime.bigint();
1491
1116
  const functionTag = "NeuroLink.tryMCPGeneration";
1492
- logger.debug(`[NeuroLink] 🚀 LOG_POINT_T001_TRY_MCP_START`, {
1493
- logPoint: "T001_TRY_MCP_START",
1494
- tryMCPId,
1495
- timestamp: new Date().toISOString(),
1496
- tryMCPStartTime,
1497
- tryMCPHrTimeStart: tryMCPHrTimeStart.toString(),
1498
- // 📊 Input options analysis
1499
- optionsAnalysis: {
1500
- provider: options.provider || "auto",
1501
- isAutoProvider: options.provider === "auto" || !options.provider,
1502
- model: options.model || "NOT_SET",
1503
- promptLength: options.prompt?.length || 0,
1504
- promptPreview: options.prompt?.substring(0, 150) || "NO_PROMPT",
1505
- hasSystemPrompt: !!options.systemPrompt,
1506
- systemPromptLength: options.systemPrompt?.length || 0,
1507
- disableTools: options.disableTools || false,
1508
- enableAnalytics: options.enableAnalytics || false,
1509
- temperature: options.temperature,
1510
- maxTokens: options.maxTokens,
1511
- },
1512
- // 🔧 MCP state analysis
1513
- mcpStateAnalysis: {
1514
- mcpInitialized: this.mcpInitialized,
1515
- hasExternalServerManager: !!this.externalServerManager,
1516
- hasToolRegistry: !!toolRegistry,
1517
- toolRegistrySize: 0, // Not accessible as size property
1518
- hasProviderRegistry: !!AIProviderFactory,
1519
- providerRegistrySize: 0, // Not accessible as size property
1520
- },
1521
- message: "Starting MCP-enhanced generation attempt with comprehensive analysis",
1522
- });
1523
1117
  try {
1524
- // 🚀 EXHAUSTIVE LOGGING POINT T002: MCP INITIALIZATION CHECK
1525
- const mcpInitCheckStartTime = process.hrtime.bigint();
1526
- logger.debug(`[NeuroLink] 🔧 LOG_POINT_T002_MCP_INIT_CHECK`, {
1527
- logPoint: "T002_MCP_INIT_CHECK",
1528
- tryMCPId,
1529
- timestamp: new Date().toISOString(),
1530
- elapsedMs: Date.now() - tryMCPStartTime,
1531
- elapsedNs: (process.hrtime.bigint() - tryMCPHrTimeStart).toString(),
1532
- mcpInitCheckStartTimeNs: mcpInitCheckStartTime.toString(),
1533
- mcpInitializedBefore: this.mcpInitialized,
1534
- needsInitialization: !this.mcpInitialized,
1535
- message: "Checking MCP initialization status before generation",
1536
- });
1537
- // Initialize MCP only when tools are enabled
1538
- if (!options.disableTools) {
1539
- await this.initializeMCP();
1540
- }
1541
- const mcpInitCheckEndTime = process.hrtime.bigint();
1542
- const mcpInitCheckDurationNs = mcpInitCheckEndTime - mcpInitCheckStartTime;
1543
- logger.debug(`[NeuroLink] ✅ LOG_POINT_T003_MCP_INIT_CHECK_COMPLETE`, {
1544
- logPoint: "T003_MCP_INIT_CHECK_COMPLETE",
1545
- tryMCPId,
1546
- timestamp: new Date().toISOString(),
1547
- elapsedMs: Date.now() - tryMCPStartTime,
1548
- elapsedNs: (process.hrtime.bigint() - tryMCPHrTimeStart).toString(),
1549
- mcpInitCheckDurationNs: mcpInitCheckDurationNs.toString(),
1550
- mcpInitCheckDurationMs: Number(mcpInitCheckDurationNs) / NANOSECOND_TO_MS_DIVISOR,
1551
- mcpInitializedAfter: this.mcpInitialized,
1552
- initializationSuccessful: this.mcpInitialized,
1553
- message: "MCP initialization check completed",
1554
- });
1118
+ // Initialize MCP if needed
1119
+ await this.initializeMCP();
1555
1120
  if (!this.mcpInitialized) {
1556
1121
  logger.warn(`[NeuroLink] ⚠️ LOG_POINT_T004_MCP_NOT_AVAILABLE`, {
1557
1122
  logPoint: "T004_MCP_NOT_AVAILABLE",
@@ -1577,8 +1142,27 @@ export class NeuroLink {
1577
1142
  : options.provider;
1578
1143
  // Get available tools
1579
1144
  const availableTools = await this.getAllAvailableTools();
1145
+ const targetTool = availableTools.find((t) => t.name.includes("SuccessRateSRByTime") ||
1146
+ t.name.includes("juspay-analytics"));
1147
+ logger.debug("Available tools for AI prompt generation", {
1148
+ toolsCount: availableTools.length,
1149
+ toolNames: availableTools.map((t) => t.name),
1150
+ hasTargetTool: !!targetTool,
1151
+ targetToolDetails: targetTool
1152
+ ? {
1153
+ name: targetTool.name,
1154
+ description: targetTool.description,
1155
+ server: targetTool.server,
1156
+ }
1157
+ : null,
1158
+ });
1580
1159
  // Create tool-aware system prompt
1581
1160
  const enhancedSystemPrompt = this.createToolAwareSystemPrompt(options.systemPrompt, availableTools);
1161
+ logger.debug("Tool-aware system prompt created", {
1162
+ originalPromptLength: options.systemPrompt?.length || 0,
1163
+ enhancedPromptLength: enhancedSystemPrompt.length,
1164
+ enhancedPromptPreview: enhancedSystemPrompt.substring(0, 500) + "...",
1165
+ });
1582
1166
  // Get conversation messages for context
1583
1167
  const conversationMessages = await getConversationMessages(this.conversationMemory, options);
1584
1168
  // Create provider and generate
@@ -1664,12 +1248,18 @@ export class NeuroLink {
1664
1248
  ];
1665
1249
  const requestedProvider = options.provider === "auto" ? undefined : options.provider;
1666
1250
  // Check for orchestrated preferred provider in context
1667
- const preferredOrchestrated = options.context && typeof options.context === 'object' && '__orchestratedPreferredProvider' in options.context
1668
- ? options.context.__orchestratedPreferredProvider
1251
+ const preferredOrchestrated = options.context &&
1252
+ typeof options.context === "object" &&
1253
+ "__orchestratedPreferredProvider" in options.context
1254
+ ? options.context
1255
+ .__orchestratedPreferredProvider
1669
1256
  : undefined;
1670
1257
  // Build provider list with orchestrated preference first, then fallback to full list
1671
1258
  const tryProviders = preferredOrchestrated
1672
- ? [preferredOrchestrated, ...providerPriority.filter((p) => p !== preferredOrchestrated)]
1259
+ ? [
1260
+ preferredOrchestrated,
1261
+ ...providerPriority.filter((p) => p !== preferredOrchestrated),
1262
+ ]
1673
1263
  : requestedProvider
1674
1264
  ? [requestedProvider]
1675
1265
  : providerPriority;
@@ -1741,7 +1331,15 @@ export class NeuroLink {
1741
1331
  * Create tool-aware system prompt that informs AI about available tools
1742
1332
  */
1743
1333
  createToolAwareSystemPrompt(originalSystemPrompt, availableTools) {
1334
+ // AI prompt generation with tool analysis and structured logging
1335
+ const promptGenerationData = {
1336
+ originalPromptLength: originalSystemPrompt?.length || 0,
1337
+ availableToolsCount: availableTools.length,
1338
+ hasOriginalPrompt: !!originalSystemPrompt,
1339
+ };
1340
+ logger.debug("AI prompt generation with tool schemas", promptGenerationData);
1744
1341
  if (availableTools.length === 0) {
1342
+ logger.debug("No tools available - returning original prompt");
1745
1343
  return originalSystemPrompt || "";
1746
1344
  }
1747
1345
  const toolDescriptions = transformToolsToDescriptions(availableTools.map((t) => ({
@@ -1750,8 +1348,22 @@ export class NeuroLink {
1750
1348
  server: t.serverId ?? "unknown",
1751
1349
  inputSchema: t.inputSchema,
1752
1350
  })));
1351
+ const transformationResult = {
1352
+ toolDescriptionsLength: toolDescriptions.length,
1353
+ toolDescriptionsCharCount: toolDescriptions.length,
1354
+ hasDescriptions: toolDescriptions.length > 0,
1355
+ };
1356
+ logger.debug("Tool descriptions transformation completed", transformationResult);
1753
1357
  const toolPrompt = `\n\nYou have access to these additional tools if needed:\n${toolDescriptions}\n\nIMPORTANT: You are a general-purpose AI assistant. Answer all requests directly and creatively. These tools are optional helpers - use them only when they would genuinely improve your response. For creative tasks like storytelling, writing, or general conversation, respond naturally without requiring tools.`;
1754
- return (originalSystemPrompt || "") + toolPrompt;
1358
+ const finalPrompt = (originalSystemPrompt || "") + toolPrompt;
1359
+ const finalPromptData = {
1360
+ originalPromptLength: originalSystemPrompt?.length || 0,
1361
+ toolPromptLength: toolPrompt.length,
1362
+ finalPromptLength: finalPrompt.length,
1363
+ promptEnhanced: toolPrompt.length > 0,
1364
+ };
1365
+ logger.debug("AI prompt generation completed", finalPromptData);
1366
+ return finalPrompt;
1755
1367
  }
1756
1368
  /**
1757
1369
  * Execute tools if available through centralized registry
@@ -1772,31 +1384,6 @@ export class NeuroLink {
1772
1384
  return { toolResults: [], enhancedPrompt: prompt };
1773
1385
  }
1774
1386
  }
1775
- /**
1776
- * Enhance prompt with tool results (domain-agnostic)
1777
- */
1778
- enhancePromptWithToolResults(prompt, toolResults) {
1779
- if (toolResults.length === 0) {
1780
- return prompt;
1781
- }
1782
- let enhancedPrompt = prompt;
1783
- for (const result of toolResults) {
1784
- if (result && typeof result === "object") {
1785
- enhancedPrompt += `\n\nTool Results:\n`;
1786
- // Handle structured result generically
1787
- try {
1788
- const resultStr = typeof result === "string"
1789
- ? result
1790
- : JSON.stringify(result, null, 2);
1791
- enhancedPrompt += resultStr + "\n";
1792
- }
1793
- catch {
1794
- enhancedPrompt += "Tool execution completed\n";
1795
- }
1796
- }
1797
- }
1798
- return enhancedPrompt;
1799
- }
1800
1387
  /**
1801
1388
  * BACKWARD COMPATIBILITY: Legacy streamText method
1802
1389
  * Internally calls stream() and converts result format
@@ -1877,13 +1464,9 @@ export class NeuroLink {
1877
1464
  async stream(options) {
1878
1465
  const startTime = Date.now();
1879
1466
  const hrTimeStart = process.hrtime.bigint();
1880
- const functionTag = "NeuroLink.stream";
1881
1467
  const streamId = `neurolink-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
1882
- const journeyStartTime = new Date().toISOString();
1883
1468
  const originalPrompt = options.input.text; // Store the original prompt for memory storage
1884
- this.logStreamEntryPoint(streamId, journeyStartTime, functionTag, startTime, hrTimeStart, options);
1885
- this.logPerformanceBaseline(streamId, startTime, hrTimeStart);
1886
- await this.validateStreamInput(options, streamId, startTime, hrTimeStart);
1469
+ await this.validateStreamInput(options);
1887
1470
  this.emitStreamStartEvents(options, startTime);
1888
1471
  let enhancedOptions;
1889
1472
  let factoryResult;
@@ -1976,89 +1559,13 @@ export class NeuroLink {
1976
1559
  return this.handleStreamError(error, options, startTime, streamId, undefined, undefined);
1977
1560
  }
1978
1561
  }
1979
- /**
1980
- * Log stream entry point with comprehensive analysis
1981
- */
1982
- logStreamEntryPoint(streamId, journeyStartTime, functionTag, startTime, hrTimeStart, options) {
1983
- logger.debug(`[NeuroLink] 🎯 LOG_POINT_001_STREAM_ENTRY_START`, {
1984
- logPoint: "001_STREAM_ENTRY_START",
1985
- streamId,
1986
- timestamp: journeyStartTime,
1987
- functionTag,
1988
- startTime,
1989
- hrTimeStart: hrTimeStart.toString(),
1990
- nodeVersion: process.version,
1991
- platform: process.platform,
1992
- arch: process.arch,
1993
- memoryUsage: process.memoryUsage(),
1994
- cpuUsage: process.cpuUsage(),
1995
- hasOptions: !!options,
1996
- optionsType: typeof options,
1997
- optionsKeys: options ? Object.keys(options) : [],
1998
- optionsSize: options ? JSON.stringify(options).length : 0,
1999
- hasInput: !!options?.input,
2000
- inputType: typeof options?.input,
2001
- inputKeys: options?.input ? Object.keys(options.input) : [],
2002
- hasInputText: !!options?.input?.text,
2003
- inputTextType: typeof options?.input?.text,
2004
- inputTextLength: options?.input?.text?.length || 0,
2005
- inputTextPreview: options?.input?.text?.substring(0, 200) || "NO_TEXT",
2006
- hasProvider: !!options?.provider,
2007
- providerValue: options?.provider || "NOT_SET",
2008
- isAutoProvider: options?.provider === "auto" || !options?.provider,
2009
- hasModel: !!options?.model,
2010
- modelValue: options?.model || "NOT_SET",
2011
- message: "EXHAUSTIVE NeuroLink main stream method entry point with comprehensive environment analysis",
2012
- });
2013
- }
2014
- /**
2015
- * Log performance baseline
2016
- */
2017
- logPerformanceBaseline(streamId, startTime, hrTimeStart) {
2018
- const memoryBaseline = process.memoryUsage();
2019
- const cpuBaseline = process.cpuUsage();
2020
- logger.debug(`[NeuroLink] 🎯 LOG_POINT_002_PERFORMANCE_BASELINE`, {
2021
- logPoint: "002_PERFORMANCE_BASELINE",
2022
- streamId,
2023
- timestamp: new Date().toISOString(),
2024
- elapsedMs: Date.now() - startTime,
2025
- elapsedNs: (process.hrtime.bigint() - hrTimeStart).toString(),
2026
- memoryBaseline: {
2027
- rss: memoryBaseline.rss,
2028
- heapTotal: memoryBaseline.heapTotal,
2029
- heapUsed: memoryBaseline.heapUsed,
2030
- external: memoryBaseline.external,
2031
- arrayBuffers: memoryBaseline.arrayBuffers,
2032
- },
2033
- cpuBaseline: {
2034
- user: cpuBaseline.user,
2035
- system: cpuBaseline.system,
2036
- },
2037
- gcStats: global.gc
2038
- ? (() => {
2039
- try {
2040
- global.gc();
2041
- return process.memoryUsage();
2042
- }
2043
- catch {
2044
- return null;
2045
- }
2046
- })()
2047
- : null,
2048
- message: "Performance baseline metrics captured for stream processing",
2049
- });
2050
- }
2051
1562
  /**
2052
1563
  * Validate stream input with comprehensive error reporting
2053
1564
  */
2054
- async validateStreamInput(options, streamId, startTime, hrTimeStart) {
1565
+ async validateStreamInput(options) {
2055
1566
  const validationStartTime = process.hrtime.bigint();
2056
1567
  logger.debug(`[NeuroLink] 🎯 LOG_POINT_003_VALIDATION_START`, {
2057
1568
  logPoint: "003_VALIDATION_START",
2058
- streamId,
2059
- timestamp: new Date().toISOString(),
2060
- elapsedMs: Date.now() - startTime,
2061
- elapsedNs: (process.hrtime.bigint() - hrTimeStart).toString(),
2062
1569
  validationStartTimeNs: validationStartTime.toString(),
2063
1570
  message: "Starting comprehensive input validation process",
2064
1571
  });
@@ -2069,38 +1576,8 @@ export class NeuroLink {
2069
1576
  options.input.audio.frames &&
2070
1577
  typeof options.input.audio.frames[Symbol.asyncIterator] !== "undefined");
2071
1578
  if (!hasText && !hasAudio) {
2072
- const validationFailTime = process.hrtime.bigint();
2073
- const validationDurationNs = validationFailTime - validationStartTime;
2074
- logger.debug(`[NeuroLink] 💥 LOG_POINT_005_VALIDATION_FAILED`, {
2075
- logPoint: "005_VALIDATION_FAILED",
2076
- streamId,
2077
- timestamp: new Date().toISOString(),
2078
- elapsedMs: Date.now() - startTime,
2079
- elapsedNs: (process.hrtime.bigint() - hrTimeStart).toString(),
2080
- validationDurationNs: validationDurationNs.toString(),
2081
- validationDurationMs: Number(validationDurationNs) / NANOSECOND_TO_MS_DIVISOR,
2082
- validationError: "Stream options must include either input.text or input.audio",
2083
- message: "EXHAUSTIVE validation failure analysis with character-level debugging",
2084
- });
2085
1579
  throw new Error("Stream options must include either input.text or input.audio");
2086
1580
  }
2087
- const validationSuccessTime = process.hrtime.bigint();
2088
- const validationDurationNs = validationSuccessTime - validationStartTime;
2089
- logger.debug(`[NeuroLink] ✅ LOG_POINT_006_VALIDATION_SUCCESS`, {
2090
- logPoint: "006_VALIDATION_SUCCESS",
2091
- streamId,
2092
- timestamp: new Date().toISOString(),
2093
- elapsedMs: Date.now() - startTime,
2094
- elapsedNs: (process.hrtime.bigint() - hrTimeStart).toString(),
2095
- validationDurationNs: validationDurationNs.toString(),
2096
- validationDurationMs: Number(validationDurationNs) / NANOSECOND_TO_MS_DIVISOR,
2097
- inputTextValid: hasText,
2098
- inputAudioPresent: hasAudio,
2099
- inputTextLength: hasText ? options.input.text.length : 0,
2100
- inputTextTrimmedLength: hasText ? options.input.text.trim().length : 0,
2101
- inputTextPreview: hasText ? options.input.text.substring(0, 100) : "",
2102
- message: "EXHAUSTIVE validation success - proceeding with stream processing",
2103
- });
2104
1581
  }
2105
1582
  /**
2106
1583
  * Emit stream start events
@@ -2435,6 +1912,134 @@ export class NeuroLink {
2435
1912
  return this.emitter;
2436
1913
  }
2437
1914
  // ========================================
1915
+ // ENHANCED: Tool Event Emission API
1916
+ // ========================================
1917
+ // TODO: Add ToolExecutionEvent utility methods in future version
1918
+ // Will provide structured event format for consistent tool event processing
1919
+ /**
1920
+ * Emit tool start event with execution tracking
1921
+ * @param toolName - Name of the tool being executed
1922
+ * @param input - Input parameters for the tool
1923
+ * @param startTime - Timestamp when execution started
1924
+ * @returns executionId for tracking this specific execution
1925
+ */
1926
+ emitToolStart(toolName, input, startTime = Date.now()) {
1927
+ const executionId = `${toolName}-${startTime}-${Math.random().toString(36).substr(2, 9)}`;
1928
+ // Create execution context for tracking
1929
+ const context = {
1930
+ executionId,
1931
+ tool: toolName,
1932
+ startTime,
1933
+ metadata: {
1934
+ inputType: typeof input,
1935
+ hasInput: input !== undefined && input !== null,
1936
+ },
1937
+ };
1938
+ // Store in active executions
1939
+ this.activeToolExecutions.set(executionId, context);
1940
+ this.currentStreamToolExecutions.push(context);
1941
+ // Emit event (NeuroLinkEvents format for compatibility)
1942
+ this.emitter.emit("tool:start", {
1943
+ tool: toolName,
1944
+ input,
1945
+ timestamp: startTime,
1946
+ executionId,
1947
+ });
1948
+ logger.debug(`tool:start emitted for ${toolName}`, {
1949
+ toolName,
1950
+ executionId,
1951
+ timestamp: startTime,
1952
+ inputProvided: input !== undefined,
1953
+ });
1954
+ return executionId;
1955
+ }
1956
+ /**
1957
+ * Emit tool end event with execution summary
1958
+ * @param toolName - Name of the tool that finished
1959
+ * @param result - Result from the tool execution
1960
+ * @param error - Error message if execution failed
1961
+ * @param startTime - When execution started
1962
+ * @param endTime - When execution finished
1963
+ * @param executionId - Optional execution ID for tracking
1964
+ */
1965
+ emitToolEnd(toolName, result, error, startTime, endTime = Date.now(), executionId) {
1966
+ const actualStartTime = startTime || endTime - 1000; // Fallback if no start time
1967
+ const duration = endTime - actualStartTime;
1968
+ const success = !error;
1969
+ // Find execution context or create fallback
1970
+ let context;
1971
+ if (executionId) {
1972
+ context = this.activeToolExecutions.get(executionId);
1973
+ }
1974
+ else {
1975
+ // Find by tool name (fallback for executions without ID tracking)
1976
+ context = Array.from(this.activeToolExecutions.values()).find((ctx) => ctx.tool === toolName && !ctx.endTime);
1977
+ }
1978
+ const finalExecutionId = executionId ||
1979
+ context?.executionId ||
1980
+ `${toolName}-${actualStartTime}-fallback-${Math.random().toString(36).substr(2, 9)}`;
1981
+ // Update execution context
1982
+ if (context) {
1983
+ context.endTime = endTime;
1984
+ context.result = result;
1985
+ context.error = error;
1986
+ this.activeToolExecutions.delete(context.executionId);
1987
+ }
1988
+ // Create execution summary
1989
+ const summary = {
1990
+ tool: toolName,
1991
+ startTime: actualStartTime,
1992
+ endTime,
1993
+ duration,
1994
+ success,
1995
+ result,
1996
+ error,
1997
+ executionId: finalExecutionId,
1998
+ metadata: {
1999
+ toolCategory: "custom", // Default, can be overridden
2000
+ },
2001
+ };
2002
+ // Store in history
2003
+ this.toolExecutionHistory.push(summary);
2004
+ // Emit event (NeuroLinkEvents format for compatibility)
2005
+ this.emitter.emit("tool:end", {
2006
+ tool: toolName,
2007
+ result,
2008
+ error,
2009
+ timestamp: endTime,
2010
+ duration,
2011
+ executionId: finalExecutionId,
2012
+ });
2013
+ logger.debug(`tool:end emitted for ${toolName}`, {
2014
+ toolName,
2015
+ executionId: finalExecutionId,
2016
+ duration,
2017
+ success,
2018
+ hasResult: result !== undefined,
2019
+ hasError: !!error,
2020
+ });
2021
+ }
2022
+ /**
2023
+ * Get current tool execution contexts for stream metadata
2024
+ */
2025
+ getCurrentToolExecutions() {
2026
+ return [...this.currentStreamToolExecutions];
2027
+ }
2028
+ /**
2029
+ * Get tool execution history
2030
+ */
2031
+ getToolExecutionHistory() {
2032
+ return [...this.toolExecutionHistory];
2033
+ }
2034
+ /**
2035
+ * Clear current stream tool executions (called at stream start)
2036
+ */
2037
+ clearCurrentStreamExecutions() {
2038
+ this.currentStreamToolExecutions = [];
2039
+ }
2040
+ // TODO: Add getToolExecutionEvents() method in future version
2041
+ // Will return properly formatted ToolExecutionEvent objects for structured event processing
2042
+ // ========================================
2438
2043
  // Tool Registration API
2439
2044
  // ========================================
2440
2045
  /**
@@ -2450,7 +2055,6 @@ export class NeuroLink {
2450
2055
  timestamp: Date.now(),
2451
2056
  });
2452
2057
  try {
2453
- // --- Start: Enhanced Validation Logic with FlexibleToolValidator ---
2454
2058
  if (!name || typeof name !== "string") {
2455
2059
  throw new Error("Invalid tool name");
2456
2060
  }
@@ -2460,41 +2064,42 @@ export class NeuroLink {
2460
2064
  if (typeof tool.execute !== "function") {
2461
2065
  throw new Error(`Tool '${name}' must have an execute method.`);
2462
2066
  }
2463
- // Use FlexibleToolValidator for consistent validation across SDK and toolRegistry
2464
- try {
2465
- const flexibleValidatorModule = require("./mcp/flexibleToolValidator.js");
2466
- const FlexibleToolValidator = flexibleValidatorModule.FlexibleToolValidator;
2467
- // Use the same validation logic as toolRegistry (static method)
2468
- const validationResult = FlexibleToolValidator.validateToolName(name);
2469
- if (!validationResult.isValid) {
2470
- throw new Error(`Tool validation failed: ${validationResult.error}`);
2471
- }
2067
+ if (name.trim() === "") {
2068
+ throw new Error("Tool name cannot be empty");
2472
2069
  }
2473
- catch (error) {
2474
- // If FlexibleToolValidator import fails, use basic safety checks
2475
- logger.warn("FlexibleToolValidator not available, using basic validation", {
2476
- error: error instanceof Error ? error.message : String(error),
2477
- });
2478
- // Basic safety checks to prevent obvious issues
2479
- if (name.trim() === "") {
2480
- throw new Error("Tool name cannot be empty");
2481
- }
2482
- if (name.length > 100) {
2483
- throw new Error("Tool name is too long (maximum 100 characters)");
2484
- }
2485
- // eslint-disable-next-line no-control-regex
2486
- if (/[\x00-\x1F\x7F]/.test(name)) {
2487
- throw new Error("Tool name contains invalid control characters");
2488
- }
2070
+ if (name.length > 100) {
2071
+ throw new Error("Tool name is too long (maximum 100 characters)");
2072
+ }
2073
+ // eslint-disable-next-line no-control-regex
2074
+ if (/[\x00-\x1F\x7F]/.test(name)) {
2075
+ throw new Error("Tool name contains invalid control characters");
2489
2076
  }
2490
- // --- End: Enhanced Validation Logic ---
2491
- // Tool object validation is now handled by FlexibleToolValidator above
2492
2077
  // Proceed with tool registration since validation passed
2078
+ // Convert tool to proper MCPExecutableTool format with schema conversion
2079
+ const convertedTool = {
2080
+ name: tool.name || name,
2081
+ description: tool.description || name,
2082
+ execute: tool.execute,
2083
+ inputSchema: (() => {
2084
+ // Check if tool has 'parameters' field (SDK SimpleTool format)
2085
+ if ("parameters" in tool && tool.parameters) {
2086
+ if (isZodSchema(tool.parameters)) {
2087
+ return tool.parameters;
2088
+ }
2089
+ // If it's already a JSON Schema object, return as-is
2090
+ if (typeof tool.parameters === "object") {
2091
+ return tool.parameters;
2092
+ }
2093
+ }
2094
+ // Fall back to existing inputSchema or empty object
2095
+ const fallbackSchema = tool.inputSchema || {};
2096
+ return fallbackSchema;
2097
+ })(),
2098
+ };
2493
2099
  // SMART DEFAULTS: Use utility to eliminate boilerplate creation
2494
- const mcpServerInfo = createCustomToolServerInfo(name, tool);
2100
+ const mcpServerInfo = createCustomToolServerInfo(name, convertedTool);
2495
2101
  // Register with toolRegistry using MCPServerInfo directly
2496
2102
  toolRegistry.registerServer(mcpServerInfo);
2497
- logger.info(`Registered custom tool: ${name}`);
2498
2103
  // Emit tool registration success event
2499
2104
  this.emitter.emit("tools-register:end", {
2500
2105
  toolName: name,
@@ -2507,6 +2112,36 @@ export class NeuroLink {
2507
2112
  throw error;
2508
2113
  }
2509
2114
  }
2115
+ /**
2116
+ * Set the context that will be passed to tools during execution
2117
+ * This context will be merged with any runtime context passed by the AI model
2118
+ * @param context - Context object containing session info, tokens, shop data, etc.
2119
+ */
2120
+ setToolContext(context) {
2121
+ this.toolExecutionContext = { ...context };
2122
+ logger.debug("Tool execution context updated", {
2123
+ sessionId: context.sessionId,
2124
+ contextKeys: Object.keys(context),
2125
+ hasJuspayToken: !!context.juspayToken,
2126
+ hasShopId: !!context.shopId,
2127
+ });
2128
+ }
2129
+ /**
2130
+ * Get the current tool execution context
2131
+ * @returns Current context or undefined if not set
2132
+ */
2133
+ getToolContext() {
2134
+ return this.toolExecutionContext
2135
+ ? { ...this.toolExecutionContext }
2136
+ : undefined;
2137
+ }
2138
+ /**
2139
+ * Clear the tool execution context
2140
+ */
2141
+ clearToolContext() {
2142
+ this.toolExecutionContext = undefined;
2143
+ logger.debug("Tool execution context cleared");
2144
+ }
2510
2145
  /**
2511
2146
  * Register multiple tools at once - Supports both object and array formats
2512
2147
  * @param tools - Object mapping tool names to MCPExecutableTool format OR Array of tools with names
@@ -2551,16 +2186,61 @@ export class NeuroLink {
2551
2186
  const customTools = toolRegistry.getToolsByCategory(detectCategory({ isCustomTool: true }));
2552
2187
  const toolMap = new Map();
2553
2188
  for (const tool of customTools) {
2189
+ const effectiveSchema = tool.inputSchema || tool.parameters;
2190
+ logger.debug(`Processing tool schema for Claude`, {
2191
+ toolName: tool.name,
2192
+ hasDescription: !!tool.description,
2193
+ description: tool.description,
2194
+ hasParameters: !!tool.parameters,
2195
+ parametersType: typeof tool.parameters,
2196
+ parametersKeys: tool.parameters && typeof tool.parameters === "object"
2197
+ ? Object.keys(tool.parameters)
2198
+ : "NOT_OBJECT",
2199
+ hasInputSchema: !!tool.inputSchema,
2200
+ inputSchemaType: typeof tool.inputSchema,
2201
+ inputSchemaKeys: tool.inputSchema && typeof tool.inputSchema === "object"
2202
+ ? Object.keys(tool.inputSchema)
2203
+ : "NOT_OBJECT",
2204
+ hasEffectiveSchema: !!effectiveSchema,
2205
+ effectiveSchemaType: typeof effectiveSchema,
2206
+ effectiveSchemaHasProperties: !!effectiveSchema?.properties,
2207
+ effectiveSchemaHasRequired: !!effectiveSchema?.required,
2208
+ originalInputSchema: tool.inputSchema,
2209
+ phase: "AFTER_SCHEMA_FIX",
2210
+ timestamp: Date.now(),
2211
+ });
2554
2212
  // Return MCPServerInfo.tools format directly - no conversion needed
2555
2213
  toolMap.set(tool.name, {
2556
2214
  name: tool.name,
2557
2215
  description: tool.description || "",
2558
- inputSchema: {},
2216
+ inputSchema: tool.inputSchema || tool.parameters || {},
2559
2217
  execute: async (params, context) => {
2560
- // Type guard to ensure context is compatible with ExecutionContext
2561
- const executionContext = context && isNonNullObject(context)
2218
+ // CONTEXT MERGING: Combine all available contexts for maximum information
2219
+ const storedContext = this.toolExecutionContext || {};
2220
+ const runtimeContext = context && isNonNullObject(context)
2562
2221
  ? context
2563
- : undefined;
2222
+ : {};
2223
+ // Merge contexts with runtime context taking precedence
2224
+ // This ensures we have the richest possible context for tool execution
2225
+ const executionContext = {
2226
+ ...storedContext, // Base context from setToolContext (session, tokens, etc.)
2227
+ ...runtimeContext, // Runtime context from AI model (if any)
2228
+ // Ensure we always have at least a sessionId for tracing
2229
+ sessionId: runtimeContext.sessionId ||
2230
+ storedContext.sessionId ||
2231
+ `fallback-${Date.now()}`,
2232
+ };
2233
+ // Enhanced logging for context debugging
2234
+ logger.debug("Tool execution context merged", {
2235
+ toolName: tool.name,
2236
+ storedContextKeys: Object.keys(storedContext),
2237
+ runtimeContextKeys: Object.keys(runtimeContext),
2238
+ finalContextKeys: Object.keys(executionContext),
2239
+ hasJuspayToken: !!executionContext
2240
+ .juspayToken,
2241
+ hasShopId: !!executionContext.shopId,
2242
+ sessionId: executionContext.sessionId,
2243
+ });
2564
2244
  return await toolRegistry.executeTool(tool.name, params, executionContext);
2565
2245
  },
2566
2246
  });
@@ -2637,7 +2317,7 @@ export class NeuroLink {
2637
2317
  * Supports both custom tools and MCP server tools with timeout, retry, and circuit breaker patterns
2638
2318
  * @param toolName - Name of the tool to execute
2639
2319
  * @param params - Parameters to pass to the tool
2640
- * @param options - Execution options
2320
+ * @param options - Execution options including optional authentication context
2641
2321
  * @returns Tool execution result
2642
2322
  */
2643
2323
  async executeTool(toolName, params = {}, options) {
@@ -2651,6 +2331,28 @@ export class NeuroLink {
2651
2331
  : params,
2652
2332
  hasExternalManager: !!this.externalServerManager,
2653
2333
  });
2334
+ // 🔧 PARAMETER TRACE: Log tool execution details for debugging
2335
+ logger.debug(`Tool execution detailed analysis`, {
2336
+ toolName,
2337
+ executionStartTime,
2338
+ paramsAnalysis: {
2339
+ type: typeof params,
2340
+ isNull: params === null,
2341
+ isUndefined: params === undefined,
2342
+ isEmpty: params &&
2343
+ typeof params === "object" &&
2344
+ Object.keys(params).length === 0,
2345
+ keys: params && typeof params === "object"
2346
+ ? Object.keys(params)
2347
+ : "NOT_OBJECT",
2348
+ keysLength: params && typeof params === "object"
2349
+ ? Object.keys(params).length
2350
+ : 0,
2351
+ },
2352
+ isTargetTool: toolName === "juspay-analytics_SuccessRateSRByTime",
2353
+ options,
2354
+ hasExternalManager: !!this.externalServerManager,
2355
+ });
2654
2356
  // Emit tool start event (NeuroLink format - keep existing)
2655
2357
  this.emitter.emit("tool:start", {
2656
2358
  toolName,
@@ -2664,6 +2366,7 @@ export class NeuroLink {
2664
2366
  timeout: options?.timeout || TOOL_TIMEOUTS.EXECUTION_DEFAULT_MS, // 30 second default timeout
2665
2367
  maxRetries: options?.maxRetries || RETRY_ATTEMPTS.DEFAULT, // Default 2 retries for retriable errors
2666
2368
  retryDelayMs: options?.retryDelayMs || RETRY_DELAYS.BASE_MS, // 1 second delay between retries
2369
+ authContext: options?.authContext, // Pass through authentication context
2667
2370
  };
2668
2371
  // Track memory usage for tool execution
2669
2372
  const { MemoryManager } = await import("./utils/performance.js");
@@ -2836,12 +2539,18 @@ export class NeuroLink {
2836
2539
  throw ErrorFactory.toolExecutionFailed(toolName, error instanceof Error ? error : new Error(String(error)), externalTool.serverId);
2837
2540
  }
2838
2541
  }
2839
- // If not found in custom tools, in-memory servers, or external servers, try unified registry
2840
2542
  try {
2543
+ const storedContext = this.toolExecutionContext || {};
2544
+ const passedAuthContext = options.authContext || {};
2841
2545
  const context = {
2842
- sessionId: `neurolink-tool-${Date.now()}`,
2843
- userId: "neurolink-user",
2546
+ ...storedContext,
2547
+ ...passedAuthContext,
2844
2548
  };
2549
+ logger.debug(`[Using merged context for unified registry tool:`, {
2550
+ toolName,
2551
+ storedContextKeys: Object.keys(storedContext),
2552
+ finalContextKeys: Object.keys(context),
2553
+ });
2845
2554
  const result = (await toolRegistry.executeTool(toolName, params, context));
2846
2555
  // ADD: Check if result indicates a failure and emit error event
2847
2556
  if (result &&
@@ -2855,7 +2564,6 @@ export class NeuroLink {
2855
2564
  return result;
2856
2565
  }
2857
2566
  catch (error) {
2858
- // ADD: Emergency error event emission (fallback)
2859
2567
  const errorToEmit = error instanceof Error ? error : new Error(String(error));
2860
2568
  this.emitter.emit("error", errorToEmit);
2861
2569
  // Check if tool was not found
@@ -2910,32 +2618,9 @@ export class NeuroLink {
2910
2618
  // Track memory usage for tool listing operations
2911
2619
  const { MemoryManager } = await import("./utils/performance.js");
2912
2620
  const startMemory = MemoryManager.getMemoryUsageMB();
2913
- logger.debug(`[NeuroLink] 📊 LOG_POINT_A002_MEMORY_BASELINE`, {
2914
- logPoint: "A002_MEMORY_BASELINE",
2915
- getAllToolsId,
2916
- timestamp: new Date().toISOString(),
2917
- elapsedMs: Date.now() - getAllToolsStartTime,
2918
- elapsedNs: (process.hrtime.bigint() - getAllToolsHrTimeStart).toString(),
2919
- memoryBaseline: startMemory,
2920
- heapUsed: startMemory.heapUsed,
2921
- heapTotal: startMemory.heapTotal,
2922
- external: startMemory.external,
2923
- message: "Established memory baseline before tool enumeration",
2924
- });
2925
2621
  try {
2926
2622
  // Optimized: Collect all tools with minimal object creation
2927
2623
  const allTools = new Map();
2928
- // 🚀 EXHAUSTIVE LOGGING POINT A003: MCP TOOLS COLLECTION START
2929
- const mcpToolsStartTime = process.hrtime.bigint();
2930
- logger.debug(`[NeuroLink] 🔧 LOG_POINT_A003_MCP_TOOLS_START`, {
2931
- logPoint: "A003_MCP_TOOLS_START",
2932
- getAllToolsId,
2933
- timestamp: new Date().toISOString(),
2934
- elapsedMs: Date.now() - getAllToolsStartTime,
2935
- elapsedNs: (process.hrtime.bigint() - getAllToolsHrTimeStart).toString(),
2936
- mcpToolsStartTimeNs: mcpToolsStartTime.toString(),
2937
- message: "Starting MCP server tools collection",
2938
- });
2939
2624
  // 1. Add MCP server tools (built-in direct tools)
2940
2625
  const mcpToolsRaw = await toolRegistry.listTools();
2941
2626
  for (const tool of mcpToolsRaw) {
@@ -3089,9 +2774,20 @@ export class NeuroLink {
3089
2774
  if (!response.ok) {
3090
2775
  throw new Error("Ollama service not responding");
3091
2776
  }
3092
- const { models } = await response.json();
2777
+ const responseData = await response.json();
2778
+ const models = responseData?.models;
3093
2779
  const defaultOllamaModel = "llama3.2:latest";
3094
- const modelIsAvailable = models.some((m) => m.name === defaultOllamaModel);
2780
+ // Runtime-safe guard: ensure models is an array with valid objects
2781
+ if (!Array.isArray(models)) {
2782
+ logger.warn("Ollama API returned invalid models format in testProvider", {
2783
+ responseData,
2784
+ modelsType: typeof models,
2785
+ });
2786
+ throw new Error("Invalid models format from Ollama API");
2787
+ }
2788
+ // Filter and validate models before comparison
2789
+ const validModels = models.filter((m) => m && typeof m === "object" && typeof m.name === "string");
2790
+ const modelIsAvailable = validModels.some((m) => m.name === defaultOllamaModel);
3095
2791
  if (modelIsAvailable) {
3096
2792
  return {
3097
2793
  provider: providerName,
@@ -3536,6 +3232,24 @@ export class NeuroLink {
3536
3232
  // ============================================================================
3537
3233
  // CONVERSATION MEMORY PUBLIC API
3538
3234
  // ============================================================================
3235
+ /**
3236
+ * Initialize conversation memory if enabled (public method for explicit initialization)
3237
+ * This is useful for testing or when you want to ensure conversation memory is ready
3238
+ * @returns Promise resolving to true if initialization was successful, false otherwise
3239
+ */
3240
+ async ensureConversationMemoryInitialized() {
3241
+ try {
3242
+ const initId = `manual-init-${Date.now()}`;
3243
+ await this.initializeConversationMemoryForGeneration(initId, Date.now(), process.hrtime.bigint());
3244
+ return !!this.conversationMemory;
3245
+ }
3246
+ catch (error) {
3247
+ logger.error("Failed to initialize conversation memory", {
3248
+ error: error instanceof Error ? error.message : String(error),
3249
+ });
3250
+ return false;
3251
+ }
3252
+ }
3539
3253
  /**
3540
3254
  * Get conversation memory statistics (public API)
3541
3255
  */
@@ -3606,6 +3320,52 @@ export class NeuroLink {
3606
3320
  }
3607
3321
  await this.conversationMemory.clearAllSessions();
3608
3322
  }
3323
+ /**
3324
+ * Store tool executions in conversation memory if enabled and Redis is configured
3325
+ * @param sessionId - Session identifier
3326
+ * @param userId - User identifier (optional)
3327
+ * @param toolCalls - Array of tool calls
3328
+ * @param toolResults - Array of tool results
3329
+ * @returns Promise resolving when storage is complete
3330
+ */
3331
+ async storeToolExecutions(sessionId, userId, toolCalls, toolResults) {
3332
+ // Check if tools are not empty
3333
+ const hasToolData = (toolCalls && toolCalls.length > 0) ||
3334
+ (toolResults && toolResults.length > 0);
3335
+ if (!hasToolData) {
3336
+ logger.debug("Tool execution storage skipped", {
3337
+ hasToolData,
3338
+ toolCallsCount: toolCalls?.length || 0,
3339
+ toolResultsCount: toolResults?.length || 0,
3340
+ });
3341
+ return;
3342
+ }
3343
+ // Type guard to ensure it's Redis conversation memory manager
3344
+ const redisMemory = this
3345
+ .conversationMemory;
3346
+ try {
3347
+ await redisMemory.storeToolExecution(sessionId, userId, toolCalls, toolResults);
3348
+ }
3349
+ catch (error) {
3350
+ logger.warn("Failed to store tool executions", {
3351
+ sessionId,
3352
+ userId,
3353
+ error: error instanceof Error ? error.message : String(error),
3354
+ });
3355
+ // Don't throw - tool storage failures shouldn't break generation
3356
+ }
3357
+ }
3358
+ /**
3359
+ * Check if tool execution storage is available
3360
+ * @returns boolean indicating if Redis storage is configured and available
3361
+ */
3362
+ isToolExecutionStorageAvailable() {
3363
+ const isRedisStorage = process.env.STORAGE_TYPE === "redis";
3364
+ const hasRedisConversationMemory = this.conversationMemory &&
3365
+ this.conversationMemory.constructor.name ===
3366
+ "RedisConversationMemoryManager";
3367
+ return !!(isRedisStorage && hasRedisConversationMemory);
3368
+ }
3609
3369
  // ===== EXTERNAL MCP SERVER METHODS =====
3610
3370
  /**
3611
3371
  * Add an external MCP server