@juspay/neurolink 9.41.0 → 9.42.1

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 (212) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/README.md +7 -1
  3. package/dist/auth/anthropicOAuth.d.ts +18 -3
  4. package/dist/auth/anthropicOAuth.js +149 -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 +354 -334
  11. package/dist/cli/commands/mcp.d.ts +6 -0
  12. package/dist/cli/commands/mcp.js +188 -181
  13. package/dist/cli/commands/proxy.d.ts +2 -1
  14. package/dist/cli/commands/proxy.js +713 -431
  15. package/dist/cli/commands/task.js +3 -0
  16. package/dist/cli/factories/commandFactory.d.ts +2 -0
  17. package/dist/cli/factories/commandFactory.js +38 -0
  18. package/dist/cli/parser.js +4 -3
  19. package/dist/client/aiSdkAdapter.js +3 -0
  20. package/dist/client/streamingClient.js +30 -10
  21. package/dist/core/baseProvider.d.ts +6 -1
  22. package/dist/core/baseProvider.js +208 -230
  23. package/dist/core/factory.d.ts +3 -0
  24. package/dist/core/factory.js +138 -188
  25. package/dist/core/modules/GenerationHandler.js +3 -2
  26. package/dist/core/redisConversationMemoryManager.js +7 -3
  27. package/dist/evaluation/BatchEvaluator.js +4 -1
  28. package/dist/evaluation/hooks/observabilityHooks.js +5 -3
  29. package/dist/evaluation/pipeline/evaluationPipeline.d.ts +3 -2
  30. package/dist/evaluation/pipeline/evaluationPipeline.js +24 -9
  31. package/dist/evaluation/pipeline/strategies/batchStrategy.js +6 -3
  32. package/dist/evaluation/pipeline/strategies/samplingStrategy.js +18 -10
  33. package/dist/evaluation/scorers/scorerRegistry.d.ts +3 -0
  34. package/dist/evaluation/scorers/scorerRegistry.js +353 -282
  35. package/dist/lib/auth/anthropicOAuth.d.ts +18 -3
  36. package/dist/lib/auth/anthropicOAuth.js +149 -4
  37. package/dist/lib/auth/providers/firebase.js +5 -1
  38. package/dist/lib/auth/providers/jwt.js +5 -1
  39. package/dist/lib/auth/providers/workos.js +5 -1
  40. package/dist/lib/auth/sessionManager.d.ts +1 -1
  41. package/dist/lib/auth/sessionManager.js +58 -27
  42. package/dist/lib/client/aiSdkAdapter.js +3 -0
  43. package/dist/lib/client/streamingClient.js +30 -10
  44. package/dist/lib/core/baseProvider.d.ts +6 -1
  45. package/dist/lib/core/baseProvider.js +208 -230
  46. package/dist/lib/core/factory.d.ts +3 -0
  47. package/dist/lib/core/factory.js +138 -188
  48. package/dist/lib/core/modules/GenerationHandler.js +3 -2
  49. package/dist/lib/core/redisConversationMemoryManager.js +7 -3
  50. package/dist/lib/evaluation/BatchEvaluator.js +4 -1
  51. package/dist/lib/evaluation/hooks/observabilityHooks.js +5 -3
  52. package/dist/lib/evaluation/pipeline/evaluationPipeline.d.ts +3 -2
  53. package/dist/lib/evaluation/pipeline/evaluationPipeline.js +24 -9
  54. package/dist/lib/evaluation/pipeline/strategies/batchStrategy.js +6 -3
  55. package/dist/lib/evaluation/pipeline/strategies/samplingStrategy.js +18 -10
  56. package/dist/lib/evaluation/scorers/scorerRegistry.d.ts +3 -0
  57. package/dist/lib/evaluation/scorers/scorerRegistry.js +353 -282
  58. package/dist/lib/mcp/toolRegistry.d.ts +2 -0
  59. package/dist/lib/mcp/toolRegistry.js +32 -31
  60. package/dist/lib/neurolink.d.ts +41 -2
  61. package/dist/lib/neurolink.js +1616 -1681
  62. package/dist/lib/observability/otelBridge.d.ts +2 -2
  63. package/dist/lib/observability/otelBridge.js +12 -3
  64. package/dist/lib/providers/amazonBedrock.js +2 -4
  65. package/dist/lib/providers/anthropic.d.ts +9 -5
  66. package/dist/lib/providers/anthropic.js +19 -14
  67. package/dist/lib/providers/anthropicBaseProvider.d.ts +3 -3
  68. package/dist/lib/providers/anthropicBaseProvider.js +5 -4
  69. package/dist/lib/providers/azureOpenai.d.ts +1 -1
  70. package/dist/lib/providers/azureOpenai.js +5 -4
  71. package/dist/lib/providers/googleAiStudio.js +30 -6
  72. package/dist/lib/providers/googleVertex.d.ts +10 -0
  73. package/dist/lib/providers/googleVertex.js +437 -423
  74. package/dist/lib/providers/huggingFace.d.ts +3 -3
  75. package/dist/lib/providers/huggingFace.js +6 -8
  76. package/dist/lib/providers/litellm.d.ts +1 -0
  77. package/dist/lib/providers/litellm.js +76 -55
  78. package/dist/lib/providers/mistral.js +2 -1
  79. package/dist/lib/providers/ollama.js +93 -23
  80. package/dist/lib/providers/openAI.d.ts +2 -0
  81. package/dist/lib/providers/openAI.js +141 -141
  82. package/dist/lib/providers/openRouter.js +2 -1
  83. package/dist/lib/providers/openaiCompatible.d.ts +4 -4
  84. package/dist/lib/providers/openaiCompatible.js +4 -4
  85. package/dist/lib/proxy/claudeFormat.d.ts +3 -2
  86. package/dist/lib/proxy/claudeFormat.js +27 -14
  87. package/dist/lib/proxy/cloaking/plugins/sessionIdentity.d.ts +2 -6
  88. package/dist/lib/proxy/cloaking/plugins/sessionIdentity.js +9 -33
  89. package/dist/lib/proxy/modelRouter.js +3 -0
  90. package/dist/lib/proxy/oauthFetch.d.ts +1 -1
  91. package/dist/lib/proxy/oauthFetch.js +289 -316
  92. package/dist/lib/proxy/proxyConfig.js +46 -24
  93. package/dist/lib/proxy/proxyEnv.d.ts +19 -0
  94. package/dist/lib/proxy/proxyEnv.js +73 -0
  95. package/dist/lib/proxy/proxyFetch.js +291 -217
  96. package/dist/lib/proxy/proxyTracer.d.ts +133 -0
  97. package/dist/lib/proxy/proxyTracer.js +645 -0
  98. package/dist/lib/proxy/rawStreamCapture.d.ts +10 -0
  99. package/dist/lib/proxy/rawStreamCapture.js +83 -0
  100. package/dist/lib/proxy/requestLogger.d.ts +32 -5
  101. package/dist/lib/proxy/requestLogger.js +503 -47
  102. package/dist/lib/proxy/sseInterceptor.d.ts +97 -0
  103. package/dist/lib/proxy/sseInterceptor.js +427 -0
  104. package/dist/lib/proxy/usageStats.d.ts +4 -3
  105. package/dist/lib/proxy/usageStats.js +25 -12
  106. package/dist/lib/rag/chunkers/MarkdownChunker.js +13 -5
  107. package/dist/lib/rag/chunking/markdownChunker.js +15 -6
  108. package/dist/lib/server/routes/claudeProxyRoutes.d.ts +17 -3
  109. package/dist/lib/server/routes/claudeProxyRoutes.js +3032 -1349
  110. package/dist/lib/services/server/ai/observability/instrumentation.d.ts +7 -1
  111. package/dist/lib/services/server/ai/observability/instrumentation.js +337 -161
  112. package/dist/lib/tasks/backends/bullmqBackend.d.ts +1 -0
  113. package/dist/lib/tasks/backends/bullmqBackend.js +35 -22
  114. package/dist/lib/tasks/store/redisTaskStore.d.ts +1 -0
  115. package/dist/lib/tasks/store/redisTaskStore.js +54 -39
  116. package/dist/lib/tasks/taskManager.d.ts +5 -0
  117. package/dist/lib/tasks/taskManager.js +158 -30
  118. package/dist/lib/telemetry/index.d.ts +2 -1
  119. package/dist/lib/telemetry/index.js +2 -1
  120. package/dist/lib/telemetry/telemetryService.d.ts +3 -0
  121. package/dist/lib/telemetry/telemetryService.js +69 -5
  122. package/dist/lib/types/cli.d.ts +10 -0
  123. package/dist/lib/types/proxyTypes.d.ts +160 -5
  124. package/dist/lib/types/streamTypes.d.ts +25 -3
  125. package/dist/lib/utils/messageBuilder.js +3 -2
  126. package/dist/lib/utils/providerHealth.d.ts +19 -0
  127. package/dist/lib/utils/providerHealth.js +279 -33
  128. package/dist/lib/utils/providerUtils.js +17 -22
  129. package/dist/lib/utils/toolChoice.d.ts +4 -0
  130. package/dist/lib/utils/toolChoice.js +7 -0
  131. package/dist/mcp/toolRegistry.d.ts +2 -0
  132. package/dist/mcp/toolRegistry.js +32 -31
  133. package/dist/neurolink.d.ts +41 -2
  134. package/dist/neurolink.js +1616 -1681
  135. package/dist/observability/otelBridge.d.ts +2 -2
  136. package/dist/observability/otelBridge.js +12 -3
  137. package/dist/providers/amazonBedrock.js +2 -4
  138. package/dist/providers/anthropic.d.ts +9 -5
  139. package/dist/providers/anthropic.js +19 -14
  140. package/dist/providers/anthropicBaseProvider.d.ts +3 -3
  141. package/dist/providers/anthropicBaseProvider.js +5 -4
  142. package/dist/providers/azureOpenai.d.ts +1 -1
  143. package/dist/providers/azureOpenai.js +5 -4
  144. package/dist/providers/googleAiStudio.js +30 -6
  145. package/dist/providers/googleVertex.d.ts +10 -0
  146. package/dist/providers/googleVertex.js +437 -423
  147. package/dist/providers/huggingFace.d.ts +3 -3
  148. package/dist/providers/huggingFace.js +6 -7
  149. package/dist/providers/litellm.d.ts +1 -0
  150. package/dist/providers/litellm.js +76 -55
  151. package/dist/providers/mistral.js +2 -1
  152. package/dist/providers/ollama.js +93 -23
  153. package/dist/providers/openAI.d.ts +2 -0
  154. package/dist/providers/openAI.js +141 -141
  155. package/dist/providers/openRouter.js +2 -1
  156. package/dist/providers/openaiCompatible.d.ts +4 -4
  157. package/dist/providers/openaiCompatible.js +4 -3
  158. package/dist/proxy/claudeFormat.d.ts +3 -2
  159. package/dist/proxy/claudeFormat.js +27 -14
  160. package/dist/proxy/cloaking/plugins/sessionIdentity.d.ts +2 -6
  161. package/dist/proxy/cloaking/plugins/sessionIdentity.js +9 -33
  162. package/dist/proxy/modelRouter.js +3 -0
  163. package/dist/proxy/oauthFetch.d.ts +1 -1
  164. package/dist/proxy/oauthFetch.js +289 -316
  165. package/dist/proxy/proxyConfig.js +46 -24
  166. package/dist/proxy/proxyEnv.d.ts +19 -0
  167. package/dist/proxy/proxyEnv.js +72 -0
  168. package/dist/proxy/proxyFetch.js +291 -217
  169. package/dist/proxy/proxyTracer.d.ts +133 -0
  170. package/dist/proxy/proxyTracer.js +644 -0
  171. package/dist/proxy/rawStreamCapture.d.ts +10 -0
  172. package/dist/proxy/rawStreamCapture.js +82 -0
  173. package/dist/proxy/requestLogger.d.ts +32 -5
  174. package/dist/proxy/requestLogger.js +503 -47
  175. package/dist/proxy/sseInterceptor.d.ts +97 -0
  176. package/dist/proxy/sseInterceptor.js +426 -0
  177. package/dist/proxy/usageStats.d.ts +4 -3
  178. package/dist/proxy/usageStats.js +25 -12
  179. package/dist/rag/chunkers/MarkdownChunker.js +13 -5
  180. package/dist/rag/chunking/markdownChunker.js +15 -6
  181. package/dist/server/routes/claudeProxyRoutes.d.ts +17 -3
  182. package/dist/server/routes/claudeProxyRoutes.js +3032 -1349
  183. package/dist/services/server/ai/observability/instrumentation.d.ts +7 -1
  184. package/dist/services/server/ai/observability/instrumentation.js +337 -161
  185. package/dist/tasks/backends/bullmqBackend.d.ts +1 -0
  186. package/dist/tasks/backends/bullmqBackend.js +35 -22
  187. package/dist/tasks/store/redisTaskStore.d.ts +1 -0
  188. package/dist/tasks/store/redisTaskStore.js +54 -39
  189. package/dist/tasks/taskManager.d.ts +5 -0
  190. package/dist/tasks/taskManager.js +158 -30
  191. package/dist/telemetry/index.d.ts +2 -1
  192. package/dist/telemetry/index.js +2 -1
  193. package/dist/telemetry/telemetryService.d.ts +3 -0
  194. package/dist/telemetry/telemetryService.js +69 -5
  195. package/dist/types/cli.d.ts +10 -0
  196. package/dist/types/proxyTypes.d.ts +160 -5
  197. package/dist/types/streamTypes.d.ts +25 -3
  198. package/dist/utils/messageBuilder.js +3 -2
  199. package/dist/utils/providerHealth.d.ts +19 -0
  200. package/dist/utils/providerHealth.js +279 -33
  201. package/dist/utils/providerUtils.js +18 -22
  202. package/dist/utils/toolChoice.d.ts +4 -0
  203. package/dist/utils/toolChoice.js +6 -0
  204. package/docs/assets/dashboards/neurolink-proxy-observability-dashboard.json +6609 -0
  205. package/docs/changelog.md +252 -0
  206. package/package.json +19 -2
  207. package/scripts/observability/check-proxy-telemetry.mjs +235 -0
  208. package/scripts/observability/docker-compose.proxy-observability.yaml +55 -0
  209. package/scripts/observability/import-openobserve-dashboard.mjs +240 -0
  210. package/scripts/observability/manage-local-openobserve.sh +215 -0
  211. package/scripts/observability/otel-collector.proxy-observability.yaml +78 -0
  212. package/scripts/observability/proxy-observability.env.example +23 -0
@@ -14,7 +14,7 @@
14
14
  *
15
15
  * @module auth/anthropicOAuth
16
16
  */
17
- import { createHash, randomBytes } from "crypto";
17
+ import { createHash, createHmac, randomBytes, randomUUID } from "crypto";
18
18
  import { createServer, IncomingMessage, ServerResponse } from "http";
19
19
  import { OAuthError, OAuthConfigurationError, OAuthTokenExchangeError, OAuthTokenRefreshError, OAuthTokenRevocationError, OAuthCallbackServerError, } from "../types/errors.js";
20
20
  import { logger } from "../utils/logger.js";
@@ -73,13 +73,158 @@ export const DEFAULT_SCOPES = [
73
73
  /**
74
74
  * User-Agent string to spoof Claude CLI
75
75
  */
76
- export const CLAUDE_CLI_USER_AGENT = "claude-cli/2.1.2 (external, cli)";
76
+ export const CLAUDE_CODE_VERSION = "2.1.87.6d6";
77
+ export const CLAUDE_CODE_ENTRYPOINT = "sdk-cli";
78
+ export const CLAUDE_CLI_USER_AGENT = "claude-cli/2.1.87 (external, sdk-cli)";
79
+ const CLAUDE_CODE_IDENTITY_TTL_MS = 3_600_000;
80
+ const CLAUDE_CODE_IDENTITY_CACHE_MAX_ENTRIES = 1024;
81
+ const CLAUDE_CODE_IDENTITY_NAMESPACE = "neurolink-claude-code-identity-v1";
82
+ const claudeCodeIdentityCache = new Map();
83
+ function stableIdentityDigest(input) {
84
+ // These identifiers are deterministic pseudonyms for Claude Code metadata,
85
+ // not password hashes or authentication secrets.
86
+ return createHmac("sha256", CLAUDE_CODE_IDENTITY_NAMESPACE)
87
+ .update(input)
88
+ .digest("hex");
89
+ }
90
+ function hexToUuid(hex) {
91
+ const trimmed = hex.replace(/-/g, "").slice(0, 32).padEnd(32, "0");
92
+ return `${trimmed.slice(0, 8)}-${trimmed.slice(8, 12)}-${trimmed.slice(12, 16)}-${trimmed.slice(16, 20)}-${trimmed.slice(20, 32)}`;
93
+ }
94
+ function isUuid(value) {
95
+ return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value);
96
+ }
97
+ function buildMetadataUserId(identity) {
98
+ return JSON.stringify({
99
+ device_id: identity.deviceId,
100
+ account_uuid: identity.accountUuid,
101
+ session_id: identity.sessionId,
102
+ });
103
+ }
104
+ export function parseClaudeCodeUserId(userId) {
105
+ if (typeof userId !== "string") {
106
+ return null;
107
+ }
108
+ try {
109
+ const parsed = JSON.parse(userId);
110
+ if (typeof parsed.device_id !== "string" ||
111
+ !/^[0-9a-f]{64}$/i.test(parsed.device_id) ||
112
+ typeof parsed.account_uuid !== "string" ||
113
+ !isUuid(parsed.account_uuid) ||
114
+ typeof parsed.session_id !== "string" ||
115
+ !isUuid(parsed.session_id)) {
116
+ return null;
117
+ }
118
+ return {
119
+ deviceId: parsed.device_id,
120
+ accountUuid: parsed.account_uuid,
121
+ sessionId: parsed.session_id,
122
+ metadataUserId: buildMetadataUserId({
123
+ deviceId: parsed.device_id,
124
+ accountUuid: parsed.account_uuid,
125
+ sessionId: parsed.session_id,
126
+ }),
127
+ };
128
+ }
129
+ catch {
130
+ return null;
131
+ }
132
+ }
133
+ export function getOrCreateClaudeCodeIdentity(seed, options) {
134
+ const parsedExisting = parseClaudeCodeUserId(options?.existingUserId);
135
+ if (parsedExisting) {
136
+ if (options?.preferredSessionId && isUuid(options.preferredSessionId)) {
137
+ return {
138
+ deviceId: parsedExisting.deviceId,
139
+ accountUuid: parsedExisting.accountUuid,
140
+ sessionId: options.preferredSessionId,
141
+ metadataUserId: buildMetadataUserId({
142
+ deviceId: parsedExisting.deviceId,
143
+ accountUuid: parsedExisting.accountUuid,
144
+ sessionId: options.preferredSessionId,
145
+ }),
146
+ };
147
+ }
148
+ return parsedExisting;
149
+ }
150
+ const now = Date.now();
151
+ const cacheKey = seed || "default";
152
+ const cached = claudeCodeIdentityCache.get(cacheKey);
153
+ if (cached && cached.expiresAt > now) {
154
+ if (options?.preferredSessionId && isUuid(options.preferredSessionId)) {
155
+ return {
156
+ deviceId: cached.deviceId,
157
+ accountUuid: cached.accountUuid,
158
+ sessionId: options.preferredSessionId,
159
+ metadataUserId: buildMetadataUserId({
160
+ deviceId: cached.deviceId,
161
+ accountUuid: cached.accountUuid,
162
+ sessionId: options.preferredSessionId,
163
+ }),
164
+ };
165
+ }
166
+ return cached;
167
+ }
168
+ const deviceId = stableIdentityDigest(`${cacheKey}:device`);
169
+ const accountUuid = hexToUuid(stableIdentityDigest(`${cacheKey}:account`));
170
+ const sessionId = options?.preferredSessionId && isUuid(options.preferredSessionId)
171
+ ? options.preferredSessionId
172
+ : randomUUID();
173
+ const identity = {
174
+ deviceId,
175
+ accountUuid,
176
+ sessionId,
177
+ metadataUserId: buildMetadataUserId({
178
+ deviceId,
179
+ accountUuid,
180
+ sessionId,
181
+ }),
182
+ expiresAt: now + CLAUDE_CODE_IDENTITY_TTL_MS,
183
+ };
184
+ enforceClaudeCodeIdentityCacheLimit(now);
185
+ claudeCodeIdentityCache.set(cacheKey, identity);
186
+ return identity;
187
+ }
188
+ export function purgeExpiredClaudeCodeIdentities(now = Date.now()) {
189
+ let removed = 0;
190
+ for (const [cacheKey, identity] of claudeCodeIdentityCache.entries()) {
191
+ if (identity.expiresAt <= now) {
192
+ claudeCodeIdentityCache.delete(cacheKey);
193
+ removed += 1;
194
+ }
195
+ }
196
+ return removed;
197
+ }
198
+ function enforceClaudeCodeIdentityCacheLimit(now = Date.now()) {
199
+ purgeExpiredClaudeCodeIdentities(now);
200
+ while (claudeCodeIdentityCache.size >= CLAUDE_CODE_IDENTITY_CACHE_MAX_ENTRIES) {
201
+ const oldestKey = claudeCodeIdentityCache.keys().next().value;
202
+ if (!oldestKey) {
203
+ break;
204
+ }
205
+ claudeCodeIdentityCache.delete(oldestKey);
206
+ }
207
+ }
208
+ export function buildStableClaudeCodeBillingHeader(originalText) {
209
+ const version = originalText?.match(/cc_version=([^;]+)/)?.[1]?.trim() ||
210
+ CLAUDE_CODE_VERSION;
211
+ const entrypoint = originalText?.match(/cc_entrypoint=([^;]+)/)?.[1]?.trim() ||
212
+ CLAUDE_CODE_ENTRYPOINT;
213
+ return `x-anthropic-billing-header: cc_version=${version}; cc_entrypoint=${entrypoint}; cch=00000;`;
214
+ }
77
215
  /**
78
216
  * Required beta headers for OAuth API requests.
79
217
  * The "oauth-2025-04-20" header is CRITICAL for OAuth authentication.
80
- * The "interleaved-thinking-2025-05-14" enables extended thinking.
81
218
  */
82
- export const OAUTH_BETA_HEADERS = "oauth-2025-04-20,interleaved-thinking-2025-05-14";
219
+ export const OAUTH_BETA_HEADERS = "oauth-2025-04-20";
220
+ export const CLAUDE_CODE_OAUTH_BETAS = [
221
+ "oauth-2025-04-20",
222
+ "claude-code-20250219",
223
+ "context-management-2025-06-27",
224
+ "prompt-caching-scope-2026-01-05",
225
+ "advanced-tool-use-2025-11-20",
226
+ "effort-2025-11-24",
227
+ ];
83
228
  /**
84
229
  * Tool name prefix required for OAuth API requests
85
230
  */
@@ -60,8 +60,12 @@ export class FirebaseAuthProvider extends BaseAuthProvider {
60
60
  await this.initialize();
61
61
  }
62
62
  try {
63
+ const jwks = this.jwks;
64
+ if (!jwks) {
65
+ throw AuthError.create("PROVIDER_INIT_FAILED", "Firebase JWKS was not initialized", { details: { provider: "firebase" } });
66
+ }
63
67
  // Verify the token using Google's public keys
64
- const { payload } = await jose.jwtVerify(token, this.jwks, {
68
+ const { payload } = await jose.jwtVerify(token, jwks, {
65
69
  issuer: `https://securetoken.google.com/${this.projectId}`,
66
70
  audience: this.projectId,
67
71
  });
@@ -92,6 +92,10 @@ export class JWTProvider extends BaseAuthProvider {
92
92
  await this.initialize();
93
93
  }
94
94
  try {
95
+ const keyObject = this.keyObject;
96
+ if (!keyObject) {
97
+ throw AuthError.create("PROVIDER_INIT_FAILED", "JWT verification key was not initialized", { details: { provider: "jwt" } });
98
+ }
95
99
  const verifyOptions = {};
96
100
  if (this.algorithms.length > 0) {
97
101
  verifyOptions.algorithms = this
@@ -103,7 +107,7 @@ export class JWTProvider extends BaseAuthProvider {
103
107
  if (this.audience) {
104
108
  verifyOptions.audience = this.audience;
105
109
  }
106
- const { payload } = await jose.jwtVerify(token, this.keyObject, verifyOptions);
110
+ const { payload } = await jose.jwtVerify(token, keyObject, verifyOptions);
107
111
  // Reject tokens without a non-empty sub claim
108
112
  if (!payload.sub) {
109
113
  return {
@@ -65,8 +65,12 @@ export class WorkOSProvider extends BaseAuthProvider {
65
65
  await this.initialize();
66
66
  }
67
67
  try {
68
+ const jwks = this.jwks;
69
+ if (!jwks) {
70
+ throw AuthError.create("PROVIDER_INIT_FAILED", "WorkOS JWKS was not initialized", { details: { provider: "workos" } });
71
+ }
68
72
  // Verify the JWT
69
- const { payload } = await jose.jwtVerify(token, this.jwks, {
73
+ const { payload } = await jose.jwtVerify(token, jwks, {
70
74
  audience: this.clientId,
71
75
  });
72
76
  // Enforce organizationId if configured
@@ -46,7 +46,7 @@ export declare class MemorySessionStorage implements SessionManagerStorage {
46
46
  * Redis session storage
47
47
  *
48
48
  * Distributed session storage using Redis. Suitable for multi-instance
49
- * deployments. Requires ioredis or similar Redis client.
49
+ * deployments. Requires the "redis" (node-redis) package.
50
50
  *
51
51
  * Note: Redis client must be provided or configured via environment.
52
52
  */
@@ -1,4 +1,5 @@
1
1
  // src/lib/auth/sessionManager.ts
2
+ import { withTimeout } from "../utils/async/withTimeout.js";
2
3
  import { logger } from "../utils/logger.js";
3
4
  /** Mask an identifier for safe logging: show first 4 chars + "***" */
4
5
  function maskId(id) {
@@ -7,6 +8,7 @@ function maskId(id) {
7
8
  }
8
9
  return `${id.slice(0, 4)}***`;
9
10
  }
11
+ const REDIS_CONNECT_TIMEOUT_MS = 5000;
10
12
  /**
11
13
  * In-memory session storage
12
14
  *
@@ -31,10 +33,12 @@ export class MemorySessionStorage {
31
33
  async set(session) {
32
34
  this.sessions.set(session.id, session);
33
35
  // Track user's sessions
34
- if (!this.userSessions.has(session.user.id)) {
35
- this.userSessions.set(session.user.id, new Set());
36
+ let sessionIds = this.userSessions.get(session.user.id);
37
+ if (!sessionIds) {
38
+ sessionIds = new Set();
39
+ this.userSessions.set(session.user.id, sessionIds);
36
40
  }
37
- this.userSessions.get(session.user.id).add(session.id);
41
+ sessionIds.add(session.id);
38
42
  }
39
43
  async delete(sessionId) {
40
44
  const session = this.sessions.get(sessionId);
@@ -84,7 +88,7 @@ export class MemorySessionStorage {
84
88
  * Redis session storage
85
89
  *
86
90
  * Distributed session storage using Redis. Suitable for multi-instance
87
- * deployments. Requires ioredis or similar Redis client.
91
+ * deployments. Requires the "redis" (node-redis) package.
88
92
  *
89
93
  * Note: Redis client must be provided or configured via environment.
90
94
  */
@@ -111,16 +115,25 @@ export class RedisSessionStorage {
111
115
  async createClient() {
112
116
  try {
113
117
  // Use variable indirection to prevent TypeScript from resolving the module at compile time
114
- const moduleName = "ioredis";
115
- const ioredis = await import(/* @vite-ignore */ moduleName);
116
- const Redis = ioredis.default || ioredis.Redis;
117
- this.client = new Redis(this.redisUrl);
118
- return this.client;
118
+ const moduleName = "redis";
119
+ const redisModule = (await import(
120
+ /* @vite-ignore */ moduleName));
121
+ const client = redisModule.createClient({
122
+ url: this.redisUrl,
123
+ });
124
+ client.on("error", (err) => {
125
+ logger.error("Redis session client error:", err.message);
126
+ });
127
+ await withTimeout(client.connect(), REDIS_CONNECT_TIMEOUT_MS, `Redis session client connect timed out after ${REDIS_CONNECT_TIMEOUT_MS}ms`);
128
+ this.client = client;
129
+ return client;
119
130
  }
120
- catch {
131
+ catch (error) {
121
132
  this.initPromise = null;
122
- logger.error('Redis client (ioredis) not available. Install ioredis package and ensure Redis is reachable when using storage: "redis".');
123
- throw new Error("Redis client not available");
133
+ logger.error('Redis client not available. Ensure the "redis" package is installed and Redis is reachable when using storage: "redis".');
134
+ throw error instanceof Error
135
+ ? error
136
+ : new Error("Redis client not available");
124
137
  }
125
138
  }
126
139
  sessionKey(sessionId) {
@@ -136,6 +149,13 @@ export class RedisSessionStorage {
136
149
  if (!data) {
137
150
  return null;
138
151
  }
152
+ if (typeof data !== "string") {
153
+ logger.warn("Unexpected Redis session payload type", {
154
+ sessionId: maskId(sessionId),
155
+ type: typeof data,
156
+ });
157
+ return null;
158
+ }
139
159
  const session = JSON.parse(data);
140
160
  // Parse dates
141
161
  session.createdAt = new Date(session.createdAt);
@@ -143,7 +163,7 @@ export class RedisSessionStorage {
143
163
  session.expiresAt = new Date(session.expiresAt);
144
164
  }
145
165
  // Check expiration
146
- if (new Date() > session.expiresAt) {
166
+ if (session.expiresAt && new Date() > session.expiresAt) {
147
167
  await this.delete(sessionId);
148
168
  return null;
149
169
  }
@@ -162,9 +182,9 @@ export class RedisSessionStorage {
162
182
  ? Math.max(1, Math.floor((session.expiresAt.getTime() - Date.now()) / 1000))
163
183
  : this.ttl;
164
184
  // Store session
165
- await client.setex(this.sessionKey(session.id), ttlSeconds, JSON.stringify(session));
185
+ await client.setEx(this.sessionKey(session.id), ttlSeconds, JSON.stringify(session));
166
186
  // Track user's sessions
167
- await client.sadd(this.userSessionsKey(session.user.id), session.id);
187
+ await client.sAdd(this.userSessionsKey(session.user.id), session.id);
168
188
  await client.expire(this.userSessionsKey(session.user.id), this.ttl);
169
189
  }
170
190
  catch (error) {
@@ -180,13 +200,21 @@ export class RedisSessionStorage {
180
200
  // recursion for expired sessions.
181
201
  const data = await client.get(this.sessionKey(sessionId));
182
202
  if (data) {
183
- try {
184
- const session = JSON.parse(data);
185
- await client.srem(this.userSessionsKey(session.user.id), sessionId);
203
+ if (typeof data !== "string") {
204
+ logger.warn("Unexpected Redis session payload type during delete", {
205
+ sessionId: maskId(sessionId),
206
+ type: typeof data,
207
+ });
186
208
  }
187
- catch {
188
- // If parsing fails, we still delete the key below
189
- logger.warn(`Failed to parse session data for cleanup: ${maskId(sessionId)}`);
209
+ else {
210
+ try {
211
+ const session = JSON.parse(data);
212
+ await client.sRem(this.userSessionsKey(session.user.id), sessionId);
213
+ }
214
+ catch {
215
+ // If parsing fails, we still delete the key below
216
+ logger.warn(`Failed to parse session data for cleanup: ${maskId(sessionId)}`);
217
+ }
190
218
  }
191
219
  }
192
220
  await client.del(this.sessionKey(sessionId));
@@ -198,7 +226,7 @@ export class RedisSessionStorage {
198
226
  async getUserSessions(userId) {
199
227
  try {
200
228
  const client = await this.getClient();
201
- const sessionIds = await client.smembers(this.userSessionsKey(userId));
229
+ const sessionIds = await client.sMembers(this.userSessionsKey(userId));
202
230
  const sessions = [];
203
231
  for (const sessionId of sessionIds) {
204
232
  const session = await this.get(sessionId);
@@ -216,7 +244,7 @@ export class RedisSessionStorage {
216
244
  async deleteUserSessions(userId) {
217
245
  try {
218
246
  const client = await this.getClient();
219
- const sessionIds = await client.smembers(this.userSessionsKey(userId));
247
+ const sessionIds = await client.sMembers(this.userSessionsKey(userId));
220
248
  for (const sessionId of sessionIds) {
221
249
  await client.del(this.sessionKey(sessionId));
222
250
  }
@@ -232,10 +260,13 @@ export class RedisSessionStorage {
232
260
  // Use SCAN instead of KEYS to avoid blocking Redis in production
233
261
  let cursor = "0";
234
262
  do {
235
- const [nextCursor, keys] = await client.scan(cursor, "MATCH", `${this.prefix}*`, "COUNT", "100");
236
- cursor = nextCursor;
237
- if (keys.length > 0) {
238
- await client.del(...keys);
263
+ const result = await client.scan(cursor, {
264
+ MATCH: `${this.prefix}*`,
265
+ COUNT: 100,
266
+ });
267
+ cursor = result.cursor;
268
+ if (result.keys.length > 0) {
269
+ await client.del(result.keys);
239
270
  }
240
271
  } while (cursor !== "0");
241
272
  }
@@ -186,6 +186,9 @@ export class NeuroLinkLanguageModel {
186
186
  // Drain anything already buffered.
187
187
  while (buffer.length > 0) {
188
188
  const chunk = buffer.shift();
189
+ if (!chunk) {
190
+ break;
191
+ }
189
192
  yield chunk;
190
193
  if (chunk.type === "finish") {
191
194
  return;
@@ -251,10 +251,12 @@ export class SSEClient {
251
251
  * Register event handler
252
252
  */
253
253
  on(event, callback) {
254
- if (!this.eventHandlers.has(event)) {
255
- this.eventHandlers.set(event, new Set());
254
+ let handlers = this.eventHandlers.get(event);
255
+ if (!handlers) {
256
+ handlers = new Set();
257
+ this.eventHandlers.set(event, handlers);
256
258
  }
257
- this.eventHandlers.get(event).add(callback);
259
+ handlers.add(callback);
258
260
  }
259
261
  /**
260
262
  * Remove event handler
@@ -315,7 +317,11 @@ export class SSEClient {
315
317
  });
316
318
  while (!done && !error) {
317
319
  if (events.length > 0) {
318
- yield events.shift();
320
+ const nextEvent = events.shift();
321
+ if (!nextEvent) {
322
+ continue;
323
+ }
324
+ yield nextEvent;
319
325
  }
320
326
  else {
321
327
  await new Promise((resolve) => {
@@ -325,7 +331,11 @@ export class SSEClient {
325
331
  }
326
332
  // Yield remaining events
327
333
  while (events.length > 0) {
328
- yield events.shift();
334
+ const nextEvent = events.shift();
335
+ if (!nextEvent) {
336
+ continue;
337
+ }
338
+ yield nextEvent;
329
339
  }
330
340
  if (error) {
331
341
  throw error;
@@ -545,10 +555,12 @@ export class WebSocketStreamingClient {
545
555
  * Register event handler
546
556
  */
547
557
  on(event, callback) {
548
- if (!this.eventHandlers.has(event)) {
549
- this.eventHandlers.set(event, new Set());
558
+ let handlers = this.eventHandlers.get(event);
559
+ if (!handlers) {
560
+ handlers = new Set();
561
+ this.eventHandlers.set(event, handlers);
550
562
  }
551
- this.eventHandlers.get(event).add(callback);
563
+ handlers.add(callback);
552
564
  }
553
565
  /**
554
566
  * Remove event handler
@@ -591,7 +603,11 @@ export class WebSocketStreamingClient {
591
603
  try {
592
604
  while (!disconnected) {
593
605
  if (messageQueue.length > 0) {
594
- yield messageQueue.shift();
606
+ const nextMessage = messageQueue.shift();
607
+ if (nextMessage === undefined) {
608
+ continue;
609
+ }
610
+ yield nextMessage;
595
611
  }
596
612
  else {
597
613
  await new Promise((resolve) => {
@@ -601,7 +617,11 @@ export class WebSocketStreamingClient {
601
617
  }
602
618
  // Yield remaining messages
603
619
  while (messageQueue.length > 0) {
604
- yield messageQueue.shift();
620
+ const nextMessage = messageQueue.shift();
621
+ if (nextMessage === undefined) {
622
+ continue;
623
+ }
624
+ yield nextMessage;
605
625
  }
606
626
  }
607
627
  finally {
@@ -1,4 +1,4 @@
1
- import type { ModelMessage, LanguageModel, Tool } from "ai";
1
+ import type { LanguageModel, ModelMessage, Tool } from "ai";
2
2
  import type { AIProviderName } from "../constants/enums.js";
3
3
  import type { EvaluationData } from "../index.js";
4
4
  import type { NeuroLink } from "../neurolink.js";
@@ -136,6 +136,11 @@ export declare abstract class BaseProvider implements AIProvider {
136
136
  * Alias for generate method - implements AIProvider interface
137
137
  */
138
138
  gen(optionsOrPrompt: TextGenerationOptions | string, analysisSchema?: ValidationSchema): Promise<EnhancedGenerateResult | null>;
139
+ private runGenerateInActiveContext;
140
+ private handleDirectTTSSynthesis;
141
+ private handleVideoFrameGeneration;
142
+ private executeStandardGenerateFlow;
143
+ private synthesizeAIResponseIfNeeded;
139
144
  /**
140
145
  * BACKWARD COMPATIBILITY: Legacy generateText method
141
146
  * Converts EnhancedGenerateResult to TextGenerationResult format