@juspay/neurolink 9.41.0 → 9.42.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 (189) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +7 -1
  3. package/dist/auth/anthropicOAuth.d.ts +18 -3
  4. package/dist/auth/anthropicOAuth.js +137 -4
  5. package/dist/auth/providers/firebase.js +5 -1
  6. package/dist/auth/providers/jwt.js +5 -1
  7. package/dist/auth/providers/workos.js +5 -1
  8. package/dist/auth/sessionManager.d.ts +1 -1
  9. package/dist/auth/sessionManager.js +58 -27
  10. package/dist/browser/neurolink.min.js +337 -318
  11. package/dist/cli/commands/mcp.js +3 -0
  12. package/dist/cli/commands/proxy.d.ts +2 -1
  13. package/dist/cli/commands/proxy.js +279 -16
  14. package/dist/cli/commands/task.js +3 -0
  15. package/dist/cli/factories/commandFactory.d.ts +2 -0
  16. package/dist/cli/factories/commandFactory.js +38 -0
  17. package/dist/cli/parser.js +4 -3
  18. package/dist/client/aiSdkAdapter.js +3 -0
  19. package/dist/client/streamingClient.js +30 -10
  20. package/dist/core/modules/GenerationHandler.js +3 -2
  21. package/dist/core/redisConversationMemoryManager.js +7 -3
  22. package/dist/evaluation/BatchEvaluator.js +4 -1
  23. package/dist/evaluation/hooks/observabilityHooks.js +5 -3
  24. package/dist/evaluation/pipeline/evaluationPipeline.d.ts +3 -2
  25. package/dist/evaluation/pipeline/evaluationPipeline.js +20 -8
  26. package/dist/evaluation/pipeline/strategies/batchStrategy.js +6 -3
  27. package/dist/evaluation/pipeline/strategies/samplingStrategy.js +18 -10
  28. package/dist/lib/auth/anthropicOAuth.d.ts +18 -3
  29. package/dist/lib/auth/anthropicOAuth.js +137 -4
  30. package/dist/lib/auth/providers/firebase.js +5 -1
  31. package/dist/lib/auth/providers/jwt.js +5 -1
  32. package/dist/lib/auth/providers/workos.js +5 -1
  33. package/dist/lib/auth/sessionManager.d.ts +1 -1
  34. package/dist/lib/auth/sessionManager.js +58 -27
  35. package/dist/lib/client/aiSdkAdapter.js +3 -0
  36. package/dist/lib/client/streamingClient.js +30 -10
  37. package/dist/lib/core/modules/GenerationHandler.js +3 -2
  38. package/dist/lib/core/redisConversationMemoryManager.js +7 -3
  39. package/dist/lib/evaluation/BatchEvaluator.js +4 -1
  40. package/dist/lib/evaluation/hooks/observabilityHooks.js +5 -3
  41. package/dist/lib/evaluation/pipeline/evaluationPipeline.d.ts +3 -2
  42. package/dist/lib/evaluation/pipeline/evaluationPipeline.js +20 -8
  43. package/dist/lib/evaluation/pipeline/strategies/batchStrategy.js +6 -3
  44. package/dist/lib/evaluation/pipeline/strategies/samplingStrategy.js +18 -10
  45. package/dist/lib/neurolink.d.ts +3 -2
  46. package/dist/lib/neurolink.js +260 -494
  47. package/dist/lib/observability/otelBridge.d.ts +2 -2
  48. package/dist/lib/observability/otelBridge.js +12 -3
  49. package/dist/lib/providers/amazonBedrock.js +2 -4
  50. package/dist/lib/providers/anthropic.d.ts +9 -5
  51. package/dist/lib/providers/anthropic.js +19 -14
  52. package/dist/lib/providers/anthropicBaseProvider.d.ts +3 -3
  53. package/dist/lib/providers/anthropicBaseProvider.js +5 -4
  54. package/dist/lib/providers/azureOpenai.d.ts +1 -1
  55. package/dist/lib/providers/azureOpenai.js +5 -4
  56. package/dist/lib/providers/googleAiStudio.js +30 -1
  57. package/dist/lib/providers/googleVertex.js +28 -6
  58. package/dist/lib/providers/huggingFace.d.ts +3 -3
  59. package/dist/lib/providers/huggingFace.js +6 -8
  60. package/dist/lib/providers/litellm.js +41 -29
  61. package/dist/lib/providers/mistral.js +2 -1
  62. package/dist/lib/providers/ollama.js +80 -23
  63. package/dist/lib/providers/openAI.js +3 -2
  64. package/dist/lib/providers/openRouter.js +2 -1
  65. package/dist/lib/providers/openaiCompatible.d.ts +4 -4
  66. package/dist/lib/providers/openaiCompatible.js +4 -4
  67. package/dist/lib/proxy/claudeFormat.d.ts +3 -2
  68. package/dist/lib/proxy/claudeFormat.js +25 -20
  69. package/dist/lib/proxy/cloaking/plugins/sessionIdentity.d.ts +2 -6
  70. package/dist/lib/proxy/cloaking/plugins/sessionIdentity.js +9 -33
  71. package/dist/lib/proxy/modelRouter.js +3 -0
  72. package/dist/lib/proxy/oauthFetch.d.ts +1 -1
  73. package/dist/lib/proxy/oauthFetch.js +65 -72
  74. package/dist/lib/proxy/proxyConfig.js +44 -24
  75. package/dist/lib/proxy/proxyEnv.d.ts +19 -0
  76. package/dist/lib/proxy/proxyEnv.js +73 -0
  77. package/dist/lib/proxy/proxyFetch.js +50 -4
  78. package/dist/lib/proxy/proxyTracer.d.ts +133 -0
  79. package/dist/lib/proxy/proxyTracer.js +645 -0
  80. package/dist/lib/proxy/rawStreamCapture.d.ts +10 -0
  81. package/dist/lib/proxy/rawStreamCapture.js +83 -0
  82. package/dist/lib/proxy/requestLogger.d.ts +32 -5
  83. package/dist/lib/proxy/requestLogger.js +406 -37
  84. package/dist/lib/proxy/sseInterceptor.d.ts +97 -0
  85. package/dist/lib/proxy/sseInterceptor.js +402 -0
  86. package/dist/lib/proxy/usageStats.d.ts +4 -3
  87. package/dist/lib/proxy/usageStats.js +25 -12
  88. package/dist/lib/rag/chunkers/MarkdownChunker.js +13 -5
  89. package/dist/lib/rag/chunking/markdownChunker.js +15 -6
  90. package/dist/lib/server/routes/claudeProxyRoutes.d.ts +7 -2
  91. package/dist/lib/server/routes/claudeProxyRoutes.js +1737 -508
  92. package/dist/lib/services/server/ai/observability/instrumentation.d.ts +7 -1
  93. package/dist/lib/services/server/ai/observability/instrumentation.js +240 -40
  94. package/dist/lib/tasks/backends/bullmqBackend.d.ts +1 -0
  95. package/dist/lib/tasks/backends/bullmqBackend.js +14 -7
  96. package/dist/lib/tasks/store/redisTaskStore.d.ts +1 -0
  97. package/dist/lib/tasks/store/redisTaskStore.js +34 -26
  98. package/dist/lib/tasks/taskManager.d.ts +3 -0
  99. package/dist/lib/tasks/taskManager.js +63 -30
  100. package/dist/lib/telemetry/index.d.ts +2 -1
  101. package/dist/lib/telemetry/index.js +2 -1
  102. package/dist/lib/telemetry/telemetryService.d.ts +3 -0
  103. package/dist/lib/telemetry/telemetryService.js +65 -5
  104. package/dist/lib/types/cli.d.ts +10 -0
  105. package/dist/lib/types/proxyTypes.d.ts +37 -5
  106. package/dist/lib/types/streamTypes.d.ts +25 -3
  107. package/dist/lib/utils/messageBuilder.js +3 -2
  108. package/dist/lib/utils/providerHealth.d.ts +18 -0
  109. package/dist/lib/utils/providerHealth.js +240 -9
  110. package/dist/lib/utils/providerUtils.js +14 -8
  111. package/dist/lib/utils/toolChoice.d.ts +4 -0
  112. package/dist/lib/utils/toolChoice.js +7 -0
  113. package/dist/neurolink.d.ts +3 -2
  114. package/dist/neurolink.js +260 -494
  115. package/dist/observability/otelBridge.d.ts +2 -2
  116. package/dist/observability/otelBridge.js +12 -3
  117. package/dist/providers/amazonBedrock.js +2 -4
  118. package/dist/providers/anthropic.d.ts +9 -5
  119. package/dist/providers/anthropic.js +19 -14
  120. package/dist/providers/anthropicBaseProvider.d.ts +3 -3
  121. package/dist/providers/anthropicBaseProvider.js +5 -4
  122. package/dist/providers/azureOpenai.d.ts +1 -1
  123. package/dist/providers/azureOpenai.js +5 -4
  124. package/dist/providers/googleAiStudio.js +30 -1
  125. package/dist/providers/googleVertex.js +28 -6
  126. package/dist/providers/huggingFace.d.ts +3 -3
  127. package/dist/providers/huggingFace.js +6 -7
  128. package/dist/providers/litellm.js +41 -29
  129. package/dist/providers/mistral.js +2 -1
  130. package/dist/providers/ollama.js +80 -23
  131. package/dist/providers/openAI.js +3 -2
  132. package/dist/providers/openRouter.js +2 -1
  133. package/dist/providers/openaiCompatible.d.ts +4 -4
  134. package/dist/providers/openaiCompatible.js +4 -3
  135. package/dist/proxy/claudeFormat.d.ts +3 -2
  136. package/dist/proxy/claudeFormat.js +25 -20
  137. package/dist/proxy/cloaking/plugins/sessionIdentity.d.ts +2 -6
  138. package/dist/proxy/cloaking/plugins/sessionIdentity.js +9 -33
  139. package/dist/proxy/modelRouter.js +3 -0
  140. package/dist/proxy/oauthFetch.d.ts +1 -1
  141. package/dist/proxy/oauthFetch.js +65 -72
  142. package/dist/proxy/proxyConfig.js +44 -24
  143. package/dist/proxy/proxyEnv.d.ts +19 -0
  144. package/dist/proxy/proxyEnv.js +72 -0
  145. package/dist/proxy/proxyFetch.js +50 -4
  146. package/dist/proxy/proxyTracer.d.ts +133 -0
  147. package/dist/proxy/proxyTracer.js +644 -0
  148. package/dist/proxy/rawStreamCapture.d.ts +10 -0
  149. package/dist/proxy/rawStreamCapture.js +82 -0
  150. package/dist/proxy/requestLogger.d.ts +32 -5
  151. package/dist/proxy/requestLogger.js +406 -37
  152. package/dist/proxy/sseInterceptor.d.ts +97 -0
  153. package/dist/proxy/sseInterceptor.js +401 -0
  154. package/dist/proxy/usageStats.d.ts +4 -3
  155. package/dist/proxy/usageStats.js +25 -12
  156. package/dist/rag/chunkers/MarkdownChunker.js +13 -5
  157. package/dist/rag/chunking/markdownChunker.js +15 -6
  158. package/dist/server/routes/claudeProxyRoutes.d.ts +7 -2
  159. package/dist/server/routes/claudeProxyRoutes.js +1737 -508
  160. package/dist/services/server/ai/observability/instrumentation.d.ts +7 -1
  161. package/dist/services/server/ai/observability/instrumentation.js +240 -40
  162. package/dist/tasks/backends/bullmqBackend.d.ts +1 -0
  163. package/dist/tasks/backends/bullmqBackend.js +14 -7
  164. package/dist/tasks/store/redisTaskStore.d.ts +1 -0
  165. package/dist/tasks/store/redisTaskStore.js +34 -26
  166. package/dist/tasks/taskManager.d.ts +3 -0
  167. package/dist/tasks/taskManager.js +63 -30
  168. package/dist/telemetry/index.d.ts +2 -1
  169. package/dist/telemetry/index.js +2 -1
  170. package/dist/telemetry/telemetryService.d.ts +3 -0
  171. package/dist/telemetry/telemetryService.js +65 -5
  172. package/dist/types/cli.d.ts +10 -0
  173. package/dist/types/proxyTypes.d.ts +37 -5
  174. package/dist/types/streamTypes.d.ts +25 -3
  175. package/dist/utils/messageBuilder.js +3 -2
  176. package/dist/utils/providerHealth.d.ts +18 -0
  177. package/dist/utils/providerHealth.js +240 -9
  178. package/dist/utils/providerUtils.js +14 -8
  179. package/dist/utils/toolChoice.d.ts +4 -0
  180. package/dist/utils/toolChoice.js +6 -0
  181. package/docs/assets/dashboards/neurolink-proxy-observability-dashboard.json +6609 -0
  182. package/docs/changelog.md +252 -0
  183. package/package.json +17 -1
  184. package/scripts/observability/check-proxy-telemetry.mjs +235 -0
  185. package/scripts/observability/docker-compose.proxy-observability.yaml +55 -0
  186. package/scripts/observability/import-openobserve-dashboard.mjs +240 -0
  187. package/scripts/observability/manage-local-openobserve.sh +184 -0
  188. package/scripts/observability/otel-collector.proxy-observability.yaml +78 -0
  189. package/scripts/observability/proxy-observability.env.example +23 -0
package/dist/neurolink.js CHANGED
@@ -22,7 +22,7 @@ import pLimit from "p-limit";
22
22
  import { ErrorCategory, ErrorSeverity } from "./constants/enums.js";
23
23
  import { CIRCUIT_BREAKER, CIRCUIT_BREAKER_RESET_MS, MEMORY_THRESHOLDS, NANOSECOND_TO_MS_DIVISOR, PERFORMANCE_THRESHOLDS, PROVIDER_TIMEOUTS, RETRY_ATTEMPTS, RETRY_DELAYS, TOOL_TIMEOUTS, } from "./constants/index.js";
24
24
  import { checkContextBudget } from "./context/budgetChecker.js";
25
- import { ContextCompactor, } from "./context/contextCompactor.js";
25
+ import { ContextCompactor } from "./context/contextCompactor.js";
26
26
  import { emergencyContentTruncation } from "./context/emergencyTruncation.js";
27
27
  import { getContextOverflowProvider, isContextOverflowError, parseProviderOverflowDetails, } from "./context/errorDetection.js";
28
28
  import { ContextBudgetExceededError } from "./context/errors.js";
@@ -44,24 +44,27 @@ import { ToolRouter } from "./mcp/routing/index.js";
44
44
  import { directToolsServer } from "./mcp/servers/agent/directToolsServer.js";
45
45
  import { inferAnnotations, isSafeToRetry } from "./mcp/toolAnnotations.js";
46
46
  import { MCPToolRegistry } from "./mcp/toolRegistry.js";
47
- import { initializeHippocampus, } from "./memory/hippocampusInitializer.js";
47
+ import { initializeHippocampus } from "./memory/hippocampusInitializer.js";
48
48
  import { createMemoryRetrievalTools } from "./memory/memoryRetrievalTools.js";
49
- import { getMetricsAggregator, MetricsAggregator, } from "./observability/metricsAggregator.js";
49
+ import { getMetricsAggregator, MetricsAggregator } from "./observability/metricsAggregator.js";
50
50
  import { SpanStatus, SpanType } from "./observability/types/spanTypes.js";
51
51
  import { SpanSerializer } from "./observability/utils/spanSerializer.js";
52
52
  import { flushOpenTelemetry, getLangfuseHealthStatus, initializeOpenTelemetry, isOpenTelemetryInitialized, setLangfuseContext, shutdownOpenTelemetry, } from "./services/server/ai/observability/instrumentation.js";
53
+ import { TaskManager } from "./tasks/taskManager.js";
54
+ import { createTaskTools } from "./tasks/tools/taskTools.js";
53
55
  import { ATTR } from "./telemetry/attributes.js";
54
56
  import { tracers } from "./telemetry/tracers.js";
57
+ import { CircuitBreakerOpenError } from "./types/circuitBreakerErrors.js";
55
58
  import { ConversationMemoryError } from "./types/conversation.js";
56
- import { AuthenticationError, AuthorizationError, InvalidModelError, } from "./types/errors.js";
57
- import { getConversationMessages, storeConversationTurn, } from "./utils/conversationMemory.js";
59
+ import { AuthenticationError, AuthorizationError, InvalidModelError } from "./types/errors.js";
60
+ import { getConversationMessages, storeConversationTurn } from "./utils/conversationMemory.js";
58
61
  // Enhanced error handling imports
59
62
  import { CircuitBreaker, ERROR_CODES, ErrorFactory, isAbortError, isRetriableError, logStructuredError, NeuroLinkError, withRetry, withTimeout, } from "./utils/errorHandling.js";
60
- import { CircuitBreakerOpenError } from "./types/circuitBreakerErrors.js";
61
63
  // Factory processing imports
62
64
  import { createCleanStreamOptions, enhanceTextGenerationOptions, processFactoryOptions, processStreamingFactoryOptions, validateFactoryConfig, } from "./utils/factoryProcessing.js";
63
65
  import { logger, mcpLogger } from "./utils/logger.js";
64
- import { createCustomToolServerInfo, detectCategory, } from "./utils/mcpDefaults.js";
66
+ import { createCustomToolServerInfo, detectCategory } from "./utils/mcpDefaults.js";
67
+ import { resolveModel } from "./utils/modelAliasResolver.js";
65
68
  // Import orchestration components
66
69
  import { ModelRouter } from "./utils/modelRouter.js";
67
70
  import { getBestProvider } from "./utils/providerUtils.js";
@@ -72,11 +75,8 @@ import { BinaryTaskClassifier } from "./utils/taskClassifier.js";
72
75
  // Transformation utilities
73
76
  import { extractToolNames, optimizeToolForCollection, transformAvailableTools, transformParamsForLogging, transformToolExecutions, transformToolExecutionsForMCP, transformToolsForMCP, transformToolsToDescriptions, transformToolsToExpectedFormat, } from "./utils/transformationUtils.js";
74
77
  import { isNonNullObject } from "./utils/typeUtils.js";
75
- import { resolveModel } from "./utils/modelAliasResolver.js";
76
78
  import { getWorkflow } from "./workflow/core/workflowRegistry.js";
77
79
  import { runWorkflow } from "./workflow/core/workflowRunner.js";
78
- import { TaskManager } from "./tasks/taskManager.js";
79
- import { createTaskTools } from "./tasks/tools/taskTools.js";
80
80
  /**
81
81
  * NL-002: Classify MCP error messages into categories for AI disambiguation.
82
82
  * Returns a human-readable error category based on error message content.
@@ -97,9 +97,7 @@ function classifyMcpErrorMessage(text) {
97
97
  lower.includes("access denied")) {
98
98
  return "permission_denied";
99
99
  }
100
- if (lower.includes("timeout") ||
101
- lower.includes("timed out") ||
102
- lower.includes("deadline exceeded")) {
100
+ if (lower.includes("timeout") || lower.includes("timed out") || lower.includes("deadline exceeded")) {
103
101
  return "timeout";
104
102
  }
105
103
  if (lower.includes("rate limit") ||
@@ -156,11 +154,7 @@ function isNonRetryableProviderError(error) {
156
154
  // Check for HTTP status codes on error objects (e.g., from Vercel AI SDK)
157
155
  if (error && typeof error === "object") {
158
156
  const err = error;
159
- const status = typeof err.status === "number"
160
- ? err.status
161
- : typeof err.statusCode === "number"
162
- ? err.statusCode
163
- : undefined;
157
+ const status = typeof err.status === "number" ? err.status : typeof err.statusCode === "number" ? err.statusCode : undefined;
164
158
  if (status && NON_RETRYABLE_HTTP_STATUS_CODES.includes(status)) {
165
159
  return true;
166
160
  }
@@ -206,8 +200,7 @@ export class NeuroLink {
206
200
  lastCompactionMessageCount = new Map();
207
201
  /** Extract sessionId from options context for compaction watermark keying */
208
202
  getCompactionSessionId(options) {
209
- return (options.context
210
- ?.sessionId || "__default__");
203
+ return options.context?.sessionId || "__default__";
211
204
  }
212
205
  // MCP Enhancement modules - wired into core execution path
213
206
  mcpToolResultCache;
@@ -270,28 +263,19 @@ export class NeuroLink {
270
263
  * Extract and set Langfuse context from options with proper async scoping
271
264
  */
272
265
  async setLangfuseContextFromOptions(options, callback) {
273
- if (options.context &&
274
- typeof options.context === "object" &&
275
- options.context !== null) {
266
+ if (options.context && typeof options.context === "object" && options.context !== null) {
276
267
  let callbackExecuted = false;
277
268
  try {
278
269
  const ctx = options.context;
279
270
  // Trigger context scoping if any meaningful Langfuse field is present
280
- if (ctx.userId ||
281
- ctx.sessionId ||
282
- ctx.conversationId ||
283
- ctx.requestId ||
284
- ctx.traceName ||
285
- ctx.metadata) {
271
+ if (ctx.userId || ctx.sessionId || ctx.conversationId || ctx.requestId || ctx.traceName || ctx.metadata) {
286
272
  // Build customAttributes from top-level metadata string/number/boolean fields
287
273
  let customAttributes;
288
274
  if (ctx.metadata && typeof ctx.metadata === "object") {
289
275
  const metaObj = ctx.metadata;
290
276
  const attrs = {};
291
277
  for (const [k, v] of Object.entries(metaObj)) {
292
- if (typeof v === "string" ||
293
- typeof v === "number" ||
294
- typeof v === "boolean") {
278
+ if (typeof v === "string" || typeof v === "number" || typeof v === "boolean") {
295
279
  attrs[k] = v;
296
280
  }
297
281
  }
@@ -303,14 +287,10 @@ export class NeuroLink {
303
287
  setLangfuseContext({
304
288
  userId: typeof ctx.userId === "string" ? ctx.userId : null,
305
289
  sessionId: typeof ctx.sessionId === "string" ? ctx.sessionId : null,
306
- conversationId: typeof ctx.conversationId === "string"
307
- ? ctx.conversationId
308
- : null,
290
+ conversationId: typeof ctx.conversationId === "string" ? ctx.conversationId : null,
309
291
  requestId: typeof ctx.requestId === "string" ? ctx.requestId : null,
310
292
  traceName: typeof ctx.traceName === "string" ? ctx.traceName : null,
311
- metadata: ctx.metadata && typeof ctx.metadata === "object"
312
- ? ctx.metadata
313
- : null,
293
+ metadata: ctx.metadata && typeof ctx.metadata === "object" ? ctx.metadata : null,
314
294
  ...(customAttributes !== undefined && { customAttributes }),
315
295
  }, async () => {
316
296
  try {
@@ -444,9 +424,7 @@ export class NeuroLink {
444
424
  logger.setEventEmitter(this.emitter);
445
425
  // Read tool cache duration from environment variables, with a default
446
426
  const cacheDurationEnv = process.env.NEUROLINK_TOOL_CACHE_DURATION;
447
- this.toolCacheDuration = cacheDurationEnv
448
- ? parseInt(cacheDurationEnv, 10)
449
- : 20000;
427
+ this.toolCacheDuration = cacheDurationEnv ? parseInt(cacheDurationEnv, 10) : 20000;
450
428
  const constructorStartTime = Date.now();
451
429
  const constructorHrTimeStart = process.hrtime.bigint();
452
430
  const constructorId = `neurolink-constructor-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
@@ -801,9 +779,7 @@ export class NeuroLink {
801
779
  // memory manager supports getSessionRaw.
802
780
  const memConfig = this.conversationMemoryConfig?.conversationMemory;
803
781
  const hasRedisConfig = !!memConfig?.redisConfig ||
804
- (memConfig &&
805
- "redis" in memConfig &&
806
- !!memConfig.redis) ||
782
+ (memConfig && "redis" in memConfig && !!memConfig.redis) ||
807
783
  process.env.STORAGE_TYPE === "redis";
808
784
  if (!memConfig?.enabled || !hasRedisConfig) {
809
785
  logger.debug("[NeuroLink] Skipping memory retrieval tools — requires Redis conversation memory");
@@ -834,13 +810,8 @@ export class NeuroLink {
834
810
  messages: [],
835
811
  });
836
812
  // Check if the tool itself reported an error
837
- const hasError = result &&
838
- typeof result === "object" &&
839
- "error" in result &&
840
- !("messages" in result);
841
- const errorMsg = hasError
842
- ? result.error
843
- : undefined;
813
+ const hasError = result && typeof result === "object" && "error" in result && !("messages" in result);
814
+ const errorMsg = hasError ? result.error : undefined;
844
815
  return {
845
816
  success: !hasError,
846
817
  data: result,
@@ -917,8 +888,7 @@ Current user's request: ${currentInput}`;
917
888
  * Respects both the global memory SDK config and per-call overrides.
918
889
  */
919
890
  shouldReadMemory(perCallMemory, userId) {
920
- if (!this.conversationMemoryConfig?.conversationMemory?.memory?.enabled ||
921
- !userId) {
891
+ if (!this.conversationMemoryConfig?.conversationMemory?.memory?.enabled || !userId) {
922
892
  return false;
923
893
  }
924
894
  if (perCallMemory?.enabled === false) {
@@ -934,8 +904,7 @@ Current user's request: ${currentInput}`;
934
904
  * Respects both the global memory SDK config and per-call overrides.
935
905
  */
936
906
  shouldWriteMemory(perCallMemory, userId, content) {
937
- if (!this.conversationMemoryConfig?.conversationMemory?.memory?.enabled ||
938
- !userId) {
907
+ if (!this.conversationMemoryConfig?.conversationMemory?.memory?.enabled || !userId) {
939
908
  return false;
940
909
  }
941
910
  if (!content?.trim()) {
@@ -1009,9 +978,7 @@ Current user's request: ${currentInput}`;
1009
978
  const writeOps = [client.add(userId, content)];
1010
979
  const writableAdditional = (additionalUsers || []).filter((u) => u.write !== false);
1011
980
  for (const user of writableAdditional) {
1012
- const addOptions = user.prompt || user.maxWords
1013
- ? { prompt: user.prompt, maxWords: user.maxWords }
1014
- : undefined;
981
+ const addOptions = user.prompt || user.maxWords ? { prompt: user.prompt, maxWords: user.maxWords } : undefined;
1015
982
  writeOps.push(client.add(user.userId, content, addOptions));
1016
983
  }
1017
984
  await Promise.all(writeOps);
@@ -1170,8 +1137,7 @@ Current user's request: ${currentInput}`;
1170
1137
  try {
1171
1138
  const langfuseConfig = this.observabilityConfig?.langfuse;
1172
1139
  // Check if we should use external provider mode - bypass enabled check
1173
- const useExternalProvider = langfuseConfig?.autoDetectExternalProvider === true ||
1174
- langfuseConfig?.useExternalTracerProvider === true;
1140
+ const useExternalProvider = langfuseConfig?.autoDetectExternalProvider === true || langfuseConfig?.useExternalTracerProvider === true;
1175
1141
  if (langfuseConfig?.enabled || useExternalProvider) {
1176
1142
  logger.debug(`[NeuroLink] 📊 LOG_POINT_C019_LANGFUSE_INIT_START`, {
1177
1143
  logPoint: "C019_LANGFUSE_INIT_START",
@@ -1186,9 +1152,7 @@ Current user's request: ${currentInput}`;
1186
1152
  initializeOpenTelemetry(langfuseConfig);
1187
1153
  const healthStatus = getLangfuseHealthStatus();
1188
1154
  const langfuseInitDurationNs = process.hrtime.bigint() - langfuseInitStartTime;
1189
- if (healthStatus.initialized &&
1190
- healthStatus.hasProcessor &&
1191
- healthStatus.isHealthy) {
1155
+ if (healthStatus.initialized && healthStatus.hasProcessor && healthStatus.isHealthy) {
1192
1156
  logger.debug(`[NeuroLink] ✅ LOG_POINT_C020_LANGFUSE_INIT_SUCCESS`, {
1193
1157
  logPoint: "C020_LANGFUSE_INIT_SUCCESS",
1194
1158
  constructorId,
@@ -1464,9 +1428,7 @@ Current user's request: ${currentInput}`;
1464
1428
  }
1465
1429
  catch (configError) {
1466
1430
  mcpLogger.warn("[NeuroLink] MCP configuration loading failed", {
1467
- error: configError instanceof Error
1468
- ? configError.message
1469
- : String(configError),
1431
+ error: configError instanceof Error ? configError.message : String(configError),
1470
1432
  });
1471
1433
  }
1472
1434
  }
@@ -1591,9 +1553,7 @@ Current user's request: ${currentInput}`;
1591
1553
  taskType: classification.type,
1592
1554
  routedProvider: route.provider,
1593
1555
  routedModel: route.model,
1594
- reason: error instanceof Error
1595
- ? error.message
1596
- : "Ollama service check failed",
1556
+ reason: error instanceof Error ? error.message : "Ollama service check failed",
1597
1557
  orchestrationTime: `${Date.now() - startTime}ms`,
1598
1558
  });
1599
1559
  return {}; // Return empty object to preserve existing fallback behavior
@@ -1729,9 +1689,7 @@ Current user's request: ${currentInput}`;
1729
1689
  taskType: classification.type,
1730
1690
  routedProvider: route.provider,
1731
1691
  routedModel: route.model,
1732
- reason: error instanceof Error
1733
- ? error.message
1734
- : "Ollama service check failed",
1692
+ reason: error instanceof Error ? error.message : "Ollama service check failed",
1735
1693
  orchestrationTime: `${Date.now() - startTime}ms`,
1736
1694
  });
1737
1695
  return {}; // Return empty object to preserve existing fallback behavior
@@ -1782,9 +1740,7 @@ Current user's request: ${currentInput}`;
1782
1740
  const anyOptions = optionsOrPrompt;
1783
1741
  if (anyOptions.messages && anyOptions.messages.length > 0) {
1784
1742
  const lastMessage = anyOptions.messages[anyOptions.messages.length - 1];
1785
- return typeof lastMessage.content === "string"
1786
- ? lastMessage.content
1787
- : JSON.stringify(lastMessage.content);
1743
+ return typeof lastMessage.content === "string" ? lastMessage.content : JSON.stringify(lastMessage.content);
1788
1744
  }
1789
1745
  // Handle input.text format
1790
1746
  return optionsOrPrompt.input?.text || "";
@@ -1876,8 +1832,7 @@ Current user's request: ${currentInput}`;
1876
1832
  endpoint: otelConfig.endpoint,
1877
1833
  serviceName: otelConfig.serviceName,
1878
1834
  }
1879
- : isOpenTelemetryInitialized() ||
1880
- process.env.OTEL_EXPORTER_OTLP_ENDPOINT
1835
+ : isOpenTelemetryInitialized() || process.env.OTEL_EXPORTER_OTLP_ENDPOINT
1881
1836
  ? {
1882
1837
  enabled: isOpenTelemetryInitialized(),
1883
1838
  endpoint: process.env.OTEL_EXPORTER_OTLP_ENDPOINT,
@@ -2019,9 +1974,7 @@ Current user's request: ${currentInput}`;
2019
1974
  const result = data.result;
2020
1975
  const usage = result?.usage;
2021
1976
  const analytics = result?.analytics;
2022
- const provider = data.provider ||
2023
- result?.provider ||
2024
- "unknown";
1977
+ const provider = data.provider || result?.provider || "unknown";
2025
1978
  const model = result?.model || "unknown";
2026
1979
  const responseTime = data.responseTime || 0;
2027
1980
  const traceCtx = this._metricsTraceContext;
@@ -2040,9 +1993,7 @@ Current user's request: ${currentInput}`;
2040
1993
  span.parentSpanId = undefined;
2041
1994
  }
2042
1995
  // Mark failed generations with ERROR status so metrics count them correctly
2043
- const spanStatus = data.success === false || data.error
2044
- ? SpanStatus.ERROR
2045
- : SpanStatus.OK;
1996
+ const spanStatus = data.success === false || data.error ? SpanStatus.ERROR : SpanStatus.OK;
2046
1997
  span = SpanSerializer.endSpan(span, spanStatus, data.error ? String(data.error) : undefined);
2047
1998
  span.durationMs = responseTime;
2048
1999
  if (usage) {
@@ -2078,9 +2029,7 @@ Current user's request: ${currentInput}`;
2078
2029
  const content = result?.content || result?.text;
2079
2030
  if (content) {
2080
2031
  span = SpanSerializer.updateAttributes(span, {
2081
- output: content.length > 5000
2082
- ? content.substring(0, 5000) + "...[truncated]"
2083
- : content,
2032
+ output: content.length > 5000 ? content.substring(0, 5000) + "...[truncated]" : content,
2084
2033
  });
2085
2034
  }
2086
2035
  this.metricsAggregator.recordSpan(span);
@@ -2119,18 +2068,14 @@ Current user's request: ${currentInput}`;
2119
2068
  if (data.prompt) {
2120
2069
  const promptStr = String(data.prompt);
2121
2070
  span = SpanSerializer.updateAttributes(span, {
2122
- input: promptStr.length > 5000
2123
- ? promptStr.substring(0, 5000) + "...[truncated]"
2124
- : promptStr,
2071
+ input: promptStr.length > 5000 ? promptStr.substring(0, 5000) + "...[truncated]" : promptStr,
2125
2072
  });
2126
2073
  }
2127
2074
  // Record streamed output (truncated for safety)
2128
2075
  const streamContent = data.content;
2129
2076
  if (streamContent) {
2130
2077
  span = SpanSerializer.updateAttributes(span, {
2131
- output: streamContent.length > 5000
2132
- ? streamContent.substring(0, 5000) + "...[truncated]"
2133
- : streamContent,
2078
+ output: streamContent.length > 5000 ? streamContent.substring(0, 5000) + "...[truncated]" : streamContent,
2134
2079
  });
2135
2080
  }
2136
2081
  // Enrich stream span with token usage if available
@@ -2147,8 +2092,7 @@ Current user's request: ${currentInput}`;
2147
2092
  const pricing = tokenTracker.getModelPricing(model);
2148
2093
  if (pricing) {
2149
2094
  const inputCost = ((usage.input || 0) / 1_000_000) * pricing.inputPricePerMillion;
2150
- const outputCost = ((usage.output || 0) / 1_000_000) *
2151
- pricing.outputPricePerMillion;
2095
+ const outputCost = ((usage.output || 0) / 1_000_000) * pricing.outputPricePerMillion;
2152
2096
  const totalCost = inputCost + outputCost;
2153
2097
  if (totalCost > 0) {
2154
2098
  span = SpanSerializer.enrichWithCost(span, {
@@ -2183,8 +2127,7 @@ Current user's request: ${currentInput}`;
2183
2127
  span = SpanSerializer.endSpan(span, success ? SpanStatus.OK : SpanStatus.ERROR);
2184
2128
  span.durationMs = responseTime;
2185
2129
  if (!success && data.error) {
2186
- span.statusMessage =
2187
- data.error.message || String(data.error);
2130
+ span.statusMessage = data.error.message || String(data.error);
2188
2131
  }
2189
2132
  if (data.result) {
2190
2133
  try {
@@ -2341,10 +2284,7 @@ Current user's request: ${currentInput}`;
2341
2284
  // The generation span will be the root (no parentSpanId).
2342
2285
  // Tool spans will be children of the root span via rootSpanId.
2343
2286
  const metricsTraceId = crypto.randomUUID().replace(/-/g, "");
2344
- const metricsRootSpanId = crypto
2345
- .randomUUID()
2346
- .replace(/-/g, "")
2347
- .substring(0, 16);
2287
+ const metricsRootSpanId = crypto.randomUUID().replace(/-/g, "").substring(0, 16);
2348
2288
  // Scope trace context to this request via AsyncLocalStorage
2349
2289
  // so concurrent generate/stream calls don't race.
2350
2290
  return metricsTraceContextStorage.run({ traceId: metricsTraceId, parentSpanId: metricsRootSpanId }, async () => {
@@ -2352,24 +2292,18 @@ Current user's request: ${currentInput}`;
2352
2292
  const originalPrompt = this._extractOriginalPrompt(optionsOrPrompt);
2353
2293
  // Convert string prompt to full options
2354
2294
  // Shallow-copy caller's object to avoid mutating their original reference
2355
- const options = typeof optionsOrPrompt === "string"
2356
- ? { input: { text: optionsOrPrompt } }
2357
- : { ...optionsOrPrompt };
2295
+ const options = typeof optionsOrPrompt === "string" ? { input: { text: optionsOrPrompt } } : { ...optionsOrPrompt };
2358
2296
  // NL-004: Resolve model aliases/deprecations before processing
2359
2297
  options.model = resolveModel(options.model, this.modelAliasConfig);
2360
2298
  // MCP Enhancement: propagate disableToolCache to tool execution
2361
- this._disableToolCacheForCurrentRequest =
2362
- !!options.disableToolCache;
2299
+ this._disableToolCacheForCurrentRequest = !!options.disableToolCache;
2363
2300
  // Set span attributes for observability
2364
2301
  generateSpan.setAttribute("neurolink.provider", options.provider || "default");
2365
2302
  generateSpan.setAttribute("neurolink.model", options.model || "default");
2366
- generateSpan.setAttribute("neurolink.input_length", typeof optionsOrPrompt === "string"
2367
- ? optionsOrPrompt.length
2368
- : options.input?.text?.length || 0);
2303
+ generateSpan.setAttribute("neurolink.input_length", typeof optionsOrPrompt === "string" ? optionsOrPrompt.length : options.input?.text?.length || 0);
2369
2304
  generateSpan.setAttribute("neurolink.has_tools", !!(options.tools && Object.keys(options.tools).length > 0));
2370
2305
  // Validate prompt
2371
- if (!options.input?.text ||
2372
- typeof options.input.text !== "string") {
2306
+ if (!options.input?.text || typeof options.input.text !== "string") {
2373
2307
  throw new Error("Input text is required and must be a non-empty string");
2374
2308
  }
2375
2309
  // Check budget limit before making API call
@@ -2399,10 +2333,9 @@ Current user's request: ${currentInput}`;
2399
2333
  ...options.middleware?.middlewareConfig?.lifecycle,
2400
2334
  enabled: true,
2401
2335
  config: {
2402
- ...options.middleware?.middlewareConfig?.lifecycle
2403
- ?.config,
2404
- onFinish: options.onFinish,
2405
- onError: options.onError,
2336
+ ...options.middleware?.middlewareConfig?.lifecycle?.config,
2337
+ ...(options.onFinish !== undefined ? { onFinish: options.onFinish } : {}),
2338
+ ...(options.onError !== undefined ? { onError: options.onError } : {}),
2406
2339
  },
2407
2340
  },
2408
2341
  },
@@ -2421,9 +2354,7 @@ Current user's request: ${currentInput}`;
2421
2354
  }
2422
2355
  catch (err) {
2423
2356
  // Rethrow auth errors as-is; wrap anything else
2424
- if (err instanceof Error &&
2425
- "feature" in err &&
2426
- err.feature === "Auth") {
2357
+ if (err instanceof Error && "feature" in err && err.feature === "Auth") {
2427
2358
  throw err;
2428
2359
  }
2429
2360
  throw AuthError.create("PROVIDER_ERROR", `Auth token validation failed: ${err instanceof Error ? err.message : String(err)}`);
@@ -2483,9 +2414,7 @@ Current user's request: ${currentInput}`;
2483
2414
  return await this.setLangfuseContextFromOptions(options, async () => {
2484
2415
  const startTime = Date.now();
2485
2416
  // Apply orchestration if enabled and no specific provider/model requested
2486
- if (this.enableOrchestration &&
2487
- !options.provider &&
2488
- !options.model) {
2417
+ if (this.enableOrchestration && !options.provider && !options.model) {
2489
2418
  try {
2490
2419
  const orchestratedOptions = await this.applyOrchestration(options);
2491
2420
  logger.debug("Orchestration applied", {
@@ -2503,9 +2432,7 @@ Current user's request: ${currentInput}`;
2503
2432
  }
2504
2433
  catch (error) {
2505
2434
  logger.warn("Orchestration failed, continuing with original options", {
2506
- error: error instanceof Error
2507
- ? error.message
2508
- : String(error),
2435
+ error: error instanceof Error ? error.message : String(error),
2509
2436
  originalProvider: options.provider || "auto",
2510
2437
  });
2511
2438
  // Continue with original options if orchestration fails
@@ -2550,8 +2477,7 @@ Current user's request: ${currentInput}`;
2550
2477
  `This tool searches your local knowledge base of pre-loaded documents and is the primary source of truth.`,
2551
2478
  `Do NOT use websearchGrounding or any web search tools when the answer can be found in the loaded documents.`,
2552
2479
  ].join(" ");
2553
- options.systemPrompt =
2554
- (options.systemPrompt || "") + ragSystemInstruction;
2480
+ options.systemPrompt = (options.systemPrompt || "") + ragSystemInstruction;
2555
2481
  logger.info("[RAG] Tool injected into generate()", {
2556
2482
  toolName: ragResult.toolName,
2557
2483
  filesLoaded: ragResult.filesLoaded,
@@ -2560,15 +2486,12 @@ Current user's request: ${currentInput}`;
2560
2486
  }
2561
2487
  catch (error) {
2562
2488
  logger.warn("[RAG] Failed to prepare RAG tool, continuing without RAG", {
2563
- error: error instanceof Error
2564
- ? error.message
2565
- : String(error),
2489
+ error: error instanceof Error ? error.message : String(error),
2566
2490
  });
2567
2491
  }
2568
2492
  }
2569
2493
  // Memory retrieval for generate path
2570
- if (this.shouldReadMemory(options.memory, options.context?.userId) &&
2571
- options.context?.userId) {
2494
+ if (this.shouldReadMemory(options.memory, options.context?.userId) && options.context?.userId) {
2572
2495
  try {
2573
2496
  options.input.text = await this.retrieveMemory(options.input.text, options.context.userId, options.memory?.additionalUsers);
2574
2497
  logger.debug("Memory retrieval successful (generate)");
@@ -2615,8 +2538,7 @@ Current user's request: ${currentInput}`;
2615
2538
  if (extraContext.sessionId || extraContext.userId) {
2616
2539
  baseOptions.context = {
2617
2540
  ...baseOptions.context,
2618
- ...(extraContext.sessionId &&
2619
- !baseOptions.context?.sessionId
2541
+ ...(extraContext.sessionId && !baseOptions.context?.sessionId
2620
2542
  ? { sessionId: extraContext.sessionId }
2621
2543
  : {}),
2622
2544
  ...(extraContext.userId && !baseOptions.context?.userId
@@ -2628,8 +2550,7 @@ Current user's request: ${currentInput}`;
2628
2550
  const textOptions = enhanceTextGenerationOptions(baseOptions, factoryResult);
2629
2551
  // Pass conversation memory config if available
2630
2552
  if (this.conversationMemory) {
2631
- textOptions.conversationMemoryConfig =
2632
- this.conversationMemory.config;
2553
+ textOptions.conversationMemoryConfig = this.conversationMemory.config;
2633
2554
  // Include original prompt for context summarization
2634
2555
  textOptions.originalPrompt = originalPrompt;
2635
2556
  }
@@ -2652,8 +2573,7 @@ Current user's request: ${currentInput}`;
2652
2573
  toolsUsed: textResult.toolsUsed,
2653
2574
  timestamp: Date.now(),
2654
2575
  result: textResult, // Enhanced: include full result
2655
- prompt: options.input?.text ||
2656
- options.prompt,
2576
+ prompt: options.input?.text || options.prompt,
2657
2577
  temperature: textOptions.temperature,
2658
2578
  maxTokens: textOptions.maxTokens,
2659
2579
  });
@@ -2686,10 +2606,8 @@ Current user's request: ${currentInput}`;
2686
2606
  ? {
2687
2607
  ...textResult.evaluation,
2688
2608
  isOffTopic: textResult.evaluation.isOffTopic ?? false,
2689
- alertSeverity: textResult.evaluation.alertSeverity ??
2690
- "none",
2691
- reasoning: textResult.evaluation.reasoning ??
2692
- "No evaluation provided",
2609
+ alertSeverity: textResult.evaluation.alertSeverity ?? "none",
2610
+ reasoning: textResult.evaluation.reasoning ?? "No evaluation provided",
2693
2611
  evaluationModel: textResult.evaluation.evaluationModel ?? "unknown",
2694
2612
  evaluationTime: textResult.evaluation.evaluationTime ?? Date.now(),
2695
2613
  evaluationDomain: textResult.evaluation.evaluationDomain ??
@@ -2704,8 +2622,7 @@ Current user's request: ${currentInput}`;
2704
2622
  ...(textResult.retries && { retries: textResult.retries }),
2705
2623
  };
2706
2624
  // Accumulate session cost for budget tracking
2707
- if (generateResult.analytics?.cost &&
2708
- generateResult.analytics.cost > 0) {
2625
+ if (generateResult.analytics?.cost && generateResult.analytics.cost > 0) {
2709
2626
  this._sessionCostUsd += generateResult.analytics.cost;
2710
2627
  }
2711
2628
  this.scheduleGenerateMemoryStorage(options, originalPrompt, generateResult);
@@ -2733,9 +2650,7 @@ Current user's request: ${currentInput}`;
2733
2650
  const errProvider = typeof optionsOrPrompt === "object"
2734
2651
  ? optionsOrPrompt.provider || "unknown"
2735
2652
  : "unknown";
2736
- const errModel = typeof optionsOrPrompt === "object"
2737
- ? optionsOrPrompt.model || "unknown"
2738
- : "unknown";
2653
+ const errModel = typeof optionsOrPrompt === "object" ? optionsOrPrompt.model || "unknown" : "unknown";
2739
2654
  try {
2740
2655
  this.emitter.emit("generation:end", {
2741
2656
  provider: errProvider,
@@ -2836,11 +2751,8 @@ Current user's request: ${currentInput}`;
2836
2751
  ?.filter((m) => m.role === "user" || m.role === "assistant")
2837
2752
  .map((m) => ({
2838
2753
  role: m.role,
2839
- content: typeof m.content === "string"
2840
- ? m.content
2841
- : JSON.stringify(m.content),
2842
- })) ??
2843
- options.conversationHistory,
2754
+ content: typeof m.content === "string" ? m.content : JSON.stringify(m.content),
2755
+ })) ?? options.conversationHistory,
2844
2756
  timeout: options.timeout,
2845
2757
  verbose: false,
2846
2758
  metadata: options.context,
@@ -2850,10 +2762,8 @@ Current user's request: ${currentInput}`;
2850
2762
  // Primary output (backward compatible) - use the original best response
2851
2763
  content: workflowResult.content,
2852
2764
  // Provider info from selected response
2853
- provider: workflowResult.selectedResponse?.provider ||
2854
- workflowConfig.models[0]?.provider,
2855
- model: workflowResult.selectedResponse?.model ||
2856
- workflowConfig.models[0]?.model,
2765
+ provider: workflowResult.selectedResponse?.provider || workflowConfig.models[0]?.provider,
2766
+ model: workflowResult.selectedResponse?.model || workflowConfig.models[0]?.model,
2857
2767
  // Basic usage info
2858
2768
  usage: workflowResult.usage
2859
2769
  ? {
@@ -2935,11 +2845,8 @@ Current user's request: ${currentInput}`;
2935
2845
  ?.filter((m) => m.role === "user" || m.role === "assistant")
2936
2846
  .map((m) => ({
2937
2847
  role: m.role,
2938
- content: typeof m.content === "string"
2939
- ? m.content
2940
- : JSON.stringify(m.content),
2941
- })) ??
2942
- options.conversationHistory,
2848
+ content: typeof m.content === "string" ? m.content : JSON.stringify(m.content),
2849
+ })) ?? options.conversationHistory,
2943
2850
  timeout: options.timeout,
2944
2851
  verbose: false,
2945
2852
  metadata: options.context,
@@ -3063,9 +2970,7 @@ Current user's request: ${currentInput}`;
3063
2970
  */
3064
2971
  async generateText(options) {
3065
2972
  // Validate required parameters for backward compatibility
3066
- if (!options.prompt ||
3067
- typeof options.prompt !== "string" ||
3068
- options.prompt.trim() === "") {
2973
+ if (!options.prompt || typeof options.prompt !== "string" || options.prompt.trim() === "") {
3069
2974
  throw new Error("GenerateText options must include prompt as a non-empty string");
3070
2975
  }
3071
2976
  // NL-004: Resolve model aliases/deprecations before processing
@@ -3148,9 +3053,7 @@ Current user's request: ${currentInput}`;
3148
3053
  // the catch block needs the originals for a more effective retry
3149
3054
  if (this.conversationMemory) {
3150
3055
  const originalMessages = await getConversationMessages(this.conversationMemory, options);
3151
- options._originalConversationMessages = originalMessages
3152
- ? [...originalMessages]
3153
- : undefined;
3056
+ options._originalConversationMessages = originalMessages ? [...originalMessages] : undefined;
3154
3057
  }
3155
3058
  const directResult = await this.directProviderGeneration(options);
3156
3059
  logger.debug(`[${functionTag}] Direct generation successful`);
@@ -3200,8 +3103,7 @@ Current user's request: ${currentInput}`;
3200
3103
  // IMPROVEMENT 1: Extract actual token count from provider error if available
3201
3104
  const actualOverflow = parseProviderOverflowDetails(error);
3202
3105
  // IMPROVEMENT 2: Use ORIGINAL messages (not already-compacted ones)
3203
- const originalMessages = options._originalConversationMessages ??
3204
- (await getConversationMessages(this.conversationMemory, options));
3106
+ const originalMessages = options._originalConversationMessages ?? (await getConversationMessages(this.conversationMemory, options));
3205
3107
  // IMPROVEMENT 3: Calculate precise reduction target
3206
3108
  const recoveryBudget = checkContextBudget({
3207
3109
  provider: options.provider || "openai",
@@ -3211,16 +3113,12 @@ Current user's request: ${currentInput}`;
3211
3113
  systemPrompt: options.systemPrompt,
3212
3114
  });
3213
3115
  // Use provider's reported token count if available (more accurate than our estimate)
3214
- const actualTokens = actualOverflow?.actualTokens ??
3215
- recoveryBudget.estimatedInputTokens;
3216
- const budgetTokens = actualOverflow?.budgetTokens ??
3217
- recoveryBudget.availableInputTokens;
3116
+ const actualTokens = actualOverflow?.actualTokens ?? recoveryBudget.estimatedInputTokens;
3117
+ const budgetTokens = actualOverflow?.budgetTokens ?? recoveryBudget.availableInputTokens;
3218
3118
  // Target = 70% of budget (aggressive safety margin for recovery)
3219
3119
  const compactionTarget = Math.floor(budgetTokens * 0.7);
3220
3120
  // IMPROVEMENT 4: Calculate adaptive truncation fraction from actual numbers
3221
- const requiredReduction = actualTokens > 0
3222
- ? (actualTokens - compactionTarget) / actualTokens
3223
- : 0.5;
3121
+ const requiredReduction = actualTokens > 0 ? (actualTokens - compactionTarget) / actualTokens : 0.5;
3224
3122
  const compactor = new ContextCompactor({
3225
3123
  enableSummarize: false, // Skip LLM call for recovery (speed)
3226
3124
  enablePrune: true,
@@ -3273,9 +3171,7 @@ Current user's request: ${currentInput}`;
3273
3171
  throw retryError;
3274
3172
  }
3275
3173
  logger.error(`[${functionTag}] Recovery attempt failed`, {
3276
- error: retryError instanceof Error
3277
- ? retryError.message
3278
- : String(retryError),
3174
+ error: retryError instanceof Error ? retryError.message : String(retryError),
3279
3175
  });
3280
3176
  }
3281
3177
  }
@@ -3288,8 +3184,7 @@ Current user's request: ${currentInput}`;
3288
3184
  logger.info(`[${functionTag}] Generation aborted — storing conversation turn for title generation`, {
3289
3185
  hasMemory: !!this.conversationMemory,
3290
3186
  memoryType: this.conversationMemory?.constructor?.name || "NONE",
3291
- sessionId: options.context?.sessionId ||
3292
- "unknown",
3187
+ sessionId: options.context?.sessionId || "unknown",
3293
3188
  });
3294
3189
  try {
3295
3190
  const abortedResult = {
@@ -3302,9 +3197,7 @@ Current user's request: ${currentInput}`;
3302
3197
  }
3303
3198
  catch (storeError) {
3304
3199
  logger.warn(`[${functionTag}] Failed to store conversation turn after abort`, {
3305
- error: storeError instanceof Error
3306
- ? storeError.message
3307
- : String(storeError),
3200
+ error: storeError instanceof Error ? storeError.message : String(storeError),
3308
3201
  });
3309
3202
  }
3310
3203
  }
@@ -3321,9 +3214,7 @@ Current user's request: ${currentInput}`;
3321
3214
  catch (spanError) {
3322
3215
  internalSpan.setStatus({
3323
3216
  code: SpanStatusCode.ERROR,
3324
- message: spanError instanceof Error
3325
- ? spanError.message
3326
- : String(spanError),
3217
+ message: spanError instanceof Error ? spanError.message : String(spanError),
3327
3218
  });
3328
3219
  throw spanError;
3329
3220
  }
@@ -3403,8 +3294,7 @@ Current user's request: ${currentInput}`;
3403
3294
  * Attempt MCP generation with retry logic
3404
3295
  */
3405
3296
  async attemptMCPGeneration(options, generateInternalId, generateInternalStartTime, generateInternalHrTimeStart, functionTag) {
3406
- if (!options.disableTools &&
3407
- !(options.tts?.enabled && !options.tts?.useAiResponse)) {
3297
+ if (!options.disableTools && !(options.tts?.enabled && !options.tts?.useAiResponse)) {
3408
3298
  return await this.performMCPGenerationRetries(options, generateInternalId, generateInternalStartTime, generateInternalHrTimeStart, functionTag);
3409
3299
  }
3410
3300
  return null;
@@ -3426,9 +3316,7 @@ Current user's request: ${currentInput}`;
3426
3316
  try {
3427
3317
  logger.debug(`[${functionTag}] Attempting MCP generation (attempt ${attempt}/${maxAttempts})...`);
3428
3318
  const mcpResult = await this.tryMCPGeneration(options);
3429
- if (mcpResult &&
3430
- (mcpResult.content ||
3431
- (mcpResult.toolExecutions && mcpResult.toolExecutions.length > 0))) {
3319
+ if (mcpResult && (mcpResult.content || (mcpResult.toolExecutions && mcpResult.toolExecutions.length > 0))) {
3432
3320
  logger.debug(`[${functionTag}] MCP generation successful on attempt ${attempt}`, {
3433
3321
  contentLength: mcpResult.content?.length || 0,
3434
3322
  toolsUsed: mcpResult.toolsUsed?.length || 0,
@@ -3459,11 +3347,7 @@ Current user's request: ${currentInput}`;
3459
3347
  // NL-007: Record retry error for observability
3460
3348
  retryCount++;
3461
3349
  const errMsg = error instanceof Error ? error.message : String(error);
3462
- const errCode = error instanceof NeuroLinkError
3463
- ? error.code
3464
- : error instanceof Error
3465
- ? error.name
3466
- : "UNKNOWN";
3350
+ const errCode = error instanceof NeuroLinkError ? error.code : error instanceof Error ? error.name : "UNKNOWN";
3467
3351
  retryErrors.push({ code: errCode, message: errMsg.substring(0, 500) });
3468
3352
  logger.debug(`[${functionTag}] MCP generation failed on attempt ${attempt}/${maxAttempts}`, {
3469
3353
  error: errMsg,
@@ -3482,11 +3366,8 @@ Current user's request: ${currentInput}`;
3482
3366
  const isNonRetryable = isContextOverflowError(error) ||
3483
3367
  isToolError ||
3484
3368
  isNonRetryableProviderError(error) ||
3485
- (error instanceof Error &&
3486
- error.isRetryable ===
3487
- false) ||
3488
- (error instanceof Error &&
3489
- error.statusCode === 400);
3369
+ (error instanceof Error && error.isRetryable === false) ||
3370
+ (error instanceof Error && error.statusCode === 400);
3490
3371
  if (isNonRetryable) {
3491
3372
  logger.debug(`[${functionTag}] Non-retryable error detected, skipping remaining retries`);
3492
3373
  break;
@@ -3522,8 +3403,7 @@ Current user's request: ${currentInput}`;
3522
3403
  throw new DOMException("The operation was aborted", "AbortError");
3523
3404
  }
3524
3405
  // 🚀 EXHAUSTIVE LOGGING POINT T001: TRY MCP GENERATION ENTRY
3525
- const requestId = options.context?.requestId ||
3526
- "unknown";
3406
+ const requestId = options.context?.requestId || "unknown";
3527
3407
  const tryMCPId = `try-mcp-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
3528
3408
  const tryMCPStartTime = Date.now();
3529
3409
  const tryMCPHrTimeStart = process.hrtime.bigint();
@@ -3551,9 +3431,7 @@ Current user's request: ${currentInput}`;
3551
3431
  }
3552
3432
  // Context creation removed - was never used
3553
3433
  // Determine provider
3554
- const providerName = options.provider === "auto" || !options.provider
3555
- ? await getBestProvider()
3556
- : options.provider;
3434
+ const providerName = options.provider === "auto" || !options.provider ? await getBestProvider() : options.provider;
3557
3435
  // Get available tools
3558
3436
  let availableTools = await this.getAllAvailableTools();
3559
3437
  // NL-001: Filter out tools with OPEN circuit breakers
@@ -3563,8 +3441,7 @@ Current user's request: ${currentInput}`;
3563
3441
  availableTools = availableTools.filter((t) => cbFilteredNames.has(t.name));
3564
3442
  // Apply per-call tool filtering for system prompt tool descriptions
3565
3443
  availableTools = this.applyToolInfoFiltering(availableTools, options);
3566
- const targetTool = availableTools.find((t) => t.name.includes("SuccessRateSRByTime") ||
3567
- t.name.includes("juspay-analytics"));
3444
+ const targetTool = availableTools.find((t) => t.name.includes("SuccessRateSRByTime") || t.name.includes("juspay-analytics"));
3568
3445
  logger.debug("Available tools for AI prompt generation", {
3569
3446
  toolsCount: availableTools.length,
3570
3447
  toolNames: availableTools.map((t) => t.name),
@@ -3598,9 +3475,7 @@ Current user's request: ${currentInput}`;
3598
3475
  logger.debug("[Observability] System prompt metadata", {
3599
3476
  requestId,
3600
3477
  systemPromptLength: enhancedSystemPrompt.length,
3601
- systemPromptHash: enhancedSystemPrompt.length > 0
3602
- ? `sha256:${enhancedSystemPrompt.slice(0, 8)}...`
3603
- : "empty",
3478
+ systemPromptHash: enhancedSystemPrompt.length > 0 ? `sha256:${enhancedSystemPrompt.slice(0, 8)}...` : "empty",
3604
3479
  hasCustomSystemPrompt: !!options.systemPrompt,
3605
3480
  });
3606
3481
  // Get conversation messages for context
@@ -3627,9 +3502,7 @@ Current user's request: ${currentInput}`;
3627
3502
  index: i,
3628
3503
  role: msg.role,
3629
3504
  contentLength,
3630
- contentPreview: typeof msg.content === "string"
3631
- ? msg.content.substring(0, 200)
3632
- : "[multimodal]",
3505
+ contentPreview: typeof msg.content === "string" ? msg.content.substring(0, 200) : "[multimodal]",
3633
3506
  };
3634
3507
  }),
3635
3508
  });
@@ -3670,8 +3543,7 @@ Current user's request: ${currentInput}`;
3670
3543
  const compactionSessionId = this.getCompactionSessionId(options);
3671
3544
  if (budgetResult.shouldCompact &&
3672
3545
  this.conversationMemory &&
3673
- messageCount >
3674
- (this.lastCompactionMessageCount.get(compactionSessionId) ?? 0)) {
3546
+ messageCount > (this.lastCompactionMessageCount.get(compactionSessionId) ?? 0)) {
3675
3547
  logger.info("[NeuroLink] Context budget exceeded, triggering auto-compaction", {
3676
3548
  usageRatio: budgetResult.usageRatio,
3677
3549
  estimatedTokens: budgetResult.estimatedInputTokens,
@@ -3679,10 +3551,8 @@ Current user's request: ${currentInput}`;
3679
3551
  });
3680
3552
  const compactor = new ContextCompactor({
3681
3553
  provider: providerName,
3682
- summarizationProvider: this.conversationMemoryConfig?.conversationMemory
3683
- ?.summarizationProvider,
3684
- summarizationModel: this.conversationMemoryConfig?.conversationMemory
3685
- ?.summarizationModel,
3554
+ summarizationProvider: this.conversationMemoryConfig?.conversationMemory?.summarizationProvider,
3555
+ summarizationModel: this.conversationMemoryConfig?.conversationMemory?.summarizationModel,
3686
3556
  });
3687
3557
  const compactionResult = await compactor.compact(conversationMessages, budgetResult.availableInputTokens, this.conversationMemoryConfig?.conversationMemory, requestId);
3688
3558
  if (compactionResult.compacted) {
@@ -3862,18 +3732,12 @@ Current user's request: ${currentInput}`;
3862
3732
  ];
3863
3733
  const requestedProvider = options.provider === "auto" ? undefined : options.provider;
3864
3734
  // Check for orchestrated preferred provider in context
3865
- const preferredOrchestrated = options.context &&
3866
- typeof options.context === "object" &&
3867
- "__orchestratedPreferredProvider" in options.context
3868
- ? options.context
3869
- .__orchestratedPreferredProvider
3735
+ const preferredOrchestrated = options.context && typeof options.context === "object" && "__orchestratedPreferredProvider" in options.context
3736
+ ? options.context.__orchestratedPreferredProvider
3870
3737
  : undefined;
3871
3738
  // Build provider list with orchestrated preference first, then fallback to full list
3872
3739
  const tryProviders = preferredOrchestrated
3873
- ? [
3874
- preferredOrchestrated,
3875
- ...providerPriority.filter((p) => p !== preferredOrchestrated),
3876
- ]
3740
+ ? [preferredOrchestrated, ...providerPriority.filter((p) => p !== preferredOrchestrated)]
3877
3741
  : requestedProvider
3878
3742
  ? [requestedProvider]
3879
3743
  : providerPriority;
@@ -3893,8 +3757,7 @@ Current user's request: ${currentInput}`;
3893
3757
  logger.debug(`[${functionTag}] Attempting provider: ${providerName}`);
3894
3758
  // Get conversation messages for context (use pre-compacted if provided)
3895
3759
  const optionsWithMessages = options;
3896
- let conversationMessages = optionsWithMessages.conversationMessages
3897
- ?.length
3760
+ let conversationMessages = optionsWithMessages.conversationMessages?.length
3898
3761
  ? optionsWithMessages.conversationMessages
3899
3762
  : await getConversationMessages(this.conversationMemory, options);
3900
3763
  // Pre-generation budget check
@@ -3905,22 +3768,17 @@ Current user's request: ${currentInput}`;
3905
3768
  systemPrompt: options.systemPrompt,
3906
3769
  conversationMessages: conversationMessages,
3907
3770
  currentPrompt: options.prompt,
3908
- toolDefinitions: options.tools
3909
- ? Object.values(options.tools)
3910
- : undefined,
3771
+ toolDefinitions: options.tools ? Object.values(options.tools) : undefined,
3911
3772
  });
3912
3773
  const dpgMessageCount = conversationMessages?.length || 0;
3913
3774
  const dpgCompactionSessionId = this.getCompactionSessionId(options);
3914
3775
  if (budgetCheck.shouldCompact &&
3915
3776
  this.conversationMemory &&
3916
- dpgMessageCount >
3917
- (this.lastCompactionMessageCount.get(dpgCompactionSessionId) ?? 0)) {
3777
+ dpgMessageCount > (this.lastCompactionMessageCount.get(dpgCompactionSessionId) ?? 0)) {
3918
3778
  const compactor = new ContextCompactor({
3919
3779
  provider: providerName,
3920
- summarizationProvider: this.conversationMemoryConfig?.conversationMemory
3921
- ?.summarizationProvider,
3922
- summarizationModel: this.conversationMemoryConfig?.conversationMemory
3923
- ?.summarizationModel,
3780
+ summarizationProvider: this.conversationMemoryConfig?.conversationMemory?.summarizationProvider,
3781
+ summarizationModel: this.conversationMemoryConfig?.conversationMemory?.summarizationModel,
3924
3782
  });
3925
3783
  const compactionResult = await compactor.compact(conversationMessages, budgetCheck.availableInputTokens, this.conversationMemoryConfig?.conversationMemory, options.context?.requestId);
3926
3784
  if (compactionResult.compacted) {
@@ -3936,9 +3794,7 @@ Current user's request: ${currentInput}`;
3936
3794
  systemPrompt: options.systemPrompt,
3937
3795
  conversationMessages: conversationMessages,
3938
3796
  currentPrompt: options.prompt,
3939
- toolDefinitions: options.tools
3940
- ? Object.values(options.tools)
3941
- : undefined,
3797
+ toolDefinitions: options.tools ? Object.values(options.tools) : undefined,
3942
3798
  });
3943
3799
  if (!postCompactBudget.withinBudget) {
3944
3800
  logger.warn("[NeuroLink] directProviderGeneration: post-compaction still over budget, emergency truncation", {
@@ -3954,9 +3810,7 @@ Current user's request: ${currentInput}`;
3954
3810
  systemPrompt: options.systemPrompt,
3955
3811
  conversationMessages: conversationMessages,
3956
3812
  currentPrompt: options.prompt,
3957
- toolDefinitions: options.tools
3958
- ? Object.values(options.tools)
3959
- : undefined,
3813
+ toolDefinitions: options.tools ? Object.values(options.tools) : undefined,
3960
3814
  });
3961
3815
  if (!finalBudget.withinBudget) {
3962
3816
  throw new ContextBudgetExceededError(`Context exceeds model budget after all compaction stages. ` +
@@ -4214,10 +4068,7 @@ Current user's request: ${currentInput}`;
4214
4068
  options = { ...options };
4215
4069
  // Set metrics trace context for parent-child span linking
4216
4070
  const metricsTraceId = crypto.randomUUID().replace(/-/g, "");
4217
- const metricsParentSpanId = crypto
4218
- .randomUUID()
4219
- .replace(/-/g, "")
4220
- .substring(0, 16);
4071
+ const metricsParentSpanId = crypto.randomUUID().replace(/-/g, "").substring(0, 16);
4221
4072
  // Scope trace context to this request via AsyncLocalStorage
4222
4073
  // so concurrent generate/stream calls don't race.
4223
4074
  return metricsTraceContextStorage.run({ traceId: metricsTraceId, parentSpanId: metricsParentSpanId }, async () => {
@@ -4276,9 +4127,7 @@ Current user's request: ${currentInput}`;
4276
4127
  }
4277
4128
  catch (err) {
4278
4129
  // Rethrow auth errors as-is; wrap anything else
4279
- if (err instanceof Error &&
4280
- "feature" in err &&
4281
- err.feature === "Auth") {
4130
+ if (err instanceof Error && "feature" in err && err.feature === "Auth") {
4282
4131
  throw err;
4283
4132
  }
4284
4133
  throw AuthError.create("PROVIDER_ERROR", `Auth token validation failed: ${err instanceof Error ? err.message : String(err)}`);
@@ -4331,9 +4180,9 @@ Current user's request: ${currentInput}`;
4331
4180
  enabled: true,
4332
4181
  config: {
4333
4182
  ...options.middleware?.middlewareConfig?.lifecycle?.config,
4334
- onFinish: options.onFinish,
4335
- onError: options.onError,
4336
- onChunk: options.onChunk,
4183
+ ...(options.onFinish !== undefined ? { onFinish: options.onFinish } : {}),
4184
+ ...(options.onError !== undefined ? { onError: options.onError } : {}),
4185
+ ...(options.onChunk !== undefined ? { onChunk: options.onChunk } : {}),
4337
4186
  },
4338
4187
  },
4339
4188
  },
@@ -4372,7 +4221,12 @@ Current user's request: ${currentInput}`;
4372
4221
  try {
4373
4222
  // Prepare options: init memory, MCP, orchestration, Ollama auto-disable, tool detection
4374
4223
  const { enhancedOptions, factoryResult } = await this.prepareStreamOptions(options, streamId, startTime, hrTimeStart);
4375
- const { stream: mcpStream, provider: providerName, usage: streamUsage, model: streamModel, analytics: streamAnalytics, } = await this.createMCPStream(enhancedOptions);
4224
+ const { stream: mcpStream, provider: providerName, usage: streamUsage, model: streamModel, finishReason: streamFinishReason, toolCalls: streamToolCalls, toolResults: streamToolResults, analytics: streamAnalytics, } = await this.createMCPStream(enhancedOptions);
4225
+ const streamState = {
4226
+ finishReason: streamFinishReason ?? "stop",
4227
+ toolCalls: streamToolCalls,
4228
+ toolResults: streamToolResults,
4229
+ };
4376
4230
  // Update span with resolved provider name
4377
4231
  streamSpan.setAttribute(ATTR.NL_PROVIDER, providerName || "unknown");
4378
4232
  let accumulatedContent = "";
@@ -4394,9 +4248,7 @@ Current user's request: ${currentInput}`;
4394
4248
  try {
4395
4249
  for await (const chunk of mcpStream) {
4396
4250
  chunkCount++;
4397
- if (chunk &&
4398
- "content" in chunk &&
4399
- typeof chunk.content === "string") {
4251
+ if (chunk && "content" in chunk && typeof chunk.content === "string") {
4400
4252
  accumulatedContent += chunk.content;
4401
4253
  self.emitter.emit("response:chunk", chunk.content);
4402
4254
  // Emit stream:chunk event (Observability Solution 8)
@@ -4412,8 +4264,12 @@ Current user's request: ${currentInput}`;
4412
4264
  }
4413
4265
  yield chunk;
4414
4266
  }
4415
- if (chunkCount === 0 && !metadata.fallbackAttempted) {
4416
- yield* self.handleStreamFallback(metadata, originalPrompt, enhancedOptions, providerName, accumulatedContent, (content) => {
4267
+ if (chunkCount === 0 &&
4268
+ !metadata.fallbackAttempted &&
4269
+ !enhancedOptions.disableInternalFallback &&
4270
+ streamState.toolCalls.length === 0 &&
4271
+ streamState.toolResults.length === 0) {
4272
+ yield* self.handleStreamFallback(metadata, streamState, originalPrompt, enhancedOptions, providerName, accumulatedContent, (content) => {
4417
4273
  accumulatedContent += content;
4418
4274
  });
4419
4275
  }
@@ -4421,9 +4277,7 @@ Current user's request: ${currentInput}`;
4421
4277
  // When fallback took over, attribute the completion to the
4422
4278
  // fallback provider so downstream telemetry reflects reality.
4423
4279
  const effectiveProvider = metadata.fallbackProvider ?? providerName;
4424
- const effectiveModel = metadata.fallbackModel ??
4425
- streamModel ??
4426
- enhancedOptions.model;
4280
+ const effectiveModel = metadata.fallbackModel ?? streamModel ?? enhancedOptions.model;
4427
4281
  // Resolve analytics promise to get final token usage
4428
4282
  let resolvedUsage = streamUsage;
4429
4283
  if (!resolvedUsage && streamAnalytics) {
@@ -4442,8 +4296,7 @@ Current user's request: ${currentInput}`;
4442
4296
  content: accumulatedContent,
4443
4297
  provider: effectiveProvider,
4444
4298
  model: effectiveModel,
4445
- prompt: enhancedOptions.input?.text ||
4446
- enhancedOptions.prompt,
4299
+ prompt: enhancedOptions.input?.text || enhancedOptions.prompt,
4447
4300
  metadata: {
4448
4301
  chunkCount,
4449
4302
  totalLength: accumulatedContent.length,
@@ -4497,10 +4350,7 @@ Current user's request: ${currentInput}`;
4497
4350
  if (primaryFailed) {
4498
4351
  streamSpan.setStatus({
4499
4352
  code: SpanStatusCode.ERROR,
4500
- message: metadata.error ||
4501
- (streamError instanceof Error
4502
- ? streamError.message
4503
- : String(streamError)),
4353
+ message: metadata.error || (streamError instanceof Error ? streamError.message : String(streamError)),
4504
4354
  });
4505
4355
  }
4506
4356
  else {
@@ -4527,10 +4377,18 @@ Current user's request: ${currentInput}`;
4527
4377
  }
4528
4378
  })();
4529
4379
  const streamResult = await this.processStreamResult(processedStream, enhancedOptions, factoryResult);
4380
+ streamResult.finishReason = streamState.finishReason || streamResult.finishReason;
4381
+ streamResult.toolCalls = streamState.toolCalls;
4382
+ streamResult.toolResults = streamState.toolResults;
4383
+ if (!streamResult.usage) {
4384
+ streamResult.usage = streamUsage;
4385
+ }
4386
+ if (!streamResult.analytics) {
4387
+ streamResult.analytics = streamAnalytics instanceof Promise ? await streamAnalytics : streamAnalytics;
4388
+ }
4530
4389
  const responseTime = Date.now() - startTime;
4531
4390
  // Accumulate session cost for budget tracking
4532
- if (streamResult.analytics?.cost &&
4533
- streamResult.analytics.cost > 0) {
4391
+ if (streamResult.analytics?.cost && streamResult.analytics.cost > 0) {
4534
4392
  this._sessionCostUsd += streamResult.analytics.cost;
4535
4393
  }
4536
4394
  this.emitStreamEndEvents(streamResult);
@@ -4547,6 +4405,9 @@ Current user's request: ${currentInput}`;
4547
4405
  });
4548
4406
  }
4549
4407
  catch (error) {
4408
+ if (options.disableInternalFallback) {
4409
+ throw error;
4410
+ }
4550
4411
  return this.handleStreamError(error, options, startTime, streamId, undefined, undefined);
4551
4412
  }
4552
4413
  });
@@ -4575,8 +4436,7 @@ Current user's request: ${currentInput}`;
4575
4436
  // Initialize MCP
4576
4437
  await this.initializeMCP();
4577
4438
  // Memory retrieval
4578
- if (this.shouldReadMemory(options.memory, options.context?.userId) &&
4579
- options.context?.userId) {
4439
+ if (this.shouldReadMemory(options.memory, options.context?.userId) && options.context?.userId) {
4580
4440
  try {
4581
4441
  options.input.text = await this.retrieveMemory(options.input.text, options.context.userId, options.memory?.additionalUsers);
4582
4442
  logger.debug("Memory retrieval successful");
@@ -4621,8 +4481,7 @@ Current user's request: ${currentInput}`;
4621
4481
  if (!options.tools) {
4622
4482
  options.tools = {};
4623
4483
  }
4624
- options.tools[ragResult.toolName] =
4625
- ragResult.tool;
4484
+ options.tools[ragResult.toolName] = ragResult.tool;
4626
4485
  // Inject RAG-aware system prompt so the AI uses the RAG tool first
4627
4486
  const ragSystemInstruction = [
4628
4487
  `\n\nIMPORTANT: You have a tool called "${ragResult.toolName}" that searches through`,
@@ -4631,8 +4490,7 @@ Current user's request: ${currentInput}`;
4631
4490
  `This tool searches your local knowledge base of pre-loaded documents and is the primary source of truth.`,
4632
4491
  `Do NOT use websearchGrounding or any web search tools when the answer can be found in the loaded documents.`,
4633
4492
  ].join(" ");
4634
- options.systemPrompt =
4635
- (options.systemPrompt || "") + ragSystemInstruction;
4493
+ options.systemPrompt = (options.systemPrompt || "") + ragSystemInstruction;
4636
4494
  logger.info("[RAG] Tool injected into stream()", {
4637
4495
  toolName: ragResult.toolName,
4638
4496
  filesLoaded: ragResult.filesLoaded,
@@ -4660,8 +4518,7 @@ Current user's request: ${currentInput}`;
4660
4518
  * Prevents overwhelming smaller models with massive tool descriptions in the system message.
4661
4519
  */
4662
4520
  async autoDisableOllamaStreamTools(options) {
4663
- if ((options.provider === "ollama" ||
4664
- options.provider?.toLowerCase().includes("ollama")) &&
4521
+ if ((options.provider === "ollama" || options.provider?.toLowerCase().includes("ollama")) &&
4665
4522
  !options.disableTools) {
4666
4523
  const { ModelConfigurationManager } = await import("./core/modelConfiguration.js");
4667
4524
  const modelConfig = ModelConfigurationManager.getInstance();
@@ -4745,7 +4602,7 @@ Current user's request: ${currentInput}`;
4745
4602
  * Handle fallback when the primary stream returns 0 chunks.
4746
4603
  * Yields chunks from a fallback provider and updates metadata accordingly.
4747
4604
  */
4748
- async *handleStreamFallback(metadata, originalPrompt, enhancedOptions, providerName, _accumulatedContent, appendContent) {
4605
+ async *handleStreamFallback(metadata, streamState, originalPrompt, enhancedOptions, providerName, _accumulatedContent, appendContent) {
4749
4606
  metadata.fallbackAttempted = true;
4750
4607
  const errorMsg = "Stream completed with 0 chunks (possible guardrails block)";
4751
4608
  metadata.error = errorMsg;
@@ -4803,18 +4660,23 @@ Current user's request: ${currentInput}`;
4803
4660
  model: fallbackRoute.model,
4804
4661
  conversationMessages,
4805
4662
  });
4663
+ const fallbackToolCalls = fallbackResult.toolCalls ?? [];
4664
+ const fallbackToolResults = fallbackResult.toolResults ?? [];
4665
+ if (fallbackToolCalls.length > 0 || fallbackToolResults.length > 0) {
4666
+ streamState.toolCalls = fallbackToolCalls;
4667
+ streamState.toolResults = fallbackToolResults;
4668
+ streamState.finishReason = fallbackResult.finishReason ?? streamState.finishReason;
4669
+ }
4806
4670
  let fallbackChunkCount = 0;
4807
4671
  for await (const fallbackChunk of fallbackResult.stream) {
4808
4672
  fallbackChunkCount++;
4809
- if (fallbackChunk &&
4810
- "content" in fallbackChunk &&
4811
- typeof fallbackChunk.content === "string") {
4673
+ if (fallbackChunk && "content" in fallbackChunk && typeof fallbackChunk.content === "string") {
4812
4674
  appendContent(fallbackChunk.content);
4813
4675
  this.emitter.emit("response:chunk", fallbackChunk.content);
4814
4676
  }
4815
4677
  yield fallbackChunk;
4816
4678
  }
4817
- if (fallbackChunkCount === 0) {
4679
+ if (fallbackChunkCount === 0 && fallbackToolCalls.length === 0 && fallbackToolResults.length === 0) {
4818
4680
  throw new Error(`Fallback provider ${fallbackRoute.provider} also returned 0 chunks`);
4819
4681
  }
4820
4682
  // Fallback succeeded - likely guardrails blocked primary
@@ -4823,9 +4685,7 @@ Current user's request: ${currentInput}`;
4823
4685
  metadata.guardrailsBlocked = true;
4824
4686
  }
4825
4687
  catch (fallbackError) {
4826
- const fallbackErrorMsg = fallbackError instanceof Error
4827
- ? fallbackError.message
4828
- : String(fallbackError);
4688
+ const fallbackErrorMsg = fallbackError instanceof Error ? fallbackError.message : String(fallbackError);
4829
4689
  metadata.error = `${errorMsg}; Fallback failed: ${fallbackErrorMsg}`;
4830
4690
  logger.error("Fallback provider failed", {
4831
4691
  fallbackProvider: fallbackRoute.provider,
@@ -4839,22 +4699,19 @@ Current user's request: ${currentInput}`;
4839
4699
  * Handles conversation memory storage in the background.
4840
4700
  */
4841
4701
  async storeStreamConversationMemory(params) {
4842
- const { enhancedOptions, providerName, originalPrompt, accumulatedContent, startTime, eventSequence, } = params;
4702
+ const { enhancedOptions, providerName, originalPrompt, accumulatedContent, startTime, eventSequence } = params;
4843
4703
  // Guard: skip storing if no meaningful content was produced (no text AND no tool activity)
4844
4704
  const hasToolEvents = eventSequence.some((e) => e.type === "tool:start" || e.type === "tool:end");
4845
4705
  if (!accumulatedContent.trim() && !hasToolEvents) {
4846
4706
  logger.warn("[NeuroLink.stream] Skipping conversation turn storage — no text content or tool activity", {
4847
- sessionId: enhancedOptions.context
4848
- ?.sessionId,
4707
+ sessionId: enhancedOptions.context?.sessionId,
4849
4708
  });
4850
4709
  return;
4851
4710
  }
4852
4711
  // Store memory after stream consumption is complete
4853
4712
  if (this.conversationMemory && enhancedOptions.context?.sessionId) {
4854
- const sessionId = enhancedOptions.context
4855
- ?.sessionId;
4856
- const userId = enhancedOptions.context
4857
- ?.userId;
4713
+ const sessionId = enhancedOptions.context?.sessionId;
4714
+ const userId = enhancedOptions.context?.userId;
4858
4715
  let providerDetails;
4859
4716
  if (enhancedOptions.model) {
4860
4717
  providerDetails = {
@@ -4873,8 +4730,7 @@ Current user's request: ${currentInput}`;
4873
4730
  providerDetails,
4874
4731
  enableSummarization: enhancedOptions.enableSummarization,
4875
4732
  events: eventSequence.length > 0 ? eventSequence : undefined,
4876
- requestId: enhancedOptions.context
4877
- ?.requestId,
4733
+ requestId: enhancedOptions.context?.requestId,
4878
4734
  });
4879
4735
  this.recordMemorySpan("memory.store", { "memory.operation": "store", "memory.path": "stream" }, Date.now() - memStoreStart, SpanStatus.OK);
4880
4736
  logger.debug("[NeuroLink.stream] Stored conversation turn with events", {
@@ -4904,8 +4760,7 @@ Current user's request: ${currentInput}`;
4904
4760
  validationStartTimeNs: validationStartTime.toString(),
4905
4761
  message: "Starting comprehensive input validation process",
4906
4762
  });
4907
- const hasText = typeof options?.input?.text === "string" &&
4908
- options.input.text.trim().length > 0;
4763
+ const hasText = typeof options?.input?.text === "string" && options.input.text.trim().length > 0;
4909
4764
  // Accept audio when frames are present; sampleRateHz is optional (defaults applied later)
4910
4765
  const hasAudio = !!(options?.input?.audio &&
4911
4766
  options.input.audio.frames &&
@@ -4984,12 +4839,10 @@ Current user's request: ${currentInput}`;
4984
4839
  const streamCompactionSessionId = this.getCompactionSessionId(options);
4985
4840
  if (streamBudget.shouldCompact &&
4986
4841
  (hasCallerConversationHistory || this.conversationMemory) &&
4987
- streamMessageCount >
4988
- (this.lastCompactionMessageCount.get(streamCompactionSessionId) ?? 0)) {
4842
+ streamMessageCount > (this.lastCompactionMessageCount.get(streamCompactionSessionId) ?? 0)) {
4989
4843
  const compactor = new ContextCompactor({
4990
4844
  provider: providerName,
4991
- summarizationProvider: this.conversationMemoryConfig?.conversationMemory
4992
- ?.summarizationProvider,
4845
+ summarizationProvider: this.conversationMemoryConfig?.conversationMemory?.summarizationProvider,
4993
4846
  summarizationModel: this.conversationMemoryConfig?.conversationMemory?.summarizationModel,
4994
4847
  });
4995
4848
  const compactionResult = await compactor.compact(conversationMessages, streamBudget.availableInputTokens, this.conversationMemoryConfig?.conversationMemory, options.context?.requestId);
@@ -5059,6 +4912,9 @@ Current user's request: ${currentInput}`;
5059
4912
  provider: providerName,
5060
4913
  usage: streamResult.usage,
5061
4914
  model: streamResult.model || options.model,
4915
+ finishReason: streamResult.finishReason,
4916
+ toolCalls: streamResult.toolCalls ?? [],
4917
+ toolResults: streamResult.toolResults ?? [],
5062
4918
  analytics: streamResult.analytics,
5063
4919
  };
5064
4920
  }
@@ -5131,8 +4987,7 @@ Current user's request: ${currentInput}`;
5131
4987
  parentSpanId: traceCtx?.parentSpanId,
5132
4988
  });
5133
4989
  failedSpan = SpanSerializer.endSpan(failedSpan, SpanStatus.ERROR);
5134
- failedSpan.statusMessage =
5135
- error instanceof Error ? error.message : String(error);
4990
+ failedSpan.statusMessage = error instanceof Error ? error.message : String(error);
5136
4991
  failedSpan.durationMs = Date.now() - startTime;
5137
4992
  this.metricsAggregator.recordSpan(failedSpan);
5138
4993
  getMetricsAggregator().recordSpan(failedSpan);
@@ -5156,9 +5011,7 @@ Current user's request: ${currentInput}`;
5156
5011
  const fallbackProcessedStream = (async function* (self) {
5157
5012
  try {
5158
5013
  for await (const chunk of fallbackStreamResult.stream) {
5159
- if (chunk &&
5160
- "content" in chunk &&
5161
- typeof chunk.content === "string") {
5014
+ if (chunk && "content" in chunk && typeof chunk.content === "string") {
5162
5015
  fallbackAccumulatedContent += chunk.content;
5163
5016
  // Emit chunk event
5164
5017
  self.emitter.emit("response:chunk", chunk.content);
@@ -5177,12 +5030,9 @@ Current user's request: ${currentInput}`;
5177
5030
  }
5178
5031
  // Store memory after fallback stream consumption is complete
5179
5032
  // Guard: skip storing if fallback accumulated content is empty
5180
- if (self.conversationMemory &&
5181
- enhancedOptions?.context?.sessionId &&
5182
- fallbackAccumulatedContent.trim()) {
5033
+ if (self.conversationMemory && enhancedOptions?.context?.sessionId && fallbackAccumulatedContent.trim()) {
5183
5034
  const sessionId = enhancedOptions?.context?.sessionId;
5184
- const userId = enhancedOptions?.context
5185
- ?.userId;
5035
+ const userId = enhancedOptions?.context?.userId;
5186
5036
  let providerDetails;
5187
5037
  if (options.model) {
5188
5038
  providerDetails = {
@@ -5201,8 +5051,7 @@ Current user's request: ${currentInput}`;
5201
5051
  providerDetails,
5202
5052
  enableSummarization: enhancedOptions?.enableSummarization,
5203
5053
  requestId: enhancedOptions?.context?.requestId ||
5204
- options.context
5205
- ?.requestId,
5054
+ options.context?.requestId,
5206
5055
  });
5207
5056
  self.recordMemorySpan("memory.store", { "memory.operation": "store", "memory.path": "fallback-stream" }, Date.now() - memStoreStart, SpanStatus.OK);
5208
5057
  }
@@ -5601,7 +5450,8 @@ Current user's request: ${currentInput}`;
5601
5450
  // (direct executeTool() or AI SDK generateText() tool calling).
5602
5451
  if (options?.timeout !== undefined &&
5603
5452
  options.timeout > 0 &&
5604
- Number.isFinite(options.timeout)) {
5453
+ Number.isFinite(options.timeout) &&
5454
+ typeof convertedTool.execute === "function") {
5605
5455
  const originalExecute = convertedTool.execute;
5606
5456
  const toolTimeout = options.timeout;
5607
5457
  const toolName = name;
@@ -5610,9 +5460,7 @@ Current user's request: ${currentInput}`;
5610
5460
  // Compose with any parent abortSignal from ToolExecutionOptions
5611
5461
  const execOptions = args[1];
5612
5462
  const parentSignal = execOptions?.abortSignal;
5613
- const composedSignal = parentSignal
5614
- ? AbortSignal.any([parentSignal, timeoutSignal])
5615
- : timeoutSignal;
5463
+ const composedSignal = parentSignal ? AbortSignal.any([parentSignal, timeoutSignal]) : timeoutSignal;
5616
5464
  // Replace the abortSignal in execution options
5617
5465
  const augmentedContext = {
5618
5466
  ...execOptions,
@@ -5623,7 +5471,7 @@ Current user's request: ${currentInput}`;
5623
5471
  new Promise((_, reject) => {
5624
5472
  composedSignal.addEventListener("abort", () => {
5625
5473
  if (timeoutSignal.aborted) {
5626
- reject(new Error(`Tool '${toolName}' timed out after ${toolTimeout}ms (configured at registration)`));
5474
+ reject(ErrorFactory.toolTimeout(toolName, toolTimeout));
5627
5475
  }
5628
5476
  else {
5629
5477
  reject(new DOMException("The operation was aborted", "AbortError"));
@@ -5669,9 +5517,7 @@ Current user's request: ${currentInput}`;
5669
5517
  * @returns Current context or undefined if not set
5670
5518
  */
5671
5519
  getToolContext() {
5672
- return this.toolExecutionContext
5673
- ? { ...this.toolExecutionContext }
5674
- : undefined;
5520
+ return this.toolExecutionContext ? { ...this.toolExecutionContext } : undefined;
5675
5521
  }
5676
5522
  /**
5677
5523
  * Clear the tool execution context
@@ -5775,8 +5621,7 @@ Current user's request: ${currentInput}`;
5775
5621
  typeof this.conversationMemory.updateAgenticLoopReport !== "function") {
5776
5622
  throw new ConversationMemoryError("updateAgenticLoopReport is only supported with Redis conversation memory.", "CONFIG_ERROR");
5777
5623
  }
5778
- await withTimeout(this
5779
- .conversationMemory.updateAgenticLoopReport(sessionId, userId, report), 5000);
5624
+ await withTimeout(this.conversationMemory.updateAgenticLoopReport(sessionId, userId, report), 5000);
5780
5625
  }
5781
5626
  /**
5782
5627
  * Get all registered custom tools
@@ -5794,14 +5639,10 @@ Current user's request: ${currentInput}`;
5794
5639
  description: tool.description,
5795
5640
  hasParameters: !!tool.parameters,
5796
5641
  parametersType: typeof tool.parameters,
5797
- parametersKeys: tool.parameters && typeof tool.parameters === "object"
5798
- ? Object.keys(tool.parameters)
5799
- : "NOT_OBJECT",
5642
+ parametersKeys: tool.parameters && typeof tool.parameters === "object" ? Object.keys(tool.parameters) : "NOT_OBJECT",
5800
5643
  hasInputSchema: !!tool.inputSchema,
5801
5644
  inputSchemaType: typeof tool.inputSchema,
5802
- inputSchemaKeys: tool.inputSchema && typeof tool.inputSchema === "object"
5803
- ? Object.keys(tool.inputSchema)
5804
- : "NOT_OBJECT",
5645
+ inputSchemaKeys: tool.inputSchema && typeof tool.inputSchema === "object" ? Object.keys(tool.inputSchema) : "NOT_OBJECT",
5805
5646
  hasEffectiveSchema: !!effectiveSchema,
5806
5647
  effectiveSchemaType: typeof effectiveSchema,
5807
5648
  effectiveSchemaHasProperties: !!effectiveSchema?.properties,
@@ -5822,18 +5663,14 @@ Current user's request: ${currentInput}`;
5822
5663
  execute: async (params, context) => {
5823
5664
  // CONTEXT MERGING: Combine all available contexts for maximum information
5824
5665
  const storedContext = this.toolExecutionContext || {};
5825
- const runtimeContext = context && isNonNullObject(context)
5826
- ? context
5827
- : {};
5666
+ const runtimeContext = context && isNonNullObject(context) ? context : {};
5828
5667
  // Merge contexts with runtime context taking precedence
5829
5668
  // This ensures we have the richest possible context for tool execution
5830
5669
  const executionContext = {
5831
5670
  ...storedContext, // Base context from setToolContext (session, tokens, etc.)
5832
5671
  ...runtimeContext, // Runtime context from AI model (if any)
5833
5672
  // Ensure we always have at least a sessionId for tracing
5834
- sessionId: runtimeContext.sessionId ||
5835
- storedContext.sessionId ||
5836
- `fallback-${Date.now()}`,
5673
+ sessionId: runtimeContext.sessionId || storedContext.sessionId || `fallback-${Date.now()}`,
5837
5674
  };
5838
5675
  // Enhanced logging for context debugging
5839
5676
  logger.debug("Tool execution context merged", {
@@ -5841,8 +5678,7 @@ Current user's request: ${currentInput}`;
5841
5678
  storedContextKeys: Object.keys(storedContext),
5842
5679
  runtimeContextKeys: Object.keys(runtimeContext),
5843
5680
  finalContextKeys: Object.keys(executionContext),
5844
- hasJuspayToken: !!executionContext
5845
- .juspayToken,
5681
+ hasJuspayToken: !!executionContext.juspayToken,
5846
5682
  hasShopId: !!executionContext.shopId,
5847
5683
  sessionId: executionContext.sessionId,
5848
5684
  });
@@ -5870,9 +5706,7 @@ Current user's request: ${currentInput}`;
5870
5706
  toolMap.set(toolName, {
5871
5707
  name: toolName,
5872
5708
  description: toolDef.description || `File tool: ${toolName}`,
5873
- inputSchema: typeof toolParams === "object" && toolParams !== null
5874
- ? toolParams
5875
- : { type: "object", properties: {} },
5709
+ inputSchema: typeof toolParams === "object" && toolParams !== null ? toolParams : { type: "object", properties: {} },
5876
5710
  execute: async (params) => {
5877
5711
  return await toolDef.execute(params, {
5878
5712
  toolCallId: `file-tool-${Date.now()}`,
@@ -5983,17 +5817,9 @@ Current user's request: ${currentInput}`;
5983
5817
  // Determine tool type for span attributes
5984
5818
  const externalTools = this.externalServerManager.getAllTools();
5985
5819
  const externalTool = externalTools.find((tool) => tool.name === toolName);
5986
- const toolType = externalTool
5987
- ? "mcp"
5988
- : this.getCustomTools().has(toolName)
5989
- ? "custom"
5990
- : "external";
5820
+ const toolType = externalTool ? "mcp" : this.getCustomTools().has(toolName) ? "custom" : "external";
5991
5821
  // Compute truncated input size for the span
5992
- const inputStr = typeof params === "string"
5993
- ? params
5994
- : params
5995
- ? JSON.stringify(params)
5996
- : "";
5822
+ const inputStr = typeof params === "string" ? params : params ? JSON.stringify(params) : "";
5997
5823
  const inputSize = inputStr.length;
5998
5824
  const truncatedInput = inputStr.length > 2048 ? inputStr.substring(0, 2048) : inputStr;
5999
5825
  return tracers.mcp.startActiveSpan("neurolink.tool.execute", {
@@ -6008,9 +5834,7 @@ Current user's request: ${currentInput}`;
6008
5834
  // Debug: Log tool execution attempt
6009
5835
  logger.debug(`[${functionTag}] Tool execution requested:`, {
6010
5836
  toolName,
6011
- params: isNonNullObject(params)
6012
- ? transformParamsForLogging(params)
6013
- : params,
5837
+ params: isNonNullObject(params) ? transformParamsForLogging(params) : params,
6014
5838
  hasExternalManager: !!this.externalServerManager,
6015
5839
  });
6016
5840
  // 🔧 PARAMETER TRACE: Log tool execution details for debugging
@@ -6021,15 +5845,9 @@ Current user's request: ${currentInput}`;
6021
5845
  type: typeof params,
6022
5846
  isNull: params === null,
6023
5847
  isUndefined: params === undefined,
6024
- isEmpty: params &&
6025
- typeof params === "object" &&
6026
- Object.keys(params).length === 0,
6027
- keys: params && typeof params === "object"
6028
- ? Object.keys(params)
6029
- : "NOT_OBJECT",
6030
- keysLength: params && typeof params === "object"
6031
- ? Object.keys(params).length
6032
- : 0,
5848
+ isEmpty: params && typeof params === "object" && Object.keys(params).length === 0,
5849
+ keys: params && typeof params === "object" ? Object.keys(params) : "NOT_OBJECT",
5850
+ keysLength: params && typeof params === "object" ? Object.keys(params).length : 0,
6033
5851
  },
6034
5852
  isTargetTool: toolName === "juspay-analytics_SuccessRateSRByTime",
6035
5853
  options,
@@ -6049,12 +5867,8 @@ Current user's request: ${currentInput}`;
6049
5867
  const registeredTimeout = toolInfo?.tool?.timeoutMs;
6050
5868
  const registeredMaxRetries = toolInfo?.tool?.maxRetries;
6051
5869
  const finalOptions = {
6052
- timeout: options?.timeout ??
6053
- registeredTimeout ??
6054
- TOOL_TIMEOUTS.EXECUTION_DEFAULT_MS,
6055
- maxRetries: options?.maxRetries ??
6056
- registeredMaxRetries ??
6057
- RETRY_ATTEMPTS.DEFAULT,
5870
+ timeout: options?.timeout ?? registeredTimeout ?? TOOL_TIMEOUTS.EXECUTION_DEFAULT_MS,
5871
+ maxRetries: options?.maxRetries ?? registeredMaxRetries ?? RETRY_ATTEMPTS.DEFAULT,
6058
5872
  retryDelayMs: options?.retryDelayMs || RETRY_DELAYS.BASE_MS,
6059
5873
  authContext: options?.authContext,
6060
5874
  disableToolCache: options?.disableToolCache,
@@ -6117,9 +5931,7 @@ Current user's request: ${currentInput}`;
6117
5931
  metrics.successfulExecutions++;
6118
5932
  metrics.lastExecutionTime = executionTime;
6119
5933
  metrics.averageExecutionTime =
6120
- (metrics.averageExecutionTime *
6121
- (metrics.successfulExecutions - 1) +
6122
- executionTime) /
5934
+ (metrics.averageExecutionTime * (metrics.successfulExecutions - 1) + executionTime) /
6123
5935
  metrics.successfulExecutions;
6124
5936
  }
6125
5937
  // Track memory usage
@@ -6141,15 +5953,9 @@ Current user's request: ${currentInput}`;
6141
5953
  // Set span success attributes
6142
5954
  // Check if result has isError flag (MCP tool error result)
6143
5955
  // Also detect toolRegistry-wrapped errors that return { success: false }
6144
- const resultObj = result && typeof result === "object"
6145
- ? result
6146
- : undefined;
6147
- const isToolError = (resultObj &&
6148
- "isError" in resultObj &&
6149
- resultObj.isError === true) ||
6150
- (resultObj &&
6151
- "success" in resultObj &&
6152
- resultObj.success === false);
5956
+ const resultObj = result && typeof result === "object" ? result : undefined;
5957
+ const isToolError = (resultObj && "isError" in resultObj && resultObj.isError === true) ||
5958
+ (resultObj && "success" in resultObj && resultObj.success === false);
6153
5959
  // NL-001: Count isError:true results as circuit breaker failures
6154
5960
  // This ensures tools that return error results (not just thrown errors) are tracked
6155
5961
  // TODO(NL-009): This records a failure AFTER the circuit breaker already recorded
@@ -6180,10 +5986,7 @@ Current user's request: ${currentInput}`;
6180
5986
  const errorText = contentArr
6181
5987
  ?.filter((c) => c.type === "text" && c.text)
6182
5988
  .map((c) => c.text)
6183
- .join(" ") ||
6184
- (typeof resultObj.error === "string"
6185
- ? resultObj.error
6186
- : "Unknown error");
5989
+ .join(" ") || (typeof resultObj.error === "string" ? resultObj.error : "Unknown error");
6187
5990
  const errorCategory = classifyMcpErrorMessage(errorText);
6188
5991
  const prefix = `[TOOL_ERROR: ${toolName} failed (${errorCategory})] `;
6189
5992
  // NL-002: Clone content array to avoid mutating shared objects, then prefix error
@@ -6212,17 +6015,14 @@ Current user's request: ${currentInput}`;
6212
6015
  // which was incorrectly included as a success
6213
6016
  if (prevSuccessful > 1) {
6214
6017
  metrics.averageExecutionTime =
6215
- (metrics.averageExecutionTime * prevSuccessful -
6216
- executionTime) /
6217
- (prevSuccessful - 1);
6018
+ (metrics.averageExecutionTime * prevSuccessful - executionTime) / (prevSuccessful - 1);
6218
6019
  }
6219
6020
  else {
6220
6021
  // No remaining successful executions, reset to 0
6221
6022
  metrics.averageExecutionTime = 0;
6222
6023
  }
6223
6024
  const mappedCategory = mcpCategoryToErrorCategory(errorCategory);
6224
- metrics.errorCategories[mappedCategory] =
6225
- (metrics.errorCategories[mappedCategory] || 0) + 1;
6025
+ metrics.errorCategories[mappedCategory] = (metrics.errorCategories[mappedCategory] || 0) + 1;
6226
6026
  }
6227
6027
  }
6228
6028
  // Emit tool end event AFTER isError check so success flag is correct
@@ -6251,8 +6051,7 @@ Current user's request: ${currentInput}`;
6251
6051
  });
6252
6052
  if (metrics) {
6253
6053
  const category = ErrorCategory.EXECUTION;
6254
- metrics.errorCategories[category] =
6255
- (metrics.errorCategories[category] || 0) + 1;
6054
+ metrics.errorCategories[category] = (metrics.errorCategories[category] || 0) + 1;
6256
6055
  }
6257
6056
  // Emit tool end event for circuit breaker open
6258
6057
  this.emitToolEndEvent(toolName, executionStartTime, false, undefined);
@@ -6296,12 +6095,10 @@ Current user's request: ${currentInput}`;
6296
6095
  const availableTools = await this.getAllAvailableTools();
6297
6096
  structuredError = ErrorFactory.toolNotFound(toolName, extractToolNames(availableTools.map((t) => ({ name: t.name }))));
6298
6097
  }
6299
- else if (error.message.includes("validation") ||
6300
- error.message.includes("parameter")) {
6098
+ else if (error.message.includes("validation") || error.message.includes("parameter")) {
6301
6099
  structuredError = ErrorFactory.invalidParameters(toolName, error, params);
6302
6100
  }
6303
- else if (error.message.includes("network") ||
6304
- error.message.includes("connection")) {
6101
+ else if (error.message.includes("network") || error.message.includes("connection")) {
6305
6102
  structuredError = ErrorFactory.networkError(toolName, error);
6306
6103
  }
6307
6104
  else {
@@ -6313,8 +6110,7 @@ Current user's request: ${currentInput}`;
6313
6110
  }
6314
6111
  if (metrics) {
6315
6112
  const category = structuredError.category || ErrorCategory.EXECUTION;
6316
- metrics.errorCategories[category] =
6317
- (metrics.errorCategories[category] || 0) + 1;
6113
+ metrics.errorCategories[category] = (metrics.errorCategories[category] || 0) + 1;
6318
6114
  }
6319
6115
  // Emit tool end event BEFORE the error event.
6320
6116
  // Node.js EventEmitter throws on unhandled 'error' events,
@@ -6351,9 +6147,7 @@ Current user's request: ${currentInput}`;
6351
6147
  catch (outerError) {
6352
6148
  // If the error was not already recorded on the span (from inner catch), record it
6353
6149
  if (!(outerError instanceof NeuroLinkError)) {
6354
- const errMsg = outerError instanceof Error
6355
- ? outerError.message
6356
- : String(outerError);
6150
+ const errMsg = outerError instanceof Error ? outerError.message : String(outerError);
6357
6151
  toolSpan.recordException(outerError instanceof Error ? outerError : new Error(errMsg));
6358
6152
  toolSpan.setStatus({ code: SpanStatusCode.ERROR, message: errMsg });
6359
6153
  }
@@ -6379,9 +6173,17 @@ Current user's request: ${currentInput}`;
6379
6173
  !options.disableToolCache &&
6380
6174
  !this._disableToolCacheForCurrentRequest &&
6381
6175
  !toolAnnotations?.destructiveHint;
6176
+ const toolResultCache = this.mcpToolResultCache;
6382
6177
  // === MCP ENHANCEMENT: Cache check (before execution) ===
6383
- if (isCacheEnabled) {
6384
- const cached = this.mcpToolResultCache.getCachedResult(toolName, params);
6178
+ // Scope cache key by auth context to prevent cross-user cache leaks
6179
+ const cacheParams = options.authContext || this.toolExecutionContext
6180
+ ? {
6181
+ __args: params,
6182
+ __ctx: options.authContext ?? this.toolExecutionContext,
6183
+ }
6184
+ : params;
6185
+ if (isCacheEnabled && toolResultCache) {
6186
+ const cached = toolResultCache.getCachedResult(toolName, cacheParams);
6385
6187
  if (cached !== undefined) {
6386
6188
  logger.debug(`[${functionTag}] Cache HIT for tool: ${toolName}`);
6387
6189
  return cached;
@@ -6432,9 +6234,7 @@ Current user's request: ${currentInput}`;
6432
6234
  inputSchema: {},
6433
6235
  };
6434
6236
  const decision = this.mcpToolRouter.route(mcpTool);
6435
- externalTool =
6436
- matchingTools.find((t) => t.serverId === decision.serverId) ||
6437
- matchingTools[0];
6237
+ externalTool = matchingTools.find((t) => t.serverId === decision.serverId) || matchingTools[0];
6438
6238
  logger.debug(`[${functionTag}] Router selected server: ${decision.serverId}`, {
6439
6239
  strategy: decision.strategy,
6440
6240
  confidence: decision.confidence,
@@ -6490,10 +6290,7 @@ Current user's request: ${currentInput}`;
6490
6290
  });
6491
6291
  const result = (await this.toolRegistry.executeTool(toolName, params, context));
6492
6292
  // Check if result indicates a failure and emit error event
6493
- if (result &&
6494
- typeof result === "object" &&
6495
- "success" in result &&
6496
- result.success === false) {
6293
+ if (result && typeof result === "object" && "success" in result && result.success === false) {
6497
6294
  const errorMessage = result.error || "Tool execution failed";
6498
6295
  const errorToEmit = new Error(errorMessage);
6499
6296
  this.emitter.emit("error", errorToEmit);
@@ -6515,8 +6312,8 @@ Current user's request: ${currentInput}`;
6515
6312
  try {
6516
6313
  const result = await executeWithMiddleware(executeCore);
6517
6314
  // === MCP ENHANCEMENT: Cache store (after successful execution) ===
6518
- if (isCacheEnabled && result !== undefined) {
6519
- this.mcpToolResultCache.cacheResult(toolName, params, result);
6315
+ if (isCacheEnabled && toolResultCache && result !== undefined) {
6316
+ toolResultCache.cacheResult(toolName, cacheParams, result);
6520
6317
  logger.debug(`[${functionTag}] Cached result for tool: ${toolName}`);
6521
6318
  }
6522
6319
  return result;
@@ -6531,16 +6328,13 @@ Current user's request: ${currentInput}`;
6531
6328
  execute: async () => ({}),
6532
6329
  }
6533
6330
  : undefined;
6534
- if (toolStubForRetry &&
6535
- isSafeToRetry(toolStubForRetry) &&
6536
- error instanceof Error &&
6537
- isRetriableError(error)) {
6331
+ if (toolStubForRetry && isSafeToRetry(toolStubForRetry) && error instanceof Error && isRetriableError(error)) {
6538
6332
  logger.debug(`[${functionTag}] Tool ${toolName} is safe to retry, attempting once more`);
6539
6333
  try {
6540
6334
  const retryResult = await executeWithMiddleware(executeCore);
6541
6335
  // Cache the retry result
6542
- if (isCacheEnabled && retryResult !== undefined) {
6543
- this.mcpToolResultCache.cacheResult(toolName, params, retryResult);
6336
+ if (isCacheEnabled && toolResultCache && retryResult !== undefined) {
6337
+ toolResultCache.cacheResult(toolName, cacheParams, retryResult);
6544
6338
  }
6545
6339
  return retryResult;
6546
6340
  }
@@ -6579,8 +6373,7 @@ Current user's request: ${currentInput}`;
6579
6373
  }
6580
6374
  async getAllAvailableTools() {
6581
6375
  // Return from cache if available and not stale
6582
- if (this.toolCache &&
6583
- Date.now() - this.toolCache.timestamp < this.toolCacheDuration) {
6376
+ if (this.toolCache && Date.now() - this.toolCache.timestamp < this.toolCacheDuration) {
6584
6377
  logger.debug("Returning available tools from cache");
6585
6378
  return this.toolCache.tools;
6586
6379
  }
@@ -6661,9 +6454,7 @@ Current user's request: ${currentInput}`;
6661
6454
  if (!allTools.has(tool.name)) {
6662
6455
  const optimizedTool = optimizeToolForCollection(tool, {
6663
6456
  category: detectCategory({
6664
- existingCategory: typeof tool.metadata?.category === "string"
6665
- ? tool.metadata.category
6666
- : undefined,
6457
+ existingCategory: typeof tool.metadata?.category === "string" ? tool.metadata.category : undefined,
6667
6458
  isExternal: true,
6668
6459
  serverId: tool.serverId,
6669
6460
  }),
@@ -6819,9 +6610,7 @@ Current user's request: ${currentInput}`;
6819
6610
  status: "failed",
6820
6611
  configured: false,
6821
6612
  authenticated: false,
6822
- error: error instanceof Error
6823
- ? error.message
6824
- : "Ollama service not running",
6613
+ error: error instanceof Error ? error.message : "Ollama service not running",
6825
6614
  responseTime: Date.now() - startTime,
6826
6615
  };
6827
6616
  }
@@ -6944,9 +6733,7 @@ Current user's request: ${currentInput}`;
6944
6733
  inMemoryServerInfos.length +
6945
6734
  builtInServerInfos.length +
6946
6735
  autoDiscoveredServerInfos.length;
6947
- const availableServers = externalStats.connectedServers +
6948
- inMemoryServerInfos.length +
6949
- builtInServerInfos.length; // in-memory and built-in always available
6736
+ const availableServers = externalStats.connectedServers + inMemoryServerInfos.length + builtInServerInfos.length; // in-memory and built-in always available
6950
6737
  const totalTools = allTools.length + externalStats.totalTools;
6951
6738
  return {
6952
6739
  mcpInitialized: this.mcpInitialized,
@@ -7015,8 +6802,7 @@ Current user's request: ${currentInput}`;
7015
6802
  // Test external MCP servers
7016
6803
  const externalServer = this.externalServerManager.getServer(serverId);
7017
6804
  if (externalServer) {
7018
- return (externalServer.status === "connected" &&
7019
- externalServer.client !== null);
6805
+ return externalServer.status === "connected" && externalServer.client !== null;
7020
6806
  }
7021
6807
  return false;
7022
6808
  }
@@ -7136,9 +6922,7 @@ Current user's request: ${currentInput}`;
7136
6922
  metrics[toolName] = {
7137
6923
  ...toolMetrics,
7138
6924
  errorCategories: { ...toolMetrics.errorCategories },
7139
- successRate: toolMetrics.totalExecutions > 0
7140
- ? toolMetrics.successfulExecutions / toolMetrics.totalExecutions
7141
- : 0,
6925
+ successRate: toolMetrics.totalExecutions > 0 ? toolMetrics.successfulExecutions / toolMetrics.totalExecutions : 0,
7142
6926
  };
7143
6927
  }
7144
6928
  return metrics;
@@ -7158,7 +6942,7 @@ Current user's request: ${currentInput}`;
7158
6942
  */
7159
6943
  getToolCircuitBreakerStatus() {
7160
6944
  const status = {};
7161
- for (const [toolName, circuitBreaker,] of this.toolCircuitBreakers.entries()) {
6945
+ for (const [toolName, circuitBreaker] of this.toolCircuitBreakers.entries()) {
7162
6946
  status[toolName] = {
7163
6947
  state: circuitBreaker.getState(),
7164
6948
  failureCount: circuitBreaker.getFailureCount(),
@@ -7211,8 +6995,7 @@ Current user's request: ${currentInput}`;
7211
6995
  ? metrics.successfulExecutions / metrics.totalExecutions
7212
6996
  : 0
7213
6997
  : 0;
7214
- const isHealthy = (!circuitBreaker || circuitBreaker.getState() === "closed") &&
7215
- successRate >= 0.8;
6998
+ const isHealthy = (!circuitBreaker || circuitBreaker.getState() === "closed") && successRate >= 0.8;
7216
6999
  if (isHealthy) {
7217
7000
  healthyCount++;
7218
7001
  }
@@ -7253,9 +7036,7 @@ Current user's request: ${currentInput}`;
7253
7036
  successRate,
7254
7037
  averageExecutionTime: metrics?.averageExecutionTime || 0,
7255
7038
  lastExecutionTime: metrics?.lastExecutionTime || 0,
7256
- errorCategories: metrics?.errorCategories
7257
- ? { ...metrics.errorCategories }
7258
- : {},
7039
+ errorCategories: metrics?.errorCategories ? { ...metrics.errorCategories } : {},
7259
7040
  },
7260
7041
  circuitBreaker: {
7261
7042
  state: circuitBreaker?.getState() || "closed",
@@ -7407,8 +7188,7 @@ Current user's request: ${currentInput}`;
7407
7188
  */
7408
7189
  async storeToolExecutions(sessionId, userId, toolCalls, toolResults, currentTime) {
7409
7190
  // Check if tools are not empty
7410
- const hasToolData = (toolCalls && toolCalls.length > 0) ||
7411
- (toolResults && toolResults.length > 0);
7191
+ const hasToolData = (toolCalls && toolCalls.length > 0) || (toolResults && toolResults.length > 0);
7412
7192
  if (!hasToolData) {
7413
7193
  logger.debug("Tool execution storage skipped", {
7414
7194
  hasToolData,
@@ -7418,8 +7198,7 @@ Current user's request: ${currentInput}`;
7418
7198
  return;
7419
7199
  }
7420
7200
  // Type guard to ensure it's Redis conversation memory manager
7421
- const redisMemory = this
7422
- .conversationMemory;
7201
+ const redisMemory = this.conversationMemory;
7423
7202
  try {
7424
7203
  await redisMemory.storeToolExecution(sessionId, userId, toolCalls, toolResults, currentTime);
7425
7204
  }
@@ -7438,9 +7217,7 @@ Current user's request: ${currentInput}`;
7438
7217
  */
7439
7218
  isToolExecutionStorageAvailable() {
7440
7219
  const isRedisStorage = process.env.STORAGE_TYPE === "redis";
7441
- const hasRedisConversationMemory = this.conversationMemory &&
7442
- this.conversationMemory.constructor.name ===
7443
- "RedisConversationMemoryManager";
7220
+ const hasRedisConversationMemory = this.conversationMemory && this.conversationMemory.constructor.name === "RedisConversationMemoryManager";
7444
7221
  return !!(isRedisStorage && hasRedisConversationMemory);
7445
7222
  }
7446
7223
  /**
@@ -7959,8 +7736,7 @@ Current user's request: ${currentInput}`;
7959
7736
  return null;
7960
7737
  }
7961
7738
  // Check for explicit annotations set on the tool first
7962
- const explicitAnnotations = toolInfo.tool
7963
- .annotations;
7739
+ const explicitAnnotations = toolInfo.tool.annotations;
7964
7740
  // Infer annotations from the tool name/description as fallback
7965
7741
  const inferredAnnotations = inferAnnotations({
7966
7742
  name: toolInfo.tool.name,
@@ -7992,9 +7768,7 @@ Current user's request: ${currentInput}`;
7992
7768
  const result = await this.externalServerManager.executeTool(tool.serverId, tool.name, params, { timeout: 30000 });
7993
7769
  mcpLogger.debug(`[NeuroLink] External MCP tool execution result: ${tool.name}`, {
7994
7770
  success: !!result,
7995
- hasData: !!(result &&
7996
- typeof result === "object" &&
7997
- "content" in result),
7771
+ hasData: !!(result && typeof result === "object" && "content" in result),
7998
7772
  });
7999
7773
  return result;
8000
7774
  }
@@ -8410,9 +8184,7 @@ Current user's request: ${currentInput}`;
8410
8184
  logger.debug("[NeuroLink] OpenTelemetry shutdown successfully");
8411
8185
  }
8412
8186
  catch (error) {
8413
- const err = error instanceof Error
8414
- ? error
8415
- : new Error(`OpenTelemetry shutdown error: ${String(error)}`);
8187
+ const err = error instanceof Error ? error : new Error(`OpenTelemetry shutdown error: ${String(error)}`);
8416
8188
  cleanupErrors.push(err);
8417
8189
  logger.warn("[NeuroLink] Error shutting down OpenTelemetry:", error);
8418
8190
  }
@@ -8424,9 +8196,7 @@ Current user's request: ${currentInput}`;
8424
8196
  logger.debug("[NeuroLink] External MCP servers shutdown successfully");
8425
8197
  }
8426
8198
  catch (error) {
8427
- const err = error instanceof Error
8428
- ? error
8429
- : new Error(`External server shutdown error: ${String(error)}`);
8199
+ const err = error instanceof Error ? error : new Error(`External server shutdown error: ${String(error)}`);
8430
8200
  cleanupErrors.push(err);
8431
8201
  logger.warn("[NeuroLink] Error shutting down external MCP servers:", error);
8432
8202
  }
@@ -8440,9 +8210,7 @@ Current user's request: ${currentInput}`;
8440
8210
  logger.debug("[NeuroLink] Event listeners removed successfully");
8441
8211
  }
8442
8212
  catch (error) {
8443
- const err = error instanceof Error
8444
- ? error
8445
- : new Error(`Event emitter cleanup error: ${String(error)}`);
8213
+ const err = error instanceof Error ? error : new Error(`Event emitter cleanup error: ${String(error)}`);
8446
8214
  cleanupErrors.push(err);
8447
8215
  logger.warn("[NeuroLink] Error removing event listeners:", error);
8448
8216
  }
@@ -8455,9 +8223,7 @@ Current user's request: ${currentInput}`;
8455
8223
  logger.debug("[NeuroLink] Circuit breakers cleared successfully");
8456
8224
  }
8457
8225
  catch (error) {
8458
- const err = error instanceof Error
8459
- ? error
8460
- : new Error(`Circuit breaker cleanup error: ${String(error)}`);
8226
+ const err = error instanceof Error ? error : new Error(`Circuit breaker cleanup error: ${String(error)}`);
8461
8227
  cleanupErrors.push(err);
8462
8228
  logger.warn("[NeuroLink] Error clearing circuit breakers:", error);
8463
8229
  }
@@ -8494,9 +8260,7 @@ Current user's request: ${currentInput}`;
8494
8260
  logger.debug("[NeuroLink] Maps and caches cleared successfully");
8495
8261
  }
8496
8262
  catch (error) {
8497
- const err = error instanceof Error
8498
- ? error
8499
- : new Error(`Cache cleanup error: ${String(error)}`);
8263
+ const err = error instanceof Error ? error : new Error(`Cache cleanup error: ${String(error)}`);
8500
8264
  cleanupErrors.push(err);
8501
8265
  logger.warn("[NeuroLink] Error clearing caches:", error);
8502
8266
  }
@@ -8522,9 +8286,7 @@ Current user's request: ${currentInput}`;
8522
8286
  logger.debug("[NeuroLink] Initialization state reset successfully");
8523
8287
  }
8524
8288
  catch (error) {
8525
- const err = error instanceof Error
8526
- ? error
8527
- : new Error(`State reset error: ${String(error)}`);
8289
+ const err = error instanceof Error ? error : new Error(`State reset error: ${String(error)}`);
8528
8290
  cleanupErrors.push(err);
8529
8291
  logger.warn("[NeuroLink] Error resetting state:", error);
8530
8292
  }
@@ -8568,11 +8330,8 @@ Current user's request: ${currentInput}`;
8568
8330
  }
8569
8331
  const compactor = new ContextCompactor({
8570
8332
  ...config,
8571
- summarizationProvider: config?.summarizationProvider ??
8572
- this.conversationMemoryConfig?.conversationMemory
8573
- ?.summarizationProvider,
8574
- summarizationModel: config?.summarizationModel ??
8575
- this.conversationMemoryConfig?.conversationMemory?.summarizationModel,
8333
+ summarizationProvider: config?.summarizationProvider ?? this.conversationMemoryConfig?.conversationMemory?.summarizationProvider,
8334
+ summarizationModel: config?.summarizationModel ?? this.conversationMemoryConfig?.conversationMemory?.summarizationModel,
8576
8335
  });
8577
8336
  // Use actual context window to determine target, not arbitrary heuristic
8578
8337
  const budgetInfo = checkContextBudget({
@@ -8641,28 +8400,32 @@ Current user's request: ${currentInput}`;
8641
8400
  async setAuthProvider(config) {
8642
8401
  // Clear any pending lazy-init promise so it does not race with this call.
8643
8402
  this.authInitPromise = undefined;
8403
+ await this.initializeAuthProviderFromConfig(config);
8404
+ }
8405
+ async initializeAuthProviderFromConfig(config) {
8406
+ let provider;
8407
+ let providerType;
8644
8408
  // Duck-type check: direct MastraAuthProvider instance
8645
- if ("authenticateToken" in config &&
8646
- typeof config.authenticateToken === "function") {
8647
- this.authProvider = config;
8648
- logger.info(`Auth provider set: ${this.authProvider.type}`);
8409
+ if ("authenticateToken" in config && typeof config.authenticateToken === "function") {
8410
+ provider = config;
8411
+ providerType = provider.type;
8649
8412
  }
8650
8413
  else if ("provider" in config) {
8651
- this.authProvider = config.provider;
8652
- logger.info(`Auth provider set: ${this.authProvider.type}`);
8414
+ provider = config.provider;
8415
+ providerType = provider.type;
8653
8416
  }
8654
8417
  else {
8655
8418
  const typedConfig = config;
8656
8419
  const { AuthProviderFactory } = await import("./auth/AuthProviderFactory.js");
8657
- this.authProvider = await AuthProviderFactory.createProvider(typedConfig.type, typedConfig.config);
8658
- logger.info(`Auth provider created and set: ${typedConfig.type}`);
8659
- }
8660
- if (this.authProvider) {
8661
- this.emitter.emit("auth:provider:set", {
8662
- type: this.authProvider.type,
8663
- timestamp: Date.now(),
8664
- });
8420
+ provider = await AuthProviderFactory.createProvider(typedConfig.type, typedConfig.config);
8421
+ providerType = typedConfig.type;
8665
8422
  }
8423
+ this.authProvider = provider;
8424
+ logger.info(`Auth provider set: ${providerType}`);
8425
+ this.emitter.emit("auth:provider:set", {
8426
+ type: provider.type,
8427
+ timestamp: Date.now(),
8428
+ });
8666
8429
  }
8667
8430
  /**
8668
8431
  * Get the currently configured authentication provider
@@ -8679,14 +8442,17 @@ Current user's request: ${currentInput}`;
8679
8442
  if (this.authProvider || !this.pendingAuthConfig) {
8680
8443
  return;
8681
8444
  }
8445
+ const pendingAuthConfig = this.pendingAuthConfig;
8682
8446
  this.authInitPromise ??= (async () => {
8683
8447
  try {
8684
- await this.setAuthProvider(this.pendingAuthConfig);
8448
+ await this.initializeAuthProviderFromConfig(pendingAuthConfig);
8685
8449
  this.pendingAuthConfig = undefined;
8686
8450
  }
8687
- catch (err) {
8688
- this.authInitPromise = undefined;
8689
- throw err;
8451
+ finally {
8452
+ if (this.authInitPromise &&
8453
+ (this.pendingAuthConfig === undefined || this.pendingAuthConfig === pendingAuthConfig)) {
8454
+ this.authInitPromise = undefined;
8455
+ }
8690
8456
  }
8691
8457
  })();
8692
8458
  await this.authInitPromise;