@juspay/neurolink 7.29.0 → 7.29.2

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 (61) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/cli/commands/config.d.ts +3 -3
  3. package/dist/cli/commands/mcp.js +25 -0
  4. package/dist/cli/factories/commandFactory.d.ts +1 -0
  5. package/dist/cli/factories/commandFactory.js +115 -21
  6. package/dist/cli/index.js +8 -0
  7. package/dist/core/factory.js +77 -4
  8. package/dist/factories/providerFactory.js +3 -0
  9. package/dist/factories/providerRegistry.js +2 -2
  10. package/dist/lib/core/factory.js +77 -4
  11. package/dist/lib/factories/providerFactory.js +3 -0
  12. package/dist/lib/factories/providerRegistry.js +2 -2
  13. package/dist/lib/mcp/externalServerManager.js +13 -14
  14. package/dist/lib/mcp/flexibleToolValidator.d.ts +50 -0
  15. package/dist/lib/mcp/flexibleToolValidator.js +161 -0
  16. package/dist/lib/mcp/toolRegistry.d.ts +2 -2
  17. package/dist/lib/mcp/toolRegistry.js +25 -50
  18. package/dist/lib/neurolink.d.ts +299 -4
  19. package/dist/lib/neurolink.js +434 -73
  20. package/dist/lib/providers/amazonBedrock.d.ts +47 -6
  21. package/dist/lib/providers/amazonBedrock.js +282 -23
  22. package/dist/lib/providers/aws/credentialProvider.d.ts +58 -0
  23. package/dist/lib/providers/aws/credentialProvider.js +267 -0
  24. package/dist/lib/providers/aws/credentialTester.d.ts +49 -0
  25. package/dist/lib/providers/aws/credentialTester.js +394 -0
  26. package/dist/lib/providers/googleVertex.js +13 -4
  27. package/dist/lib/proxy/awsProxyIntegration.d.ts +23 -0
  28. package/dist/lib/proxy/awsProxyIntegration.js +285 -0
  29. package/dist/lib/proxy/proxyFetch.d.ts +9 -5
  30. package/dist/lib/proxy/proxyFetch.js +232 -98
  31. package/dist/lib/proxy/utils/noProxyUtils.d.ts +39 -0
  32. package/dist/lib/proxy/utils/noProxyUtils.js +149 -0
  33. package/dist/lib/types/providers.d.ts +43 -0
  34. package/dist/lib/utils/providerConfig.d.ts +1 -0
  35. package/dist/lib/utils/providerConfig.js +2 -1
  36. package/dist/lib/utils/providerHealth.js +123 -5
  37. package/dist/mcp/externalServerManager.js +13 -14
  38. package/dist/mcp/flexibleToolValidator.d.ts +50 -0
  39. package/dist/mcp/flexibleToolValidator.js +161 -0
  40. package/dist/mcp/toolRegistry.d.ts +2 -2
  41. package/dist/mcp/toolRegistry.js +25 -50
  42. package/dist/neurolink.d.ts +299 -4
  43. package/dist/neurolink.js +434 -73
  44. package/dist/providers/amazonBedrock.d.ts +47 -6
  45. package/dist/providers/amazonBedrock.js +282 -23
  46. package/dist/providers/aws/credentialProvider.d.ts +58 -0
  47. package/dist/providers/aws/credentialProvider.js +267 -0
  48. package/dist/providers/aws/credentialTester.d.ts +49 -0
  49. package/dist/providers/aws/credentialTester.js +394 -0
  50. package/dist/providers/googleVertex.js +13 -4
  51. package/dist/proxy/awsProxyIntegration.d.ts +23 -0
  52. package/dist/proxy/awsProxyIntegration.js +285 -0
  53. package/dist/proxy/proxyFetch.d.ts +9 -5
  54. package/dist/proxy/proxyFetch.js +232 -98
  55. package/dist/proxy/utils/noProxyUtils.d.ts +39 -0
  56. package/dist/proxy/utils/noProxyUtils.js +149 -0
  57. package/dist/types/providers.d.ts +43 -0
  58. package/dist/utils/providerConfig.d.ts +1 -0
  59. package/dist/utils/providerConfig.js +2 -1
  60. package/dist/utils/providerHealth.js +123 -5
  61. package/package.json +5 -1
package/dist/neurolink.js CHANGED
@@ -24,6 +24,7 @@ import { ProviderRegistry } from "./factories/providerRegistry.js";
24
24
  import { createCustomToolServerInfo, detectCategory, } from "./utils/mcpDefaults.js";
25
25
  // Factory processing imports
26
26
  import { processFactoryOptions, enhanceTextGenerationOptions, validateFactoryConfig, processStreamingFactoryOptions, createCleanStreamOptions, } from "./utils/factoryProcessing.js";
27
+ // Tool detection and execution imports
27
28
  // Transformation utilities
28
29
  import { transformToolExecutions, transformToolExecutionsForMCP, transformAvailableTools, transformToolsForMCP, transformToolsToExpectedFormat, transformToolsToDescriptions, extractToolNames, transformParamsForLogging, optimizeToolForCollection, } from "./utils/transformationUtils.js";
29
30
  // Enhanced error handling imports
@@ -54,17 +55,52 @@ export class NeuroLink {
54
55
  * @param toolName - Name of the tool
55
56
  * @param startTime - Timestamp when tool execution started
56
57
  * @param success - Whether the tool execution was successful
58
+ * @param result - The result of the tool execution (optional)
59
+ * @param error - The error if execution failed (optional)
57
60
  */
58
- emitToolEndEvent(toolName, startTime, success) {
61
+ emitToolEndEvent(toolName, startTime, success, result, error) {
62
+ // Emit tool end event (NeuroLink format - enhanced with result/error)
59
63
  this.emitter.emit("tool:end", {
60
64
  toolName,
61
65
  responseTime: Date.now() - startTime,
62
66
  success,
63
67
  timestamp: Date.now(),
68
+ result: result, // Enhanced: include actual result
69
+ error: error, // Enhanced: include error if present
64
70
  });
71
+ // ADD: Bedrock-compatible tool:end event (positional parameters)
72
+ this.emitter.emit("tool:end", toolName, success ? result : error);
65
73
  }
66
74
  // Conversation memory support
67
75
  conversationMemory;
76
+ /**
77
+ * Creates a new NeuroLink instance for AI text generation with MCP tool integration.
78
+ *
79
+ * @param config - Optional configuration object
80
+ * @param config.conversationMemory - Configuration for conversation memory features
81
+ * @param config.conversationMemory.enabled - Whether to enable conversation memory (default: false)
82
+ * @param config.conversationMemory.maxSessions - Maximum number of concurrent sessions (default: 100)
83
+ * @param config.conversationMemory.maxTurnsPerSession - Maximum conversation turns per session (default: 50)
84
+ *
85
+ * @example
86
+ * ```typescript
87
+ * // Basic usage
88
+ * const neurolink = new NeuroLink();
89
+ *
90
+ * // With conversation memory
91
+ * const neurolink = new NeuroLink({
92
+ * conversationMemory: {
93
+ * enabled: true,
94
+ * maxSessions: 50,
95
+ * maxTurnsPerSession: 20
96
+ * }
97
+ * });
98
+ * ```
99
+ *
100
+ * @throws {Error} When provider registry setup fails
101
+ * @throws {Error} When conversation memory initialization fails (if enabled)
102
+ * @throws {Error} When external server manager initialization fails
103
+ */
68
104
  constructor(config) {
69
105
  // 🚀 EXHAUSTIVE LOGGING POINT C001: CONSTRUCTOR ENTRY
70
106
  const constructorStartTime = Date.now();
@@ -724,6 +760,54 @@ export class NeuroLink {
724
760
  this.contextManager = new ContextManager(this.generateTextInternal.bind(this), contextConfig);
725
761
  logger.info("[NeuroLink] Automatic context summarization enabled.");
726
762
  }
763
+ /**
764
+ * Generate AI content using the best available provider with MCP tool integration.
765
+ * This is the primary method for text generation with full feature support.
766
+ *
767
+ * @param optionsOrPrompt - Either a string prompt or a comprehensive GenerateOptions object
768
+ * @param optionsOrPrompt.input - Input configuration object
769
+ * @param optionsOrPrompt.input.text - The text prompt to send to the AI (required)
770
+ * @param optionsOrPrompt.provider - AI provider to use ('auto', 'openai', 'anthropic', etc.)
771
+ * @param optionsOrPrompt.model - Specific model to use (e.g., 'gpt-4', 'claude-3-opus')
772
+ * @param optionsOrPrompt.temperature - Randomness in response (0.0 = deterministic, 2.0 = very random)
773
+ * @param optionsOrPrompt.maxTokens - Maximum tokens in response
774
+ * @param optionsOrPrompt.systemPrompt - System message to set AI behavior
775
+ * @param optionsOrPrompt.disableTools - Whether to disable MCP tool usage
776
+ * @param optionsOrPrompt.enableAnalytics - Whether to include usage analytics
777
+ * @param optionsOrPrompt.enableEvaluation - Whether to include response quality evaluation
778
+ * @param optionsOrPrompt.context - Additional context for the request
779
+ * @param optionsOrPrompt.evaluationDomain - Domain for specialized evaluation
780
+ * @param optionsOrPrompt.toolUsageContext - Context for tool usage decisions
781
+ *
782
+ * @returns Promise resolving to GenerateResult with content, usage data, and optional analytics
783
+ *
784
+ * @example
785
+ * ```typescript
786
+ * // Simple usage with string prompt
787
+ * const result = await neurolink.generate("What is artificial intelligence?");
788
+ * console.log(result.content);
789
+ *
790
+ * // Advanced usage with options
791
+ * const result = await neurolink.generate({
792
+ * input: { text: "Explain quantum computing" },
793
+ * provider: "openai",
794
+ * model: "gpt-4",
795
+ * temperature: 0.7,
796
+ * maxTokens: 500,
797
+ * enableAnalytics: true,
798
+ * enableEvaluation: true,
799
+ * context: { domain: "science", level: "intermediate" }
800
+ * });
801
+ *
802
+ * // Access analytics and evaluation data
803
+ * console.log(result.analytics?.usage);
804
+ * console.log(result.evaluation?.relevance);
805
+ * ```
806
+ *
807
+ * @throws {Error} When input text is missing or invalid
808
+ * @throws {Error} When all providers fail to generate content
809
+ * @throws {Error} When conversation memory operations fail (if enabled)
810
+ */
727
811
  async generate(optionsOrPrompt) {
728
812
  const originalPrompt = this._extractOriginalPrompt(optionsOrPrompt);
729
813
  // Convert string prompt to full options
@@ -740,11 +824,15 @@ export class NeuroLink {
740
824
  options.input.text = this.contextManager.getContextForPrompt("user", options.input.text);
741
825
  }
742
826
  const startTime = Date.now();
743
- // Emit generation start event
827
+ // Emit generation start event (NeuroLink format - keep existing)
744
828
  this.emitter.emit("generation:start", {
745
829
  provider: options.provider || "auto",
746
830
  timestamp: startTime,
747
831
  });
832
+ // ADD: Bedrock-compatible response:start event
833
+ this.emitter.emit("response:start");
834
+ // ADD: Bedrock-compatible message event
835
+ this.emitter.emit("message", `Starting ${options.provider || "auto"} text generation...`);
748
836
  // Process factory configuration
749
837
  const factoryResult = processFactoryOptions(options);
750
838
  // Validate factory configuration if present
@@ -787,13 +875,18 @@ export class NeuroLink {
787
875
  }
788
876
  // Use redesigned generation logic
789
877
  const textResult = await this.generateTextInternal(textOptions);
790
- // Emit generation completion event
878
+ // Emit generation completion event (NeuroLink format - enhanced with content)
791
879
  this.emitter.emit("generation:end", {
792
880
  provider: textResult.provider,
793
881
  responseTime: Date.now() - startTime,
794
882
  toolsUsed: textResult.toolsUsed,
795
883
  timestamp: Date.now(),
884
+ result: textResult, // Enhanced: include full result
796
885
  });
886
+ // ADD: Bedrock-compatible response:end event with content
887
+ this.emitter.emit("response:end", textResult.content || "");
888
+ // ADD: Bedrock-compatible message event
889
+ this.emitter.emit("message", `Generation completed in ${Date.now() - startTime}ms`);
797
890
  // Convert back to GenerateResult
798
891
  const generateResult = {
799
892
  content: textResult.content,
@@ -926,6 +1019,10 @@ export class NeuroLink {
926
1019
  promptLength: options.prompt?.length || 0,
927
1020
  hasConversationMemory: !!this.conversationMemory,
928
1021
  });
1022
+ // ADD: Bedrock-compatible response:start event for generateTextInternal
1023
+ this.emitter.emit("response:start");
1024
+ // ADD: Bedrock-compatible message event for generateTextInternal
1025
+ this.emitter.emit("message", `Starting ${options.provider || "auto"} text generation (internal)...`);
929
1026
  try {
930
1027
  // 🚀 EXHAUSTIVE LOGGING POINT G002: CONVERSATION MEMORY INITIALIZATION
931
1028
  const conversationMemoryStartTime = process.hrtime.bigint();
@@ -1072,6 +1169,8 @@ export class NeuroLink {
1072
1169
  });
1073
1170
  // Store conversation turn
1074
1171
  await storeConversationTurn(this.conversationMemory, options, mcpResult);
1172
+ // ADD: Bedrock-compatible response:end event for MCP success path
1173
+ this.emitter.emit("response:end", mcpResult.content || "");
1075
1174
  return mcpResult;
1076
1175
  }
1077
1176
  else {
@@ -1088,6 +1187,8 @@ export class NeuroLink {
1088
1187
  logger.debug(`[${functionTag}] Found tool executions but no content, continuing with result`);
1089
1188
  // Store conversation turn even with empty content if tools executed
1090
1189
  await storeConversationTurn(this.conversationMemory, options, mcpResult);
1190
+ // ADD: Bedrock-compatible response:end event for MCP tool execution success path
1191
+ this.emitter.emit("response:end", mcpResult.content || "");
1091
1192
  return mcpResult;
1092
1193
  }
1093
1194
  }
@@ -1112,12 +1213,20 @@ export class NeuroLink {
1112
1213
  logger.debug(`[${functionTag}] Direct generation successful`);
1113
1214
  // Store conversation turn
1114
1215
  await storeConversationTurn(this.conversationMemory, options, directResult);
1216
+ // ADD: Bedrock-compatible response:end event for generateTextInternal
1217
+ this.emitter.emit("response:end", directResult.content || "");
1218
+ // ADD: Bedrock-compatible message event for generateTextInternal completion
1219
+ this.emitter.emit("message", `Text generation completed successfully`);
1115
1220
  return directResult;
1116
1221
  }
1117
1222
  catch (error) {
1118
1223
  logger.error(`[${functionTag}] All generation methods failed`, {
1119
1224
  error: error instanceof Error ? error.message : String(error),
1120
1225
  });
1226
+ // ADD: Bedrock-compatible response:end event for error path (empty content)
1227
+ this.emitter.emit("response:end", "");
1228
+ // ADD: Centralized error event emission
1229
+ this.emitter.emit("error", error instanceof Error ? error : new Error(String(error)));
1121
1230
  throw error;
1122
1231
  }
1123
1232
  }
@@ -1223,6 +1332,9 @@ export class NeuroLink {
1223
1332
  // Create provider and generate
1224
1333
  const provider = await AIProviderFactory.createProvider(providerName, options.model, !options.disableTools, // Pass disableTools as inverse of enableMCP
1225
1334
  this);
1335
+ // ADD: Emit connection events for all providers (Bedrock-compatible)
1336
+ this.emitter.emit("connected");
1337
+ this.emitter.emit("message", `${providerName} provider initialized successfully`);
1226
1338
  // Enable tool execution for the provider using BaseProvider method
1227
1339
  provider.setupToolExecutor({
1228
1340
  customTools: this.getCustomTools(),
@@ -1317,6 +1429,9 @@ export class NeuroLink {
1317
1429
  const conversationMessages = await getConversationMessages(this.conversationMemory, options);
1318
1430
  const provider = await AIProviderFactory.createProvider(providerName, options.model, !options.disableTools, // Pass disableTools as inverse of enableMCP
1319
1431
  this);
1432
+ // ADD: Emit connection events for successful provider creation (Bedrock-compatible)
1433
+ this.emitter.emit("connected");
1434
+ this.emitter.emit("message", `${providerName} provider initialized successfully`);
1320
1435
  // Enable tool execution for direct provider generation using BaseProvider method
1321
1436
  provider.setupToolExecutor({
1322
1437
  customTools: this.getCustomTools(),
@@ -1378,7 +1493,7 @@ export class NeuroLink {
1378
1493
  * Execute tools if available through centralized registry
1379
1494
  * Simplified approach without domain detection - relies on tool registry
1380
1495
  */
1381
- async detectAndExecuteTools(prompt, domainType) {
1496
+ async detectAndExecuteTools(prompt, _domainType) {
1382
1497
  const functionTag = "NeuroLink.detectAndExecuteTools";
1383
1498
  try {
1384
1499
  // Simplified: Just return original prompt without complex detection
@@ -1439,8 +1554,55 @@ export class NeuroLink {
1439
1554
  return stringStream();
1440
1555
  }
1441
1556
  /**
1442
- * PRIMARY METHOD: Stream content using AI (recommended for new code)
1443
- * Future-ready for multi-modal capabilities with current text focus
1557
+ * Stream AI-generated content in real-time using the best available provider.
1558
+ * This method provides real-time streaming of AI responses with full MCP tool integration.
1559
+ *
1560
+ * @param options - Stream configuration options
1561
+ * @param options.input - Input configuration object
1562
+ * @param options.input.text - The text prompt to send to the AI (required)
1563
+ * @param options.provider - AI provider to use ('auto', 'openai', 'anthropic', etc.)
1564
+ * @param options.model - Specific model to use (e.g., 'gpt-4', 'claude-3-opus')
1565
+ * @param options.temperature - Randomness in response (0.0 = deterministic, 2.0 = very random)
1566
+ * @param options.maxTokens - Maximum tokens in response
1567
+ * @param options.systemPrompt - System message to set AI behavior
1568
+ * @param options.disableTools - Whether to disable MCP tool usage
1569
+ * @param options.enableAnalytics - Whether to include usage analytics
1570
+ * @param options.enableEvaluation - Whether to include response quality evaluation
1571
+ * @param options.context - Additional context for the request
1572
+ * @param options.evaluationDomain - Domain for specialized evaluation
1573
+ *
1574
+ * @returns Promise resolving to StreamResult with an async iterable stream
1575
+ *
1576
+ * @example
1577
+ * ```typescript
1578
+ * // Basic streaming usage
1579
+ * const result = await neurolink.stream({
1580
+ * input: { text: "Tell me a story about space exploration" }
1581
+ * });
1582
+ *
1583
+ * // Consume the stream
1584
+ * for await (const chunk of result.stream) {
1585
+ * process.stdout.write(chunk.content);
1586
+ * }
1587
+ *
1588
+ * // Advanced streaming with options
1589
+ * const result = await neurolink.stream({
1590
+ * input: { text: "Explain machine learning" },
1591
+ * provider: "openai",
1592
+ * model: "gpt-4",
1593
+ * temperature: 0.7,
1594
+ * enableAnalytics: true,
1595
+ * context: { domain: "education", audience: "beginners" }
1596
+ * });
1597
+ *
1598
+ * // Access metadata and analytics
1599
+ * console.log(result.provider);
1600
+ * console.log(result.analytics?.usage);
1601
+ * ```
1602
+ *
1603
+ * @throws {Error} When input text is missing or invalid
1604
+ * @throws {Error} When all providers fail to generate content
1605
+ * @throws {Error} When conversation memory operations fail (if enabled)
1444
1606
  */
1445
1607
  async stream(options) {
1446
1608
  const startTime = Date.now();
@@ -1546,7 +1708,7 @@ export class NeuroLink {
1546
1708
  global.gc();
1547
1709
  return process.memoryUsage();
1548
1710
  }
1549
- catch (e) {
1711
+ catch (_e) {
1550
1712
  return null;
1551
1713
  }
1552
1714
  })()
@@ -1684,11 +1846,15 @@ export class NeuroLink {
1684
1846
  cpuAfterValidation: process.cpuUsage(),
1685
1847
  message: "EXHAUSTIVE validation success - proceeding with stream processing",
1686
1848
  });
1687
- // Emit stream start event
1849
+ // Emit stream start event (NeuroLink format - keep existing)
1688
1850
  this.emitter.emit("stream:start", {
1689
1851
  provider: options.provider || "auto",
1690
1852
  timestamp: startTime,
1691
1853
  });
1854
+ // ADD: Bedrock-compatible response:start event
1855
+ this.emitter.emit("response:start");
1856
+ // ADD: Bedrock-compatible message event
1857
+ this.emitter.emit("message", `Starting ${options.provider || "auto"} stream generation...`);
1692
1858
  // Process factory configuration for streaming
1693
1859
  const factoryResult = processFactoryOptions(options);
1694
1860
  const streamingResult = processStreamingFactoryOptions(options);
@@ -1780,6 +1946,8 @@ export class NeuroLink {
1780
1946
  // Ensure chunk has content property and it's a string
1781
1947
  if (typeof chunk.content === "string") {
1782
1948
  accumulatedContent += chunk.content;
1949
+ // ADD: Bedrock-compatible response:chunk event
1950
+ self.emitter.emit("response:chunk", chunk.content);
1783
1951
  }
1784
1952
  else if (chunk.content === undefined ||
1785
1953
  chunk.content === null) {
@@ -1791,6 +1959,8 @@ export class NeuroLink {
1791
1959
  const stringContent = String(chunk.content || "");
1792
1960
  processedChunk = { ...chunk, content: stringContent };
1793
1961
  accumulatedContent += stringContent;
1962
+ // ADD: Bedrock-compatible response:chunk event
1963
+ self.emitter.emit("response:chunk", stringContent);
1794
1964
  }
1795
1965
  }
1796
1966
  else if (chunk === null || chunk === undefined) {
@@ -1827,11 +1997,16 @@ export class NeuroLink {
1827
1997
  responseTime,
1828
1998
  provider: providerName,
1829
1999
  });
1830
- // Emit stream completion event
2000
+ // Emit stream completion event (NeuroLink format - enhanced with content)
1831
2001
  this.emitter.emit("stream:end", {
1832
2002
  provider: providerName,
1833
2003
  responseTime,
2004
+ result: { content: accumulatedContent }, // Enhanced: include accumulated content
1834
2005
  });
2006
+ // ADD: Bedrock-compatible response:end event with full response
2007
+ this.emitter.emit("response:end", accumulatedContent);
2008
+ // ADD: Bedrock-compatible message event
2009
+ this.emitter.emit("message", `Stream completed in ${responseTime}ms (${accumulatedContent.length} chars)`);
1835
2010
  // Convert to StreamResult format - Include analytics and evaluation from provider
1836
2011
  return {
1837
2012
  stream: processedStream,
@@ -1860,6 +2035,8 @@ export class NeuroLink {
1860
2035
  };
1861
2036
  }
1862
2037
  catch (error) {
2038
+ // ADD: Error event emission for MCP streaming failure
2039
+ this.emitter.emit("error", error instanceof Error ? error : new Error(String(error)));
1863
2040
  // Fall back to regular streaming if MCP fails
1864
2041
  mcpLogger.warn(`[${functionTag}] MCP streaming failed, falling back to regular`, {
1865
2042
  error: error instanceof Error ? error.message : String(error),
@@ -1895,6 +2072,8 @@ export class NeuroLink {
1895
2072
  for await (const chunk of streamResult.stream) {
1896
2073
  if (chunk && typeof chunk.content === "string") {
1897
2074
  fallbackAccumulatedContent += chunk.content;
2075
+ // ADD: Bedrock-compatible response:chunk event for fallback
2076
+ self.emitter.emit("response:chunk", chunk.content);
1898
2077
  }
1899
2078
  yield chunk; // Preserve original streaming behavior
1900
2079
  }
@@ -1922,12 +2101,17 @@ export class NeuroLink {
1922
2101
  }
1923
2102
  })(this);
1924
2103
  const responseTime = Date.now() - startTime;
1925
- // Emit stream completion event for fallback
2104
+ // Emit stream completion event for fallback (NeuroLink format - enhanced with content)
1926
2105
  this.emitter.emit("stream:end", {
1927
2106
  provider: providerName,
1928
2107
  responseTime,
1929
2108
  fallback: true,
2109
+ result: { content: fallbackAccumulatedContent }, // Enhanced: include accumulated content
1930
2110
  });
2111
+ // ADD: Bedrock-compatible response:end event with full response
2112
+ this.emitter.emit("response:end", fallbackAccumulatedContent);
2113
+ // ADD: Bedrock-compatible message event
2114
+ this.emitter.emit("message", `Fallback stream completed in ${responseTime}ms (${fallbackAccumulatedContent.length} chars)`);
1931
2115
  return {
1932
2116
  stream: fallbackProcessedStream,
1933
2117
  provider: providerName,
@@ -1957,8 +2141,178 @@ export class NeuroLink {
1957
2141
  }
1958
2142
  }
1959
2143
  /**
1960
- * Get the EventEmitter to listen to NeuroLink events
1961
- * @returns EventEmitter instance
2144
+ * Get the EventEmitter instance to listen to NeuroLink events for real-time monitoring and debugging.
2145
+ * This method provides access to the internal event system that emits events during AI generation,
2146
+ * tool execution, streaming, and other operations for comprehensive observability.
2147
+ *
2148
+ * @returns EventEmitter instance that emits various NeuroLink operation events
2149
+ *
2150
+ * @example
2151
+ * ```typescript
2152
+ * // Basic event listening setup
2153
+ * const neurolink = new NeuroLink();
2154
+ * const emitter = neurolink.getEventEmitter();
2155
+ *
2156
+ * // Listen to generation events
2157
+ * emitter.on('generation:start', (event) => {
2158
+ * console.log(`Generation started with provider: ${event.provider}`);
2159
+ * console.log(`Started at: ${new Date(event.timestamp)}`);
2160
+ * });
2161
+ *
2162
+ * emitter.on('generation:end', (event) => {
2163
+ * console.log(`Generation completed in ${event.responseTime}ms`);
2164
+ * console.log(`Tools used: ${event.toolsUsed?.length || 0}`);
2165
+ * });
2166
+ *
2167
+ * // Listen to streaming events
2168
+ * emitter.on('stream:start', (event) => {
2169
+ * console.log(`Streaming started with provider: ${event.provider}`);
2170
+ * });
2171
+ *
2172
+ * emitter.on('stream:end', (event) => {
2173
+ * console.log(`Streaming completed in ${event.responseTime}ms`);
2174
+ * if (event.fallback) console.log('Used fallback streaming');
2175
+ * });
2176
+ *
2177
+ * // Listen to tool execution events
2178
+ * emitter.on('tool:start', (event) => {
2179
+ * console.log(`Tool execution started: ${event.toolName}`);
2180
+ * });
2181
+ *
2182
+ * emitter.on('tool:end', (event) => {
2183
+ * console.log(`Tool ${event.toolName} ${event.success ? 'succeeded' : 'failed'}`);
2184
+ * console.log(`Execution time: ${event.responseTime}ms`);
2185
+ * });
2186
+ *
2187
+ * // Listen to tool registration events
2188
+ * emitter.on('tools-register:start', (event) => {
2189
+ * console.log(`Registering tool: ${event.toolName}`);
2190
+ * });
2191
+ *
2192
+ * emitter.on('tools-register:end', (event) => {
2193
+ * console.log(`Tool registration ${event.success ? 'succeeded' : 'failed'}: ${event.toolName}`);
2194
+ * });
2195
+ *
2196
+ * // Listen to external MCP server events
2197
+ * emitter.on('externalMCP:serverConnected', (event) => {
2198
+ * console.log(`External MCP server connected: ${event.serverId}`);
2199
+ * console.log(`Tools available: ${event.toolCount || 0}`);
2200
+ * });
2201
+ *
2202
+ * emitter.on('externalMCP:serverDisconnected', (event) => {
2203
+ * console.log(`External MCP server disconnected: ${event.serverId}`);
2204
+ * console.log(`Reason: ${event.reason || 'Unknown'}`);
2205
+ * });
2206
+ *
2207
+ * emitter.on('externalMCP:toolDiscovered', (event) => {
2208
+ * console.log(`New tool discovered: ${event.toolName} from ${event.serverId}`);
2209
+ * });
2210
+ *
2211
+ * // Advanced usage with error handling
2212
+ * emitter.on('error', (error) => {
2213
+ * console.error('NeuroLink error:', error);
2214
+ * });
2215
+ *
2216
+ * // Clean up event listeners when done
2217
+ * function cleanup() {
2218
+ * emitter.removeAllListeners();
2219
+ * }
2220
+ *
2221
+ * process.on('SIGINT', cleanup);
2222
+ * process.on('SIGTERM', cleanup);
2223
+ * ```
2224
+ *
2225
+ * @example
2226
+ * ```typescript
2227
+ * // Advanced monitoring with metrics collection
2228
+ * const neurolink = new NeuroLink();
2229
+ * const emitter = neurolink.getEventEmitter();
2230
+ * const metrics = {
2231
+ * generations: 0,
2232
+ * totalResponseTime: 0,
2233
+ * toolExecutions: 0,
2234
+ * failures: 0
2235
+ * };
2236
+ *
2237
+ * // Collect performance metrics
2238
+ * emitter.on('generation:end', (event) => {
2239
+ * metrics.generations++;
2240
+ * metrics.totalResponseTime += event.responseTime;
2241
+ * metrics.toolExecutions += event.toolsUsed?.length || 0;
2242
+ * });
2243
+ *
2244
+ * emitter.on('tool:end', (event) => {
2245
+ * if (!event.success) {
2246
+ * metrics.failures++;
2247
+ * }
2248
+ * });
2249
+ *
2250
+ * // Log metrics every 10 seconds
2251
+ * setInterval(() => {
2252
+ * const avgResponseTime = metrics.generations > 0
2253
+ * ? metrics.totalResponseTime / metrics.generations
2254
+ * : 0;
2255
+ *
2256
+ * console.log('NeuroLink Metrics:', {
2257
+ * totalGenerations: metrics.generations,
2258
+ * averageResponseTime: `${avgResponseTime.toFixed(2)}ms`,
2259
+ * totalToolExecutions: metrics.toolExecutions,
2260
+ * failureRate: `${((metrics.failures / (metrics.toolExecutions || 1)) * 100).toFixed(2)}%`
2261
+ * });
2262
+ * }, 10000);
2263
+ * ```
2264
+ *
2265
+ * **Available Events:**
2266
+ *
2267
+ * **Generation Events:**
2268
+ * - `generation:start` - Fired when text generation begins
2269
+ * - `{ provider: string, timestamp: number }`
2270
+ * - `generation:end` - Fired when text generation completes
2271
+ * - `{ provider: string, responseTime: number, toolsUsed?: string[], timestamp: number }`
2272
+ *
2273
+ * **Streaming Events:**
2274
+ * - `stream:start` - Fired when streaming begins
2275
+ * - `{ provider: string, timestamp: number }`
2276
+ * - `stream:end` - Fired when streaming completes
2277
+ * - `{ provider: string, responseTime: number, fallback?: boolean }`
2278
+ *
2279
+ * **Tool Events:**
2280
+ * - `tool:start` - Fired when tool execution begins
2281
+ * - `{ toolName: string, timestamp: number }`
2282
+ * - `tool:end` - Fired when tool execution completes
2283
+ * - `{ toolName: string, responseTime: number, success: boolean, timestamp: number }`
2284
+ * - `tools-register:start` - Fired when tool registration begins
2285
+ * - `{ toolName: string, timestamp: number }`
2286
+ * - `tools-register:end` - Fired when tool registration completes
2287
+ * - `{ toolName: string, success: boolean, timestamp: number }`
2288
+ *
2289
+ * **External MCP Events:**
2290
+ * - `externalMCP:serverConnected` - Fired when external MCP server connects
2291
+ * - `{ serverId: string, toolCount?: number, timestamp: number }`
2292
+ * - `externalMCP:serverDisconnected` - Fired when external MCP server disconnects
2293
+ * - `{ serverId: string, reason?: string, timestamp: number }`
2294
+ * - `externalMCP:serverFailed` - Fired when external MCP server fails
2295
+ * - `{ serverId: string, error: string, timestamp: number }`
2296
+ * - `externalMCP:toolDiscovered` - Fired when external MCP tool is discovered
2297
+ * - `{ toolName: string, serverId: string, timestamp: number }`
2298
+ * - `externalMCP:toolRemoved` - Fired when external MCP tool is removed
2299
+ * - `{ toolName: string, serverId: string, timestamp: number }`
2300
+ * - `externalMCP:serverAdded` - Fired when external MCP server is added
2301
+ * - `{ serverId: string, config: MCPServerInfo, toolCount: number, timestamp: number }`
2302
+ * - `externalMCP:serverRemoved` - Fired when external MCP server is removed
2303
+ * - `{ serverId: string, timestamp: number }`
2304
+ *
2305
+ * **Error Events:**
2306
+ * - `error` - Fired when an error occurs
2307
+ * - `{ error: Error, context?: object }`
2308
+ *
2309
+ * @throws {Error} This method does not throw errors as it returns the internal EventEmitter
2310
+ *
2311
+ * @since 1.0.0
2312
+ * @see {@link https://nodejs.org/api/events.html} Node.js EventEmitter documentation
2313
+ * @see {@link NeuroLink.generate} for events related to text generation
2314
+ * @see {@link NeuroLink.stream} for events related to streaming
2315
+ * @see {@link NeuroLink.executeTool} for events related to tool execution
1962
2316
  */
1963
2317
  getEventEmitter() {
1964
2318
  return this.emitter;
@@ -1978,7 +2332,7 @@ export class NeuroLink {
1978
2332
  timestamp: Date.now(),
1979
2333
  });
1980
2334
  try {
1981
- // --- Start: Added Validation Logic ---
2335
+ // --- Start: Enhanced Validation Logic with FlexibleToolValidator ---
1982
2336
  if (!name || typeof name !== "string") {
1983
2337
  throw new Error("Invalid tool name");
1984
2338
  }
@@ -1988,55 +2342,36 @@ export class NeuroLink {
1988
2342
  if (typeof tool.execute !== "function") {
1989
2343
  throw new Error(`Tool '${name}' must have an execute method.`);
1990
2344
  }
1991
- // --- End: Added Validation Logic ---
1992
- // Import validation functions synchronously - they are pure functions
1993
- let validateTool;
1994
- let isToolNameAvailable;
1995
- let suggestToolNames;
2345
+ // Use FlexibleToolValidator for consistent validation across SDK and toolRegistry
1996
2346
  try {
1997
- // Try ES module import first
1998
- const toolRegistrationModule = require("./sdk/toolRegistration.js");
1999
- ({ validateTool, isToolNameAvailable, suggestToolNames } =
2000
- toolRegistrationModule);
2347
+ const flexibleValidatorModule = require("./mcp/flexibleToolValidator.js");
2348
+ const FlexibleToolValidator = flexibleValidatorModule.FlexibleToolValidator;
2349
+ // Use the same validation logic as toolRegistry (static method)
2350
+ const validationResult = FlexibleToolValidator.validateToolName(name);
2351
+ if (!validationResult.isValid) {
2352
+ throw new Error(`Tool validation failed: ${validationResult.error}`);
2353
+ }
2001
2354
  }
2002
2355
  catch (error) {
2003
- // Fallback: skip validation if import fails (graceful degradation)
2004
- logger.warn("Tool validation module not available, skipping advanced validation", {
2356
+ // If FlexibleToolValidator import fails, use basic safety checks
2357
+ logger.warn("FlexibleToolValidator not available, using basic validation", {
2005
2358
  error: error instanceof Error ? error.message : String(error),
2006
2359
  });
2007
- // Create minimal validation functions
2008
- validateTool = () => { }; // No-op
2009
- isToolNameAvailable = () => true; // Allow all names
2010
- suggestToolNames = () => ["alternative_tool"];
2011
- }
2012
- // Check if tool name is available (not reserved)
2013
- if (!isToolNameAvailable(name)) {
2014
- const suggestions = suggestToolNames(name);
2015
- throw new Error(`Tool name '${name}' is not available (reserved or invalid format). ` +
2016
- `Suggested alternatives: ${suggestions.slice(0, 3).join(", ")}`);
2017
- }
2018
- // Create a simplified tool object for validation
2019
- const toolForValidation = {
2020
- description: tool.description || "",
2021
- execute: async (params) => {
2022
- if (tool.execute) {
2023
- const result = await tool.execute(params);
2024
- return result;
2025
- }
2026
- return "";
2027
- },
2028
- parameters: tool.inputSchema,
2029
- metadata: {
2030
- category: "custom",
2031
- },
2032
- };
2033
- // Use comprehensive validation logic
2034
- try {
2035
- validateTool(name, toolForValidation);
2036
- }
2037
- catch (error) {
2038
- throw new Error(`Tool registration failed: ${error instanceof Error ? error.message : String(error)}`);
2360
+ // Basic safety checks to prevent obvious issues
2361
+ if (name.trim() === "") {
2362
+ throw new Error("Tool name cannot be empty");
2363
+ }
2364
+ if (name.length > 100) {
2365
+ throw new Error("Tool name is too long (maximum 100 characters)");
2366
+ }
2367
+ // eslint-disable-next-line no-control-regex
2368
+ if (/[\x00-\x1F\x7F]/.test(name)) {
2369
+ throw new Error("Tool name contains invalid control characters");
2370
+ }
2039
2371
  }
2372
+ // --- End: Enhanced Validation Logic ---
2373
+ // Tool object validation is now handled by FlexibleToolValidator above
2374
+ // Proceed with tool registration since validation passed
2040
2375
  // SMART DEFAULTS: Use utility to eliminate boilerplate creation
2041
2376
  const mcpServerInfo = createCustomToolServerInfo(name, tool);
2042
2377
  // Register with toolRegistry using MCPServerInfo directly
@@ -2196,11 +2531,14 @@ export class NeuroLink {
2196
2531
  : params,
2197
2532
  hasExternalManager: !!this.externalServerManager,
2198
2533
  });
2199
- // Emit tool start event
2534
+ // Emit tool start event (NeuroLink format - keep existing)
2200
2535
  this.emitter.emit("tool:start", {
2201
2536
  toolName,
2202
2537
  timestamp: executionStartTime,
2538
+ input: params, // Enhanced: add input parameters
2203
2539
  });
2540
+ // ADD: Bedrock-compatible tool:start event (positional parameters)
2541
+ this.emitter.emit("tool:start", toolName, params);
2204
2542
  // Set default options
2205
2543
  const finalOptions = {
2206
2544
  timeout: options?.timeout || 30000, // 30 second default timeout
@@ -2226,13 +2564,15 @@ export class NeuroLink {
2226
2564
  });
2227
2565
  }
2228
2566
  const metrics = this.toolExecutionMetrics.get(toolName);
2229
- metrics.totalExecutions++;
2567
+ if (metrics) {
2568
+ metrics.totalExecutions++;
2569
+ }
2230
2570
  try {
2231
2571
  mcpLogger.debug(`[${functionTag}] Executing tool: ${toolName}`, {
2232
2572
  toolName,
2233
2573
  params,
2234
2574
  options: finalOptions,
2235
- circuitBreakerState: circuitBreaker.getState(),
2575
+ circuitBreakerState: circuitBreaker?.getState(),
2236
2576
  });
2237
2577
  // Execute with circuit breaker, timeout, and retry logic
2238
2578
  const result = await circuitBreaker.execute(async () => {
@@ -2253,12 +2593,14 @@ export class NeuroLink {
2253
2593
  });
2254
2594
  // Update success metrics
2255
2595
  const executionTime = Date.now() - executionStartTime;
2256
- metrics.successfulExecutions++;
2257
- metrics.lastExecutionTime = executionTime;
2258
- metrics.averageExecutionTime =
2259
- (metrics.averageExecutionTime * (metrics.successfulExecutions - 1) +
2260
- executionTime) /
2261
- metrics.successfulExecutions;
2596
+ if (metrics) {
2597
+ metrics.successfulExecutions++;
2598
+ metrics.lastExecutionTime = executionTime;
2599
+ metrics.averageExecutionTime =
2600
+ (metrics.averageExecutionTime * (metrics.successfulExecutions - 1) +
2601
+ executionTime) /
2602
+ metrics.successfulExecutions;
2603
+ }
2262
2604
  // Track memory usage
2263
2605
  const endMemory = MemoryManager.getMemoryUsageMB();
2264
2606
  const memoryDelta = endMemory.heapUsed - startMemory.heapUsed;
@@ -2273,15 +2615,17 @@ export class NeuroLink {
2273
2615
  toolName,
2274
2616
  executionTime,
2275
2617
  memoryDelta,
2276
- circuitBreakerState: circuitBreaker.getState(),
2618
+ circuitBreakerState: circuitBreaker?.getState(),
2277
2619
  });
2278
2620
  // Emit tool end event using the helper method
2279
- this.emitToolEndEvent(toolName, executionStartTime, true);
2621
+ this.emitToolEndEvent(toolName, executionStartTime, true, result);
2280
2622
  return result;
2281
2623
  }
2282
2624
  catch (error) {
2283
2625
  // Update failure metrics
2284
- metrics.failedExecutions++;
2626
+ if (metrics) {
2627
+ metrics.failedExecutions++;
2628
+ }
2285
2629
  const executionTime = Date.now() - executionStartTime;
2286
2630
  // Create structured error
2287
2631
  let structuredError;
@@ -2312,8 +2656,10 @@ export class NeuroLink {
2312
2656
  else {
2313
2657
  structuredError = ErrorFactory.toolExecutionFailed(toolName, new Error(String(error)));
2314
2658
  }
2659
+ // ADD: Centralized error event emission
2660
+ this.emitter.emit("error", structuredError);
2315
2661
  // Emit tool end event using the helper method
2316
- this.emitToolEndEvent(toolName, executionStartTime, false);
2662
+ this.emitToolEndEvent(toolName, executionStartTime, false, undefined, structuredError);
2317
2663
  // Add execution context to structured error
2318
2664
  structuredError = new NeuroLinkError({
2319
2665
  ...structuredError,
@@ -2322,8 +2668,8 @@ export class NeuroLink {
2322
2668
  executionTime,
2323
2669
  params,
2324
2670
  options: finalOptions,
2325
- circuitBreakerState: circuitBreaker.getState(),
2326
- circuitBreakerFailures: circuitBreaker.getFailureCount(),
2671
+ circuitBreakerState: circuitBreaker?.getState(),
2672
+ circuitBreakerFailures: circuitBreaker?.getFailureCount(),
2327
2673
  metrics: { ...metrics },
2328
2674
  },
2329
2675
  });
@@ -2374,9 +2720,21 @@ export class NeuroLink {
2374
2720
  userId: "neurolink-user",
2375
2721
  };
2376
2722
  const result = (await toolRegistry.executeTool(toolName, params, context));
2723
+ // ADD: Check if result indicates a failure and emit error event
2724
+ if (result &&
2725
+ typeof result === "object" &&
2726
+ "success" in result &&
2727
+ result.success === false) {
2728
+ const errorMessage = result.error || "Tool execution failed";
2729
+ const errorToEmit = new Error(errorMessage);
2730
+ this.emitter.emit("error", errorToEmit);
2731
+ }
2377
2732
  return result;
2378
2733
  }
2379
2734
  catch (error) {
2735
+ // ADD: Emergency error event emission (fallback)
2736
+ const errorToEmit = error instanceof Error ? error : new Error(String(error));
2737
+ this.emitter.emit("error", errorToEmit);
2380
2738
  // Check if tool was not found
2381
2739
  if (error instanceof Error && error.message.includes("not found")) {
2382
2740
  const availableTools = await this.getAllAvailableTools();
@@ -2548,6 +2906,9 @@ export class NeuroLink {
2548
2906
  }
2549
2907
  const { AIProviderFactory } = await import("./core/factory.js");
2550
2908
  const { hasProviderEnvVars } = await import("./utils/providerUtils.js");
2909
+ // Keep references to prevent unused variable warnings
2910
+ void AIProviderFactory;
2911
+ void hasProviderEnvVars;
2551
2912
  const providers = [
2552
2913
  "openai",
2553
2914
  "bedrock",
@@ -2810,7 +3171,7 @@ export class NeuroLink {
2810
3171
  const inMemoryServers = this.getInMemoryServers();
2811
3172
  if (inMemoryServers.has(serverId)) {
2812
3173
  const serverInfo = inMemoryServers.get(serverId);
2813
- return !!(serverInfo.tools && serverInfo.tools.length > 0);
3174
+ return !!(serverInfo?.tools && serverInfo.tools.length > 0);
2814
3175
  }
2815
3176
  // Test external MCP servers
2816
3177
  const externalServer = this.externalServerManager.getServer(serverId);
@@ -3317,7 +3678,7 @@ export class NeuroLink {
3317
3678
  * Convert JSON Schema to AI SDK compatible format
3318
3679
  * For now, we'll skip schema validation and let the AI SDK handle parameters dynamically
3319
3680
  */
3320
- convertJSONSchemaToAISDKFormat(inputSchema) {
3681
+ convertJSONSchemaToAISDKFormat(_inputSchema) {
3321
3682
  // The simplest approach: don't provide parameters schema
3322
3683
  // This lets the AI SDK handle the tool without schema validation
3323
3684
  // Tools will still work, they just won't have strict parameter validation