@juspay/neurolink 9.32.0 → 9.33.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 (475) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/auth/anthropicOAuth.js +1 -1
  3. package/dist/cli/commands/proxy.js +18 -5
  4. package/dist/client/aiSdkAdapter.js +1 -1
  5. package/dist/client/index.js +137 -501
  6. package/dist/core/factory.js +0 -1
  7. package/dist/core/redisConversationMemoryManager.js +1 -1
  8. package/dist/features/ppt/slideGenerator.js +0 -1
  9. package/dist/features/ppt/utils.js +0 -1
  10. package/dist/lib/neurolink.d.ts +10 -0
  11. package/dist/lib/neurolink.js +41 -7
  12. package/dist/lib/server/routes/claudeProxyRoutes.js +45 -9
  13. package/dist/lib/types/generateTypes.d.ts +16 -0
  14. package/dist/lib/types/streamTypes.d.ts +15 -0
  15. package/dist/mcp/elicitationProtocol.js +1 -1
  16. package/dist/mcp/servers/agent/directToolsServer.js +0 -1
  17. package/dist/neurolink.d.ts +10 -0
  18. package/dist/neurolink.js +41 -7
  19. package/dist/providers/azureOpenai.js +1 -1
  20. package/dist/providers/huggingFace.js +0 -1
  21. package/dist/providers/openaiCompatible.js +0 -1
  22. package/dist/sdk/toolRegistration.js +0 -1
  23. package/dist/server/openapi/generator.js +1 -1
  24. package/dist/server/routes/claudeProxyRoutes.js +45 -9
  25. package/dist/types/configTypes.js +0 -5
  26. package/dist/types/generateTypes.d.ts +16 -0
  27. package/dist/types/modelTypes.js +0 -1
  28. package/dist/types/streamTypes.d.ts +15 -0
  29. package/dist/types/tools.js +0 -1
  30. package/dist/types/typeAliases.js +0 -1
  31. package/dist/types/utilities.js +1 -1
  32. package/dist/types/workflowTypes.js +0 -1
  33. package/dist/utils/providerRetry.js +0 -1
  34. package/dist/utils/providerUtils.js +0 -1
  35. package/package.json +2 -2
  36. package/dist/client/adapters/providerImageAdapter.js +0 -588
  37. package/dist/client/adapters/tts/googleTTSHandler.js +0 -344
  38. package/dist/client/adapters/video/directorPipeline.js +0 -516
  39. package/dist/client/adapters/video/ffmpegAdapter.js +0 -206
  40. package/dist/client/adapters/video/frameExtractor.js +0 -143
  41. package/dist/client/adapters/video/vertexVideoHandler.js +0 -763
  42. package/dist/client/adapters/video/videoAnalyzer.js +0 -238
  43. package/dist/client/adapters/video/videoMerger.js +0 -171
  44. package/dist/client/agent/directTools.js +0 -840
  45. package/dist/client/auth/AuthProviderFactory.js +0 -111
  46. package/dist/client/auth/AuthProviderRegistry.js +0 -190
  47. package/dist/client/auth/RequestContext.js +0 -78
  48. package/dist/client/auth/accountPool.js +0 -178
  49. package/dist/client/auth/anthropicOAuth.js +0 -974
  50. package/dist/client/auth/authContext.js +0 -314
  51. package/dist/client/auth/errors.js +0 -39
  52. package/dist/client/auth/index.js +0 -61
  53. package/dist/client/auth/middleware/AuthMiddleware.js +0 -519
  54. package/dist/client/auth/middleware/rateLimitByUser.js +0 -554
  55. package/dist/client/auth/providers/BaseAuthProvider.js +0 -723
  56. package/dist/client/auth/providers/CognitoProvider.js +0 -304
  57. package/dist/client/auth/providers/KeycloakProvider.js +0 -393
  58. package/dist/client/auth/providers/auth0.js +0 -274
  59. package/dist/client/auth/providers/betterAuth.js +0 -182
  60. package/dist/client/auth/providers/clerk.js +0 -317
  61. package/dist/client/auth/providers/custom.js +0 -112
  62. package/dist/client/auth/providers/firebase.js +0 -226
  63. package/dist/client/auth/providers/jwt.js +0 -212
  64. package/dist/client/auth/providers/oauth2.js +0 -303
  65. package/dist/client/auth/providers/supabase.js +0 -259
  66. package/dist/client/auth/providers/workos.js +0 -284
  67. package/dist/client/auth/serverBridge.js +0 -25
  68. package/dist/client/auth/sessionManager.js +0 -437
  69. package/dist/client/auth/tokenStore.js +0 -799
  70. package/dist/client/client/aiSdkAdapter.js +0 -487
  71. package/dist/client/client/auth.js +0 -473
  72. package/dist/client/client/errors.js +0 -552
  73. package/dist/client/client/httpClient.js +0 -837
  74. package/dist/client/client/index.js +0 -172
  75. package/dist/client/client/interceptors.js +0 -601
  76. package/dist/client/client/sseClient.js +0 -545
  77. package/dist/client/client/streamingClient.js +0 -917
  78. package/dist/client/client/wsClient.js +0 -369
  79. package/dist/client/config/configManager.js +0 -303
  80. package/dist/client/config/conversationMemory.js +0 -86
  81. package/dist/client/config/taskClassificationConfig.js +0 -148
  82. package/dist/client/constants/contextWindows.js +0 -295
  83. package/dist/client/constants/enums.js +0 -853
  84. package/dist/client/constants/index.js +0 -207
  85. package/dist/client/constants/performance.js +0 -389
  86. package/dist/client/constants/retry.js +0 -266
  87. package/dist/client/constants/timeouts.js +0 -182
  88. package/dist/client/constants/tokens.js +0 -380
  89. package/dist/client/constants/videoErrors.js +0 -46
  90. package/dist/client/context/budgetChecker.js +0 -98
  91. package/dist/client/context/contextCompactor.js +0 -205
  92. package/dist/client/context/emergencyTruncation.js +0 -88
  93. package/dist/client/context/errorDetection.js +0 -171
  94. package/dist/client/context/errors.js +0 -21
  95. package/dist/client/context/fileTokenBudget.js +0 -127
  96. package/dist/client/context/prompts/summarizationPrompt.js +0 -117
  97. package/dist/client/context/stages/fileReadDeduplicator.js +0 -66
  98. package/dist/client/context/stages/slidingWindowTruncator.js +0 -190
  99. package/dist/client/context/stages/structuredSummarizer.js +0 -99
  100. package/dist/client/context/stages/toolOutputPruner.js +0 -52
  101. package/dist/client/context/summarizationEngine.js +0 -136
  102. package/dist/client/context/toolOutputLimits.js +0 -78
  103. package/dist/client/context/toolPairRepair.js +0 -66
  104. package/dist/client/core/analytics.js +0 -88
  105. package/dist/client/core/baseProvider.js +0 -1385
  106. package/dist/client/core/constants.js +0 -140
  107. package/dist/client/core/conversationMemoryFactory.js +0 -141
  108. package/dist/client/core/conversationMemoryInitializer.js +0 -128
  109. package/dist/client/core/conversationMemoryManager.js +0 -344
  110. package/dist/client/core/dynamicModels.js +0 -358
  111. package/dist/client/core/evaluation.js +0 -309
  112. package/dist/client/core/evaluationProviders.js +0 -248
  113. package/dist/client/core/factory.js +0 -412
  114. package/dist/client/core/infrastructure/baseError.js +0 -22
  115. package/dist/client/core/infrastructure/baseFactory.js +0 -54
  116. package/dist/client/core/infrastructure/baseRegistry.js +0 -53
  117. package/dist/client/core/infrastructure/index.js +0 -5
  118. package/dist/client/core/infrastructure/retry.js +0 -20
  119. package/dist/client/core/infrastructure/typedEventEmitter.js +0 -23
  120. package/dist/client/core/modelConfiguration.js +0 -851
  121. package/dist/client/core/modules/GenerationHandler.js +0 -588
  122. package/dist/client/core/modules/MessageBuilder.js +0 -273
  123. package/dist/client/core/modules/StreamHandler.js +0 -185
  124. package/dist/client/core/modules/TelemetryHandler.js +0 -203
  125. package/dist/client/core/modules/ToolsManager.js +0 -499
  126. package/dist/client/core/modules/Utilities.js +0 -331
  127. package/dist/client/core/redisConversationMemoryManager.js +0 -1435
  128. package/dist/client/core/streamAnalytics.js +0 -131
  129. package/dist/client/evaluation/contextBuilder.js +0 -134
  130. package/dist/client/evaluation/index.js +0 -61
  131. package/dist/client/evaluation/prompts.js +0 -73
  132. package/dist/client/evaluation/ragasEvaluator.js +0 -110
  133. package/dist/client/evaluation/retryManager.js +0 -78
  134. package/dist/client/evaluation/scoring.js +0 -61
  135. package/dist/client/factories/providerFactory.js +0 -166
  136. package/dist/client/factories/providerRegistry.js +0 -166
  137. package/dist/client/features/ppt/constants.js +0 -896
  138. package/dist/client/features/ppt/contentPlanner.js +0 -529
  139. package/dist/client/features/ppt/presentationOrchestrator.js +0 -236
  140. package/dist/client/features/ppt/slideGenerator.js +0 -532
  141. package/dist/client/features/ppt/slideRenderers.js +0 -2383
  142. package/dist/client/features/ppt/slideTypeInference.js +0 -405
  143. package/dist/client/features/ppt/types.js +0 -13
  144. package/dist/client/features/ppt/utils.js +0 -443
  145. package/dist/client/files/fileReferenceRegistry.js +0 -1543
  146. package/dist/client/files/fileTools.js +0 -450
  147. package/dist/client/files/streamingReader.js +0 -321
  148. package/dist/client/files/types.js +0 -23
  149. package/dist/client/hitl/hitlErrors.js +0 -54
  150. package/dist/client/hitl/hitlManager.js +0 -460
  151. package/dist/client/mcp/agentExposure.js +0 -356
  152. package/dist/client/mcp/auth/index.js +0 -11
  153. package/dist/client/mcp/auth/oauthClientProvider.js +0 -325
  154. package/dist/client/mcp/auth/tokenStorage.js +0 -134
  155. package/dist/client/mcp/batching/index.js +0 -10
  156. package/dist/client/mcp/batching/requestBatcher.js +0 -441
  157. package/dist/client/mcp/caching/index.js +0 -10
  158. package/dist/client/mcp/caching/toolCache.js +0 -433
  159. package/dist/client/mcp/elicitation/elicitationManager.js +0 -376
  160. package/dist/client/mcp/elicitation/index.js +0 -11
  161. package/dist/client/mcp/elicitation/types.js +0 -10
  162. package/dist/client/mcp/elicitationProtocol.js +0 -375
  163. package/dist/client/mcp/enhancedToolDiscovery.js +0 -481
  164. package/dist/client/mcp/externalServerManager.js +0 -1478
  165. package/dist/client/mcp/factory.js +0 -161
  166. package/dist/client/mcp/flexibleToolValidator.js +0 -161
  167. package/dist/client/mcp/httpRateLimiter.js +0 -391
  168. package/dist/client/mcp/httpRetryHandler.js +0 -178
  169. package/dist/client/mcp/index.js +0 -74
  170. package/dist/client/mcp/mcpCircuitBreaker.js +0 -427
  171. package/dist/client/mcp/mcpClientFactory.js +0 -708
  172. package/dist/client/mcp/mcpRegistryClient.js +0 -488
  173. package/dist/client/mcp/mcpServerBase.js +0 -373
  174. package/dist/client/mcp/multiServerManager.js +0 -579
  175. package/dist/client/mcp/registry.js +0 -158
  176. package/dist/client/mcp/routing/index.js +0 -10
  177. package/dist/client/mcp/routing/toolRouter.js +0 -416
  178. package/dist/client/mcp/serverCapabilities.js +0 -502
  179. package/dist/client/mcp/servers/agent/directToolsServer.js +0 -150
  180. package/dist/client/mcp/toolAnnotations.js +0 -239
  181. package/dist/client/mcp/toolConverter.js +0 -258
  182. package/dist/client/mcp/toolDiscoveryService.js +0 -798
  183. package/dist/client/mcp/toolIntegration.js +0 -334
  184. package/dist/client/mcp/toolRegistry.js +0 -729
  185. package/dist/client/memory/hippocampusInitializer.js +0 -19
  186. package/dist/client/memory/memoryRetrievalTools.js +0 -166
  187. package/dist/client/middleware/builtin/analytics.js +0 -132
  188. package/dist/client/middleware/builtin/autoEvaluation.js +0 -203
  189. package/dist/client/middleware/builtin/guardrails.js +0 -109
  190. package/dist/client/middleware/builtin/lifecycle.js +0 -168
  191. package/dist/client/middleware/factory.js +0 -327
  192. package/dist/client/middleware/registry.js +0 -295
  193. package/dist/client/middleware/utils/guardrailsUtils.js +0 -396
  194. package/dist/client/models/anthropicModels.js +0 -527
  195. package/dist/client/neurolink.js +0 -8233
  196. package/dist/client/observability/exporterRegistry.js +0 -413
  197. package/dist/client/observability/exporters/arizeExporter.js +0 -138
  198. package/dist/client/observability/exporters/baseExporter.js +0 -190
  199. package/dist/client/observability/exporters/braintrustExporter.js +0 -154
  200. package/dist/client/observability/exporters/datadogExporter.js +0 -196
  201. package/dist/client/observability/exporters/laminarExporter.js +0 -302
  202. package/dist/client/observability/exporters/langfuseExporter.js +0 -209
  203. package/dist/client/observability/exporters/langsmithExporter.js +0 -143
  204. package/dist/client/observability/exporters/otelExporter.js +0 -164
  205. package/dist/client/observability/exporters/posthogExporter.js +0 -287
  206. package/dist/client/observability/exporters/sentryExporter.js +0 -165
  207. package/dist/client/observability/index.js +0 -31
  208. package/dist/client/observability/metricsAggregator.js +0 -556
  209. package/dist/client/observability/otelBridge.js +0 -131
  210. package/dist/client/observability/retryPolicy.js +0 -383
  211. package/dist/client/observability/sampling/samplers.js +0 -216
  212. package/dist/client/observability/spanProcessor.js +0 -303
  213. package/dist/client/observability/tokenTracker.js +0 -413
  214. package/dist/client/observability/types/exporterTypes.js +0 -5
  215. package/dist/client/observability/types/index.js +0 -4
  216. package/dist/client/observability/types/spanTypes.js +0 -92
  217. package/dist/client/observability/utils/safeMetadata.js +0 -25
  218. package/dist/client/observability/utils/spanSerializer.js +0 -292
  219. package/dist/client/processors/archive/ArchiveProcessor.js +0 -1308
  220. package/dist/client/processors/base/BaseFileProcessor.js +0 -614
  221. package/dist/client/processors/base/types.js +0 -82
  222. package/dist/client/processors/config/fileTypes.js +0 -520
  223. package/dist/client/processors/config/index.js +0 -92
  224. package/dist/client/processors/config/languageMap.js +0 -410
  225. package/dist/client/processors/config/mimeTypes.js +0 -363
  226. package/dist/client/processors/config/sizeLimits.js +0 -258
  227. package/dist/client/processors/document/ExcelProcessor.js +0 -590
  228. package/dist/client/processors/document/OpenDocumentProcessor.js +0 -212
  229. package/dist/client/processors/document/PptxProcessor.js +0 -157
  230. package/dist/client/processors/document/RtfProcessor.js +0 -361
  231. package/dist/client/processors/document/WordProcessor.js +0 -353
  232. package/dist/client/processors/errors/FileErrorCode.js +0 -255
  233. package/dist/client/processors/errors/errorHelpers.js +0 -386
  234. package/dist/client/processors/errors/errorSerializer.js +0 -507
  235. package/dist/client/processors/errors/index.js +0 -49
  236. package/dist/client/processors/markup/SvgProcessor.js +0 -240
  237. package/dist/client/processors/media/AudioProcessor.js +0 -707
  238. package/dist/client/processors/media/VideoProcessor.js +0 -1045
  239. package/dist/client/providers/amazonBedrock.js +0 -1512
  240. package/dist/client/providers/amazonSagemaker.js +0 -162
  241. package/dist/client/providers/anthropic.js +0 -831
  242. package/dist/client/providers/azureOpenai.js +0 -143
  243. package/dist/client/providers/googleAiStudio.js +0 -1200
  244. package/dist/client/providers/googleNativeGemini3.js +0 -543
  245. package/dist/client/providers/googleVertex.js +0 -2936
  246. package/dist/client/providers/huggingFace.js +0 -315
  247. package/dist/client/providers/litellm.js +0 -488
  248. package/dist/client/providers/mistral.js +0 -157
  249. package/dist/client/providers/ollama.js +0 -1579
  250. package/dist/client/providers/openAI.js +0 -627
  251. package/dist/client/providers/openRouter.js +0 -543
  252. package/dist/client/providers/openaiCompatible.js +0 -290
  253. package/dist/client/providers/providerTypeUtils.js +0 -46
  254. package/dist/client/providers/sagemaker/adaptive-semaphore.js +0 -215
  255. package/dist/client/providers/sagemaker/client.js +0 -472
  256. package/dist/client/providers/sagemaker/config.js +0 -317
  257. package/dist/client/providers/sagemaker/detection.js +0 -606
  258. package/dist/client/providers/sagemaker/error-constants.js +0 -227
  259. package/dist/client/providers/sagemaker/errors.js +0 -299
  260. package/dist/client/providers/sagemaker/language-model.js +0 -775
  261. package/dist/client/providers/sagemaker/parsers.js +0 -634
  262. package/dist/client/providers/sagemaker/streaming.js +0 -331
  263. package/dist/client/providers/sagemaker/structured-parser.js +0 -625
  264. package/dist/client/proxy/accountQuota.js +0 -162
  265. package/dist/client/proxy/claudeFormat.js +0 -595
  266. package/dist/client/proxy/modelRouter.js +0 -29
  267. package/dist/client/proxy/oauthFetch.js +0 -367
  268. package/dist/client/proxy/proxyFetch.js +0 -586
  269. package/dist/client/proxy/requestLogger.js +0 -207
  270. package/dist/client/proxy/tokenRefresh.js +0 -124
  271. package/dist/client/proxy/usageStats.js +0 -74
  272. package/dist/client/proxy/utils/noProxyUtils.js +0 -149
  273. package/dist/client/rag/ChunkerFactory.js +0 -320
  274. package/dist/client/rag/ChunkerRegistry.js +0 -421
  275. package/dist/client/rag/chunkers/BaseChunker.js +0 -143
  276. package/dist/client/rag/chunkers/CharacterChunker.js +0 -28
  277. package/dist/client/rag/chunkers/HTMLChunker.js +0 -38
  278. package/dist/client/rag/chunkers/JSONChunker.js +0 -68
  279. package/dist/client/rag/chunkers/LaTeXChunker.js +0 -63
  280. package/dist/client/rag/chunkers/MarkdownChunker.js +0 -306
  281. package/dist/client/rag/chunkers/RecursiveChunker.js +0 -139
  282. package/dist/client/rag/chunkers/SemanticMarkdownChunker.js +0 -138
  283. package/dist/client/rag/chunkers/SentenceChunker.js +0 -66
  284. package/dist/client/rag/chunkers/TokenChunker.js +0 -61
  285. package/dist/client/rag/chunkers/index.js +0 -15
  286. package/dist/client/rag/chunking/characterChunker.js +0 -142
  287. package/dist/client/rag/chunking/chunkerRegistry.js +0 -194
  288. package/dist/client/rag/chunking/htmlChunker.js +0 -247
  289. package/dist/client/rag/chunking/index.js +0 -17
  290. package/dist/client/rag/chunking/jsonChunker.js +0 -281
  291. package/dist/client/rag/chunking/latexChunker.js +0 -251
  292. package/dist/client/rag/chunking/markdownChunker.js +0 -373
  293. package/dist/client/rag/chunking/recursiveChunker.js +0 -148
  294. package/dist/client/rag/chunking/semanticChunker.js +0 -306
  295. package/dist/client/rag/chunking/sentenceChunker.js +0 -230
  296. package/dist/client/rag/chunking/tokenChunker.js +0 -183
  297. package/dist/client/rag/document/MDocument.js +0 -392
  298. package/dist/client/rag/document/index.js +0 -5
  299. package/dist/client/rag/document/loaders.js +0 -500
  300. package/dist/client/rag/errors/RAGError.js +0 -274
  301. package/dist/client/rag/errors/index.js +0 -6
  302. package/dist/client/rag/graphRag/graphRAG.js +0 -401
  303. package/dist/client/rag/graphRag/index.js +0 -4
  304. package/dist/client/rag/index.js +0 -141
  305. package/dist/client/rag/metadata/MetadataExtractorFactory.js +0 -418
  306. package/dist/client/rag/metadata/MetadataExtractorRegistry.js +0 -362
  307. package/dist/client/rag/metadata/index.js +0 -9
  308. package/dist/client/rag/metadata/metadataExtractor.js +0 -280
  309. package/dist/client/rag/pipeline/RAGPipeline.js +0 -436
  310. package/dist/client/rag/pipeline/contextAssembly.js +0 -341
  311. package/dist/client/rag/pipeline/index.js +0 -5
  312. package/dist/client/rag/ragIntegration.js +0 -321
  313. package/dist/client/rag/reranker/RerankerFactory.js +0 -430
  314. package/dist/client/rag/reranker/RerankerRegistry.js +0 -402
  315. package/dist/client/rag/reranker/index.js +0 -9
  316. package/dist/client/rag/reranker/reranker.js +0 -277
  317. package/dist/client/rag/resilience/CircuitBreaker.js +0 -431
  318. package/dist/client/rag/resilience/RetryHandler.js +0 -304
  319. package/dist/client/rag/resilience/index.js +0 -7
  320. package/dist/client/rag/retrieval/hybridSearch.js +0 -335
  321. package/dist/client/rag/retrieval/index.js +0 -5
  322. package/dist/client/rag/retrieval/vectorQueryTool.js +0 -307
  323. package/dist/client/rag/types.js +0 -8
  324. package/dist/client/sdk/toolRegistration.js +0 -377
  325. package/dist/client/server/abstract/baseServerAdapter.js +0 -575
  326. package/dist/client/server/adapters/expressAdapter.js +0 -486
  327. package/dist/client/server/adapters/fastifyAdapter.js +0 -472
  328. package/dist/client/server/adapters/honoAdapter.js +0 -632
  329. package/dist/client/server/adapters/koaAdapter.js +0 -510
  330. package/dist/client/server/errors.js +0 -486
  331. package/dist/client/server/factory/serverAdapterFactory.js +0 -160
  332. package/dist/client/server/index.js +0 -108
  333. package/dist/client/server/middleware/abortSignal.js +0 -111
  334. package/dist/client/server/middleware/auth.js +0 -388
  335. package/dist/client/server/middleware/cache.js +0 -359
  336. package/dist/client/server/middleware/common.js +0 -281
  337. package/dist/client/server/middleware/deprecation.js +0 -190
  338. package/dist/client/server/middleware/mcpBodyAttachment.js +0 -63
  339. package/dist/client/server/middleware/rateLimit.js +0 -227
  340. package/dist/client/server/middleware/validation.js +0 -388
  341. package/dist/client/server/openapi/generator.js +0 -398
  342. package/dist/client/server/openapi/index.js +0 -36
  343. package/dist/client/server/openapi/schemas.js +0 -695
  344. package/dist/client/server/openapi/templates.js +0 -374
  345. package/dist/client/server/routes/agentRoutes.js +0 -189
  346. package/dist/client/server/routes/claudeProxyRoutes.js +0 -1600
  347. package/dist/client/server/routes/healthRoutes.js +0 -187
  348. package/dist/client/server/routes/index.js +0 -57
  349. package/dist/client/server/routes/mcpRoutes.js +0 -342
  350. package/dist/client/server/routes/memoryRoutes.js +0 -350
  351. package/dist/client/server/routes/openApiRoutes.js +0 -126
  352. package/dist/client/server/routes/toolRoutes.js +0 -199
  353. package/dist/client/server/streaming/dataStream.js +0 -486
  354. package/dist/client/server/streaming/index.js +0 -11
  355. package/dist/client/server/types.js +0 -67
  356. package/dist/client/server/utils/redaction.js +0 -334
  357. package/dist/client/server/utils/validation.js +0 -243
  358. package/dist/client/server/websocket/WebSocketHandler.js +0 -383
  359. package/dist/client/server/websocket/index.js +0 -4
  360. package/dist/client/services/server/ai/observability/instrumentation.js +0 -808
  361. package/dist/client/telemetry/attributes.js +0 -100
  362. package/dist/client/telemetry/index.js +0 -26
  363. package/dist/client/telemetry/telemetryService.js +0 -308
  364. package/dist/client/telemetry/tracers.js +0 -17
  365. package/dist/client/telemetry/withSpan.js +0 -34
  366. package/dist/client/types/actionTypes.js +0 -6
  367. package/dist/client/types/analytics.js +0 -5
  368. package/dist/client/types/authTypes.js +0 -9
  369. package/dist/client/types/circuitBreakerErrors.js +0 -34
  370. package/dist/client/types/cli.js +0 -21
  371. package/dist/client/types/clientTypes.js +0 -10
  372. package/dist/client/types/common.js +0 -51
  373. package/dist/client/types/configTypes.js +0 -49
  374. package/dist/client/types/content.js +0 -19
  375. package/dist/client/types/contextTypes.js +0 -400
  376. package/dist/client/types/conversation.js +0 -47
  377. package/dist/client/types/conversationMemoryInterface.js +0 -6
  378. package/dist/client/types/domainTypes.js +0 -5
  379. package/dist/client/types/errors.js +0 -167
  380. package/dist/client/types/evaluation.js +0 -5
  381. package/dist/client/types/evaluationProviders.js +0 -5
  382. package/dist/client/types/evaluationTypes.js +0 -1
  383. package/dist/client/types/externalMcp.js +0 -6
  384. package/dist/client/types/fileReferenceTypes.js +0 -8
  385. package/dist/client/types/fileTypes.js +0 -4
  386. package/dist/client/types/generateTypes.js +0 -1
  387. package/dist/client/types/guardrails.js +0 -1
  388. package/dist/client/types/hitlTypes.js +0 -8
  389. package/dist/client/types/index.js +0 -57
  390. package/dist/client/types/mcpTypes.js +0 -5
  391. package/dist/client/types/middlewareTypes.js +0 -1
  392. package/dist/client/types/modelTypes.js +0 -30
  393. package/dist/client/types/multimodal.js +0 -135
  394. package/dist/client/types/observability.js +0 -6
  395. package/dist/client/types/pptTypes.js +0 -82
  396. package/dist/client/types/providers.js +0 -111
  397. package/dist/client/types/proxyTypes.js +0 -16
  398. package/dist/client/types/ragTypes.js +0 -7
  399. package/dist/client/types/sdkTypes.js +0 -8
  400. package/dist/client/types/serviceTypes.js +0 -5
  401. package/dist/client/types/streamTypes.js +0 -1
  402. package/dist/client/types/subscriptionTypes.js +0 -9
  403. package/dist/client/types/taskClassificationTypes.js +0 -5
  404. package/dist/client/types/tools.js +0 -24
  405. package/dist/client/types/ttsTypes.js +0 -57
  406. package/dist/client/types/typeAliases.js +0 -48
  407. package/dist/client/types/utilities.js +0 -4
  408. package/dist/client/types/workflowTypes.js +0 -30
  409. package/dist/client/utils/async/withTimeout.js +0 -98
  410. package/dist/client/utils/asyncMutex.js +0 -60
  411. package/dist/client/utils/conversationMemory.js +0 -431
  412. package/dist/client/utils/csvProcessor.js +0 -846
  413. package/dist/client/utils/errorHandling.js +0 -936
  414. package/dist/client/utils/evaluationUtils.js +0 -131
  415. package/dist/client/utils/factoryProcessing.js +0 -589
  416. package/dist/client/utils/fileDetector.js +0 -2161
  417. package/dist/client/utils/imageCache.js +0 -376
  418. package/dist/client/utils/imageProcessor.js +0 -704
  419. package/dist/client/utils/logger.js +0 -491
  420. package/dist/client/utils/mcpDefaults.js +0 -134
  421. package/dist/client/utils/messageBuilder.js +0 -1653
  422. package/dist/client/utils/modelAliasResolver.js +0 -54
  423. package/dist/client/utils/modelDetection.js +0 -80
  424. package/dist/client/utils/modelRouter.js +0 -292
  425. package/dist/client/utils/multimodalOptionsBuilder.js +0 -65
  426. package/dist/client/utils/observabilityHelpers.js +0 -47
  427. package/dist/client/utils/parameterValidation.js +0 -966
  428. package/dist/client/utils/pdfProcessor.js +0 -410
  429. package/dist/client/utils/performance.js +0 -222
  430. package/dist/client/utils/pricing.js +0 -340
  431. package/dist/client/utils/promptRedaction.js +0 -62
  432. package/dist/client/utils/providerConfig.js +0 -1009
  433. package/dist/client/utils/providerHealth.js +0 -1237
  434. package/dist/client/utils/providerRetry.js +0 -112
  435. package/dist/client/utils/providerUtils.js +0 -434
  436. package/dist/client/utils/rateLimiter.js +0 -200
  437. package/dist/client/utils/redis.js +0 -368
  438. package/dist/client/utils/retryHandler.js +0 -269
  439. package/dist/client/utils/retryability.js +0 -22
  440. package/dist/client/utils/sanitizers/svg.js +0 -481
  441. package/dist/client/utils/schemaConversion.js +0 -255
  442. package/dist/client/utils/taskClassificationUtils.js +0 -149
  443. package/dist/client/utils/taskClassifier.js +0 -94
  444. package/dist/client/utils/thinkingConfig.js +0 -104
  445. package/dist/client/utils/timeout.js +0 -359
  446. package/dist/client/utils/tokenEstimation.js +0 -142
  447. package/dist/client/utils/tokenLimits.js +0 -125
  448. package/dist/client/utils/tokenUtils.js +0 -239
  449. package/dist/client/utils/toolUtils.js +0 -75
  450. package/dist/client/utils/transformationUtils.js +0 -554
  451. package/dist/client/utils/ttsProcessor.js +0 -286
  452. package/dist/client/utils/typeUtils.js +0 -97
  453. package/dist/client/utils/videoAnalysisProcessor.js +0 -67
  454. package/dist/client/workflow/config.js +0 -398
  455. package/dist/client/workflow/core/ensembleExecutor.js +0 -407
  456. package/dist/client/workflow/core/judgeScorer.js +0 -544
  457. package/dist/client/workflow/core/responseConditioner.js +0 -225
  458. package/dist/client/workflow/core/types/conditionerTypes.js +0 -7
  459. package/dist/client/workflow/core/types/ensembleTypes.js +0 -7
  460. package/dist/client/workflow/core/types/index.js +0 -7
  461. package/dist/client/workflow/core/types/judgeTypes.js +0 -7
  462. package/dist/client/workflow/core/types/layerTypes.js +0 -7
  463. package/dist/client/workflow/core/types/registryTypes.js +0 -7
  464. package/dist/client/workflow/core/workflowRegistry.js +0 -304
  465. package/dist/client/workflow/core/workflowRunner.js +0 -586
  466. package/dist/client/workflow/index.js +0 -50
  467. package/dist/client/workflow/types.js +0 -9
  468. package/dist/client/workflow/utils/types/index.js +0 -7
  469. package/dist/client/workflow/utils/workflowMetrics.js +0 -311
  470. package/dist/client/workflow/utils/workflowValidation.js +0 -420
  471. package/dist/client/workflow/workflows/adaptiveWorkflow.js +0 -366
  472. package/dist/client/workflow/workflows/consensusWorkflow.js +0 -192
  473. package/dist/client/workflow/workflows/fallbackWorkflow.js +0 -225
  474. package/dist/client/workflow/workflows/multiJudgeWorkflow.js +0 -351
  475. /package/dist/client/{client/reactHooks.js → reactHooks.js} +0 -0
@@ -1,1435 +0,0 @@
1
- /**
2
- * Redis Conversation Memory Manager for NeuroLink
3
- * Redis-based implementation of conversation storage with same interface as ConversationMemoryManager
4
- */
5
- import { SpanKind, SpanStatusCode } from "@opentelemetry/api";
6
- import { tracers } from "../telemetry/tracers.js";
7
- import { randomUUID } from "crypto";
8
- import { MESSAGES_PER_TURN } from "../config/conversationMemory.js";
9
- import { generateToolOutputPreview } from "../context/toolOutputLimits.js";
10
- import { SummarizationEngine } from "../context/summarizationEngine.js";
11
- import { NeuroLink } from "../neurolink.js";
12
- import { ConversationMemoryError } from "../types/conversation.js";
13
- import { withTimeout } from "../utils/errorHandling.js";
14
- import { buildContextFromPointer, getEffectiveTokenThreshold, } from "../utils/conversationMemory.js";
15
- import { runWithCurrentLangfuseContext } from "../services/server/ai/observability/instrumentation.js";
16
- import { logger } from "../utils/logger.js";
17
- import { deserializeConversation, getNormalizedConfig, getPooledRedisClient, getSessionKey, getUserSessionsKey, releasePooledRedisClient, scanKeys, serializeConversation, } from "../utils/redis.js";
18
- const redisTracer = tracers.redis;
19
- /**
20
- * Redis-based implementation of the ConversationMemoryManager
21
- * Uses the same interface but stores data in Redis
22
- */
23
- export class RedisConversationMemoryManager {
24
- config;
25
- isInitialized = false;
26
- summarizationEngine = new SummarizationEngine();
27
- redisConfig;
28
- redisClient = null;
29
- /**
30
- * Temporary storage for tool execution data to prevent race conditions
31
- * Key format: "${sessionId}:${userId}"
32
- */
33
- pendingToolExecutions = new Map();
34
- /**
35
- * Track sessions currently generating titles to prevent race conditions
36
- * Key format: "${sessionId}:${userId}"
37
- */
38
- titleGenerationInProgress = new Set();
39
- /**
40
- * Track sessions currently being summarized to prevent race conditions
41
- * Key format: "${sessionId}:${userId}"
42
- */
43
- summarizationInProgress = new Set();
44
- constructor(config, redisConfig = {}) {
45
- this.config = config;
46
- this.redisConfig = getNormalizedConfig(redisConfig);
47
- }
48
- /**
49
- * Initialize the memory manager with Redis connection
50
- */
51
- async initialize() {
52
- if (this.isInitialized) {
53
- logger.debug("[RedisConversationMemoryManager] Already initialized, skipping");
54
- return;
55
- }
56
- await redisTracer.startActiveSpan("neurolink.memory.initialize", {
57
- kind: SpanKind.CLIENT,
58
- attributes: {
59
- "redis.host": this.redisConfig.host,
60
- "redis.port": this.redisConfig.port,
61
- "redis.key_prefix": this.redisConfig.keyPrefix,
62
- },
63
- }, async (span) => {
64
- try {
65
- logger.debug("[RedisConversationMemoryManager] Initializing with config", {
66
- host: this.redisConfig.host,
67
- port: this.redisConfig.port,
68
- keyPrefix: this.redisConfig.keyPrefix,
69
- ttl: this.redisConfig.ttl,
70
- });
71
- this.redisClient = await getPooledRedisClient(this.redisConfig);
72
- this.isInitialized = true;
73
- logger.info("RedisConversationMemoryManager initialized", {
74
- storage: "redis",
75
- host: this.redisConfig.host,
76
- port: this.redisConfig.port,
77
- maxSessions: this.config.maxSessions,
78
- maxTurnsPerSession: this.config.maxTurnsPerSession,
79
- });
80
- logger.debug("[RedisConversationMemoryManager] Redis client created successfully", {
81
- clientType: this.redisClient?.constructor?.name || "unknown",
82
- isConnected: !!this.redisClient,
83
- });
84
- }
85
- catch (error) {
86
- span.setStatus({
87
- code: SpanStatusCode.ERROR,
88
- message: error instanceof Error ? error.message : String(error),
89
- });
90
- span.recordException(error instanceof Error ? error : new Error(String(error)));
91
- logger.error("[RedisConversationMemoryManager] Failed to initialize", {
92
- error: error instanceof Error ? error.message : String(error),
93
- stack: error instanceof Error ? error.stack : undefined,
94
- config: {
95
- host: this.redisConfig.host,
96
- port: this.redisConfig.port,
97
- },
98
- });
99
- throw new ConversationMemoryError("Failed to initialize Redis conversation memory", "CONFIG_ERROR", {
100
- error: error instanceof Error ? error.message : String(error),
101
- });
102
- }
103
- finally {
104
- span.end();
105
- }
106
- });
107
- }
108
- /** Whether this memory manager can persist data (Redis connected and initialized) */
109
- get canPersist() {
110
- return (this.isInitialized && this.redisClient !== null && this.redisClient.isOpen);
111
- }
112
- /** Whether Redis client is configured and connected */
113
- get isRedisConfigured() {
114
- return this.redisClient !== null && this.redisClient.isOpen;
115
- }
116
- /** Get health status for monitoring */
117
- getHealthStatus() {
118
- return {
119
- initialized: this.isInitialized,
120
- connected: this.redisClient?.isOpen ?? false,
121
- host: this.redisConfig.host,
122
- keyPrefix: this.redisConfig.keyPrefix,
123
- };
124
- }
125
- /**
126
- * Get session by ID, reconstructing a SessionMemory from Redis storage.
127
- */
128
- async getSession(sessionId, userId, requestId) {
129
- await this.ensureInitialized();
130
- if (!this.redisClient) {
131
- return undefined;
132
- }
133
- return redisTracer.startActiveSpan("neurolink.memory.getSession", { kind: SpanKind.CLIENT, attributes: { "session.id": sessionId } }, async (span) => {
134
- if (userId) {
135
- span.setAttribute("user.id", userId);
136
- }
137
- try {
138
- const redisKey = getSessionKey(this.redisConfig, sessionId, userId);
139
- const conversationData = await this.redisClient.get(redisKey);
140
- const conversation = deserializeConversation(conversationData || null);
141
- if (!conversation) {
142
- span.setAttribute("session.found", false);
143
- return undefined;
144
- }
145
- span.setAttribute("session.found", true);
146
- // Log session load metadata for observability
147
- const blobSizeBytes = conversationData
148
- ? Buffer.byteLength(conversationData, "utf8")
149
- : 0;
150
- const messageCount = conversation.messages.length;
151
- const hasSummary = !!conversation.summarizedUpToMessageId;
152
- const pointerIndex = hasSummary
153
- ? conversation.messages.findIndex((msg) => msg.id === conversation.summarizedUpToMessageId)
154
- : -1;
155
- const recentMessageCount = hasSummary && pointerIndex !== -1
156
- ? messageCount - pointerIndex - 1
157
- : messageCount;
158
- span.setAttribute("message.count", messageCount);
159
- span.setAttribute("blob.size_bytes", blobSizeBytes);
160
- logger.info("[ConversationMemory] Session loaded", {
161
- requestId,
162
- sessionId,
163
- blobSizeBytes,
164
- messageCount,
165
- hasSummary,
166
- recentMessageCount,
167
- });
168
- if (blobSizeBytes > 512 * 1024) {
169
- logger.warn("[ConversationMemory] Large session blob", {
170
- requestId,
171
- sessionId,
172
- blobSizeBytes,
173
- messageCount,
174
- });
175
- }
176
- return {
177
- sessionId: conversation.sessionId,
178
- userId: conversation.userId,
179
- messages: conversation.messages,
180
- summarizedUpToMessageId: conversation.summarizedUpToMessageId,
181
- summarizedMessage: conversation.summarizedMessage,
182
- tokenThreshold: conversation.tokenThreshold,
183
- lastTokenCount: conversation.lastTokenCount,
184
- lastCountedAt: conversation.lastCountedAt,
185
- lastApiTokenCount: conversation.lastApiTokenCount,
186
- createdAt: new Date(conversation.createdAt).getTime(),
187
- lastActivity: new Date(conversation.updatedAt).getTime(),
188
- };
189
- }
190
- catch (error) {
191
- span.setStatus({
192
- code: SpanStatusCode.ERROR,
193
- message: error instanceof Error ? error.message : String(error),
194
- });
195
- span.recordException(error instanceof Error ? error : new Error(String(error)));
196
- logger.error("[RedisConversationMemoryManager] Failed to get session", {
197
- sessionId,
198
- userId,
199
- error: error instanceof Error ? error.message : String(error),
200
- });
201
- return undefined;
202
- }
203
- finally {
204
- span.end();
205
- }
206
- });
207
- }
208
- /**
209
- * Get raw session data without any filtering or transformation.
210
- * Used by the memory retrieval tool and internal APIs that need
211
- * access to full message data including unmodified tool outputs.
212
- */
213
- async getSessionRaw(sessionId, userId) {
214
- try {
215
- await this.ensureInitialized();
216
- if (!this.redisClient) {
217
- return null;
218
- }
219
- const redisKey = getSessionKey(this.redisConfig, sessionId, userId);
220
- const conversationData = await this.redisClient.get(redisKey);
221
- return deserializeConversation(conversationData || null);
222
- }
223
- catch (error) {
224
- logger.error("[RedisConversationMemoryManager] Failed to get raw session", {
225
- sessionId,
226
- userId,
227
- error: error instanceof Error ? error.message : String(error),
228
- });
229
- return null;
230
- }
231
- }
232
- /**
233
- * Get all sessions for a specific user
234
- */
235
- async getUserSessions(userId) {
236
- // Ensure initialization
237
- await this.ensureInitialized();
238
- if (!this.redisClient) {
239
- logger.warn("[RedisConversationMemoryManager] Redis client not available", { userId });
240
- return [];
241
- }
242
- try {
243
- const userSessionsKey = getUserSessionsKey(this.redisConfig, userId);
244
- const sessions = await this.redisClient.sMembers(userSessionsKey);
245
- return Array.from(sessions).map(String);
246
- }
247
- catch (error) {
248
- logger.error("[RedisConversationMemoryManager] Failed to get user sessions", {
249
- userId,
250
- error: error instanceof Error ? error.message : String(error),
251
- });
252
- return [];
253
- }
254
- }
255
- /**
256
- * Add a session to user's session set (private method)
257
- */
258
- async addUserSession(userId, sessionId) {
259
- if (!this.redisClient || !userId) {
260
- return;
261
- }
262
- try {
263
- const userSessionsKey = getUserSessionsKey(this.redisConfig, userId);
264
- await this.redisClient.sAdd(userSessionsKey, sessionId);
265
- if (this.redisConfig.ttl > 0) {
266
- await this.redisClient.expire(userSessionsKey, this.redisConfig.ttl);
267
- }
268
- }
269
- catch (error) {
270
- logger.error("[RedisConversationMemoryManager] Failed to add session to user set", {
271
- userId,
272
- sessionId,
273
- error: error instanceof Error ? error.message : String(error),
274
- });
275
- }
276
- }
277
- /**
278
- * Remove a session from user's session set (private method)
279
- */
280
- async removeUserSession(userId, sessionId) {
281
- if (!this.redisClient || !userId) {
282
- return false;
283
- }
284
- try {
285
- const userSessionsKey = getUserSessionsKey(this.redisConfig, userId);
286
- const result = await this.redisClient.sRem(userSessionsKey, sessionId);
287
- return Number(result) > 0;
288
- }
289
- catch (error) {
290
- logger.error("[RedisConversationMemoryManager] Failed to remove session from user set", {
291
- userId,
292
- sessionId,
293
- error: error instanceof Error ? error.message : String(error),
294
- });
295
- return false;
296
- }
297
- }
298
- /**
299
- * Generate current timestamp in ISO format
300
- */
301
- generateTimestamp() {
302
- return new Date().toISOString();
303
- }
304
- /**
305
- * Store tool execution data for a session (temporarily to avoid race conditions)
306
- */
307
- async storeToolExecution(sessionId, userId, toolCalls, toolResults, currentTime) {
308
- logger.debug("[RedisConversationMemoryManager] Storing tool execution temporarily", {
309
- sessionId,
310
- userId,
311
- toolCallsCount: toolCalls?.length || 0,
312
- toolResultsCount: toolResults?.length || 0,
313
- });
314
- try {
315
- const normalizedUserId = userId || "randomUser";
316
- const pendingKey = `${sessionId}:${normalizedUserId}`;
317
- // Store tool execution data temporarily to prevent race conditions
318
- const pendingData = {
319
- toolCalls: (toolCalls || []).map((call) => ({
320
- ...call,
321
- timestamp: currentTime,
322
- })),
323
- toolResults: (toolResults || []).map((result) => ({
324
- ...result,
325
- timestamp: currentTime,
326
- })),
327
- timestamp: Date.now(),
328
- };
329
- // Check if there's existing pending data and merge
330
- const existingData = this.pendingToolExecutions.get(pendingKey);
331
- if (existingData) {
332
- logger.debug("[RedisConversationMemoryManager] Merging with existing pending tool data", {
333
- sessionId,
334
- existingToolCalls: existingData.toolCalls.length,
335
- existingToolResults: existingData.toolResults.length,
336
- newToolCalls: toolCalls?.length || 0,
337
- newToolResults: toolResults?.length || 0,
338
- });
339
- // Merge tool calls and results
340
- pendingData.toolCalls = [
341
- ...existingData.toolCalls,
342
- ...pendingData.toolCalls,
343
- ];
344
- pendingData.toolResults = [
345
- ...existingData.toolResults,
346
- ...pendingData.toolResults,
347
- ];
348
- }
349
- this.pendingToolExecutions.set(pendingKey, pendingData);
350
- logger.debug("[RedisConversationMemoryManager] Tool execution stored temporarily", {
351
- sessionId,
352
- userId: normalizedUserId,
353
- pendingKey,
354
- totalToolCalls: pendingData.toolCalls.length,
355
- totalToolResults: pendingData.toolResults.length,
356
- });
357
- // Clean up stale pending data (older than 5 minutes)
358
- this.cleanupStalePendingData();
359
- }
360
- catch (error) {
361
- logger.error("[RedisConversationMemoryManager] Failed to store tool execution temporarily", {
362
- sessionId,
363
- error: error instanceof Error ? error.message : String(error),
364
- });
365
- // Don't throw - tool storage failures shouldn't break generation
366
- }
367
- }
368
- /**
369
- * Store a conversation turn for a session
370
- */
371
- async storeConversationTurn(options) {
372
- logger.debug("[RedisConversationMemoryManager] Storing conversation turn", {
373
- sessionId: options.sessionId,
374
- userId: options.userId,
375
- });
376
- await this.ensureInitialized();
377
- // NLK-GAP-012: Add span for storeTurn CRUD operation
378
- return redisTracer.startActiveSpan("neurolink.memory.storeTurn", {
379
- kind: SpanKind.CLIENT,
380
- attributes: {
381
- "session.id": options.sessionId,
382
- ...(options.userId && { "user.id": options.userId }),
383
- },
384
- }, async (span) => {
385
- try {
386
- if (!this.redisClient) {
387
- throw new Error("Redis client not initialized");
388
- }
389
- const redisKey = getSessionKey(this.redisConfig, options.sessionId, options.userId);
390
- const conversationData = await this.redisClient.get(redisKey);
391
- let conversation = deserializeConversation(conversationData);
392
- const currentTime = new Date().toISOString();
393
- const normalizedUserId = options.userId || "randomUser";
394
- if (!conversation) {
395
- const titleGenerationKey = `${options.sessionId}:${normalizedUserId}`;
396
- // Capture the current Langfuse ALS context before setImmediate,
397
- // which breaks automatic AsyncLocalStorage propagation and would
398
- // otherwise cause orphaned traces in Langfuse.
399
- const generateTitleWithContext = runWithCurrentLangfuseContext(async () => {
400
- if (this.titleGenerationInProgress.has(titleGenerationKey)) {
401
- return;
402
- }
403
- this.titleGenerationInProgress.add(titleGenerationKey);
404
- try {
405
- const title = await this.generateConversationTitle(options.userMessage);
406
- const updatedRedisKey = getSessionKey(this.redisConfig, options.sessionId, options.userId || undefined);
407
- const updatedConversationData = await this.redisClient?.get(updatedRedisKey);
408
- const updatedConversation = deserializeConversation(updatedConversationData || null);
409
- if (updatedConversation) {
410
- updatedConversation.title = title;
411
- updatedConversation.updatedAt = new Date().toISOString();
412
- const serializedData = serializeConversation(updatedConversation);
413
- await this.redisClient?.set(updatedRedisKey, serializedData);
414
- if (this.redisConfig.ttl > 0) {
415
- await this.redisClient?.expire(updatedRedisKey, this.redisConfig.ttl);
416
- }
417
- }
418
- }
419
- catch (titleError) {
420
- logger.warn("[RedisConversationMemoryManager] Failed to generate conversation title in background", {
421
- sessionId: options.sessionId,
422
- userId: normalizedUserId,
423
- error: titleError instanceof Error
424
- ? titleError.message
425
- : String(titleError),
426
- });
427
- }
428
- finally {
429
- this.titleGenerationInProgress.delete(titleGenerationKey);
430
- }
431
- });
432
- setImmediate(generateTitleWithContext);
433
- conversation = {
434
- id: randomUUID(),
435
- title: "New Conversation", // Temporary title until generated
436
- sessionId: options.sessionId,
437
- userId: normalizedUserId,
438
- createdAt: options.startTimeStamp?.toISOString() || currentTime,
439
- updatedAt: options.startTimeStamp?.toISOString() || currentTime,
440
- messages: [],
441
- };
442
- }
443
- else {
444
- conversation.updatedAt = currentTime;
445
- }
446
- const tokenThreshold = options.providerDetails
447
- ? getEffectiveTokenThreshold(options.providerDetails.provider, options.providerDetails.model, this.config.tokenThreshold, conversation.tokenThreshold)
448
- : this.config.tokenThreshold || 50000;
449
- const userMsg = {
450
- id: randomUUID(),
451
- timestamp: options.startTimeStamp?.toISOString() || this.generateTimestamp(),
452
- role: "user",
453
- content: options.userMessage,
454
- };
455
- conversation.messages.push(userMsg);
456
- await this.flushPendingToolData(conversation, options.sessionId, normalizedUserId);
457
- const assistantMsg = {
458
- id: randomUUID(),
459
- timestamp: this.generateTimestamp(),
460
- role: "assistant",
461
- content: options.aiResponse,
462
- events: options.events || undefined,
463
- };
464
- conversation.messages.push(assistantMsg);
465
- // Store API-reported token counts if available
466
- if (options.tokenUsage) {
467
- conversation.lastApiTokenCount = options.tokenUsage;
468
- }
469
- logger.info("[RedisConversationMemoryManager] Added new messages", {
470
- sessionId: conversation.sessionId,
471
- userId: conversation.userId,
472
- });
473
- // Use per-request enableSummarization with higher priority than instance config
474
- const shouldSummarize = options.enableSummarization !== undefined
475
- ? options.enableSummarization
476
- : this.config.enableSummarization;
477
- if (shouldSummarize) {
478
- const normalizedUserId = options.userId || "randomUser";
479
- const summarizationKey = `${options.sessionId}:${normalizedUserId}`;
480
- // Only trigger summarization if not already in progress for this session
481
- if (!this.summarizationInProgress.has(summarizationKey)) {
482
- // Capture the current Langfuse ALS context before setImmediate,
483
- // which breaks automatic AsyncLocalStorage propagation and would
484
- // otherwise cause orphaned traces in Langfuse.
485
- const summarizeWithContext = runWithCurrentLangfuseContext(async () => {
486
- try {
487
- await this.checkAndSummarize(conversation, tokenThreshold, options.sessionId, options.userId, options.requestId);
488
- }
489
- catch (error) {
490
- logger.error("Background summarization failed", {
491
- sessionId: conversation.sessionId,
492
- error: error instanceof Error ? error.message : String(error),
493
- });
494
- }
495
- });
496
- setImmediate(summarizeWithContext);
497
- }
498
- else {
499
- logger.debug("[RedisConversationMemoryManager] Summarization already in progress, skipping", {
500
- sessionId: options.sessionId,
501
- userId: normalizedUserId,
502
- });
503
- }
504
- }
505
- const serializedData = serializeConversation(conversation);
506
- await this.redisClient.set(redisKey, serializedData);
507
- // Log turn storage metadata for observability
508
- const blobSizeBytes = Buffer.byteLength(serializedData, "utf8");
509
- logger.info("[ConversationMemory] Turn stored", {
510
- requestId: options.requestId,
511
- sessionId: options.sessionId,
512
- blobSizeBytes,
513
- totalMessages: conversation.messages.length,
514
- userMsgChars: options.userMessage.length,
515
- assistantMsgChars: options.aiResponse.length,
516
- });
517
- if (blobSizeBytes > 512 * 1024) {
518
- logger.warn("[ConversationMemory] Large session blob", {
519
- requestId: options.requestId,
520
- sessionId: options.sessionId,
521
- blobSizeBytes,
522
- messageCount: conversation.messages.length,
523
- });
524
- }
525
- if (this.redisConfig.ttl > 0) {
526
- await this.redisClient.expire(redisKey, this.redisConfig.ttl);
527
- }
528
- if (options.userId) {
529
- await this.addUserSession(options.userId, options.sessionId);
530
- }
531
- span.setAttribute("message.count", conversation.messages.length);
532
- span.setStatus({ code: SpanStatusCode.OK });
533
- logger.debug("[RedisConversationMemoryManager] Successfully stored conversation turn", {
534
- sessionId: options.sessionId,
535
- totalMessages: conversation.messages.length,
536
- title: conversation.title,
537
- });
538
- }
539
- catch (error) {
540
- span.setStatus({
541
- code: SpanStatusCode.ERROR,
542
- message: error instanceof Error ? error.message : String(error),
543
- });
544
- span.recordException(error instanceof Error ? error : new Error(String(error)));
545
- throw new ConversationMemoryError(`Failed to store conversation turn in Redis for session ${options.sessionId}`, "STORAGE_ERROR", {
546
- sessionId: options.sessionId,
547
- error: error instanceof Error ? error.message : String(error),
548
- });
549
- }
550
- finally {
551
- span.end();
552
- }
553
- });
554
- }
555
- /**
556
- * Check if summarization is needed based on token count
557
- */
558
- async checkAndSummarize(conversation, threshold, sessionId, userId, requestId) {
559
- const normalizedUserId = userId || "randomUser";
560
- const summarizationKey = `${sessionId}:${normalizedUserId}`;
561
- if (this.summarizationInProgress.has(summarizationKey)) {
562
- logger.debug("[RedisConversationMemoryManager] Summarization already in progress, skipping", {
563
- sessionId,
564
- userId: normalizedUserId,
565
- });
566
- return;
567
- }
568
- this.summarizationInProgress.add(summarizationKey);
569
- try {
570
- const session = {
571
- sessionId: conversation.sessionId,
572
- userId: conversation.userId,
573
- messages: conversation.messages,
574
- summarizedUpToMessageId: conversation.summarizedUpToMessageId,
575
- summarizedMessage: conversation.summarizedMessage,
576
- tokenThreshold: conversation.tokenThreshold,
577
- lastTokenCount: conversation.lastTokenCount,
578
- lastCountedAt: conversation.lastCountedAt,
579
- createdAt: new Date(conversation.createdAt).getTime(),
580
- lastActivity: new Date(conversation.updatedAt).getTime(),
581
- };
582
- const summarized = await this.summarizationEngine.checkAndSummarize(session, threshold, this.config, "[RedisConversationMemoryManager]", requestId);
583
- conversation.lastTokenCount = session.lastTokenCount;
584
- conversation.lastCountedAt = session.lastCountedAt;
585
- if (summarized) {
586
- conversation.summarizedUpToMessageId = session.summarizedUpToMessageId;
587
- conversation.summarizedMessage = session.summarizedMessage;
588
- if (this.redisClient) {
589
- const redisKey = getSessionKey(this.redisConfig, sessionId, userId);
590
- // Re-read current state to avoid clobbering messages added during summarization
591
- const latestData = await this.redisClient.get(redisKey);
592
- if (latestData) {
593
- const latestConversation = deserializeConversation(latestData);
594
- if (latestConversation) {
595
- // Apply only summarization metadata onto the fresh state
596
- latestConversation.summarizedUpToMessageId =
597
- conversation.summarizedUpToMessageId;
598
- latestConversation.summarizedMessage =
599
- conversation.summarizedMessage;
600
- latestConversation.lastTokenCount = conversation.lastTokenCount;
601
- latestConversation.lastCountedAt = conversation.lastCountedAt;
602
- const freshSerialized = serializeConversation(latestConversation);
603
- await this.redisClient.set(redisKey, freshSerialized);
604
- if (this.redisConfig.ttl > 0) {
605
- await this.redisClient.expire(redisKey, this.redisConfig.ttl);
606
- }
607
- }
608
- }
609
- }
610
- }
611
- }
612
- catch (error) {
613
- logger.error("Token counting or summarization failed", {
614
- sessionId: conversation.sessionId,
615
- error: error instanceof Error ? error.message : String(error),
616
- });
617
- }
618
- finally {
619
- this.summarizationInProgress.delete(summarizationKey);
620
- }
621
- }
622
- /**
623
- * Build context messages for AI prompt injection (TOKEN-BASED)
624
- * Returns messages from pointer onwards (or all if no pointer)
625
- * Applies sendToolPreview toggle and hydrates result.result for backward compat
626
- */
627
- async buildContextMessages(sessionId, userId, enableSummarization, requestId) {
628
- await this.ensureInitialized();
629
- if (!this.redisClient) {
630
- logger.warn("[RedisConversationMemoryManager] Redis client not available in buildContextMessages");
631
- return [];
632
- }
633
- // NLK-GAP-012: Add span for buildContext CRUD operation
634
- return redisTracer.startActiveSpan("neurolink.memory.buildContext", {
635
- kind: SpanKind.CLIENT,
636
- attributes: {
637
- "session.id": sessionId,
638
- ...(userId && { "user.id": userId }),
639
- },
640
- }, async (span) => {
641
- try {
642
- logger.info("[RedisConversationMemoryManager] Building context messages", {
643
- sessionId,
644
- userId,
645
- method: "buildContextMessages",
646
- });
647
- const redisKey = getSessionKey(this.redisConfig, sessionId, userId);
648
- const conversationData = await this.redisClient.get(redisKey);
649
- const conversation = deserializeConversation(conversationData || null);
650
- if (!conversation) {
651
- span.setAttribute("session.found", false);
652
- span.setStatus({ code: SpanStatusCode.OK });
653
- return [];
654
- }
655
- const session = {
656
- sessionId: conversation.sessionId,
657
- userId: conversation.userId,
658
- messages: conversation.messages,
659
- summarizedUpToMessageId: conversation.summarizedUpToMessageId,
660
- summarizedMessage: conversation.summarizedMessage,
661
- tokenThreshold: conversation.tokenThreshold,
662
- lastTokenCount: conversation.lastTokenCount,
663
- lastCountedAt: conversation.lastCountedAt,
664
- createdAt: new Date(conversation.createdAt).getTime(),
665
- lastActivity: new Date(conversation.updatedAt).getTime(),
666
- };
667
- const contextMessages = buildContextFromPointer(session, requestId);
668
- const sendToolPreview = this.config?.contextCompaction?.sendToolPreview === true;
669
- // Map tool_result messages: apply preview toggle + hydrate result.result
670
- const finalMessages = contextMessages.map((msg) => {
671
- if (msg.role !== "tool_result") {
672
- return msg;
673
- }
674
- // Toggle: swap content to preview if enabled AND a preview exists
675
- const content = sendToolPreview && msg.metadata?.toolOutputPreview
676
- ? msg.metadata.toolOutputPreview
677
- : msg.content;
678
- // Hydrate result.result from content for backward compatibility
679
- // (result.result is no longer stored — inferred from content at read time)
680
- let hydratedResult = msg.result;
681
- if (msg.result && msg.result.result === undefined) {
682
- let parsedResult = content;
683
- try {
684
- parsedResult = JSON.parse(content);
685
- }
686
- catch {
687
- /* plain text — use as-is */
688
- }
689
- hydratedResult = { ...msg.result, result: parsedResult };
690
- }
691
- return { ...msg, content, result: hydratedResult };
692
- });
693
- // Tool messages now have real content and participate in context properly.
694
- // The tool output pruner (Stage 1) handles bounding old tool outputs.
695
- span.setAttribute("context.message_count", finalMessages.length);
696
- span.setStatus({ code: SpanStatusCode.OK });
697
- logger.info("[RedisConversationMemoryManager] Retrieved context messages", {
698
- sessionId,
699
- userId,
700
- });
701
- return finalMessages;
702
- }
703
- catch (error) {
704
- span.setStatus({
705
- code: SpanStatusCode.ERROR,
706
- message: error instanceof Error ? error.message : String(error),
707
- });
708
- span.recordException(error instanceof Error ? error : new Error(String(error)));
709
- throw error;
710
- }
711
- finally {
712
- span.end();
713
- }
714
- });
715
- }
716
- /**
717
- * Get session metadata for a specific user session (optimized for listing)
718
- * Fetches only essential metadata without heavy message arrays
719
- *
720
- * @param userId The user identifier
721
- * @param sessionId The session identifier
722
- * @returns Session metadata or null if session doesn't exist
723
- */
724
- async getUserSessionMetadata(userId, sessionId) {
725
- logger.debug("[RedisConversationMemoryManager] Getting user session metadata", {
726
- userId,
727
- sessionId,
728
- });
729
- await this.ensureInitialized();
730
- if (!this.redisClient) {
731
- logger.warn("[RedisConversationMemoryManager] Redis client not available", { userId, sessionId });
732
- return null;
733
- }
734
- try {
735
- const sessionKey = getSessionKey(this.redisConfig, sessionId, userId);
736
- const conversationData = await this.redisClient.get(sessionKey);
737
- if (!conversationData) {
738
- logger.debug("[RedisConversationMemoryManager] No session data found", {
739
- userId,
740
- sessionId,
741
- sessionKey,
742
- });
743
- return null;
744
- }
745
- // Deserialize conversation object but extract only metadata
746
- const conversation = deserializeConversation(conversationData);
747
- if (conversation) {
748
- return {
749
- id: conversation.sessionId,
750
- title: conversation.title,
751
- createdAt: conversation.createdAt,
752
- updatedAt: conversation.updatedAt,
753
- metadata: conversation.additionalMetadata?.agenticLoopReports
754
- ? {
755
- agenticLoopReports: conversation.additionalMetadata.agenticLoopReports,
756
- }
757
- : undefined,
758
- };
759
- }
760
- logger.debug("[RedisConversationMemoryManager] No valid conversation data found", {
761
- userId,
762
- sessionId,
763
- sessionKey,
764
- });
765
- return null;
766
- }
767
- catch (error) {
768
- logger.error("[RedisConversationMemoryManager] Failed to get user session metadata", {
769
- userId,
770
- sessionId,
771
- error: error instanceof Error ? error.message : String(error),
772
- stack: error instanceof Error ? error.stack : undefined,
773
- });
774
- return null;
775
- }
776
- }
777
- /**
778
- * Get conversation history for a specific user session
779
- *
780
- * @param userId The user identifier
781
- * @param sessionId The session identifier
782
- * @returns Array of chat messages or null if session doesn't exist
783
- */
784
- async getUserSessionHistory(userId, sessionId) {
785
- logger.debug("[RedisConversationMemoryManager] Getting user session history via getUserSessionObject", {
786
- userId,
787
- sessionId,
788
- });
789
- try {
790
- const sessionObject = await this.getUserSessionObject(userId, sessionId);
791
- if (!sessionObject) {
792
- logger.debug("[RedisConversationMemoryManager] No session object found, returning null", {
793
- userId,
794
- sessionId,
795
- });
796
- return null;
797
- }
798
- return sessionObject.messages;
799
- }
800
- catch (error) {
801
- logger.error("[RedisConversationMemoryManager] Failed to get user session history via getUserSessionObject", {
802
- userId,
803
- sessionId,
804
- error: error instanceof Error ? error.message : String(error),
805
- errorName: error instanceof Error ? error.name : "UnknownError",
806
- stack: error instanceof Error ? error.stack : undefined,
807
- });
808
- return null;
809
- }
810
- }
811
- /**
812
- * Get the complete conversation object for a specific user session
813
- *
814
- * This method returns the full conversation object including title, metadata,
815
- * timestamps, and all chat messages. Unlike getUserSessionHistory() which returns
816
- * only the messages array, this method provides the complete conversation context.
817
- *
818
- * @param userId The user identifier who owns the session
819
- * @param sessionId The unique session identifier
820
- * @returns Complete conversation object with all data, or null if session doesn't exist
821
- */
822
- async getUserSessionObject(userId, sessionId) {
823
- logger.debug("[RedisConversationMemoryManager] Getting complete user session object", {
824
- userId,
825
- sessionId,
826
- method: "getUserSessionObject",
827
- });
828
- // Validate input parameters
829
- if (!userId || typeof userId !== "string") {
830
- logger.warn("[RedisConversationMemoryManager] Invalid userId provided", {
831
- userId,
832
- sessionId,
833
- });
834
- return null;
835
- }
836
- if (!sessionId || typeof sessionId !== "string") {
837
- logger.warn("[RedisConversationMemoryManager] Invalid sessionId provided", { userId, sessionId });
838
- return null;
839
- }
840
- await this.ensureInitialized();
841
- if (!this.redisClient) {
842
- logger.warn("[RedisConversationMemoryManager] Redis client not available for getUserSessionObject", {
843
- userId,
844
- sessionId,
845
- });
846
- return null;
847
- }
848
- try {
849
- const sessionKey = getSessionKey(this.redisConfig, sessionId, userId);
850
- const conversationData = await this.redisClient.get(sessionKey);
851
- if (!conversationData) {
852
- logger.debug("[RedisConversationMemoryManager] No conversation data found in Redis", {
853
- userId,
854
- sessionId,
855
- sessionKey,
856
- });
857
- return null;
858
- }
859
- // Deserialize the complete conversation object
860
- const conversation = deserializeConversation(conversationData);
861
- if (!conversation) {
862
- logger.debug("[RedisConversationMemoryManager] Failed to deserialize conversation data", {
863
- userId,
864
- sessionId,
865
- sessionKey,
866
- dataLength: conversationData.length,
867
- });
868
- return null;
869
- }
870
- // Validate conversation object structure
871
- if (!conversation.messages || !Array.isArray(conversation.messages)) {
872
- logger.warn("[RedisConversationMemoryManager] Invalid conversation structure - missing messages array", {
873
- userId,
874
- sessionId,
875
- hasMessages: !!conversation.messages,
876
- messagesType: typeof conversation.messages,
877
- });
878
- return null;
879
- }
880
- return conversation;
881
- }
882
- catch (error) {
883
- logger.error("[RedisConversationMemoryManager] Failed to get complete user session object", {
884
- userId,
885
- sessionId,
886
- error: error instanceof Error ? error.message : String(error),
887
- errorName: error instanceof Error ? error.name : "UnknownError",
888
- stack: error instanceof Error ? error.stack : undefined,
889
- });
890
- return null;
891
- }
892
- }
893
- /**
894
- * Generate a conversation title from the first user message
895
- * Uses AI to create a concise, descriptive title (5-8 words)
896
- */
897
- async generateConversationTitle(userMessage) {
898
- logger.info("[RedisConversationMemoryManager] Generating conversation title", {
899
- userMessageLength: userMessage.length,
900
- userMessagePreview: userMessage.substring(0, 100),
901
- });
902
- try {
903
- // Create a NeuroLink instance for title generation
904
- const titleGenerator = new NeuroLink({
905
- conversationMemory: { enabled: false },
906
- });
907
- const titlePrompt = `Generate a clear, concise, and descriptive title (5–8 words maximum) for a conversation based on the following user message.
908
- The title must meaningfully reflect the topic or intent of the message.
909
- Do not output anything unrelated, vague, or generic.
910
- Do not say you cannot create a title. Always return a valid title.
911
-
912
- User message: "${userMessage}"`;
913
- const result = await titleGenerator.generate({
914
- input: { text: titlePrompt },
915
- provider: this.config.summarizationProvider || "vertex",
916
- model: this.config.summarizationModel || "gemini-2.5-flash",
917
- disableTools: true, // Title generation doesn't need tools — saves ~600 tokens of tool descriptions
918
- });
919
- // Clean up the generated title
920
- let title = result.content?.trim() || "New Conversation";
921
- // Remove common prefixes/suffixes that might be added by the AI
922
- title = title.replace(/^(Title:|Here's a title:|The title is:)\s*/i, "");
923
- title = title.replace(/['"]/g, ""); // Remove quotes
924
- title = title.replace(/\.$/, ""); // Remove trailing period
925
- if (title.length > 60) {
926
- title = title.substring(0, 57) + "...";
927
- }
928
- if (title.length < 3) {
929
- title = "New Conversation";
930
- }
931
- logger.info("[RedisConversationMemoryManager] Generated conversation title", {
932
- originalLength: result.content?.length || 0,
933
- cleanedTitle: title,
934
- titleLength: title.length,
935
- });
936
- return title;
937
- }
938
- catch (error) {
939
- logger.error("[RedisConversationMemoryManager] Failed to generate conversation title", {
940
- error: error instanceof Error ? error.message : String(error),
941
- userMessagePreview: userMessage.substring(0, 100),
942
- });
943
- // Fallback to a simple title based on the user message
944
- const fallbackTitle = userMessage.length > 30
945
- ? userMessage.substring(0, 30) + "..."
946
- : userMessage || "New Conversation";
947
- return fallbackTitle;
948
- }
949
- }
950
- /**
951
- * Create summary system message
952
- */
953
- createSummarySystemMessage(content, summarizesFrom, summarizesTo) {
954
- return {
955
- id: `summary-${randomUUID()}`,
956
- role: "system",
957
- content: `Summary of previous conversation turns:\n\n${content}`,
958
- timestamp: new Date().toISOString(),
959
- metadata: {
960
- isSummary: true,
961
- summarizesFrom,
962
- summarizesTo,
963
- },
964
- };
965
- }
966
- /**
967
- * Get the raw messages array for a session.
968
- * Returns the full messages list without context filtering or summarization.
969
- */
970
- async getSessionMessages(sessionId, userId) {
971
- await this.ensureInitialized();
972
- if (!this.redisClient) {
973
- return [];
974
- }
975
- try {
976
- const redisKey = getSessionKey(this.redisConfig, sessionId, userId);
977
- const conversationData = await this.redisClient.get(redisKey);
978
- const conversation = deserializeConversation(conversationData || null);
979
- return conversation?.messages ?? [];
980
- }
981
- catch (error) {
982
- logger.error("[RedisConversationMemoryManager] Failed to get session messages", {
983
- sessionId,
984
- userId,
985
- error: error instanceof Error ? error.message : String(error),
986
- });
987
- return [];
988
- }
989
- }
990
- /**
991
- * Replace the entire messages array for a session.
992
- * The session must already exist in Redis.
993
- */
994
- async setSessionMessages(sessionId, messages, userId) {
995
- await this.ensureInitialized();
996
- if (!this.redisClient) {
997
- throw new ConversationMemoryError("Redis client not initialized", "STORAGE_ERROR", { sessionId });
998
- }
999
- try {
1000
- const redisKey = getSessionKey(this.redisConfig, sessionId, userId);
1001
- const conversationData = await this.redisClient.get(redisKey);
1002
- const conversation = deserializeConversation(conversationData || null);
1003
- if (!conversation) {
1004
- throw new ConversationMemoryError(`Session ${sessionId} not found`, "STORAGE_ERROR", { sessionId });
1005
- }
1006
- conversation.messages = messages;
1007
- conversation.updatedAt = new Date().toISOString();
1008
- // Reset summarization pointers — the old summary no longer applies
1009
- // to the replaced messages array
1010
- conversation.summarizedUpToMessageId = undefined;
1011
- conversation.summarizedMessage = undefined;
1012
- conversation.lastTokenCount = undefined;
1013
- conversation.lastCountedAt = undefined;
1014
- const serializedData = serializeConversation(conversation);
1015
- await this.redisClient.set(redisKey, serializedData);
1016
- if (this.redisConfig.ttl > 0) {
1017
- await this.redisClient.expire(redisKey, this.redisConfig.ttl);
1018
- }
1019
- logger.debug("[RedisConversationMemoryManager] Session messages replaced", {
1020
- sessionId,
1021
- userId,
1022
- messageCount: messages.length,
1023
- });
1024
- }
1025
- catch (error) {
1026
- if (error instanceof ConversationMemoryError) {
1027
- throw error;
1028
- }
1029
- throw new ConversationMemoryError(`Failed to set session messages for session ${sessionId}`, "STORAGE_ERROR", {
1030
- sessionId,
1031
- error: error instanceof Error ? error.message : String(error),
1032
- });
1033
- }
1034
- }
1035
- /**
1036
- * Close Redis connection
1037
- */
1038
- async close() {
1039
- if (this.redisClient) {
1040
- await releasePooledRedisClient(this.redisConfig);
1041
- this.redisClient = null;
1042
- this.isInitialized = false;
1043
- logger.info("Redis connection closed");
1044
- }
1045
- }
1046
- /**
1047
- * Get statistics about conversation storage
1048
- */
1049
- async getStats() {
1050
- await this.ensureInitialized();
1051
- if (!this.redisClient) {
1052
- return { totalSessions: 0, totalTurns: 0 };
1053
- }
1054
- // Get all session keys using SCAN instead of KEYS to avoid blocking
1055
- const pattern = `${this.redisConfig.keyPrefix}*`;
1056
- const keys = await scanKeys(this.redisClient, pattern);
1057
- logger.debug("[RedisConversationMemoryManager] Got session keys with SCAN", {
1058
- pattern,
1059
- keyCount: keys.length,
1060
- });
1061
- // Count messages in each session
1062
- let totalTurns = 0;
1063
- for (const key of keys) {
1064
- const conversationData = await this.redisClient.get(key);
1065
- const conversation = deserializeConversation(conversationData);
1066
- if (conversation?.messages) {
1067
- totalTurns += conversation.messages.length / MESSAGES_PER_TURN;
1068
- }
1069
- }
1070
- return {
1071
- totalSessions: keys.length,
1072
- totalTurns,
1073
- };
1074
- }
1075
- /**
1076
- * Clear a specific session
1077
- */
1078
- async clearSession(sessionId, userId) {
1079
- await this.ensureInitialized();
1080
- if (!this.redisClient) {
1081
- return false;
1082
- }
1083
- // NLK-GAP-012: Add span for clearSession CRUD operation
1084
- return redisTracer.startActiveSpan("neurolink.memory.clear", {
1085
- kind: SpanKind.CLIENT,
1086
- attributes: {
1087
- "session.id": sessionId,
1088
- ...(userId && { "user.id": userId }),
1089
- },
1090
- }, async (span) => {
1091
- try {
1092
- const redisKey = getSessionKey(this.redisConfig, sessionId, userId);
1093
- const result = await this.redisClient.del(redisKey);
1094
- if (Number(result) > 0) {
1095
- // Remove session from user's session set
1096
- if (userId) {
1097
- await this.removeUserSession(userId, sessionId);
1098
- }
1099
- span.setAttribute("session.deleted", true);
1100
- span.setStatus({ code: SpanStatusCode.OK });
1101
- logger.info("Redis session cleared", { sessionId });
1102
- return true;
1103
- }
1104
- span.setAttribute("session.deleted", false);
1105
- span.setStatus({ code: SpanStatusCode.OK });
1106
- return false;
1107
- }
1108
- catch (error) {
1109
- span.setStatus({
1110
- code: SpanStatusCode.ERROR,
1111
- message: error instanceof Error ? error.message : String(error),
1112
- });
1113
- span.recordException(error instanceof Error ? error : new Error(String(error)));
1114
- throw error;
1115
- }
1116
- finally {
1117
- span.end();
1118
- }
1119
- });
1120
- }
1121
- /**
1122
- * Clear all sessions
1123
- */
1124
- async clearAllSessions() {
1125
- await this.ensureInitialized();
1126
- if (!this.redisClient) {
1127
- return;
1128
- }
1129
- const conversationPattern = `${this.redisConfig.keyPrefix}*`;
1130
- const userSessionsPattern = `${this.redisConfig.userSessionsKeyPrefix}*`;
1131
- // Use SCAN instead of KEYS to avoid blocking the server
1132
- const conversationKeys = await scanKeys(this.redisClient, conversationPattern);
1133
- const userSessionsKeys = await scanKeys(this.redisClient, userSessionsPattern);
1134
- const allKeys = [...conversationKeys, ...userSessionsKeys];
1135
- logger.debug("[RedisConversationMemoryManager] Got all keys with SCAN for clearing", {
1136
- conversationPattern,
1137
- userSessionsPattern,
1138
- conversationKeyCount: conversationKeys.length,
1139
- userSessionsKeyCount: userSessionsKeys.length,
1140
- totalKeyCount: allKeys.length,
1141
- });
1142
- if (allKeys.length > 0) {
1143
- // Process keys in batches to avoid blocking Redis for too long
1144
- const batchSize = 100;
1145
- for (let i = 0; i < allKeys.length; i += batchSize) {
1146
- const batch = allKeys.slice(i, i + batchSize);
1147
- await this.redisClient.del(batch);
1148
- logger.debug("[RedisConversationMemoryManager] Cleared batch of sessions and user mappings", {
1149
- batchIndex: Math.floor(i / batchSize) + 1,
1150
- batchSize: batch.length,
1151
- totalProcessed: i + batch.length,
1152
- totalKeys: allKeys.length,
1153
- });
1154
- }
1155
- logger.info("All Redis sessions and user session mappings cleared", {
1156
- clearedCount: allKeys.length,
1157
- conversationSessions: conversationKeys.length,
1158
- userSessionMappings: userSessionsKeys.length,
1159
- });
1160
- }
1161
- }
1162
- /**
1163
- * Ensure Redis client is initialized
1164
- */
1165
- async ensureInitialized() {
1166
- logger.debug("[RedisConversationMemoryManager] Ensuring initialization");
1167
- if (!this.isInitialized) {
1168
- logger.debug("[RedisConversationMemoryManager] Not initialized, initializing now");
1169
- await this.initialize();
1170
- }
1171
- else {
1172
- logger.debug("[RedisConversationMemoryManager] Already initialized");
1173
- }
1174
- }
1175
- /**
1176
- * Get session metadata for all sessions of a user (optimized for listing)
1177
- * Returns only essential metadata without heavy message arrays
1178
- *
1179
- * @param userId The user identifier
1180
- * @returns Array of session metadata objects
1181
- */
1182
- async getUserAllSessionsHistory(userId) {
1183
- await this.ensureInitialized();
1184
- if (!this.redisClient) {
1185
- logger.warn("[RedisConversationMemoryManager] Redis client not available", { userId });
1186
- return [];
1187
- }
1188
- const results = [];
1189
- try {
1190
- // Get all session IDs for the user using existing method
1191
- const sessionIds = await this.getUserSessions(userId);
1192
- if (sessionIds.length === 0) {
1193
- return results;
1194
- }
1195
- // Fetch metadata for each session using our optimized helper method
1196
- for (const sessionId of sessionIds) {
1197
- try {
1198
- const metadata = await this.getUserSessionMetadata(userId, sessionId);
1199
- if (metadata) {
1200
- results.push(metadata);
1201
- }
1202
- else {
1203
- logger.debug("[RedisConversationMemoryManager] Empty or missing session metadata - removing from user history", {
1204
- userId,
1205
- sessionId,
1206
- });
1207
- await this.removeUserSession(userId, sessionId);
1208
- }
1209
- }
1210
- catch (sessionError) {
1211
- logger.error("[RedisConversationMemoryManager] Failed to get session metadata", {
1212
- userId,
1213
- sessionId,
1214
- error: sessionError instanceof Error
1215
- ? sessionError.message
1216
- : String(sessionError),
1217
- });
1218
- }
1219
- }
1220
- return results;
1221
- }
1222
- catch (error) {
1223
- logger.error("[RedisConversationMemoryManager] Failed to get user all sessions metadata", {
1224
- userId,
1225
- error: error instanceof Error ? error.message : String(error),
1226
- stack: error instanceof Error ? error.stack : undefined,
1227
- });
1228
- return results;
1229
- }
1230
- }
1231
- /**
1232
- * Clean up stale pending tool execution data
1233
- * Removes data older than 5 minutes to prevent memory leaks
1234
- */
1235
- cleanupStalePendingData() {
1236
- const fiveMinutesAgo = Date.now() - 5 * 60 * 1000;
1237
- const keysToDelete = [];
1238
- for (const [key, data] of this.pendingToolExecutions) {
1239
- if (data.timestamp < fiveMinutesAgo) {
1240
- keysToDelete.push(key);
1241
- }
1242
- }
1243
- if (keysToDelete.length > 0) {
1244
- logger.debug("[RedisConversationMemoryManager] Cleaning up stale pending tool data", {
1245
- stalePendingKeys: keysToDelete.length,
1246
- totalPendingKeys: this.pendingToolExecutions.size,
1247
- });
1248
- keysToDelete.forEach((key) => this.pendingToolExecutions.delete(key));
1249
- }
1250
- }
1251
- /**
1252
- * Flush pending tool execution data for a session and merge into conversation
1253
- */
1254
- async flushPendingToolData(conversation, sessionId, userId) {
1255
- const pendingKey = `${sessionId}:${userId}`;
1256
- const pendingData = this.pendingToolExecutions.get(pendingKey);
1257
- if (!pendingData) {
1258
- logger.debug("[RedisConversationMemoryManager] No pending tool data to flush", {
1259
- sessionId,
1260
- userId,
1261
- pendingKey,
1262
- });
1263
- return;
1264
- }
1265
- logger.debug("[RedisConversationMemoryManager] Flushing pending tool data", {
1266
- sessionId,
1267
- userId,
1268
- toolCallsCount: pendingData.toolCalls.length,
1269
- toolResultsCount: pendingData.toolResults.length,
1270
- });
1271
- try {
1272
- // Create a mapping from toolCallId to toolName for matching tool results
1273
- const toolCallMap = new Map();
1274
- // Create separate messages for tool calls and build the mapping
1275
- for (const toolCall of pendingData.toolCalls) {
1276
- const toolCallId = toolCall.toolCallId ?? "";
1277
- const toolName = toolCall.toolName ?? "";
1278
- // Store in mapping for tool results
1279
- toolCallMap.set(toolCallId, toolName);
1280
- const toolCallMessage = {
1281
- id: randomUUID(),
1282
- timestamp: toolCall.timestamp?.toISOString() || this.generateTimestamp(),
1283
- role: "tool_call",
1284
- content: "", // Can be empty for tool calls
1285
- tool: toolName,
1286
- args: (toolCall.args ||
1287
- toolCall.arguments ||
1288
- toolCall.parameters ||
1289
- {}),
1290
- };
1291
- conversation.messages.push(toolCallMessage);
1292
- }
1293
- // Create separate messages for tool results using the mapping
1294
- for (const toolResult of pendingData.toolResults) {
1295
- const toolCallId = String(toolResult.toolCallId || toolResult.id || "unknown");
1296
- const toolName = toolCallMap.get(toolCallId) || "unknown";
1297
- // Serialize the tool result to string for content field
1298
- let serializedResult;
1299
- if (typeof toolResult.result === "string") {
1300
- serializedResult = toolResult.result;
1301
- }
1302
- else if (toolResult.result === undefined ||
1303
- toolResult.result === null) {
1304
- serializedResult = String(toolResult.result ?? "null");
1305
- }
1306
- else {
1307
- try {
1308
- serializedResult = JSON.stringify(toolResult.result, null, 2);
1309
- }
1310
- catch (serializeError) {
1311
- serializedResult = `[Serialization failed: ${serializeError instanceof Error ? serializeError.message : String(serializeError)}]`;
1312
- }
1313
- }
1314
- // Generate preview (uses existing config fields that were previously unused)
1315
- const { preview, truncated, originalSize } = generateToolOutputPreview(serializedResult, {
1316
- maxBytes: this.config?.contextCompaction?.maxToolOutputBytes,
1317
- maxLines: this.config?.contextCompaction?.maxToolOutputLines,
1318
- });
1319
- // Build metadata — only store preview when truncation occurred (no duplication)
1320
- const metadata = {
1321
- truncated,
1322
- ...(truncated && { toolOutputPreview: preview }),
1323
- ...(truncated && { originalSize }),
1324
- };
1325
- // Build result — success/error metadata only, NOT the output data
1326
- const result = {
1327
- success: !toolResult.error,
1328
- // result.result intentionally NOT stored — inferred from content at read time
1329
- error: toolResult.error ? String(toolResult.error) : undefined,
1330
- };
1331
- const toolResultMessage = {
1332
- id: randomUUID(),
1333
- timestamp: toolResult.timestamp?.toISOString() || this.generateTimestamp(),
1334
- role: "tool_result",
1335
- content: serializedResult, // Full output (was "")
1336
- tool: toolName,
1337
- result,
1338
- metadata,
1339
- };
1340
- conversation.messages.push(toolResultMessage);
1341
- }
1342
- logger.debug("[RedisConversationMemoryManager] Successfully flushed pending tool data", {
1343
- sessionId,
1344
- userId,
1345
- toolMessagesAdded: pendingData.toolCalls.length + pendingData.toolResults.length,
1346
- totalMessages: conversation.messages.length,
1347
- });
1348
- }
1349
- finally {
1350
- // Always clean up pending data, even on failure, to prevent infinite retry loops
1351
- this.pendingToolExecutions.delete(pendingKey);
1352
- }
1353
- }
1354
- /**
1355
- * Update agentic loop report metadata for a conversation session.
1356
- * Upserts a report entry by reportId — updates existing or adds new.
1357
- * Follows the read → patch → write pattern (same as title generation).
1358
- *
1359
- * @param sessionId The session identifier
1360
- * @param userId The user identifier (optional)
1361
- * @param report The report metadata to upsert
1362
- */
1363
- async updateAgenticLoopReport(sessionId, userId, report) {
1364
- logger.debug("[RedisConversationMemoryManager] Updating agentic loop report", {
1365
- sessionId,
1366
- userId,
1367
- reportId: report.reportId,
1368
- reportType: report.reportType,
1369
- reportStatus: report.reportStatus,
1370
- });
1371
- await this.ensureInitialized();
1372
- if (!this.redisClient) {
1373
- logger.warn("[RedisConversationMemoryManager] Redis client not available for report update", { sessionId, userId });
1374
- return;
1375
- }
1376
- try {
1377
- const redisKey = getSessionKey(this.redisConfig, sessionId, userId || undefined);
1378
- const conversationData = await withTimeout(this.redisClient.get(redisKey), 5000);
1379
- if (!conversationData) {
1380
- logger.warn("[RedisConversationMemoryManager] No conversation found for report update", { sessionId, userId });
1381
- return;
1382
- }
1383
- const conversation = deserializeConversation(conversationData);
1384
- if (!conversation) {
1385
- logger.warn("[RedisConversationMemoryManager] Failed to deserialize conversation for report update", { sessionId, userId });
1386
- return;
1387
- }
1388
- // Initialize additionalMetadata and agenticLoopReports if needed
1389
- if (!conversation.additionalMetadata) {
1390
- conversation.additionalMetadata = {};
1391
- }
1392
- if (!conversation.additionalMetadata.agenticLoopReports) {
1393
- conversation.additionalMetadata.agenticLoopReports = [];
1394
- }
1395
- // Upsert: find existing report by reportId and update, or push new entry
1396
- const existingIndex = conversation.additionalMetadata.agenticLoopReports.findIndex((r) => r.reportId === report.reportId);
1397
- if (existingIndex >= 0) {
1398
- conversation.additionalMetadata.agenticLoopReports[existingIndex] =
1399
- report;
1400
- logger.debug("[RedisConversationMemoryManager] Updated existing agentic loop report", { sessionId, reportId: report.reportId });
1401
- }
1402
- else {
1403
- conversation.additionalMetadata.agenticLoopReports.push(report);
1404
- logger.debug("[RedisConversationMemoryManager] Added new agentic loop report", { sessionId, reportId: report.reportId });
1405
- }
1406
- conversation.updatedAt = new Date().toISOString();
1407
- // Write back to Redis
1408
- const serializedData = serializeConversation(conversation);
1409
- await withTimeout(this.redisClient.set(redisKey, serializedData), 5000);
1410
- if (this.redisConfig.ttl > 0) {
1411
- await withTimeout(this.redisClient.expire(redisKey, this.redisConfig.ttl), 5000);
1412
- }
1413
- logger.info("[RedisConversationMemoryManager] Successfully updated agentic loop report", {
1414
- sessionId,
1415
- userId,
1416
- reportId: report.reportId,
1417
- reportStatus: report.reportStatus,
1418
- });
1419
- }
1420
- catch (error) {
1421
- logger.error("[RedisConversationMemoryManager] Failed to update agentic loop report", {
1422
- sessionId,
1423
- userId,
1424
- reportId: report.reportId,
1425
- error: error instanceof Error ? error.message : String(error),
1426
- });
1427
- throw new ConversationMemoryError("Failed to update agentic loop report", "STORAGE_ERROR", {
1428
- sessionId,
1429
- userId,
1430
- reportId: report.reportId,
1431
- error: error instanceof Error ? error.message : String(error),
1432
- });
1433
- }
1434
- }
1435
- }