@juspay/neurolink 7.48.1 → 7.50.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 (153) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +215 -16
  3. package/dist/agent/directTools.d.ts +55 -0
  4. package/dist/agent/directTools.js +266 -0
  5. package/dist/cli/factories/commandFactory.d.ts +6 -0
  6. package/dist/cli/factories/commandFactory.js +149 -16
  7. package/dist/cli/index.js +13 -2
  8. package/dist/cli/loop/conversationSelector.d.ts +45 -0
  9. package/dist/cli/loop/conversationSelector.js +222 -0
  10. package/dist/cli/loop/optionsSchema.d.ts +1 -1
  11. package/dist/cli/loop/session.d.ts +36 -8
  12. package/dist/cli/loop/session.js +257 -61
  13. package/dist/core/baseProvider.d.ts +9 -0
  14. package/dist/core/baseProvider.js +45 -5
  15. package/dist/core/evaluation.js +5 -2
  16. package/dist/factories/providerRegistry.js +2 -2
  17. package/dist/index.d.ts +8 -2
  18. package/dist/index.js +11 -10
  19. package/dist/lib/agent/directTools.d.ts +55 -0
  20. package/dist/lib/agent/directTools.js +266 -0
  21. package/dist/lib/core/baseProvider.d.ts +9 -0
  22. package/dist/lib/core/baseProvider.js +45 -5
  23. package/dist/lib/core/evaluation.js +5 -2
  24. package/dist/lib/factories/providerRegistry.js +2 -2
  25. package/dist/lib/index.d.ts +8 -2
  26. package/dist/lib/index.js +11 -10
  27. package/dist/lib/mcp/factory.d.ts +2 -157
  28. package/dist/lib/mcp/flexibleToolValidator.d.ts +1 -5
  29. package/dist/lib/mcp/index.d.ts +3 -2
  30. package/dist/lib/mcp/mcpCircuitBreaker.d.ts +1 -75
  31. package/dist/lib/mcp/mcpClientFactory.d.ts +1 -20
  32. package/dist/lib/mcp/mcpClientFactory.js +1 -0
  33. package/dist/lib/mcp/registry.d.ts +3 -10
  34. package/dist/lib/mcp/servers/agent/directToolsServer.d.ts +1 -1
  35. package/dist/lib/mcp/servers/aiProviders/aiCoreServer.d.ts +1 -1
  36. package/dist/lib/mcp/servers/utilities/utilityServer.d.ts +1 -1
  37. package/dist/lib/mcp/toolDiscoveryService.d.ts +3 -84
  38. package/dist/lib/mcp/toolRegistry.d.ts +2 -24
  39. package/dist/lib/middleware/builtin/guardrails.d.ts +5 -16
  40. package/dist/lib/middleware/builtin/guardrails.js +44 -39
  41. package/dist/lib/middleware/utils/guardrailsUtils.d.ts +64 -0
  42. package/dist/lib/middleware/utils/guardrailsUtils.js +387 -0
  43. package/dist/lib/neurolink.d.ts +36 -7
  44. package/dist/lib/neurolink.js +141 -0
  45. package/dist/lib/providers/anthropic.js +47 -3
  46. package/dist/lib/providers/azureOpenai.js +9 -2
  47. package/dist/lib/providers/googleAiStudio.js +9 -2
  48. package/dist/lib/providers/googleVertex.js +12 -2
  49. package/dist/lib/providers/huggingFace.js +1 -1
  50. package/dist/lib/providers/litellm.js +1 -1
  51. package/dist/lib/providers/mistral.js +1 -1
  52. package/dist/lib/providers/openAI.js +47 -3
  53. package/dist/lib/services/server/ai/observability/instrumentation.d.ts +57 -0
  54. package/dist/lib/services/server/ai/observability/instrumentation.js +170 -0
  55. package/dist/lib/session/globalSessionState.d.ts +26 -0
  56. package/dist/lib/session/globalSessionState.js +86 -1
  57. package/dist/lib/telemetry/index.d.ts +1 -0
  58. package/dist/lib/telemetry/telemetryService.d.ts +2 -0
  59. package/dist/lib/telemetry/telemetryService.js +7 -7
  60. package/dist/lib/types/cli.d.ts +28 -0
  61. package/dist/lib/types/content.d.ts +18 -5
  62. package/dist/lib/types/contextTypes.d.ts +1 -1
  63. package/dist/lib/types/conversation.d.ts +57 -4
  64. package/dist/lib/types/fileTypes.d.ts +65 -0
  65. package/dist/lib/types/fileTypes.js +4 -0
  66. package/dist/lib/types/generateTypes.d.ts +12 -0
  67. package/dist/lib/types/guardrails.d.ts +103 -0
  68. package/dist/lib/types/guardrails.js +1 -0
  69. package/dist/lib/types/index.d.ts +4 -2
  70. package/dist/lib/types/index.js +4 -0
  71. package/dist/lib/types/mcpTypes.d.ts +407 -14
  72. package/dist/lib/types/modelTypes.d.ts +6 -6
  73. package/dist/lib/types/observability.d.ts +49 -0
  74. package/dist/lib/types/observability.js +6 -0
  75. package/dist/lib/types/streamTypes.d.ts +7 -0
  76. package/dist/lib/types/tools.d.ts +132 -35
  77. package/dist/lib/utils/csvProcessor.d.ts +68 -0
  78. package/dist/lib/utils/csvProcessor.js +277 -0
  79. package/dist/lib/utils/fileDetector.d.ts +57 -0
  80. package/dist/lib/utils/fileDetector.js +457 -0
  81. package/dist/lib/utils/imageProcessor.d.ts +10 -0
  82. package/dist/lib/utils/imageProcessor.js +22 -0
  83. package/dist/lib/utils/loopUtils.d.ts +71 -0
  84. package/dist/lib/utils/loopUtils.js +262 -0
  85. package/dist/lib/utils/messageBuilder.d.ts +2 -1
  86. package/dist/lib/utils/messageBuilder.js +197 -2
  87. package/dist/lib/utils/optionsUtils.d.ts +1 -1
  88. package/dist/mcp/factory.d.ts +2 -157
  89. package/dist/mcp/flexibleToolValidator.d.ts +1 -5
  90. package/dist/mcp/index.d.ts +3 -2
  91. package/dist/mcp/mcpCircuitBreaker.d.ts +1 -75
  92. package/dist/mcp/mcpClientFactory.d.ts +1 -20
  93. package/dist/mcp/mcpClientFactory.js +1 -0
  94. package/dist/mcp/registry.d.ts +3 -10
  95. package/dist/mcp/servers/agent/directToolsServer.d.ts +1 -1
  96. package/dist/mcp/servers/aiProviders/aiCoreServer.d.ts +1 -1
  97. package/dist/mcp/servers/utilities/utilityServer.d.ts +1 -1
  98. package/dist/mcp/toolDiscoveryService.d.ts +3 -84
  99. package/dist/mcp/toolRegistry.d.ts +2 -24
  100. package/dist/middleware/builtin/guardrails.d.ts +5 -16
  101. package/dist/middleware/builtin/guardrails.js +44 -39
  102. package/dist/middleware/utils/guardrailsUtils.d.ts +64 -0
  103. package/dist/middleware/utils/guardrailsUtils.js +387 -0
  104. package/dist/neurolink.d.ts +36 -7
  105. package/dist/neurolink.js +141 -0
  106. package/dist/providers/anthropic.js +47 -3
  107. package/dist/providers/azureOpenai.js +9 -2
  108. package/dist/providers/googleAiStudio.js +9 -2
  109. package/dist/providers/googleVertex.js +12 -2
  110. package/dist/providers/huggingFace.js +1 -1
  111. package/dist/providers/litellm.js +1 -1
  112. package/dist/providers/mistral.js +1 -1
  113. package/dist/providers/openAI.js +47 -3
  114. package/dist/services/server/ai/observability/instrumentation.d.ts +57 -0
  115. package/dist/services/server/ai/observability/instrumentation.js +170 -0
  116. package/dist/session/globalSessionState.d.ts +26 -0
  117. package/dist/session/globalSessionState.js +86 -1
  118. package/dist/telemetry/index.d.ts +1 -0
  119. package/dist/telemetry/telemetryService.d.ts +2 -0
  120. package/dist/telemetry/telemetryService.js +7 -7
  121. package/dist/types/cli.d.ts +28 -0
  122. package/dist/types/content.d.ts +18 -5
  123. package/dist/types/contextTypes.d.ts +1 -1
  124. package/dist/types/conversation.d.ts +57 -4
  125. package/dist/types/fileTypes.d.ts +65 -0
  126. package/dist/types/fileTypes.js +4 -0
  127. package/dist/types/generateTypes.d.ts +12 -0
  128. package/dist/types/guardrails.d.ts +103 -0
  129. package/dist/types/guardrails.js +1 -0
  130. package/dist/types/index.d.ts +4 -2
  131. package/dist/types/index.js +4 -0
  132. package/dist/types/mcpTypes.d.ts +407 -14
  133. package/dist/types/modelTypes.d.ts +6 -6
  134. package/dist/types/observability.d.ts +49 -0
  135. package/dist/types/observability.js +6 -0
  136. package/dist/types/streamTypes.d.ts +7 -0
  137. package/dist/types/tools.d.ts +132 -35
  138. package/dist/utils/csvProcessor.d.ts +68 -0
  139. package/dist/utils/csvProcessor.js +277 -0
  140. package/dist/utils/fileDetector.d.ts +57 -0
  141. package/dist/utils/fileDetector.js +457 -0
  142. package/dist/utils/imageProcessor.d.ts +10 -0
  143. package/dist/utils/imageProcessor.js +22 -0
  144. package/dist/utils/loopUtils.d.ts +71 -0
  145. package/dist/utils/loopUtils.js +262 -0
  146. package/dist/utils/messageBuilder.d.ts +2 -1
  147. package/dist/utils/messageBuilder.js +197 -2
  148. package/dist/utils/optionsUtils.d.ts +1 -1
  149. package/package.json +18 -16
  150. package/dist/lib/mcp/contracts/mcpContract.d.ts +0 -106
  151. package/dist/lib/mcp/contracts/mcpContract.js +0 -5
  152. package/dist/mcp/contracts/mcpContract.d.ts +0 -106
  153. package/dist/mcp/contracts/mcpContract.js +0 -5
@@ -1,12 +1,12 @@
1
1
  import chalk from "chalk";
2
2
  import readline from "readline";
3
- import fs from "fs/promises";
4
- import path from "path";
5
- import os from "os";
6
3
  import { logger } from "../../lib/utils/logger.js";
7
4
  import { globalSession } from "../../lib/session/globalSessionState.js";
8
5
  import { textGenerationOptionsSchema } from "./optionsSchema.js";
9
6
  import { handleError } from "../errorHandler.js";
7
+ import { ConversationSelector } from "./conversationSelector.js";
8
+ import { NeuroLink } from "../../lib/neurolink.js";
9
+ import { displaySessionMessage, verifyConversationContext, getConversationPreview, loadCommandHistory, saveCommandToHistory, displayConversationPreview, parseValue, restoreSessionVariables, } from "../../lib/utils/loopUtils.js";
10
10
  // Banner Art
11
11
  const NEUROLINK_BANNER = `
12
12
  ▗▖ ▗▖▗▄▄▄▖▗▖ ▗▖▗▄▄▖ ▗▄▖ ▗▖ ▗▄▄▄▖▗▖ ▗▖▗▖ ▗▖
@@ -14,33 +14,58 @@ const NEUROLINK_BANNER = `
14
14
  ▐▌ ▝▜▌▐▛▀▀▘▐▌ ▐▌▐▛▀▚▖▐▌ ▐▌▐▌ █ ▐▌ ▝▜▌▐▛▚▖
15
15
  ▐▌ ▐▌▐▙▄▄▖▝▚▄▞▘▐▌ ▐▌▝▚▄▞▘▐▙▄▄▖▗▄█▄▖▐▌ ▐▌▐▌ ▐▌
16
16
  `;
17
- // Global command history file
18
- const HISTORY_FILE = path.join(os.homedir(), ".neurolink_history");
19
17
  export class LoopSession {
20
18
  conversationMemoryConfig;
19
+ options;
21
20
  initializeCliParser;
22
21
  isRunning = false;
23
22
  sessionId;
24
23
  commandHistory = [];
25
24
  sessionVariablesSchema = textGenerationOptionsSchema;
26
- constructor(initializeCliParser, conversationMemoryConfig) {
25
+ constructor(initializeCliParser, conversationMemoryConfig, options) {
27
26
  this.conversationMemoryConfig = conversationMemoryConfig;
27
+ this.options = options;
28
28
  this.initializeCliParser = initializeCliParser;
29
29
  }
30
30
  async start() {
31
31
  // Initialize global session state
32
32
  this.sessionId = globalSession.setLoopSession(this.conversationMemoryConfig);
33
33
  // Load command history from global file, reverse once for most recent first
34
- this.commandHistory = (await this.loadHistory()).reverse();
34
+ this.commandHistory = (await loadCommandHistory()).reverse();
35
35
  this.isRunning = true;
36
36
  logger.always(chalk.bold.green(NEUROLINK_BANNER));
37
37
  logger.always(chalk.bold.green("Welcome to NeuroLink Loop Mode!"));
38
+ // Check for direct CLI options
39
+ const directResumeSessionId = this.options?.directResumeSessionId;
40
+ const forceNewSession = this.options?.forceNewSession;
41
+ // Handle conversation discovery and selection if memory is enabled
38
42
  if (this.conversationMemoryConfig?.enabled) {
39
- logger.always(chalk.gray(`Session ID: ${this.sessionId}`));
40
43
  logger.always(chalk.gray("Conversation memory enabled"));
44
+ // Handle direct resume option
45
+ if (directResumeSessionId) {
46
+ await this.handleDirectSessionResume(directResumeSessionId);
47
+ }
48
+ // Handle force new session option
49
+ else if (forceNewSession) {
50
+ logger.always(chalk.blue("Force starting new conversation..."));
51
+ this.sessionId = globalSession.setLoopSession(this.conversationMemoryConfig);
52
+ }
53
+ // Default behavior: check for existing conversations
54
+ else {
55
+ await this.handleConversationSelection();
56
+ }
57
+ // Display session information
58
+ logger.always(chalk.gray(`Session ID: ${this.sessionId}`));
41
59
  logger.always(chalk.gray(`Max sessions: ${this.conversationMemoryConfig.maxSessions}`));
42
60
  logger.always(chalk.gray(`Max turns per session: ${this.conversationMemoryConfig.maxTurnsPerSession}\n`));
43
61
  }
62
+ else {
63
+ // No conversation memory - just create a new session
64
+ this.sessionId = globalSession.setLoopSession(this.conversationMemoryConfig);
65
+ logger.always(chalk.gray(`Session ID: ${this.sessionId}`));
66
+ }
67
+ // Load command history from global file
68
+ this.commandHistory = (await loadCommandHistory()).reverse();
44
69
  logger.always(chalk.gray('Type "help" for a list of commands.'));
45
70
  logger.always(chalk.gray('Type "exit", "quit", or ":q" to leave the loop.'));
46
71
  while (this.isRunning) {
@@ -61,7 +86,7 @@ export class LoopSession {
61
86
  // Save session commands to history (both memory and file)
62
87
  if (command && command.trim()) {
63
88
  this.commandHistory.unshift(command);
64
- await this.saveCommand(command);
89
+ await saveCommandToHistory(command);
65
90
  }
66
91
  continue;
67
92
  }
@@ -81,7 +106,7 @@ export class LoopSession {
81
106
  // Save command to history (both memory and file)
82
107
  if (command && command.trim()) {
83
108
  this.commandHistory.unshift(command);
84
- await this.saveCommand(command);
109
+ await saveCommandToHistory(command);
85
110
  }
86
111
  }
87
112
  catch (error) {
@@ -90,8 +115,96 @@ export class LoopSession {
90
115
  }
91
116
  }
92
117
  // Cleanup on exit
93
- globalSession.clearLoopSession();
94
- logger.always(chalk.yellow("Loop session ended."));
118
+ this.cleanup();
119
+ }
120
+ /**
121
+ * Handle direct session resume from CLI option
122
+ */
123
+ async handleDirectSessionResume(directResumeSessionId) {
124
+ logger.always(chalk.blue(`Attempting to resume session: ${directResumeSessionId.slice(0, 12)}...`));
125
+ try {
126
+ const restoreResult = await this.restoreSession(directResumeSessionId);
127
+ if (restoreResult.success) {
128
+ displaySessionMessage(restoreResult);
129
+ this.sessionId = directResumeSessionId;
130
+ // Display conversation preview
131
+ const preview = await getConversationPreview(directResumeSessionId, 2);
132
+ displayConversationPreview(preview, 2);
133
+ }
134
+ else {
135
+ displaySessionMessage(restoreResult);
136
+ logger.always(chalk.yellow("Starting new conversation instead..."));
137
+ this.sessionId = globalSession.setLoopSession(this.conversationMemoryConfig);
138
+ }
139
+ }
140
+ catch (error) {
141
+ logger.error(`Failed to resume session ${directResumeSessionId}:`, error);
142
+ logger.always(chalk.yellow("Starting new conversation instead..."));
143
+ this.sessionId = globalSession.setLoopSession(this.conversationMemoryConfig);
144
+ }
145
+ }
146
+ /**
147
+ * Handle conversation selection logic when no direct resume is specified
148
+ */
149
+ async handleConversationSelection() {
150
+ logger.always(chalk.gray("Checking for existing conversations...\n"));
151
+ try {
152
+ const conversationSelector = new ConversationSelector();
153
+ // Check if there are any stored conversations
154
+ const hasStoredConversations = await conversationSelector.hasStoredConversations();
155
+ if (hasStoredConversations) {
156
+ // Show conversation selection menu
157
+ const selectedSessionId = await conversationSelector.displayConversationMenu();
158
+ if (selectedSessionId !== "NEW_CONVERSATION") {
159
+ // Restore the selected conversation
160
+ logger.always(chalk.blue("Restoring conversation..."));
161
+ const restoreResult = await this.restoreSession(selectedSessionId);
162
+ if (restoreResult.success) {
163
+ displaySessionMessage(restoreResult);
164
+ this.sessionId = selectedSessionId;
165
+ // Display conversation preview
166
+ const preview = await getConversationPreview(selectedSessionId, 2);
167
+ displayConversationPreview(preview, 2);
168
+ }
169
+ else {
170
+ displaySessionMessage(restoreResult);
171
+ logger.always(chalk.yellow("Starting new conversation instead..."));
172
+ this.sessionId = globalSession.setLoopSession(this.conversationMemoryConfig);
173
+ }
174
+ }
175
+ else {
176
+ // User chose to start new conversation
177
+ logger.always(chalk.blue("Starting new conversation..."));
178
+ this.sessionId = globalSession.setLoopSession(this.conversationMemoryConfig);
179
+ }
180
+ }
181
+ else {
182
+ // No existing conversations found
183
+ logger.always(chalk.gray("No existing conversations found."));
184
+ logger.always(chalk.blue("Starting new conversation..."));
185
+ this.sessionId = globalSession.setLoopSession(this.conversationMemoryConfig);
186
+ }
187
+ // Close the conversation selector
188
+ await conversationSelector.close();
189
+ }
190
+ catch (error) {
191
+ logger.warn("Failed to check for existing conversations:", error);
192
+ logger.always(chalk.yellow("Starting new conversation..."));
193
+ this.sessionId = globalSession.setLoopSession(this.conversationMemoryConfig);
194
+ }
195
+ }
196
+ /**
197
+ * Clean up session resources and connections
198
+ */
199
+ cleanup() {
200
+ try {
201
+ globalSession.clearLoopSession();
202
+ logger.always(chalk.yellow("Loop session ended."));
203
+ }
204
+ catch (error) {
205
+ // Silently handle cleanup errors to avoid hanging
206
+ logger.error("Error during cleanup:", error);
207
+ }
95
208
  }
96
209
  async handleSessionCommands(command) {
97
210
  const parts = command.split(" ");
@@ -113,7 +226,7 @@ export class LoopSession {
113
226
  return true;
114
227
  }
115
228
  const valueStr = parts.slice(2).join(" ");
116
- let value = this.parseValue(valueStr);
229
+ let value = parseValue(valueStr);
117
230
  // Validate type
118
231
  if (schema.type === "boolean" && typeof value !== "boolean") {
119
232
  logger.always(chalk.red(`Error: Invalid value for "${key}". Expected a boolean (true/false).`));
@@ -196,21 +309,6 @@ export class LoopSession {
196
309
  return false;
197
310
  }
198
311
  }
199
- parseValue(value) {
200
- // Try to parse as number
201
- if (!isNaN(Number(value))) {
202
- return Number(value);
203
- }
204
- // Try to parse as boolean
205
- if (value.toLowerCase() === "true") {
206
- return true;
207
- }
208
- if (value.toLowerCase() === "false") {
209
- return false;
210
- }
211
- // Return as string
212
- return value;
213
- }
214
312
  showHelp() {
215
313
  logger.always(chalk.cyan("Available Loop Mode Commands:"));
216
314
  const commands = [
@@ -254,39 +352,6 @@ export class LoopSession {
254
352
  }
255
353
  }
256
354
  }
257
- /**
258
- * Load command history from the global history file
259
- */
260
- async loadHistory() {
261
- try {
262
- const content = await fs.readFile(HISTORY_FILE, "utf8");
263
- return content.split("\n").filter((line) => line.trim());
264
- }
265
- catch {
266
- // File doesn't exist yet or can't be read
267
- return [];
268
- }
269
- }
270
- /**
271
- * Save a command to the global history file
272
- */
273
- async saveCommand(command) {
274
- try {
275
- // Skip potentially sensitive commands
276
- const sensitivePattern = /\b(api[-_]?key|token|password|secret|authorization)\b/i;
277
- if (sensitivePattern.test(command)) {
278
- return;
279
- }
280
- // Use writeFile with flag 'a' and mode 0o600 to ensure permissions on creation
281
- await fs.writeFile(HISTORY_FILE, command + "\n", { flag: "a", mode: 0o600 });
282
- // Ensure existing file remains private (best-effort)
283
- await fs.chmod(HISTORY_FILE, 0o600);
284
- }
285
- catch (error) {
286
- // Log file write errors as warnings, but do not interrupt CLI flow
287
- logger.warn("Warning: Could not save command to history:", error);
288
- }
289
- }
290
355
  /**
291
356
  * Get command input with history support using readline
292
357
  */
@@ -310,4 +375,135 @@ export class LoopSession {
310
375
  });
311
376
  });
312
377
  }
378
+ // === SESSION RESTORATION METHODS ===
379
+ /**
380
+ * Restore a conversation session and set up the global session state
381
+ */
382
+ async restoreSession(sessionId, userId) {
383
+ // Local helper for creating failure results with bound sessionId
384
+ const createFailure = (error) => ({
385
+ success: false,
386
+ sessionId,
387
+ messageCount: 0,
388
+ error,
389
+ });
390
+ try {
391
+ logger.debug(`Attempting to restore session: ${sessionId}`);
392
+ // 1. Create NeuroLink instance and validate conversation in one step
393
+ const { neurolinkInstance, conversationData } = await this.createAndValidateNeurolinkInstance(sessionId, userId);
394
+ if (!conversationData) {
395
+ return createFailure(`Conversation ${sessionId} not found or inaccessible`);
396
+ }
397
+ // 2. Set up tool execution context
398
+ await this.configureToolContext(neurolinkInstance, sessionId, userId);
399
+ // 3. Restore global session state
400
+ this.restoreGlobalSessionState(sessionId, neurolinkInstance, conversationData);
401
+ // 4. Verify conversation context accessibility
402
+ await verifyConversationContext(sessionId);
403
+ const result = {
404
+ success: true,
405
+ sessionId,
406
+ messageCount: conversationData.messages?.length || 0,
407
+ lastActivity: conversationData.updatedAt,
408
+ };
409
+ logger.info(`Session restored successfully: ${sessionId}`, {
410
+ messageCount: result.messageCount,
411
+ lastActivity: result.lastActivity,
412
+ });
413
+ return result;
414
+ }
415
+ catch (error) {
416
+ logger.error(`Failed to restore session ${sessionId}:`, error);
417
+ return createFailure(error instanceof Error ? error.message : String(error));
418
+ }
419
+ }
420
+ /**
421
+ * Create NeuroLink instance and validate conversation in one step
422
+ * Eliminates redundant instance creation and initialization
423
+ */
424
+ async createAndValidateNeurolinkInstance(sessionId, userId) {
425
+ // Create NeuroLink instance with proper configuration
426
+ const neurolinkOptions = {};
427
+ if (this.conversationMemoryConfig?.enabled) {
428
+ neurolinkOptions.conversationMemory = {
429
+ enabled: true,
430
+ maxSessions: this.conversationMemoryConfig.maxSessions,
431
+ maxTurnsPerSession: this.conversationMemoryConfig.maxTurnsPerSession,
432
+ };
433
+ neurolinkOptions.sessionId = sessionId;
434
+ }
435
+ const neurolinkInstance = new NeuroLink(neurolinkOptions);
436
+ await neurolinkInstance.ensureConversationMemoryInitialized();
437
+ // Use the same instance to validate conversation exists
438
+ try {
439
+ const messages = await neurolinkInstance.getConversationHistory(sessionId);
440
+ if (!messages || messages.length === 0) {
441
+ logger.debug(`No conversation messages found for session ${sessionId}`);
442
+ return { neurolinkInstance, conversationData: null };
443
+ }
444
+ // Create conversation object with available data
445
+ const conversationData = {
446
+ id: sessionId,
447
+ sessionId,
448
+ userId: userId || "unknown",
449
+ messages,
450
+ createdAt: new Date().toISOString(), // Fallback
451
+ updatedAt: new Date().toISOString(), // Fallback
452
+ title: messages[0]?.content?.slice(0, 50) + "..." || "Untitled Conversation",
453
+ };
454
+ return { neurolinkInstance, conversationData };
455
+ }
456
+ catch (error) {
457
+ logger.debug(`Error accessing conversation for session ${sessionId}:`, error);
458
+ return { neurolinkInstance, conversationData: null };
459
+ }
460
+ }
461
+ /**
462
+ * Configure tool execution context for the restored session
463
+ */
464
+ async configureToolContext(neurolinkInstance, sessionId, userId) {
465
+ const toolContext = {
466
+ sessionId,
467
+ userId: userId || "loop-user",
468
+ source: "loop-mode",
469
+ restored: true,
470
+ timestamp: new Date().toISOString(),
471
+ };
472
+ neurolinkInstance.setToolContext(toolContext);
473
+ logger.debug("Tool execution context configured for restored session", {
474
+ sessionId,
475
+ userId,
476
+ hasToolContext: true,
477
+ });
478
+ await this.verifyToolAvailability(neurolinkInstance);
479
+ }
480
+ /**
481
+ * Verify that tools are available and working in the restored session
482
+ */
483
+ async verifyToolAvailability(neurolinkInstance) {
484
+ try {
485
+ const availableTools = await neurolinkInstance.getAllAvailableTools();
486
+ logger.debug(`Tools available in restored session: ${availableTools.length} tools`, {
487
+ toolNames: availableTools.slice(0, 5).map((t) => t.name),
488
+ hasFileTools: availableTools.some((t) => t.name.includes("file") || t.name.includes("File")),
489
+ hasDirectoryTools: availableTools.some((t) => t.name.includes("directory") || t.name.includes("Directory")),
490
+ });
491
+ if (availableTools.length === 0) {
492
+ logger.warn("No tools available in restored session - this may affect AI capabilities");
493
+ }
494
+ }
495
+ catch (error) {
496
+ logger.warn("Could not verify tool availability in restored session:", error);
497
+ }
498
+ }
499
+ /**
500
+ * Restore global session state and session variables
501
+ */
502
+ restoreGlobalSessionState(sessionId, neurolinkInstance, conversationData) {
503
+ globalSession.clearLoopSession();
504
+ globalSession.restoreLoopSession(sessionId, neurolinkInstance, this.conversationMemoryConfig, {});
505
+ if (conversationData) {
506
+ restoreSessionVariables(conversationData);
507
+ }
508
+ }
313
509
  }
@@ -236,4 +236,13 @@ export declare abstract class BaseProvider implements AIProvider {
236
236
  * @returns Array of prompt chunks
237
237
  */
238
238
  static chunkPrompt(prompt: string, maxChunkSize?: number, overlap?: number): string[];
239
+ /**
240
+ * Create telemetry configuration for Vercel AI SDK experimental_telemetry
241
+ * This enables automatic OpenTelemetry tracing when telemetry is enabled
242
+ */
243
+ protected getStreamTelemetryConfig(options: StreamOptions | TextGenerationOptions, operationType?: "stream" | "generate"): {
244
+ isEnabled: boolean;
245
+ functionId?: string;
246
+ metadata?: Record<string, string | number | boolean>;
247
+ } | undefined;
239
248
  }
@@ -6,6 +6,8 @@ import { DEFAULT_MAX_STEPS, STEP_LIMITS } from "../core/constants.js";
6
6
  import { directAgentTools } from "../agent/directTools.js";
7
7
  import { getSafeMaxTokens } from "../utils/tokenLimits.js";
8
8
  import { createTimeoutController, TimeoutError } from "../utils/timeout.js";
9
+ import { nanoid } from "nanoid";
10
+ import { createAnalytics } from "./analytics.js";
9
11
  import { shouldDisableBuiltinTools } from "../utils/toolUtils.js";
10
12
  import { buildMessagesArray, buildMultimodalMessagesArray, } from "../utils/messageBuilder.js";
11
13
  import { getKeysAsString, getKeyCount } from "../utils/transformationUtils.js";
@@ -98,6 +100,7 @@ export class BaseProvider {
98
100
  // Convert stream options to text generation options
99
101
  const textOptions = {
100
102
  prompt: options.input?.text || "",
103
+ input: options.input,
101
104
  systemPrompt: options.systemPrompt,
102
105
  temperature: options.temperature,
103
106
  maxTokens: options.maxTokens,
@@ -111,6 +114,7 @@ export class BaseProvider {
111
114
  evaluationDomain: options.evaluationDomain,
112
115
  toolUsageContext: options.toolUsageContext,
113
116
  context: options.context,
117
+ csvOptions: options.csvOptions,
114
118
  };
115
119
  logger.debug(`Calling generate for fake streaming`, {
116
120
  provider: this.providerName,
@@ -229,7 +233,9 @@ export class BaseProvider {
229
233
  const input = opts.input;
230
234
  const hasImages = !!input?.images?.length;
231
235
  const hasContent = !!input?.content?.length;
232
- return hasImages || hasContent;
236
+ const hasCSVFiles = !!input?.csvFiles?.length;
237
+ const hasFiles = !!input?.files?.length;
238
+ return hasImages || hasContent || hasCSVFiles || hasFiles;
233
239
  };
234
240
  let messages;
235
241
  if (hasMultimodalInput(options)) {
@@ -242,7 +248,10 @@ export class BaseProvider {
242
248
  text: options.prompt || options.input?.text || "",
243
249
  images: input?.images,
244
250
  content: input?.content,
251
+ csvFiles: input?.csvFiles,
252
+ files: input?.files,
245
253
  },
254
+ csvOptions: options.csvOptions,
246
255
  provider: options.provider,
247
256
  model: options.model,
248
257
  temperature: options.temperature,
@@ -258,7 +267,7 @@ export class BaseProvider {
258
267
  if (process.env.NEUROLINK_DEBUG === "true") {
259
268
  logger.debug("No multimodal input detected, using standard message builder");
260
269
  }
261
- messages = buildMessagesArray(options);
270
+ messages = await buildMessagesArray(options);
262
271
  }
263
272
  // Convert messages to Vercel AI SDK format
264
273
  return messages.map((msg) => {
@@ -297,6 +306,7 @@ export class BaseProvider {
297
306
  toolChoice: shouldUseTools ? "auto" : "none",
298
307
  temperature: options.temperature,
299
308
  maxTokens: options.maxTokens,
309
+ experimental_telemetry: this.getStreamTelemetryConfig(options, "generate"),
300
310
  onStepFinish: ({ toolCalls, toolResults }) => {
301
311
  logger.info("Tool execution completed", { toolResults, toolCalls });
302
312
  // Handle tool execution storage
@@ -1276,9 +1286,8 @@ export class BaseProvider {
1276
1286
  */
1277
1287
  async createStreamAnalytics(result, startTime, options) {
1278
1288
  try {
1279
- const { createAnalytics } = await import("./analytics.js");
1280
1289
  const analytics = createAnalytics(this.providerName, this.modelName, result, Date.now() - startTime, {
1281
- requestId: `${this.providerName}-stream-${Date.now()}`,
1290
+ requestId: `${this.providerName}-stream-${nanoid()}`,
1282
1291
  streamingMode: true,
1283
1292
  ...options.context,
1284
1293
  });
@@ -1511,7 +1520,7 @@ export class BaseProvider {
1511
1520
  }
1512
1521
  const sessionId = options.context?.sessionId ||
1513
1522
  options.sessionId ||
1514
- `session-${Date.now()}`;
1523
+ `session-${nanoid()}`;
1515
1524
  const userId = options.context?.userId ||
1516
1525
  options.userId;
1517
1526
  try {
@@ -1558,4 +1567,35 @@ export class BaseProvider {
1558
1567
  }
1559
1568
  return chunks;
1560
1569
  }
1570
+ /**
1571
+ * Create telemetry configuration for Vercel AI SDK experimental_telemetry
1572
+ * This enables automatic OpenTelemetry tracing when telemetry is enabled
1573
+ */
1574
+ getStreamTelemetryConfig(options, operationType = "stream") {
1575
+ // Check if telemetry is enabled via NeuroLink observability config
1576
+ if (!this.neurolink?.isTelemetryEnabled()) {
1577
+ return undefined;
1578
+ }
1579
+ const functionId = `${this.providerName}-${operationType}-${nanoid()}`;
1580
+ const metadata = {
1581
+ provider: this.providerName,
1582
+ model: this.modelName,
1583
+ toolsEnabled: !options.disableTools,
1584
+ neurolink: true,
1585
+ };
1586
+ // Add sessionId if available
1587
+ if ("sessionId" in options && options.sessionId) {
1588
+ const sessionId = options.sessionId;
1589
+ if (typeof sessionId === "string" ||
1590
+ typeof sessionId === "number" ||
1591
+ typeof sessionId === "boolean") {
1592
+ metadata.sessionId = sessionId;
1593
+ }
1594
+ }
1595
+ return {
1596
+ isEnabled: true,
1597
+ functionId,
1598
+ metadata,
1599
+ };
1600
+ }
1561
1601
  }
@@ -225,8 +225,11 @@ Completeness: [score]
225
225
  Overall: [score]
226
226
  Reasoning: [Provide a detailed explanation of your evaluation, explaining why you gave these scores. Include specific observations about the response's strengths and all possible areas for improvement.]
227
227
  `;
228
- // Generate evaluation
229
- const result = await provider.generate(prompt);
228
+ // Generate evaluation (simple text prompt only - no file processing)
229
+ const result = await provider.generate({
230
+ input: { text: prompt },
231
+ disableTools: true, // Evaluation doesn't need tools
232
+ });
230
233
  if (!result) {
231
234
  logger.debug(`[${functionTag}] No response from provider`);
232
235
  return getDefaultEvaluation("no-response", Date.now() - startTime, context);
@@ -45,9 +45,9 @@ export class ProviderRegistry {
45
45
  }, undefined, // Let provider read BEDROCK_MODEL from .env
46
46
  ["bedrock", "aws"]);
47
47
  // Register Azure OpenAI provider
48
- ProviderFactory.registerProvider(AIProviderName.AZURE, async (modelName) => {
48
+ ProviderFactory.registerProvider(AIProviderName.AZURE, async (modelName, _providerName, sdk) => {
49
49
  const { AzureOpenAIProvider } = await import("../providers/azureOpenai.js");
50
- return new AzureOpenAIProvider(modelName);
50
+ return new AzureOpenAIProvider(modelName, sdk);
51
51
  }, process.env.AZURE_MODEL ||
52
52
  process.env.AZURE_OPENAI_MODEL ||
53
53
  process.env.AZURE_OPENAI_DEPLOYMENT ||
package/dist/index.d.ts CHANGED
@@ -21,6 +21,9 @@ import { NeuroLink } from "./neurolink.js";
21
21
  export { NeuroLink };
22
22
  export type { ProviderStatus, MCPStatus } from "./neurolink.js";
23
23
  export type { MCPServerInfo } from "./types/mcpTypes.js";
24
+ export type { ObservabilityConfig, LangfuseConfig, OpenTelemetryConfig, } from "./types/observability.js";
25
+ import { initializeOpenTelemetry, shutdownOpenTelemetry, flushOpenTelemetry, getLangfuseHealthStatus } from "./services/server/ai/observability/instrumentation.js";
26
+ export { initializeOpenTelemetry, shutdownOpenTelemetry, flushOpenTelemetry, getLangfuseHealthStatus, };
24
27
  export type { NeuroLinkMiddleware, MiddlewareContext, MiddlewareFactoryOptions, MiddlewarePreset, MiddlewareConfig, } from "./types/middlewareTypes.js";
25
28
  export { MiddlewareFactory } from "./middleware/factory.js";
26
29
  export declare const VERSION = "1.0.0";
@@ -82,10 +85,13 @@ export declare function createBestAIProvider(requestedProvider?: string, modelNa
82
85
  export { initializeMCPEcosystem, listMCPs, executeMCP, getMCPStats, mcpLogger, } from "./mcp/index.js";
83
86
  export type { McpMetadata, ExecutionContext, DiscoveredMcp, ToolInfo, ToolExecutionResult, LogLevel, } from "./mcp/index.js";
84
87
  export declare function initializeTelemetry(): Promise<boolean>;
85
- export declare function getTelemetryStatus(): {
88
+ export declare function getTelemetryStatus(): Promise<{
86
89
  enabled: boolean;
87
90
  initialized: boolean;
88
- };
91
+ endpoint?: string;
92
+ service?: string;
93
+ version?: string;
94
+ }>;
89
95
  export type { TextGenerationOptions, TextGenerationResult, AnalyticsData, EvaluationData, } from "./types/index.js";
90
96
  /**
91
97
  * BACKWARD COMPATIBILITY: Legacy generateText function
package/dist/index.js CHANGED
@@ -19,6 +19,9 @@ export { dynamicModelProvider } from "./core/dynamicModels.js";
19
19
  // Main NeuroLink wrapper class and diagnostic types
20
20
  import { NeuroLink } from "./neurolink.js";
21
21
  export { NeuroLink };
22
+ import { initializeOpenTelemetry, shutdownOpenTelemetry, flushOpenTelemetry, getLangfuseHealthStatus, } from "./services/server/ai/observability/instrumentation.js";
23
+ import { initializeTelemetry as init, getTelemetryStatus as getStatus, } from "./telemetry/index.js";
24
+ export { initializeOpenTelemetry, shutdownOpenTelemetry, flushOpenTelemetry, getLangfuseHealthStatus, };
22
25
  export { MiddlewareFactory } from "./middleware/factory.js";
23
26
  // Version
24
27
  export const VERSION = "1.0.0";
@@ -96,20 +99,18 @@ initializeMCPEcosystem, listMCPs, executeMCP, getMCPStats, mcpLogger, } from "./
96
99
  // Real-time Services (Phase 1) - Basic SSE functionality only
97
100
  // export { createEnhancedChatService } from './chat/index.js';
98
101
  // export type * from './services/types.js';
99
- // Optional Telemetry (Phase 2) - Conditional export based on feature flag
102
+ // Optional Telemetry (Phase 2) - Telemetry service initialization
100
103
  export async function initializeTelemetry() {
101
- if (process.env.NEUROLINK_TELEMETRY_ENABLED === "true") {
102
- const { initializeTelemetry: init } = await import("./telemetry/index.js");
104
+ try {
103
105
  const result = await init();
104
- return !!result; // Convert TelemetryService to boolean
106
+ return !!result;
105
107
  }
106
- return Promise.resolve(false);
107
- }
108
- export function getTelemetryStatus() {
109
- if (process.env.NEUROLINK_TELEMETRY_ENABLED === "true") {
110
- return { enabled: true, initialized: false };
108
+ catch {
109
+ return false;
111
110
  }
112
- return { enabled: false, initialized: false };
111
+ }
112
+ export async function getTelemetryStatus() {
113
+ return getStatus();
113
114
  }
114
115
  /**
115
116
  * BACKWARD COMPATIBILITY: Legacy generateText function