@juspay/neurolink 9.64.0 → 9.65.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 (322) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +18 -17
  3. package/dist/adapters/providerImageAdapter.js +29 -1
  4. package/dist/adapters/replicate/auth.d.ts +19 -0
  5. package/dist/adapters/replicate/auth.js +32 -0
  6. package/dist/adapters/replicate/predictionLifecycle.d.ts +46 -0
  7. package/dist/adapters/replicate/predictionLifecycle.js +283 -0
  8. package/dist/adapters/video/klingVideoHandler.d.ts +37 -0
  9. package/dist/adapters/video/klingVideoHandler.js +305 -0
  10. package/dist/adapters/video/replicateVideoHandler.d.ts +29 -0
  11. package/dist/adapters/video/replicateVideoHandler.js +157 -0
  12. package/dist/adapters/video/runwayVideoHandler.d.ts +32 -0
  13. package/dist/adapters/video/runwayVideoHandler.js +316 -0
  14. package/dist/adapters/video/vertexVideoHandler.d.ts +19 -1
  15. package/dist/adapters/video/vertexVideoHandler.js +33 -9
  16. package/dist/autoresearch/runner.js +8 -2
  17. package/dist/avatar/index.d.ts +13 -0
  18. package/dist/avatar/index.js +13 -0
  19. package/dist/avatar/providers/DIDAvatar.d.ts +49 -0
  20. package/dist/avatar/providers/DIDAvatar.js +501 -0
  21. package/dist/avatar/providers/HeyGenAvatar.d.ts +30 -0
  22. package/dist/avatar/providers/HeyGenAvatar.js +337 -0
  23. package/dist/avatar/providers/ReplicateAvatar.d.ts +36 -0
  24. package/dist/avatar/providers/ReplicateAvatar.js +267 -0
  25. package/dist/browser/neurolink.min.js +633 -610
  26. package/dist/cli/commands/mcp.js +29 -0
  27. package/dist/cli/commands/proxy.js +24 -5
  28. package/dist/cli/factories/commandFactory.d.ts +11 -1
  29. package/dist/cli/factories/commandFactory.js +291 -38
  30. package/dist/constants/contextWindows.js +101 -0
  31. package/dist/constants/enums.d.ts +273 -2
  32. package/dist/constants/enums.js +290 -1
  33. package/dist/constants/videoErrors.d.ts +4 -0
  34. package/dist/constants/videoErrors.js +4 -0
  35. package/dist/core/baseProvider.d.ts +22 -2
  36. package/dist/core/baseProvider.js +217 -11
  37. package/dist/core/constants.d.ts +11 -0
  38. package/dist/core/constants.js +69 -1
  39. package/dist/core/redisConversationMemoryManager.js +6 -0
  40. package/dist/evaluation/index.d.ts +2 -0
  41. package/dist/evaluation/index.js +4 -0
  42. package/dist/factories/providerFactory.js +7 -1
  43. package/dist/factories/providerRegistry.js +202 -5
  44. package/dist/features/ppt/contentPlanner.js +42 -14
  45. package/dist/index.d.ts +9 -1
  46. package/dist/index.js +16 -1
  47. package/dist/lib/adapters/providerImageAdapter.js +29 -1
  48. package/dist/lib/adapters/replicate/auth.d.ts +19 -0
  49. package/dist/lib/adapters/replicate/auth.js +33 -0
  50. package/dist/lib/adapters/replicate/predictionLifecycle.d.ts +46 -0
  51. package/dist/lib/adapters/replicate/predictionLifecycle.js +284 -0
  52. package/dist/lib/adapters/video/klingVideoHandler.d.ts +37 -0
  53. package/dist/lib/adapters/video/klingVideoHandler.js +306 -0
  54. package/dist/lib/adapters/video/replicateVideoHandler.d.ts +29 -0
  55. package/dist/lib/adapters/video/replicateVideoHandler.js +158 -0
  56. package/dist/lib/adapters/video/runwayVideoHandler.d.ts +32 -0
  57. package/dist/lib/adapters/video/runwayVideoHandler.js +317 -0
  58. package/dist/lib/adapters/video/vertexVideoHandler.d.ts +19 -1
  59. package/dist/lib/adapters/video/vertexVideoHandler.js +33 -9
  60. package/dist/lib/autoresearch/runner.js +8 -2
  61. package/dist/lib/avatar/index.d.ts +13 -0
  62. package/dist/lib/avatar/index.js +14 -0
  63. package/dist/lib/avatar/providers/DIDAvatar.d.ts +49 -0
  64. package/dist/lib/avatar/providers/DIDAvatar.js +502 -0
  65. package/dist/lib/avatar/providers/HeyGenAvatar.d.ts +30 -0
  66. package/dist/lib/avatar/providers/HeyGenAvatar.js +338 -0
  67. package/dist/lib/avatar/providers/ReplicateAvatar.d.ts +36 -0
  68. package/dist/lib/avatar/providers/ReplicateAvatar.js +268 -0
  69. package/dist/lib/constants/contextWindows.js +101 -0
  70. package/dist/lib/constants/enums.d.ts +273 -2
  71. package/dist/lib/constants/enums.js +290 -1
  72. package/dist/lib/constants/videoErrors.d.ts +4 -0
  73. package/dist/lib/constants/videoErrors.js +4 -0
  74. package/dist/lib/core/baseProvider.d.ts +22 -2
  75. package/dist/lib/core/baseProvider.js +217 -11
  76. package/dist/lib/core/constants.d.ts +11 -0
  77. package/dist/lib/core/constants.js +69 -1
  78. package/dist/lib/core/redisConversationMemoryManager.js +6 -0
  79. package/dist/lib/evaluation/index.d.ts +2 -0
  80. package/dist/lib/evaluation/index.js +4 -0
  81. package/dist/lib/factories/providerFactory.js +7 -1
  82. package/dist/lib/factories/providerRegistry.js +202 -5
  83. package/dist/lib/features/ppt/contentPlanner.js +42 -14
  84. package/dist/lib/index.d.ts +9 -1
  85. package/dist/lib/index.js +16 -1
  86. package/dist/lib/middleware/builtin/lifecycle.js +39 -9
  87. package/dist/lib/music/index.d.ts +13 -0
  88. package/dist/lib/music/index.js +14 -0
  89. package/dist/lib/music/providers/BeatovenMusic.d.ts +31 -0
  90. package/dist/lib/music/providers/BeatovenMusic.js +334 -0
  91. package/dist/lib/music/providers/ElevenLabsMusic.d.ts +30 -0
  92. package/dist/lib/music/providers/ElevenLabsMusic.js +169 -0
  93. package/dist/lib/music/providers/LyriaMusic.d.ts +29 -0
  94. package/dist/lib/music/providers/LyriaMusic.js +173 -0
  95. package/dist/lib/music/providers/ReplicateMusic.d.ts +31 -0
  96. package/dist/lib/music/providers/ReplicateMusic.js +262 -0
  97. package/dist/lib/neurolink.d.ts +30 -0
  98. package/dist/lib/neurolink.js +323 -77
  99. package/dist/lib/providers/amazonBedrock.d.ts +10 -0
  100. package/dist/lib/providers/amazonBedrock.js +94 -39
  101. package/dist/lib/providers/anthropic.js +55 -7
  102. package/dist/lib/providers/anthropicBaseProvider.js +1 -1
  103. package/dist/lib/providers/azureOpenai.js +66 -17
  104. package/dist/lib/providers/cloudflare.d.ts +35 -0
  105. package/dist/lib/providers/cloudflare.js +174 -0
  106. package/dist/lib/providers/cohere.d.ts +52 -0
  107. package/dist/lib/providers/cohere.js +253 -0
  108. package/dist/lib/providers/deepseek.js +72 -17
  109. package/dist/lib/providers/fireworks.d.ts +33 -0
  110. package/dist/lib/providers/fireworks.js +164 -0
  111. package/dist/lib/providers/googleAiStudio.js +45 -6
  112. package/dist/lib/providers/googleNativeGemini3.d.ts +24 -1
  113. package/dist/lib/providers/googleNativeGemini3.js +173 -21
  114. package/dist/lib/providers/googleVertex.js +173 -17
  115. package/dist/lib/providers/groq.d.ts +33 -0
  116. package/dist/lib/providers/groq.js +181 -0
  117. package/dist/lib/providers/huggingFace.js +9 -8
  118. package/dist/lib/providers/ideogram.d.ts +34 -0
  119. package/dist/lib/providers/ideogram.js +184 -0
  120. package/dist/lib/providers/index.d.ts +13 -0
  121. package/dist/lib/providers/index.js +13 -0
  122. package/dist/lib/providers/jina.d.ts +59 -0
  123. package/dist/lib/providers/jina.js +218 -0
  124. package/dist/lib/providers/llamaCpp.js +14 -46
  125. package/dist/lib/providers/lmStudio.js +14 -47
  126. package/dist/lib/providers/mistral.js +7 -7
  127. package/dist/lib/providers/nvidiaNim.js +160 -19
  128. package/dist/lib/providers/ollama.js +7 -7
  129. package/dist/lib/providers/openAI.d.ts +22 -1
  130. package/dist/lib/providers/openAI.js +181 -0
  131. package/dist/lib/providers/openRouter.js +35 -23
  132. package/dist/lib/providers/openaiCompatible.js +9 -8
  133. package/dist/lib/providers/perplexity.d.ts +33 -0
  134. package/dist/lib/providers/perplexity.js +179 -0
  135. package/dist/lib/providers/recraft.d.ts +34 -0
  136. package/dist/lib/providers/recraft.js +197 -0
  137. package/dist/lib/providers/replicate.d.ts +75 -0
  138. package/dist/lib/providers/replicate.js +403 -0
  139. package/dist/lib/providers/stability.d.ts +37 -0
  140. package/dist/lib/providers/stability.js +191 -0
  141. package/dist/lib/providers/togetherAi.d.ts +33 -0
  142. package/dist/lib/providers/togetherAi.js +176 -0
  143. package/dist/lib/providers/voyage.d.ts +47 -0
  144. package/dist/lib/providers/voyage.js +177 -0
  145. package/dist/lib/providers/xai.d.ts +33 -0
  146. package/dist/lib/providers/xai.js +172 -0
  147. package/dist/lib/telemetry/index.d.ts +1 -1
  148. package/dist/lib/telemetry/index.js +1 -1
  149. package/dist/lib/telemetry/tracers.d.ts +19 -0
  150. package/dist/lib/telemetry/tracers.js +19 -0
  151. package/dist/lib/telemetry/withSpan.d.ts +35 -0
  152. package/dist/lib/telemetry/withSpan.js +103 -0
  153. package/dist/lib/types/avatar.d.ts +143 -0
  154. package/dist/lib/types/avatar.js +20 -0
  155. package/dist/lib/types/cli.d.ts +6 -0
  156. package/dist/lib/types/generate.d.ts +62 -5
  157. package/dist/lib/types/index.d.ts +5 -0
  158. package/dist/lib/types/index.js +7 -0
  159. package/dist/lib/types/middleware.d.ts +27 -0
  160. package/dist/lib/types/multimodal.d.ts +35 -2
  161. package/dist/lib/types/music.d.ts +165 -0
  162. package/dist/lib/types/music.js +21 -0
  163. package/dist/lib/types/providers.d.ts +144 -1
  164. package/dist/lib/types/replicate.d.ts +67 -0
  165. package/dist/lib/types/replicate.js +10 -0
  166. package/dist/lib/types/safeFetch.d.ts +15 -0
  167. package/dist/lib/types/safeFetch.js +7 -0
  168. package/dist/lib/types/stream.d.ts +2 -1
  169. package/dist/lib/types/tools.d.ts +13 -0
  170. package/dist/lib/types/video.d.ts +89 -0
  171. package/dist/lib/types/video.js +15 -0
  172. package/dist/lib/utils/avatarProcessor.d.ts +68 -0
  173. package/dist/lib/utils/avatarProcessor.js +172 -0
  174. package/dist/lib/utils/cloneOptions.d.ts +36 -0
  175. package/dist/lib/utils/cloneOptions.js +62 -0
  176. package/dist/lib/utils/lifecycleCallbacks.d.ts +51 -8
  177. package/dist/lib/utils/lifecycleCallbacks.js +82 -26
  178. package/dist/lib/utils/lifecycleTimeout.d.ts +25 -0
  179. package/dist/lib/utils/lifecycleTimeout.js +39 -0
  180. package/dist/lib/utils/logSanitize.d.ts +49 -0
  181. package/dist/lib/utils/logSanitize.js +170 -0
  182. package/dist/lib/utils/loggingFetch.d.ts +29 -0
  183. package/dist/lib/utils/loggingFetch.js +60 -0
  184. package/dist/lib/utils/messageBuilder.js +43 -25
  185. package/dist/lib/utils/modelChoices.js +236 -3
  186. package/dist/lib/utils/musicProcessor.d.ts +67 -0
  187. package/dist/lib/utils/musicProcessor.js +189 -0
  188. package/dist/lib/utils/optionsConversion.js +3 -2
  189. package/dist/lib/utils/parameterValidation.js +14 -4
  190. package/dist/lib/utils/pricing.js +193 -0
  191. package/dist/lib/utils/providerConfig.d.ts +55 -0
  192. package/dist/lib/utils/providerConfig.js +224 -0
  193. package/dist/lib/utils/safeFetch.d.ts +26 -0
  194. package/dist/lib/utils/safeFetch.js +83 -0
  195. package/dist/lib/utils/sizeGuard.d.ts +34 -0
  196. package/dist/lib/utils/sizeGuard.js +45 -0
  197. package/dist/lib/utils/ssrfGuard.d.ts +52 -0
  198. package/dist/lib/utils/ssrfGuard.js +411 -0
  199. package/dist/lib/utils/videoProcessor.d.ts +60 -0
  200. package/dist/lib/utils/videoProcessor.js +201 -0
  201. package/dist/lib/voice/providers/FishAudioTTS.d.ts +27 -0
  202. package/dist/lib/voice/providers/FishAudioTTS.js +183 -0
  203. package/dist/lib/workflow/core/ensembleExecutor.js +26 -9
  204. package/dist/middleware/builtin/lifecycle.js +39 -9
  205. package/dist/music/index.d.ts +13 -0
  206. package/dist/music/index.js +13 -0
  207. package/dist/music/providers/BeatovenMusic.d.ts +31 -0
  208. package/dist/music/providers/BeatovenMusic.js +333 -0
  209. package/dist/music/providers/ElevenLabsMusic.d.ts +30 -0
  210. package/dist/music/providers/ElevenLabsMusic.js +168 -0
  211. package/dist/music/providers/LyriaMusic.d.ts +29 -0
  212. package/dist/music/providers/LyriaMusic.js +172 -0
  213. package/dist/music/providers/ReplicateMusic.d.ts +31 -0
  214. package/dist/music/providers/ReplicateMusic.js +261 -0
  215. package/dist/neurolink.d.ts +30 -0
  216. package/dist/neurolink.js +323 -77
  217. package/dist/providers/amazonBedrock.d.ts +10 -0
  218. package/dist/providers/amazonBedrock.js +94 -39
  219. package/dist/providers/anthropic.js +55 -7
  220. package/dist/providers/anthropicBaseProvider.js +1 -1
  221. package/dist/providers/azureOpenai.js +66 -17
  222. package/dist/providers/cloudflare.d.ts +35 -0
  223. package/dist/providers/cloudflare.js +173 -0
  224. package/dist/providers/cohere.d.ts +52 -0
  225. package/dist/providers/cohere.js +252 -0
  226. package/dist/providers/deepseek.js +72 -17
  227. package/dist/providers/fireworks.d.ts +33 -0
  228. package/dist/providers/fireworks.js +163 -0
  229. package/dist/providers/googleAiStudio.js +45 -6
  230. package/dist/providers/googleNativeGemini3.d.ts +24 -1
  231. package/dist/providers/googleNativeGemini3.js +173 -21
  232. package/dist/providers/googleVertex.js +173 -17
  233. package/dist/providers/groq.d.ts +33 -0
  234. package/dist/providers/groq.js +180 -0
  235. package/dist/providers/huggingFace.js +9 -8
  236. package/dist/providers/ideogram.d.ts +34 -0
  237. package/dist/providers/ideogram.js +183 -0
  238. package/dist/providers/index.d.ts +13 -0
  239. package/dist/providers/index.js +13 -0
  240. package/dist/providers/jina.d.ts +59 -0
  241. package/dist/providers/jina.js +217 -0
  242. package/dist/providers/llamaCpp.js +14 -46
  243. package/dist/providers/lmStudio.js +14 -47
  244. package/dist/providers/mistral.js +7 -7
  245. package/dist/providers/nvidiaNim.js +160 -19
  246. package/dist/providers/ollama.js +7 -7
  247. package/dist/providers/openAI.d.ts +22 -1
  248. package/dist/providers/openAI.js +181 -0
  249. package/dist/providers/openRouter.js +35 -23
  250. package/dist/providers/openaiCompatible.js +9 -8
  251. package/dist/providers/perplexity.d.ts +33 -0
  252. package/dist/providers/perplexity.js +178 -0
  253. package/dist/providers/recraft.d.ts +34 -0
  254. package/dist/providers/recraft.js +196 -0
  255. package/dist/providers/replicate.d.ts +75 -0
  256. package/dist/providers/replicate.js +402 -0
  257. package/dist/providers/stability.d.ts +37 -0
  258. package/dist/providers/stability.js +190 -0
  259. package/dist/providers/togetherAi.d.ts +33 -0
  260. package/dist/providers/togetherAi.js +175 -0
  261. package/dist/providers/voyage.d.ts +47 -0
  262. package/dist/providers/voyage.js +176 -0
  263. package/dist/providers/xai.d.ts +33 -0
  264. package/dist/providers/xai.js +171 -0
  265. package/dist/telemetry/index.d.ts +1 -1
  266. package/dist/telemetry/index.js +1 -1
  267. package/dist/telemetry/tracers.d.ts +19 -0
  268. package/dist/telemetry/tracers.js +19 -0
  269. package/dist/telemetry/withSpan.d.ts +35 -0
  270. package/dist/telemetry/withSpan.js +103 -0
  271. package/dist/types/avatar.d.ts +143 -0
  272. package/dist/types/avatar.js +19 -0
  273. package/dist/types/cli.d.ts +6 -0
  274. package/dist/types/generate.d.ts +62 -5
  275. package/dist/types/index.d.ts +5 -0
  276. package/dist/types/index.js +7 -0
  277. package/dist/types/middleware.d.ts +27 -0
  278. package/dist/types/multimodal.d.ts +35 -2
  279. package/dist/types/music.d.ts +165 -0
  280. package/dist/types/music.js +20 -0
  281. package/dist/types/providers.d.ts +144 -1
  282. package/dist/types/replicate.d.ts +67 -0
  283. package/dist/types/replicate.js +9 -0
  284. package/dist/types/safeFetch.d.ts +15 -0
  285. package/dist/types/safeFetch.js +6 -0
  286. package/dist/types/stream.d.ts +2 -1
  287. package/dist/types/tools.d.ts +13 -0
  288. package/dist/types/video.d.ts +89 -0
  289. package/dist/types/video.js +14 -0
  290. package/dist/utils/avatarProcessor.d.ts +68 -0
  291. package/dist/utils/avatarProcessor.js +171 -0
  292. package/dist/utils/cloneOptions.d.ts +36 -0
  293. package/dist/utils/cloneOptions.js +61 -0
  294. package/dist/utils/lifecycleCallbacks.d.ts +51 -8
  295. package/dist/utils/lifecycleCallbacks.js +82 -26
  296. package/dist/utils/lifecycleTimeout.d.ts +25 -0
  297. package/dist/utils/lifecycleTimeout.js +38 -0
  298. package/dist/utils/logSanitize.d.ts +49 -0
  299. package/dist/utils/logSanitize.js +169 -0
  300. package/dist/utils/loggingFetch.d.ts +29 -0
  301. package/dist/utils/loggingFetch.js +59 -0
  302. package/dist/utils/messageBuilder.js +43 -25
  303. package/dist/utils/modelChoices.js +236 -3
  304. package/dist/utils/musicProcessor.d.ts +67 -0
  305. package/dist/utils/musicProcessor.js +188 -0
  306. package/dist/utils/optionsConversion.js +3 -2
  307. package/dist/utils/parameterValidation.js +14 -4
  308. package/dist/utils/pricing.js +193 -0
  309. package/dist/utils/providerConfig.d.ts +55 -0
  310. package/dist/utils/providerConfig.js +224 -0
  311. package/dist/utils/safeFetch.d.ts +26 -0
  312. package/dist/utils/safeFetch.js +82 -0
  313. package/dist/utils/sizeGuard.d.ts +34 -0
  314. package/dist/utils/sizeGuard.js +44 -0
  315. package/dist/utils/ssrfGuard.d.ts +52 -0
  316. package/dist/utils/ssrfGuard.js +410 -0
  317. package/dist/utils/videoProcessor.d.ts +60 -0
  318. package/dist/utils/videoProcessor.js +200 -0
  319. package/dist/voice/providers/FishAudioTTS.d.ts +27 -0
  320. package/dist/voice/providers/FishAudioTTS.js +182 -0
  321. package/dist/workflow/core/ensembleExecutor.js +26 -9
  322. package/package.json +32 -5
@@ -62,9 +62,11 @@ import { tracers } from "./telemetry/tracers.js";
62
62
  import { getConversationMessages, storeConversationTurn, } from "./utils/conversationMemory.js";
63
63
  // Enhanced error handling imports
64
64
  import { CircuitBreaker, ERROR_CODES, ErrorFactory, isAbortError, isRetriableError, logStructuredError, NeuroLinkError, withRetry, withTimeout, } from "./utils/errorHandling.js";
65
+ import { hasLifecycleErrorFired, markLifecycleErrorFired, } from "./utils/lifecycleCallbacks.js";
66
+ import { resolveLifecycleTimeoutMs } from "./utils/lifecycleTimeout.js";
67
+ import { cloneOptionsForCallIsolation } from "./utils/cloneOptions.js";
65
68
  // Factory processing imports
66
69
  import { createCleanStreamOptions, enhanceTextGenerationOptions, processFactoryOptions, processStreamingFactoryOptions, validateFactoryConfig, } from "./utils/factoryProcessing.js";
67
- import { fireOnErrorOnce } from "./utils/lifecycleCallbacks.js";
68
70
  import { logger, mcpLogger } from "./utils/logger.js";
69
71
  import { extractMcpErrorText } from "./utils/mcpErrorText.js";
70
72
  import { createCustomToolServerInfo, detectCategory, } from "./utils/mcpDefaults.js";
@@ -334,7 +336,30 @@ export function markStreamProviderEmittedGenerationEnd(options) {
334
336
  ctx.providerEmitted = true;
335
337
  }
336
338
  }
339
+ /**
340
+ * Symbol-based brand for cross-module identification without circular imports.
341
+ *
342
+ * Provider constructors receive `sdk?: unknown` (the factory layer's
343
+ * contract). Rather than duck-typing via `"getInMemoryServers" in sdk`,
344
+ * use `isNeuroLink(value)` from this module to do a brand check —
345
+ * survives minification AND doesn't rely on method-name stability.
346
+ */
347
+ export const NEUROLINK_BRAND = Symbol.for("@juspay/neurolink/sdk-brand");
348
+ /**
349
+ * Type-guard for opaque values that should be a {@link NeuroLink} instance.
350
+ *
351
+ * Designed for the provider-factory boundary where TS can't carry the type
352
+ * through `UnknownRecord` without forcing every caller into a circular
353
+ * dependency. Cheap to call and unaffected by minification.
354
+ */
355
+ export function isNeuroLink(value) {
356
+ return (typeof value === "object" &&
357
+ value !== null &&
358
+ value[NEUROLINK_BRAND] === true);
359
+ }
337
360
  export class NeuroLink {
361
+ /** @internal Brand for cross-module identification — see {@link isNeuroLink}. */
362
+ [NEUROLINK_BRAND] = true;
338
363
  mcpInitialized = false;
339
364
  mcpSkipped = false;
340
365
  mcpInitPromise = null;
@@ -2754,28 +2779,65 @@ Current user's request: ${currentInput}`;
2754
2779
  * @since 1.0.0
2755
2780
  */
2756
2781
  async generate(optionsOrPrompt) {
2757
- const startTime = Date.now();
2782
+ // Defensive call-isolation clone — mirrors stream(): downstream
2783
+ // generate-prep (memory retrieval, orchestration, RAG/MCP tool
2784
+ // injection) mutates nested branches on the caller-supplied options
2785
+ // object. Without cloning here, callers reusing a single options
2786
+ // bag across generate() calls accumulate state across them.
2787
+ // String prompts are immutable, so they pass through.
2788
+ if (typeof optionsOrPrompt !== "string") {
2789
+ optionsOrPrompt = cloneOptionsForCallIsolation(optionsOrPrompt);
2790
+ }
2791
+ const startedAt = Date.now();
2758
2792
  try {
2759
2793
  return await this.runWithFallbackOrchestration(optionsOrPrompt, "generate", (opts) => tracers.sdk.startActiveSpan("neurolink.generate", { kind: SpanKind.INTERNAL }, (generateSpan) => this.executeGenerateWithMetricsContext(opts, generateSpan)));
2760
2794
  }
2761
2795
  catch (error) {
2762
- // Fire `onError` lifecycle callback for ANY thrown error — including
2763
- // ones raised before the provider is even instantiated (invalid
2764
- // provider name, missing credentials, etc.). The downstream
2765
- // LifecycleMiddleware only fires `onError` once it has wrapped the
2766
- // AI SDK doGenerate, which is too late for early-resolution failures.
2767
- // `fireOnErrorOnce` dedupes against the middleware path so the
2768
- // consumer callback fires at most once per logical failure.
2769
- const onError = typeof optionsOrPrompt === "object" && optionsOrPrompt !== null
2770
- ? optionsOrPrompt.onError
2771
- : undefined;
2772
- const err = error instanceof Error ? error : new Error(String(error));
2773
- fireOnErrorOnce(onError, error, {
2796
+ // Lifecycle middleware (wrapGenerate.catch in builtin/lifecycle.ts)
2797
+ // stamps errors it already surfaced with the shared Symbol marker
2798
+ // (see utils/lifecycleCallbacks.ts). For errors thrown BEFORE the
2799
+ // language model was wrapped (e.g. unknown provider name,
2800
+ // validation failures, factory exceptions), the mark is absent
2801
+ // fire the consumer's onError here so it sees every failure path.
2802
+ // Awaited so async handlers fully settle before generate()
2803
+ // rethrows, matching the middleware-managed path.
2804
+ await this.fireConsumerOnErrorIfNotFired(optionsOrPrompt, error, startedAt);
2805
+ throw error;
2806
+ }
2807
+ }
2808
+ async fireConsumerOnErrorIfNotFired(optionsOrPrompt, error, startedAt) {
2809
+ if (hasLifecycleErrorFired(error)) {
2810
+ return;
2811
+ }
2812
+ if (typeof optionsOrPrompt !== "object" || optionsOrPrompt === null) {
2813
+ return;
2814
+ }
2815
+ const opts = optionsOrPrompt;
2816
+ const userOnError = opts.onError;
2817
+ if (!userOnError) {
2818
+ return;
2819
+ }
2820
+ const err = error instanceof Error ? error : new Error(String(error));
2821
+ // Bound the consumer callback so a never-settling handler can't hang
2822
+ // generate()/stream(). The deadline honors per-call
2823
+ // `lifecycle.timeoutMs` and the `NEUROLINK_LIFECYCLE_TIMEOUT_MS` env
2824
+ // var (CLI surface), falling back to 5_000. Errors raised here are
2825
+ // logged and swallowed; the original failure still propagates to the
2826
+ // caller because the outer catch re-throws.
2827
+ const lifecycle = opts.middleware?.middlewareConfig?.lifecycle?.config;
2828
+ const timeoutMs = resolveLifecycleTimeoutMs(lifecycle);
2829
+ // Mark the error first so the AI-SDK lifecycle middleware can't
2830
+ // re-fire the same callback if the throw races a parallel catch.
2831
+ markLifecycleErrorFired(err);
2832
+ try {
2833
+ await withTimeout(Promise.resolve(userOnError({
2774
2834
  error: err,
2775
- duration: Date.now() - startTime,
2835
+ duration: Date.now() - startedAt,
2776
2836
  recoverable: false,
2777
- });
2778
- throw error;
2837
+ })), timeoutMs, new Error(`consumer onError callback timed out after ${timeoutMs}ms`));
2838
+ }
2839
+ catch (e) {
2840
+ logger.warn("[NeuroLink] consumer onError callback error:", e);
2779
2841
  }
2780
2842
  }
2781
2843
  /**
@@ -2861,15 +2923,23 @@ Current user's request: ${currentInput}`;
2861
2923
  catch {
2862
2924
  /* listener errors are non-fatal */
2863
2925
  }
2926
+ // Defensive call-isolation clone for the retry attempt. The shallow
2927
+ // spread below would keep nested branches (`input`, `tools`, `memory`,
2928
+ // `rag`, …) pointing at the same objects the previous attempt's
2929
+ // prepare stages mutated — so the retry inherits e.g. memory-retrieved
2930
+ // history appended to `input.messages`, RAG-injected tools, etc.
2931
+ // Re-cloning at the retry boundary mirrors the entry-level isolation
2932
+ // applied in generate()/stream() so each fallback attempt sees a
2933
+ // fresh options bag.
2864
2934
  const retriedOptions = typeof optionsOrPrompt === "object"
2865
- ? {
2935
+ ? cloneOptionsForCallIsolation({
2866
2936
  ...optionsOrPrompt,
2867
2937
  ...(next.provider && { provider: next.provider }),
2868
2938
  ...(next.model && { model: next.model }),
2869
2939
  // Strip the fallback hooks so the retry doesn't re-orchestrate.
2870
2940
  providerFallback: undefined,
2871
2941
  modelChain: undefined,
2872
- }
2942
+ })
2873
2943
  : optionsOrPrompt;
2874
2944
  const retryAttempt = await this.attemptInner(inner, retriedOptions);
2875
2945
  if ("ok" in retryAttempt) {
@@ -2945,6 +3015,12 @@ Current user's request: ${currentInput}`;
2945
3015
  const options = typeof optionsOrPrompt === "string"
2946
3016
  ? { input: { text: optionsOrPrompt } }
2947
3017
  : { ...optionsOrPrompt };
3018
+ // Normalise: all downstream code assumes options.input is defined.
3019
+ // Media-only callers may omit `input` entirely (or pass `input: {}`);
3020
+ // synthesise an empty shell so the rest of the pipeline can rely on it.
3021
+ if (!options.input) {
3022
+ options.input = {};
3023
+ }
2948
3024
  // Dynamic argument resolution — resolve any function-valued options before downstream use
2949
3025
  await this.resolveDynamicOptions(options);
2950
3026
  options.model = resolveModel(options.model, this.modelAliasConfig);
@@ -2955,13 +3031,28 @@ Current user's request: ${currentInput}`;
2955
3031
  ? optionsOrPrompt.length
2956
3032
  : options.input?.text?.length || 0);
2957
3033
  generateSpan.setAttribute("neurolink.has_tools", !!(options.tools && Object.keys(options.tools).length > 0));
2958
- // When STT audio is provided, ensure options.input exists (the transcription
2959
- // will supply the text inside runStandardGenerateRequest) and skip text validation.
3034
+ // Ensure options.input is always an object callers may omit it for
3035
+ // media-only modes (avatar / music / video) or STT flows. Downstream code
3036
+ // accesses it unconditionally, so we guarantee a non-null object here and
3037
+ // rely on the per-mode checks below to populate / validate the text field.
3038
+ options.input ??= {};
3039
+ // When STT audio is provided, ensure options.input.text is initialised
3040
+ // (the transcription will supply the text inside runStandardGenerateRequest).
2960
3041
  const hasSttAudio = !!(options.stt?.enabled && options.stt?.audio);
2961
- if (hasSttAudio && !options.input) {
2962
- options.input = { text: "" };
2963
- }
2964
- if (!hasSttAudio) {
3042
+ if (hasSttAudio && !options.input.text) {
3043
+ options.input.text = "";
3044
+ }
3045
+ // Modality dispatch (video / avatar / music) carries the prompt inside
3046
+ // `output.{video|avatar|music}` — input.text is not meaningful for these
3047
+ // modes. Synthesize an empty input.text and skip validation, mirroring
3048
+ // the STT-audio exception above.
3049
+ const isMediaModalityMode = options.output?.mode === "video" ||
3050
+ options.output?.mode === "avatar" ||
3051
+ options.output?.mode === "music";
3052
+ if (isMediaModalityMode && !options.input.text) {
3053
+ options.input.text = "";
3054
+ }
3055
+ if (!hasSttAudio && !isMediaModalityMode) {
2965
3056
  this.assertInputText(options.input?.text, "Input text is required and must be a non-empty string");
2966
3057
  }
2967
3058
  this.enforceSessionBudget(options.maxBudgetUsd);
@@ -2971,6 +3062,16 @@ Current user's request: ${currentInput}`;
2971
3062
  }
2972
3063
  async maybeHandleEarlyGenerateResult(options, generateSpan) {
2973
3064
  if (options.workflow || options.workflowConfig) {
3065
+ // Workflow engine operates on text; media generation modes (avatar, music,
3066
+ // video, ppt) use dedicated code paths and are incompatible with workflows.
3067
+ const workflowMediaMode = options.output?.mode;
3068
+ if (workflowMediaMode === "avatar" ||
3069
+ workflowMediaMode === "music" ||
3070
+ workflowMediaMode === "video" ||
3071
+ workflowMediaMode === "ppt") {
3072
+ throw new Error(`Workflow mode is not compatible with output.mode="${workflowMediaMode}". ` +
3073
+ 'Remove the workflow config or use output.mode="text".');
3074
+ }
2974
3075
  if (options.stt?.enabled && options.stt?.audio) {
2975
3076
  // prepareGenerateRequest synthesizes input.text = "" for audio-only
2976
3077
  // calls, so without this guard generateWithWorkflow runs with an
@@ -2982,6 +3083,12 @@ Current user's request: ${currentInput}`;
2982
3083
  }
2983
3084
  return this.generateWithWorkflow(options);
2984
3085
  }
3086
+ if (options.output?.mode === "music") {
3087
+ return this.generateWithMusic(options, generateSpan);
3088
+ }
3089
+ if (options.output?.mode === "avatar") {
3090
+ return this.generateWithAvatar(options, generateSpan);
3091
+ }
2985
3092
  if (options.output?.mode !== "ppt") {
2986
3093
  return null;
2987
3094
  }
@@ -3099,7 +3206,7 @@ Current user's request: ${currentInput}`;
3099
3206
  originalProvider: options.provider || "auto",
3100
3207
  orchestratedProvider: orchestratedOptions.provider,
3101
3208
  orchestratedModel: orchestratedOptions.model,
3102
- prompt: options.input.text.substring(0, 100),
3209
+ prompt: (options.input?.text ?? "").substring(0, 100),
3103
3210
  });
3104
3211
  Object.assign(options, orchestratedOptions);
3105
3212
  if (orchestratedOptions.model) {
@@ -3144,12 +3251,22 @@ Current user's request: ${currentInput}`;
3144
3251
  });
3145
3252
  }
3146
3253
  }
3147
- if (!this.shouldReadMemory(options.memory, options.context?.userId) ||
3254
+ // Media-only modes (avatar, music, video, ppt) do not have a meaningful
3255
+ // text prompt to augment with memory — skip injection to avoid corrupting
3256
+ // the empty/synthesized input.text that was set for these modes.
3257
+ const mediaOnlyMode = options.output?.mode === "avatar" ||
3258
+ options.output?.mode === "music" ||
3259
+ options.output?.mode === "video" ||
3260
+ options.output?.mode === "ppt";
3261
+ if (mediaOnlyMode ||
3262
+ !this.shouldReadMemory(options.memory, options.context?.userId) ||
3148
3263
  !options.context?.userId) {
3149
3264
  return;
3150
3265
  }
3151
3266
  try {
3152
- options.input.text = await this.retrieveMemory(options.input.text, options.context.userId, options.memory?.additionalUsers);
3267
+ if (options.input) {
3268
+ options.input.text = await this.retrieveMemory(options.input.text ?? "", options.context.userId, options.memory?.additionalUsers);
3269
+ }
3153
3270
  logger.debug("Memory retrieval successful (generate)");
3154
3271
  }
3155
3272
  catch (error) {
@@ -3158,7 +3275,7 @@ Current user's request: ${currentInput}`;
3158
3275
  }
3159
3276
  async buildGenerateTextOptions(options, originalPrompt, factoryResult) {
3160
3277
  const baseOptions = {
3161
- prompt: options.input.text,
3278
+ prompt: options.input?.text,
3162
3279
  provider: options.provider,
3163
3280
  model: options.model,
3164
3281
  temperature: options.temperature,
@@ -3214,11 +3331,11 @@ Current user's request: ${currentInput}`;
3214
3331
  textOptions.conversationMemoryConfig = this.conversationMemory.config;
3215
3332
  textOptions.originalPrompt = originalPrompt;
3216
3333
  }
3217
- const { toolResults, enhancedPrompt } = await this.detectAndExecuteTools(textOptions.prompt || options.input.text, factoryResult.domainType);
3334
+ const { toolResults, enhancedPrompt } = await this.detectAndExecuteTools(textOptions.prompt || options.input?.text || "", factoryResult.domainType);
3218
3335
  if (enhancedPrompt !== textOptions.prompt) {
3219
3336
  textOptions.prompt = enhancedPrompt;
3220
3337
  logger.debug("Enhanced prompt with tool results", {
3221
- originalLength: options.input.text.length,
3338
+ originalLength: (options.input?.text ?? "").length,
3222
3339
  enhancedLength: enhancedPrompt.length,
3223
3340
  toolResults: toolResults.length,
3224
3341
  });
@@ -3227,26 +3344,35 @@ Current user's request: ${currentInput}`;
3227
3344
  }
3228
3345
  finalizeGenerateRequestResult(params) {
3229
3346
  const { generateSpan, options, textOptions, textResult, factoryResult, originalPrompt, startTime, } = params;
3230
- this.emitter.emit("generation:end", {
3231
- provider: textResult.provider,
3232
- responseTime: Date.now() - startTime,
3233
- toolsUsed: textResult.toolsUsed,
3234
- timestamp: Date.now(),
3235
- result: textResult,
3236
- // Use the effective prompt (which already incorporates STT-transcribed
3237
- // text for audio-only calls) so observers see the real prompt instead
3238
- // of an empty string. Falls back through the same chain as before for
3239
- // text-only calls.
3240
- prompt: originalPrompt ||
3241
- options.input?.text ||
3242
- options.prompt,
3243
- temperature: textOptions.temperature,
3244
- maxTokens: textOptions.maxTokens,
3245
- // A2 fix: Signal that Pipeline A (AI SDK @langfuse/otel) already
3246
- // creates a GENERATION observation for this call. The generation:end
3247
- // listener should skip creating a duplicate Pipeline B span.
3248
- pipelineAHandled: true,
3249
- });
3347
+ // Skip the top-level `generation:end` emission when the provider already
3348
+ // emitted it from its native generate path (Vertex / Google AI Studio).
3349
+ // Without this guard, native-path providers would surface TWO events
3350
+ // per generate call (Pipeline A path: 1 from provider native + 1 here;
3351
+ // standard AI-SDK path: 1 here). The observability suite asserts on
3352
+ // emission count and would (and did) fail.
3353
+ const nativeAlreadyEmitted = !!textResult._generationEndEmitted;
3354
+ if (!nativeAlreadyEmitted) {
3355
+ this.emitter.emit("generation:end", {
3356
+ provider: textResult.provider,
3357
+ responseTime: Date.now() - startTime,
3358
+ toolsUsed: textResult.toolsUsed,
3359
+ timestamp: Date.now(),
3360
+ result: textResult,
3361
+ // Use the effective prompt (which already incorporates STT-transcribed
3362
+ // text for audio-only calls) so observers see the real prompt instead
3363
+ // of an empty string. Falls back through the same chain as before for
3364
+ // text-only calls.
3365
+ prompt: originalPrompt ||
3366
+ options.input?.text ||
3367
+ options.prompt,
3368
+ temperature: textOptions.temperature,
3369
+ maxTokens: textOptions.maxTokens,
3370
+ // A2 fix: Signal that Pipeline A (AI SDK → @langfuse/otel) already
3371
+ // creates a GENERATION observation for this call. The generation:end
3372
+ // listener should skip creating a duplicate Pipeline B span.
3373
+ pipelineAHandled: true,
3374
+ });
3375
+ }
3250
3376
  this.emitter.emit("response:end", textResult.content || "");
3251
3377
  this.emitter.emit("message", `Generation completed in ${Date.now() - startTime}ms`);
3252
3378
  const generateResult = {
@@ -3284,7 +3410,18 @@ Current user's request: ${currentInput}`;
3284
3410
  audio: textResult.audio,
3285
3411
  transcription: textResult.transcription,
3286
3412
  video: textResult.video,
3413
+ avatar: textResult.avatar,
3414
+ music: textResult.music,
3287
3415
  ppt: textResult.ppt,
3416
+ // Forward reasoning/reasoningTokens from the provider layer.
3417
+ // BaseProvider's GenerationHandler extracts these from AI-SDK reasoning
3418
+ // parts (DeepSeek's `reasoning_content`, Anthropic thinking blocks,
3419
+ // Gemini thought parts, OpenAI o1) and they're declared on
3420
+ // `GenerateResult`, but the builder previously dropped them on the
3421
+ // floor — so callers asking for `result.reasoning` got `undefined`
3422
+ // even when the model emitted a chain-of-thought.
3423
+ reasoning: textResult.reasoning,
3424
+ reasoningTokens: textResult.reasoningTokens,
3288
3425
  ...(textResult.retries && { retries: textResult.retries }),
3289
3426
  };
3290
3427
  if (generateResult.analytics?.cost && generateResult.analytics.cost > 0) {
@@ -3377,6 +3514,67 @@ Current user's request: ${currentInput}`;
3377
3514
  ppt: pptResult,
3378
3515
  };
3379
3516
  }
3517
+ /**
3518
+ * Dispatch a music-generation request to the registered music handler
3519
+ * for the provider named in `options.output.music.provider`.
3520
+ */
3521
+ async generateWithMusic(options, generateSpan) {
3522
+ const startTime = Date.now();
3523
+ const musicOptions = options.output?.music;
3524
+ if (!musicOptions) {
3525
+ throw new Error('output.mode="music" requires output.music with at least { provider, prompt }.');
3526
+ }
3527
+ const providerName = musicOptions.provider;
3528
+ if (!providerName) {
3529
+ throw new Error('output.music.provider is required (e.g. "beatoven", "elevenlabs-music", "lyria", "replicate").');
3530
+ }
3531
+ const { MusicProcessor } = await import("./utils/musicProcessor.js");
3532
+ const musicResult = await MusicProcessor.generate(providerName, {
3533
+ ...musicOptions,
3534
+ prompt: musicOptions.prompt ?? options.input?.text ?? "",
3535
+ });
3536
+ generateSpan.setAttribute("neurolink.music.provider", providerName);
3537
+ generateSpan.setAttribute("neurolink.music.bytes", musicResult.size);
3538
+ generateSpan.setStatus({ code: SpanStatusCode.OK });
3539
+ return {
3540
+ content: `Music generated (${providerName}, ${musicResult.size} bytes, ${musicResult.format}).`,
3541
+ finishReason: "stop",
3542
+ provider: providerName,
3543
+ model: musicResult.metadata?.model ?? providerName,
3544
+ usage: undefined,
3545
+ responseTime: Date.now() - startTime,
3546
+ music: musicResult,
3547
+ };
3548
+ }
3549
+ /**
3550
+ * Dispatch an avatar (lip-sync) request to the registered avatar handler
3551
+ * for the provider named in `options.output.avatar.provider`.
3552
+ */
3553
+ async generateWithAvatar(options, generateSpan) {
3554
+ const startTime = Date.now();
3555
+ const avatarOptions = options.output?.avatar;
3556
+ if (!avatarOptions) {
3557
+ throw new Error('output.mode="avatar" requires output.avatar with at least { provider, image, audio|text }.');
3558
+ }
3559
+ const providerName = avatarOptions.provider;
3560
+ if (!providerName) {
3561
+ throw new Error('output.avatar.provider is required (e.g. "d-id", "heygen", "replicate").');
3562
+ }
3563
+ const { AvatarProcessor } = await import("./utils/avatarProcessor.js");
3564
+ const avatarResult = await AvatarProcessor.generate(providerName, avatarOptions);
3565
+ generateSpan.setAttribute("neurolink.avatar.provider", providerName);
3566
+ generateSpan.setAttribute("neurolink.avatar.bytes", avatarResult.size);
3567
+ generateSpan.setStatus({ code: SpanStatusCode.OK });
3568
+ return {
3569
+ content: `Avatar generated (${providerName}, ${avatarResult.size} bytes, ${avatarResult.format}).`,
3570
+ finishReason: "stop",
3571
+ provider: providerName,
3572
+ model: avatarResult.metadata?.model ?? providerName,
3573
+ usage: undefined,
3574
+ responseTime: Date.now() - startTime,
3575
+ avatar: avatarResult,
3576
+ };
3577
+ }
3380
3578
  /**
3381
3579
  * Generate with workflow engine integration
3382
3580
  * Returns both original and processed responses for AB testing
@@ -3386,7 +3584,7 @@ Current user's request: ${currentInput}`;
3386
3584
  logger.debug("[NeuroLink] Executing workflow generation", {
3387
3585
  workflowId: options.workflow,
3388
3586
  hasInlineConfig: !!options.workflowConfig,
3389
- prompt: options.input.text.substring(0, 100),
3587
+ prompt: (options.input?.text ?? "").substring(0, 100),
3390
3588
  startTime: workflowStartTime,
3391
3589
  });
3392
3590
  // Determine workflow configuration
@@ -3407,7 +3605,7 @@ Current user's request: ${currentInput}`;
3407
3605
  }
3408
3606
  // Execute workflow
3409
3607
  const workflowResult = await runWorkflow(workflowConfig, {
3410
- prompt: options.input.text,
3608
+ prompt: options.input?.text ?? "",
3411
3609
  conversationHistory: options.conversationMessages
3412
3610
  ?.filter((m) => m.role === "user" || m.role === "assistant")
3413
3611
  .map((m) => ({
@@ -3486,7 +3684,7 @@ Current user's request: ${currentInput}`;
3486
3684
  logger.debug("[NeuroLink] Executing workflow streaming (progressive)", {
3487
3685
  workflowId: options.workflow,
3488
3686
  hasInlineConfig: !!options.workflowConfig,
3489
- prompt: options.input.text.substring(0, 100),
3687
+ prompt: (options.input?.text ?? "").substring(0, 100),
3490
3688
  });
3491
3689
  // Determine workflow configuration
3492
3690
  let workflowConfig;
@@ -3506,7 +3704,7 @@ Current user's request: ${currentInput}`;
3506
3704
  const { runWorkflowWithStreaming } = await import("./workflow/core/workflowRunner.js");
3507
3705
  // Execute workflow with progressive streaming
3508
3706
  const workflowStream = runWorkflowWithStreaming(workflowConfig, {
3509
- prompt: options.input.text,
3707
+ prompt: options.input?.text ?? "",
3510
3708
  conversationHistory: options.conversationMessages
3511
3709
  ?.filter((m) => m.role === "user" || m.role === "assistant")
3512
3710
  .map((m) => ({
@@ -4561,10 +4759,22 @@ Current user's request: ${currentInput}`;
4561
4759
  availableTools: transformToolsForMCP(transformToolsToExpectedFormat(availableTools)),
4562
4760
  audio: result.audio,
4563
4761
  video: result.video,
4762
+ avatar: result.avatar,
4763
+ music: result.music,
4564
4764
  ppt: result.ppt,
4565
4765
  imageOutput: result.imageOutput,
4566
4766
  analytics: result.analytics,
4567
4767
  evaluation: result.evaluation,
4768
+ // Forward reasoning from provider so callers asking for `result.reasoning`
4769
+ // (DeepSeek `reasoning_content`, Anthropic thinking, Gemini thought parts,
4770
+ // OpenAI o1) actually receive it.
4771
+ reasoning: result.reasoning,
4772
+ reasoningTokens: result.reasoningTokens,
4773
+ // Propagate the native-emission flag so finalizeGenerateRequestResult
4774
+ // skips the public top-level `generation:end` emission when the
4775
+ // provider already emitted it itself (Vertex / Google AI Studio).
4776
+ _generationEndEmitted: result
4777
+ ._generationEndEmitted,
4568
4778
  };
4569
4779
  }
4570
4780
  /**
@@ -4836,9 +5046,19 @@ Current user's request: ${currentInput}`;
4836
5046
  evaluation: result.evaluation,
4837
5047
  audio: result.audio,
4838
5048
  video: result.video,
5049
+ avatar: result.avatar,
5050
+ music: result.music,
4839
5051
  ppt: result.ppt,
4840
5052
  // CRITICAL FIX: Include imageOutput for image generation models
4841
5053
  imageOutput: result.imageOutput,
5054
+ // Forward reasoning so callers asking for `result.reasoning`
5055
+ // (DeepSeek `reasoning_content`, Anthropic thinking, Gemini
5056
+ // thought parts, OpenAI o1) actually receive it.
5057
+ reasoning: result.reasoning,
5058
+ reasoningTokens: result.reasoningTokens,
5059
+ // Propagate native-emission flag — see attemptMCPGeneration comment.
5060
+ _generationEndEmitted: result
5061
+ ._generationEndEmitted,
4842
5062
  };
4843
5063
  }
4844
5064
  catch (error) {
@@ -5061,23 +5281,28 @@ Current user's request: ${currentInput}`;
5061
5281
  : [],
5062
5282
  optionKeys: Object.keys(options),
5063
5283
  });
5064
- const startTime = Date.now();
5284
+ // Defensive shallow clone of top-level options + the nested mutable
5285
+ // branches that downstream stages (prepareStreamOptions's memory
5286
+ // retrieval at `options.input.text`, applyStreamOrchestration's merge,
5287
+ // RAG/MCP tool injection, etc.) mutate. Cloning at the entry point
5288
+ // means callers can reuse a single options object across stream()
5289
+ // calls without accumulating mutations across them — the earlier
5290
+ // shallow rebind at the orchestration site only covered the
5291
+ // top-level keys and left `options.input` shared with the caller.
5292
+ options = cloneOptionsForCallIsolation(options);
5293
+ const startedAt = Date.now();
5065
5294
  try {
5066
5295
  return await this.streamWithIterationFallback(options);
5067
5296
  }
5068
5297
  catch (error) {
5069
- // Fire `onError` for early-resolution failures (invalid provider,
5070
- // missing credentials, etc.) that surface before the per-chunk
5071
- // wrapper installed by GoogleVertex / LifecycleMiddleware can run.
5072
- // `fireOnErrorOnce` dedupes against the middleware path so the
5073
- // consumer callback fires at most once per logical failure.
5074
- const onError = options.onError;
5075
- const err = error instanceof Error ? error : new Error(String(error));
5076
- fireOnErrorOnce(onError, error, {
5077
- error: err,
5078
- duration: Date.now() - startTime,
5079
- recoverable: false,
5080
- });
5298
+ // Mirror generate(): fire consumer onError for failures that
5299
+ // happened before the wrapped language-model middleware could
5300
+ // observe them (e.g. unknown provider, validation, factory
5301
+ // exceptions). The shared Symbol marker (lifecycleCallbacks.ts)
5302
+ // prevents double-fire when the AI-SDK lifecycle middleware
5303
+ // already handled the error. Awaited so async handlers fully
5304
+ // settle before stream() rethrows.
5305
+ await this.fireConsumerOnErrorIfNotFired(options, error, startedAt);
5081
5306
  throw error;
5082
5307
  }
5083
5308
  }
@@ -5673,14 +5898,36 @@ Current user's request: ${currentInput}`;
5673
5898
  streamResult.usage = streamUsage;
5674
5899
  }
5675
5900
  if (!streamResult.analytics) {
5901
+ // CRITICAL: do NOT `await` a Promise-typed analytics here. Bedrock
5902
+ // resolves its analytics from the stream's `finally` block — it
5903
+ // only completes once the consumer has fully iterated the stream.
5904
+ // Awaiting it before returning the StreamResult deadlocks: caller
5905
+ // can't iterate (no stream yet), analytics can't resolve (no
5906
+ // iteration). Pass the Promise through to the consumer instead;
5907
+ // they (or the cost listener below) can await it after consumption.
5908
+ //
5909
+ // The outer StreamResult type (StreamResult.analytics in
5910
+ // src/lib/types/stream.ts) explicitly allows `AnalyticsData |
5911
+ // Promise<AnalyticsData>` — the cast widens the local
5912
+ // `processStreamResult` return type which only declares the
5913
+ // resolved shape.
5676
5914
  streamResult.analytics =
5677
- streamAnalytics instanceof Promise
5678
- ? await streamAnalytics
5679
- : streamAnalytics;
5680
- }
5681
- if (streamResult.analytics?.cost && streamResult.analytics.cost > 0) {
5682
- this._sessionCostUsd += streamResult.analytics.cost;
5683
- }
5915
+ streamAnalytics;
5916
+ }
5917
+ // The cost listener wants a resolved cost, but if the provider gave
5918
+ // us a Promise (Bedrock) we can't tax the stream's return path on it.
5919
+ // Defer cost accumulation: resolve in the background once the analytics
5920
+ // Promise settles. Synchronous analytics (most providers) still fire
5921
+ // immediately because Promise.resolve(non-promise) is sync-ish.
5922
+ Promise.resolve(streamResult.analytics)
5923
+ .then((analytics) => {
5924
+ if (analytics?.cost && analytics.cost > 0) {
5925
+ this._sessionCostUsd += analytics.cost;
5926
+ }
5927
+ })
5928
+ .catch(() => {
5929
+ /* analytics rejection is non-fatal — cost stays unupdated */
5930
+ });
5684
5931
  this.emitStreamEndEvents(streamResult);
5685
5932
  return this.createStreamResponse(streamResult, processedStream, {
5686
5933
  providerName,
@@ -5775,7 +6022,7 @@ Current user's request: ${currentInput}`;
5775
6022
  if (this.shouldReadMemory(options.memory, options.context?.userId) &&
5776
6023
  options.context?.userId) {
5777
6024
  try {
5778
- options.input.text = await this.retrieveMemory(options.input.text, options.context.userId, options.memory?.additionalUsers);
6025
+ options.input.text = await this.retrieveMemory(options.input.text ?? "", options.context.userId, options.memory?.additionalUsers);
5779
6026
  logger.debug("Memory retrieval successful");
5780
6027
  }
5781
6028
  catch (error) {
@@ -5799,7 +6046,6 @@ Current user's request: ${currentInput}`;
5799
6046
  // obvious to future readers, and the lint suppression is scoped
5800
6047
  // narrowly to the one statement that actually rebinds the param.
5801
6048
  const mergedOptions = { ...options, ...orchestratedOptions };
5802
- // eslint-disable-next-line no-param-reassign -- see NEW2/Issue 6 above
5803
6049
  options = mergedOptions;
5804
6050
  // Re-resolve model alias in case orchestration returned an alias
5805
6051
  if (orchestratedOptions.model) {
@@ -7,6 +7,16 @@ export declare class AmazonBedrockProvider extends BaseProvider {
7
7
  private bedrockClient;
8
8
  private conversationHistory;
9
9
  private region;
10
+ /**
11
+ * Parse the region segment from a Bedrock ARN.
12
+ * Returns null when the input is not an ARN.
13
+ *
14
+ * Supports all AWS partitions:
15
+ * - `arn:aws:bedrock:…` (commercial)
16
+ * - `arn:aws-cn:bedrock:…` (China)
17
+ * - `arn:aws-us-gov:bedrock:…` (GovCloud)
18
+ */
19
+ private static extractRegionFromArn;
10
20
  constructor(modelName?: string, neurolink?: NeuroLink, region?: string, credentials?: {
11
21
  accessKeyId?: string;
12
22
  secretAccessKey?: string;