@juspay/neurolink 9.64.0 → 9.65.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 (324) hide show
  1. package/CHANGELOG.md +12 -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/agent/directTools.js +11 -3
  17. package/dist/autoresearch/runner.js +8 -2
  18. package/dist/avatar/index.d.ts +13 -0
  19. package/dist/avatar/index.js +13 -0
  20. package/dist/avatar/providers/DIDAvatar.d.ts +49 -0
  21. package/dist/avatar/providers/DIDAvatar.js +501 -0
  22. package/dist/avatar/providers/HeyGenAvatar.d.ts +30 -0
  23. package/dist/avatar/providers/HeyGenAvatar.js +337 -0
  24. package/dist/avatar/providers/ReplicateAvatar.d.ts +36 -0
  25. package/dist/avatar/providers/ReplicateAvatar.js +267 -0
  26. package/dist/browser/neurolink.min.js +624 -601
  27. package/dist/cli/commands/mcp.js +29 -0
  28. package/dist/cli/commands/proxy.js +24 -5
  29. package/dist/cli/factories/commandFactory.d.ts +11 -1
  30. package/dist/cli/factories/commandFactory.js +291 -38
  31. package/dist/constants/contextWindows.js +101 -0
  32. package/dist/constants/enums.d.ts +273 -2
  33. package/dist/constants/enums.js +290 -1
  34. package/dist/constants/videoErrors.d.ts +4 -0
  35. package/dist/constants/videoErrors.js +4 -0
  36. package/dist/core/baseProvider.d.ts +22 -2
  37. package/dist/core/baseProvider.js +217 -11
  38. package/dist/core/constants.d.ts +12 -0
  39. package/dist/core/constants.js +72 -1
  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/agent/directTools.js +11 -3
  61. package/dist/lib/autoresearch/runner.js +8 -2
  62. package/dist/lib/avatar/index.d.ts +13 -0
  63. package/dist/lib/avatar/index.js +14 -0
  64. package/dist/lib/avatar/providers/DIDAvatar.d.ts +49 -0
  65. package/dist/lib/avatar/providers/DIDAvatar.js +502 -0
  66. package/dist/lib/avatar/providers/HeyGenAvatar.d.ts +30 -0
  67. package/dist/lib/avatar/providers/HeyGenAvatar.js +338 -0
  68. package/dist/lib/avatar/providers/ReplicateAvatar.d.ts +36 -0
  69. package/dist/lib/avatar/providers/ReplicateAvatar.js +268 -0
  70. package/dist/lib/constants/contextWindows.js +101 -0
  71. package/dist/lib/constants/enums.d.ts +273 -2
  72. package/dist/lib/constants/enums.js +290 -1
  73. package/dist/lib/constants/videoErrors.d.ts +4 -0
  74. package/dist/lib/constants/videoErrors.js +4 -0
  75. package/dist/lib/core/baseProvider.d.ts +22 -2
  76. package/dist/lib/core/baseProvider.js +217 -11
  77. package/dist/lib/core/constants.d.ts +12 -0
  78. package/dist/lib/core/constants.js +72 -1
  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 +126 -10
  112. package/dist/lib/providers/googleNativeGemini3.d.ts +26 -6
  113. package/dist/lib/providers/googleNativeGemini3.js +276 -29
  114. package/dist/lib/providers/googleVertex.js +639 -181
  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/conversation.d.ts +16 -0
  157. package/dist/lib/types/generate.d.ts +62 -5
  158. package/dist/lib/types/index.d.ts +5 -0
  159. package/dist/lib/types/index.js +7 -0
  160. package/dist/lib/types/middleware.d.ts +27 -0
  161. package/dist/lib/types/multimodal.d.ts +35 -2
  162. package/dist/lib/types/music.d.ts +165 -0
  163. package/dist/lib/types/music.js +21 -0
  164. package/dist/lib/types/providers.d.ts +144 -1
  165. package/dist/lib/types/replicate.d.ts +67 -0
  166. package/dist/lib/types/replicate.js +10 -0
  167. package/dist/lib/types/safeFetch.d.ts +15 -0
  168. package/dist/lib/types/safeFetch.js +7 -0
  169. package/dist/lib/types/stream.d.ts +2 -1
  170. package/dist/lib/types/tools.d.ts +13 -0
  171. package/dist/lib/types/video.d.ts +89 -0
  172. package/dist/lib/types/video.js +15 -0
  173. package/dist/lib/utils/avatarProcessor.d.ts +68 -0
  174. package/dist/lib/utils/avatarProcessor.js +172 -0
  175. package/dist/lib/utils/cloneOptions.d.ts +36 -0
  176. package/dist/lib/utils/cloneOptions.js +62 -0
  177. package/dist/lib/utils/lifecycleCallbacks.d.ts +51 -8
  178. package/dist/lib/utils/lifecycleCallbacks.js +82 -26
  179. package/dist/lib/utils/lifecycleTimeout.d.ts +25 -0
  180. package/dist/lib/utils/lifecycleTimeout.js +39 -0
  181. package/dist/lib/utils/logSanitize.d.ts +49 -0
  182. package/dist/lib/utils/logSanitize.js +170 -0
  183. package/dist/lib/utils/loggingFetch.d.ts +29 -0
  184. package/dist/lib/utils/loggingFetch.js +60 -0
  185. package/dist/lib/utils/messageBuilder.js +43 -25
  186. package/dist/lib/utils/modelChoices.js +236 -3
  187. package/dist/lib/utils/musicProcessor.d.ts +67 -0
  188. package/dist/lib/utils/musicProcessor.js +189 -0
  189. package/dist/lib/utils/optionsConversion.js +3 -2
  190. package/dist/lib/utils/parameterValidation.js +14 -4
  191. package/dist/lib/utils/pricing.js +193 -0
  192. package/dist/lib/utils/providerConfig.d.ts +55 -0
  193. package/dist/lib/utils/providerConfig.js +224 -0
  194. package/dist/lib/utils/safeFetch.d.ts +26 -0
  195. package/dist/lib/utils/safeFetch.js +83 -0
  196. package/dist/lib/utils/sizeGuard.d.ts +34 -0
  197. package/dist/lib/utils/sizeGuard.js +45 -0
  198. package/dist/lib/utils/ssrfGuard.d.ts +52 -0
  199. package/dist/lib/utils/ssrfGuard.js +411 -0
  200. package/dist/lib/utils/videoProcessor.d.ts +60 -0
  201. package/dist/lib/utils/videoProcessor.js +201 -0
  202. package/dist/lib/voice/providers/FishAudioTTS.d.ts +27 -0
  203. package/dist/lib/voice/providers/FishAudioTTS.js +183 -0
  204. package/dist/lib/workflow/core/ensembleExecutor.js +26 -9
  205. package/dist/middleware/builtin/lifecycle.js +39 -9
  206. package/dist/music/index.d.ts +13 -0
  207. package/dist/music/index.js +13 -0
  208. package/dist/music/providers/BeatovenMusic.d.ts +31 -0
  209. package/dist/music/providers/BeatovenMusic.js +333 -0
  210. package/dist/music/providers/ElevenLabsMusic.d.ts +30 -0
  211. package/dist/music/providers/ElevenLabsMusic.js +168 -0
  212. package/dist/music/providers/LyriaMusic.d.ts +29 -0
  213. package/dist/music/providers/LyriaMusic.js +172 -0
  214. package/dist/music/providers/ReplicateMusic.d.ts +31 -0
  215. package/dist/music/providers/ReplicateMusic.js +261 -0
  216. package/dist/neurolink.d.ts +30 -0
  217. package/dist/neurolink.js +323 -77
  218. package/dist/providers/amazonBedrock.d.ts +10 -0
  219. package/dist/providers/amazonBedrock.js +94 -39
  220. package/dist/providers/anthropic.js +55 -7
  221. package/dist/providers/anthropicBaseProvider.js +1 -1
  222. package/dist/providers/azureOpenai.js +66 -17
  223. package/dist/providers/cloudflare.d.ts +35 -0
  224. package/dist/providers/cloudflare.js +173 -0
  225. package/dist/providers/cohere.d.ts +52 -0
  226. package/dist/providers/cohere.js +252 -0
  227. package/dist/providers/deepseek.js +72 -17
  228. package/dist/providers/fireworks.d.ts +33 -0
  229. package/dist/providers/fireworks.js +163 -0
  230. package/dist/providers/googleAiStudio.js +126 -10
  231. package/dist/providers/googleNativeGemini3.d.ts +26 -6
  232. package/dist/providers/googleNativeGemini3.js +276 -29
  233. package/dist/providers/googleVertex.js +639 -181
  234. package/dist/providers/groq.d.ts +33 -0
  235. package/dist/providers/groq.js +180 -0
  236. package/dist/providers/huggingFace.js +9 -8
  237. package/dist/providers/ideogram.d.ts +34 -0
  238. package/dist/providers/ideogram.js +183 -0
  239. package/dist/providers/index.d.ts +13 -0
  240. package/dist/providers/index.js +13 -0
  241. package/dist/providers/jina.d.ts +59 -0
  242. package/dist/providers/jina.js +217 -0
  243. package/dist/providers/llamaCpp.js +14 -46
  244. package/dist/providers/lmStudio.js +14 -47
  245. package/dist/providers/mistral.js +7 -7
  246. package/dist/providers/nvidiaNim.js +160 -19
  247. package/dist/providers/ollama.js +7 -7
  248. package/dist/providers/openAI.d.ts +22 -1
  249. package/dist/providers/openAI.js +181 -0
  250. package/dist/providers/openRouter.js +35 -23
  251. package/dist/providers/openaiCompatible.js +9 -8
  252. package/dist/providers/perplexity.d.ts +33 -0
  253. package/dist/providers/perplexity.js +178 -0
  254. package/dist/providers/recraft.d.ts +34 -0
  255. package/dist/providers/recraft.js +196 -0
  256. package/dist/providers/replicate.d.ts +75 -0
  257. package/dist/providers/replicate.js +402 -0
  258. package/dist/providers/stability.d.ts +37 -0
  259. package/dist/providers/stability.js +190 -0
  260. package/dist/providers/togetherAi.d.ts +33 -0
  261. package/dist/providers/togetherAi.js +175 -0
  262. package/dist/providers/voyage.d.ts +47 -0
  263. package/dist/providers/voyage.js +176 -0
  264. package/dist/providers/xai.d.ts +33 -0
  265. package/dist/providers/xai.js +171 -0
  266. package/dist/telemetry/index.d.ts +1 -1
  267. package/dist/telemetry/index.js +1 -1
  268. package/dist/telemetry/tracers.d.ts +19 -0
  269. package/dist/telemetry/tracers.js +19 -0
  270. package/dist/telemetry/withSpan.d.ts +35 -0
  271. package/dist/telemetry/withSpan.js +103 -0
  272. package/dist/types/avatar.d.ts +143 -0
  273. package/dist/types/avatar.js +19 -0
  274. package/dist/types/cli.d.ts +6 -0
  275. package/dist/types/conversation.d.ts +16 -0
  276. package/dist/types/generate.d.ts +62 -5
  277. package/dist/types/index.d.ts +5 -0
  278. package/dist/types/index.js +7 -0
  279. package/dist/types/middleware.d.ts +27 -0
  280. package/dist/types/multimodal.d.ts +35 -2
  281. package/dist/types/music.d.ts +165 -0
  282. package/dist/types/music.js +20 -0
  283. package/dist/types/providers.d.ts +144 -1
  284. package/dist/types/replicate.d.ts +67 -0
  285. package/dist/types/replicate.js +9 -0
  286. package/dist/types/safeFetch.d.ts +15 -0
  287. package/dist/types/safeFetch.js +6 -0
  288. package/dist/types/stream.d.ts +2 -1
  289. package/dist/types/tools.d.ts +13 -0
  290. package/dist/types/video.d.ts +89 -0
  291. package/dist/types/video.js +14 -0
  292. package/dist/utils/avatarProcessor.d.ts +68 -0
  293. package/dist/utils/avatarProcessor.js +171 -0
  294. package/dist/utils/cloneOptions.d.ts +36 -0
  295. package/dist/utils/cloneOptions.js +61 -0
  296. package/dist/utils/lifecycleCallbacks.d.ts +51 -8
  297. package/dist/utils/lifecycleCallbacks.js +82 -26
  298. package/dist/utils/lifecycleTimeout.d.ts +25 -0
  299. package/dist/utils/lifecycleTimeout.js +38 -0
  300. package/dist/utils/logSanitize.d.ts +49 -0
  301. package/dist/utils/logSanitize.js +169 -0
  302. package/dist/utils/loggingFetch.d.ts +29 -0
  303. package/dist/utils/loggingFetch.js +59 -0
  304. package/dist/utils/messageBuilder.js +43 -25
  305. package/dist/utils/modelChoices.js +236 -3
  306. package/dist/utils/musicProcessor.d.ts +67 -0
  307. package/dist/utils/musicProcessor.js +188 -0
  308. package/dist/utils/optionsConversion.js +3 -2
  309. package/dist/utils/parameterValidation.js +14 -4
  310. package/dist/utils/pricing.js +193 -0
  311. package/dist/utils/providerConfig.d.ts +55 -0
  312. package/dist/utils/providerConfig.js +224 -0
  313. package/dist/utils/safeFetch.d.ts +26 -0
  314. package/dist/utils/safeFetch.js +82 -0
  315. package/dist/utils/sizeGuard.d.ts +34 -0
  316. package/dist/utils/sizeGuard.js +44 -0
  317. package/dist/utils/ssrfGuard.d.ts +52 -0
  318. package/dist/utils/ssrfGuard.js +410 -0
  319. package/dist/utils/videoProcessor.d.ts +60 -0
  320. package/dist/utils/videoProcessor.js +200 -0
  321. package/dist/voice/providers/FishAudioTTS.d.ts +27 -0
  322. package/dist/voice/providers/FishAudioTTS.js +182 -0
  323. package/dist/workflow/core/ensembleExecutor.js +26 -9
  324. package/package.json +32 -5
@@ -17,6 +17,61 @@ import { logger } from "../utils/logger.js";
17
17
  import { convertZodToJsonSchema, ensureNestedSchemaTypes, inlineJsonSchema, isZodSchema, normalizeJsonSchemaObject, } from "../utils/schemaConversion.js";
18
18
  import { createNativeThinkingConfig } from "../utils/thinkingConfig.js";
19
19
  // ── Functions ──
20
+ /**
21
+ * Google's `function_declarations[].name` validator regex.
22
+ *
23
+ * Empirically (and per the Vertex/AI Studio API error message), the server
24
+ * enforces `[A-Za-z_][A-Za-z0-9_.:-]{0,127}`. Tool names that don't match
25
+ * fail with HTTP 400 "Invalid function name", which surfaces as a misleading
26
+ * tool-calling failure for the whole request.
27
+ *
28
+ * MCP-imported or user-registered tools may legally contain characters
29
+ * outside this set (e.g. `/`, spaces, unicode), so we sanitize defensively
30
+ * before sending to Google. The sanitized name is also used as the
31
+ * `executeMap` key so the round-trip from Google's function-call response
32
+ * back to our executor still works.
33
+ */
34
+ const GOOGLE_FN_NAME_REGEX = /^[A-Za-z_][A-Za-z0-9_.:-]{0,127}$/;
35
+ const GOOGLE_FN_NAME_MAX_LENGTH = 128;
36
+ export function sanitizeForGoogleFunctionName(name) {
37
+ if (GOOGLE_FN_NAME_REGEX.test(name)) {
38
+ return name;
39
+ }
40
+ let sanitized = name.replace(/[^A-Za-z0-9_.:-]/g, "_");
41
+ if (!/^[A-Za-z_]/.test(sanitized)) {
42
+ sanitized = `_${sanitized}`;
43
+ }
44
+ if (sanitized.length > GOOGLE_FN_NAME_MAX_LENGTH) {
45
+ sanitized = sanitized.slice(0, GOOGLE_FN_NAME_MAX_LENGTH);
46
+ }
47
+ return sanitized;
48
+ }
49
+ /**
50
+ * Resolve a sanitized Gemini tool name to one that is both unique within
51
+ * the current request and at most 128 characters. When the candidate
52
+ * collides with an already-used name we append `_2`, `_3`, … — but
53
+ * reserve room for the suffix by truncating the base first so the
54
+ * resolved name never exceeds Google's `function_declarations[].name`
55
+ * limit.
56
+ *
57
+ * @param base The already-sanitized candidate name.
58
+ * @param isTaken Predicate that returns true if `name` is already used.
59
+ */
60
+ export function resolveUniqueGoogleFunctionName(base, isTaken) {
61
+ if (!isTaken(base)) {
62
+ return base;
63
+ }
64
+ let suffix = 2;
65
+ while (true) {
66
+ const suffixStr = `_${suffix}`;
67
+ const trimmedBase = base.slice(0, GOOGLE_FN_NAME_MAX_LENGTH - suffixStr.length);
68
+ const candidate = `${trimmedBase}${suffixStr}`;
69
+ if (!isTaken(candidate)) {
70
+ return candidate;
71
+ }
72
+ suffix++;
73
+ }
74
+ }
20
75
  /**
21
76
  * Sanitize a JSON Schema for Gemini's proto-based API.
22
77
  *
@@ -102,6 +157,40 @@ export function sanitizeSchemaForGemini(schema) {
102
157
  result[branch] = sanitizeSchemaForGemini(result[branch]);
103
158
  }
104
159
  }
160
+ // JSON Schema Draft-4 `exclusiveMinimum: true` / `exclusiveMaximum: true`
161
+ // (boolean form) is rejected by Gemini's OpenAPI 3.0 validator, which
162
+ // expects a numeric bound. zod-to-json-schema's openApi3 target still
163
+ // emits the Draft-4 form for `z.number().positive()` etc. Translate to
164
+ // the numeric form when paired with `minimum`/`maximum`, or drop.
165
+ if (typeof result.exclusiveMinimum === "boolean") {
166
+ if (result.exclusiveMinimum === true &&
167
+ typeof result.minimum === "number") {
168
+ result.exclusiveMinimum = result.minimum;
169
+ delete result.minimum;
170
+ }
171
+ else {
172
+ delete result.exclusiveMinimum;
173
+ }
174
+ }
175
+ if (typeof result.exclusiveMaximum === "boolean") {
176
+ if (result.exclusiveMaximum === true &&
177
+ typeof result.maximum === "number") {
178
+ result.exclusiveMaximum = result.maximum;
179
+ delete result.maximum;
180
+ }
181
+ else {
182
+ delete result.exclusiveMaximum;
183
+ }
184
+ }
185
+ // Clamp `maximum`/`minimum` past int32 — Gemini's protobuf serializer
186
+ // treats `type: "integer"` as int32 and rejects bounds beyond ~2.1e9.
187
+ const INT32_MAX = 2147483647;
188
+ if (typeof result.maximum === "number" && result.maximum > INT32_MAX) {
189
+ delete result.maximum;
190
+ }
191
+ if (typeof result.minimum === "number" && result.minimum < -INT32_MAX) {
192
+ delete result.minimum;
193
+ }
105
194
  return result;
106
195
  }
107
196
  /**
@@ -117,8 +206,27 @@ export function sanitizeSchemaForGemini(schema) {
117
206
  export function sanitizeToolsForGemini(tools) {
118
207
  const sanitized = {};
119
208
  const dropped = [];
209
+ const renamed = [];
210
+ const originalNameMap = new Map();
120
211
  for (const [name, tool] of Object.entries(tools)) {
121
212
  try {
213
+ // Sanitize the tool name to fit Google's function_declarations regex.
214
+ // Without this, MCP-imported or user-registered tools whose names contain
215
+ // characters outside [A-Za-z_][A-Za-z0-9_.:-]{0,127} cause the entire
216
+ // request to 400 with "Invalid function name", surfacing as a misleading
217
+ // tool-calling failure. Distinct originals that collapse onto the same
218
+ // sanitized name (e.g. "my/tool" and "my-tool" → "my_tool") are
219
+ // disambiguated with a numeric suffix that preserves Google's 128-char
220
+ // ceiling.
221
+ const candidate = sanitizeForGoogleFunctionName(name);
222
+ const safeName = resolveUniqueGoogleFunctionName(candidate, (n) => n in sanitized);
223
+ // Always record the mapping so downstream code can translate every
224
+ // safeName back to the original — including the no-rename identity
225
+ // mapping, which simplifies the lookup path.
226
+ originalNameMap.set(safeName, name);
227
+ if (safeName !== name) {
228
+ renamed.push({ from: name, to: safeName });
229
+ }
122
230
  // Access the legacy `parameters` field that may exist on older AI SDK tools.
123
231
  // AI SDK v6 uses `inputSchema`, but v3/v4 tools and third-party wrappers use `parameters`.
124
232
  const legacyTool = tool;
@@ -134,8 +242,8 @@ export function sanitizeToolsForGemini(tools) {
134
242
  // additionalProperties are removed. The resulting schema is Gemini-compatible
135
243
  // but loses some type constraints from the original Zod schema.
136
244
  const sanitizedSchema = sanitizeSchemaForGemini(inlined);
137
- sanitized[name] = createAISDKTool({
138
- description: tool.description || `Tool: ${name}`,
245
+ sanitized[safeName] = createAISDKTool({
246
+ description: tool.description || `Tool: ${safeName}`,
139
247
  inputSchema: aiJsonSchema(sanitizedSchema),
140
248
  execute: tool.execute,
141
249
  });
@@ -147,14 +255,14 @@ export function sanitizeToolsForGemini(tools) {
147
255
  const rawSchema = params
148
256
  .jsonSchema;
149
257
  const sanitizedSchema = sanitizeSchemaForGemini(inlineJsonSchema(rawSchema));
150
- sanitized[name] = createAISDKTool({
151
- description: tool.description || `Tool: ${name}`,
258
+ sanitized[safeName] = createAISDKTool({
259
+ description: tool.description || `Tool: ${safeName}`,
152
260
  inputSchema: aiJsonSchema(sanitizedSchema),
153
261
  execute: tool.execute,
154
262
  });
155
263
  }
156
264
  else {
157
- sanitized[name] = tool;
265
+ sanitized[safeName] = tool;
158
266
  }
159
267
  }
160
268
  catch (error) {
@@ -163,7 +271,12 @@ export function sanitizeToolsForGemini(tools) {
163
271
  dropped.push(name);
164
272
  }
165
273
  }
166
- return { tools: sanitized, dropped };
274
+ if (renamed.length > 0) {
275
+ logger.warn(`[Gemini] ${renamed.length} tool name(s) sanitized for Google's function-name regex: ${renamed
276
+ .map((r) => `"${r.from}" -> "${r.to}"`)
277
+ .join(", ")}`);
278
+ }
279
+ return { tools: sanitized, dropped, originalNameMap };
167
280
  }
168
281
  export function normalizeToolsForJsonSchemaProvider(tools) {
169
282
  const normalizedTools = {};
@@ -212,11 +325,29 @@ export function buildNativeToolDeclarations(tools) {
212
325
  const functionDeclarations = [];
213
326
  const executeMap = new Map();
214
327
  const skippedTools = [];
328
+ const renamedTools = [];
329
+ // Disambiguate distinct originals that collapse onto the same sanitized
330
+ // name (e.g. "my/tool" and "my-tool" both → "my_tool") via
331
+ // resolveUniqueGoogleFunctionName, which appends `_N` while keeping the
332
+ // final string within Google's 128-char limit. Track all assigned names
333
+ // regardless of whether the tool has an `execute` function (tools without
334
+ // execute are still pushed to functionDeclarations). The originalNameMap
335
+ // lets the calling stream loop translate Google-returned function-call
336
+ // names back to the consumer-facing identifier so the sanitization is
337
+ // transport-only.
338
+ const usedNames = new Set();
339
+ const originalNameMap = new Map();
215
340
  for (const [name, tool] of Object.entries(tools)) {
216
341
  try {
342
+ const candidate = sanitizeForGoogleFunctionName(name);
343
+ const safeName = resolveUniqueGoogleFunctionName(candidate, (n) => usedNames.has(n));
344
+ originalNameMap.set(safeName, name);
345
+ if (safeName !== name) {
346
+ renamedTools.push({ from: name, to: safeName });
347
+ }
217
348
  const decl = {
218
- name,
219
- description: tool.description || `Tool: ${name}`,
349
+ name: safeName,
350
+ description: tool.description || `Tool: ${safeName}`,
220
351
  };
221
352
  // Access legacy `parameters` (AI SDK v3/v4) or current `inputSchema` (v6)
222
353
  const legacyTool = tool;
@@ -241,8 +372,9 @@ export function buildNativeToolDeclarations(tools) {
241
372
  decl.parametersJsonSchema = sanitizeSchemaForGemini(inlineJsonSchema(rawSchema));
242
373
  }
243
374
  functionDeclarations.push(decl);
375
+ usedNames.add(safeName);
244
376
  if (tool.execute) {
245
- executeMap.set(name, tool.execute);
377
+ executeMap.set(decl.name, tool.execute);
246
378
  }
247
379
  }
248
380
  catch (err) {
@@ -253,7 +385,16 @@ export function buildNativeToolDeclarations(tools) {
253
385
  if (skippedTools.length > 0) {
254
386
  logger.warn(`[buildNativeToolDeclarations] ${skippedTools.length} tool(s) skipped due to schema errors: ${skippedTools.join(", ")}`);
255
387
  }
256
- return { toolsConfig: [{ functionDeclarations }], executeMap };
388
+ if (renamedTools.length > 0) {
389
+ logger.warn(`[buildNativeToolDeclarations] ${renamedTools.length} tool name(s) sanitized for Google's function-name regex: ${renamedTools
390
+ .map((r) => `"${r.from}" -> "${r.to}"`)
391
+ .join(", ")}`);
392
+ }
393
+ return {
394
+ toolsConfig: [{ functionDeclarations }],
395
+ executeMap,
396
+ originalNameMap,
397
+ };
257
398
  }
258
399
  /**
259
400
  * Build the native @google/genai config object shared by stream and generate.
@@ -493,27 +634,42 @@ export function extractTextFromParts(rawResponseParts) {
493
634
  * @param executeMap - Map of tool name to execute function
494
635
  * @param failedTools - Mutable map tracking per-tool failure counts
495
636
  * @param allToolCalls - Mutable array accumulating all tool call records
496
- * @param options - Optional settings for execution tracking and cancellation
637
+ * @param options - Optional settings for execution tracking and cancellation,
638
+ * plus an `originalNameMap` (Google-safe → consumer-supplied
639
+ * identifier) so the sanitization stays transport-only and
640
+ * consumers see the names they registered.
497
641
  * @returns Array of function responses for conversation history
498
642
  */
499
643
  export async function executeNativeToolCalls(logLabel, stepFunctionCalls, executeMap, failedTools, allToolCalls, options) {
500
644
  const functionResponses = [];
645
+ // Translate a Google-safe sanitized name back to the consumer-facing
646
+ // original name. Falls back to the safe name if the map is missing or
647
+ // doesn't contain the call (e.g. tool added mid-conversation).
648
+ const externalName = (safeName) => options?.originalNameMap?.get(safeName) ?? safeName;
649
+ // Note: tool:start / tool:end events are emitted by ToolsManager's
650
+ // `execute` wrapper (see src/lib/core/modules/ToolsManager.ts:355 and :790)
651
+ // around every tool's execute function. The native paths invoke that same
652
+ // wrapped execute via the executeMap, so emitting here would duplicate.
501
653
  for (const call of stepFunctionCalls) {
502
- allToolCalls.push({ toolName: call.name, args: call.args });
654
+ const exposedName = externalName(call.name);
655
+ allToolCalls.push({ toolName: exposedName, args: call.args });
503
656
  // Check if this tool has already exceeded retry limit
504
657
  const failedInfo = failedTools.get(call.name);
505
658
  if (failedInfo && failedInfo.count >= DEFAULT_TOOL_MAX_RETRIES) {
506
- logger.warn(`${logLabel} Tool "${call.name}" has exceeded retry limit (${DEFAULT_TOOL_MAX_RETRIES}), skipping execution`);
659
+ logger.warn(`${logLabel} Tool "${exposedName}" has exceeded retry limit (${DEFAULT_TOOL_MAX_RETRIES}), skipping execution`);
507
660
  const errorOutput = {
508
- error: `TOOL_PERMANENTLY_FAILED: The tool "${call.name}" has failed ${failedInfo.count} times and will not be retried. Last error: ${failedInfo.lastError}. Please proceed without using this tool or inform the user that this functionality is unavailable.`,
661
+ error: `TOOL_PERMANENTLY_FAILED: The tool "${exposedName}" has failed ${failedInfo.count} times and will not be retried. Last error: ${failedInfo.lastError}. Please proceed without using this tool or inform the user that this functionality is unavailable.`,
509
662
  status: "permanently_failed",
510
663
  do_not_retry: true,
511
664
  };
665
+ // Wire transport-side `name: call.name` (Google needs the sanitized
666
+ // form to match the function declaration) while exposing the
667
+ // consumer-facing name in toolExecutions metadata.
512
668
  functionResponses.push({
513
669
  functionResponse: { name: call.name, response: errorOutput },
514
670
  });
515
671
  options?.toolExecutions?.push({
516
- name: call.name,
672
+ name: exposedName,
517
673
  input: call.args,
518
674
  output: errorOutput,
519
675
  });
@@ -534,7 +690,7 @@ export async function executeNativeToolCalls(logLabel, stepFunctionCalls, execut
534
690
  functionResponse: { name: call.name, response: { result } },
535
691
  });
536
692
  options?.toolExecutions?.push({
537
- name: call.name,
693
+ name: exposedName,
538
694
  input: call.args,
539
695
  output: result,
540
696
  });
@@ -549,12 +705,12 @@ export async function executeNativeToolCalls(logLabel, stepFunctionCalls, execut
549
705
  currentFailInfo.count++;
550
706
  currentFailInfo.lastError = errorMessage;
551
707
  failedTools.set(call.name, currentFailInfo);
552
- logger.warn(`${logLabel} Tool "${call.name}" failed (attempt ${currentFailInfo.count}/${DEFAULT_TOOL_MAX_RETRIES}): ${errorMessage}`);
708
+ logger.warn(`${logLabel} Tool "${exposedName}" failed (attempt ${currentFailInfo.count}/${DEFAULT_TOOL_MAX_RETRIES}): ${errorMessage}`);
553
709
  // Determine if this is a permanent failure
554
710
  const isPermanentFailure = currentFailInfo.count >= DEFAULT_TOOL_MAX_RETRIES;
555
711
  const errorOutput = {
556
712
  error: isPermanentFailure
557
- ? `TOOL_PERMANENTLY_FAILED: The tool "${call.name}" has failed ${currentFailInfo.count} times with error: ${errorMessage}. This tool will not be retried. Please proceed without using this tool or inform the user that this functionality is unavailable.`
713
+ ? `TOOL_PERMANENTLY_FAILED: The tool "${exposedName}" has failed ${currentFailInfo.count} times with error: ${errorMessage}. This tool will not be retried. Please proceed without using this tool or inform the user that this functionality is unavailable.`
558
714
  : `TOOL_EXECUTION_ERROR: ${errorMessage}. Retry attempt ${currentFailInfo.count}/${DEFAULT_TOOL_MAX_RETRIES}.`,
559
715
  status: isPermanentFailure ? "permanently_failed" : "failed",
560
716
  do_not_retry: isPermanentFailure,
@@ -565,7 +721,7 @@ export async function executeNativeToolCalls(logLabel, stepFunctionCalls, execut
565
721
  functionResponse: { name: call.name, response: errorOutput },
566
722
  });
567
723
  options?.toolExecutions?.push({
568
- name: call.name,
724
+ name: exposedName,
569
725
  input: call.args,
570
726
  output: errorOutput,
571
727
  });
@@ -574,7 +730,7 @@ export async function executeNativeToolCalls(logLabel, stepFunctionCalls, execut
574
730
  else {
575
731
  // Tool not found is a permanent error
576
732
  const errorOutput = {
577
- error: `TOOL_NOT_FOUND: The tool "${call.name}" does not exist. Do not attempt to call this tool again.`,
733
+ error: `TOOL_NOT_FOUND: The tool "${exposedName}" does not exist. Do not attempt to call this tool again.`,
578
734
  status: "permanently_failed",
579
735
  do_not_retry: true,
580
736
  };
@@ -582,7 +738,7 @@ export async function executeNativeToolCalls(logLabel, stepFunctionCalls, execut
582
738
  functionResponse: { name: call.name, response: errorOutput },
583
739
  });
584
740
  options?.toolExecutions?.push({
585
- name: call.name,
741
+ name: exposedName,
586
742
  input: call.args,
587
743
  output: errorOutput,
588
744
  });
@@ -656,22 +812,113 @@ export function buildGeminiResponseSchema(schema) {
656
812
  * - The current user input should be appended AFTER calling this helper
657
813
  * so the prior turns appear first in chronological order.
658
814
  */
659
- export function prependConversationMessages(contents, conversationMessages) {
815
+ export function prependConversationMessages(contents,
816
+ // Accept either the full ChatMessage shape (when callers pass real Redis-
817
+ // backed history) or the reduced MinimalChatMessage shape (tests / synthetic
818
+ // callers). Only role, content, tool, args, and metadata.* are read here.
819
+ conversationMessages) {
660
820
  if (!conversationMessages || conversationMessages.length === 0) {
661
821
  return;
662
822
  }
823
+ // Walk prior turns building ordered segments. Tool_call / tool_result rows
824
+ // get grouped by (turnCounter, stepIndex) so parallel calls within a step
825
+ // stay together and don't bleed across turn boundaries. Regular user/
826
+ // assistant messages act as those boundaries.
827
+ //
828
+ // Without this reconstruction, a text-only mapper would strip tool rows
829
+ // from history — leaving the model unaware of any tools it called in
830
+ // prior turns. The grouped emit (model with functionCall parts → user
831
+ // with functionResponse parts) is what @google/genai's own
832
+ // automaticFunctionCalling produces, so the SDK validates it as a
833
+ // well-formed multi-turn conversation.
834
+ const stepMap = new Map();
835
+ const segments = [];
836
+ let turnCounter = 0;
837
+ const makeKey = (stepIndex) => `${turnCounter}:${stepIndex ?? "undefined"}`;
838
+ const getOrCreateStep = (stepIndex) => {
839
+ const key = makeKey(stepIndex);
840
+ const existing = stepMap.get(key);
841
+ if (existing) {
842
+ return existing;
843
+ }
844
+ const step = {
845
+ type: "tool_step",
846
+ callParts: [],
847
+ resultParts: [],
848
+ };
849
+ stepMap.set(key, step);
850
+ segments.push(step);
851
+ return step;
852
+ };
663
853
  for (const msg of conversationMessages) {
664
- if (msg.role !== "user" && msg.role !== "assistant") {
854
+ if (msg.role === "tool_call") {
855
+ const step = getOrCreateStep(msg.metadata?.stepIndex);
856
+ const fcPart = {
857
+ functionCall: {
858
+ name: msg.tool || "unknown",
859
+ args: msg.args || {},
860
+ },
861
+ };
862
+ if (msg.metadata?.thoughtSignature) {
863
+ fcPart.thoughtSignature = msg.metadata.thoughtSignature;
864
+ }
865
+ step.callParts.push(fcPart);
665
866
  continue;
666
867
  }
667
- const text = typeof msg.content === "string" ? msg.content : "";
668
- if (text.length === 0) {
868
+ if (msg.role === "tool_result") {
869
+ const step = getOrCreateStep(msg.metadata?.stepIndex);
870
+ let responsePayload;
871
+ try {
872
+ responsePayload =
873
+ msg.content !== undefined && msg.content !== null
874
+ ? { result: JSON.parse(msg.content) }
875
+ : { result: "success" };
876
+ }
877
+ catch {
878
+ responsePayload = { result: msg.content ?? "success" };
879
+ }
880
+ step.resultParts.push({
881
+ functionResponse: {
882
+ name: msg.tool || "unknown",
883
+ response: responsePayload,
884
+ },
885
+ });
886
+ continue;
887
+ }
888
+ // Regular (user / assistant) message — acts as a turn boundary.
889
+ const role = msg.role === "assistant" ? "model" : msg.role;
890
+ if (role !== "user" && role !== "model") {
669
891
  continue;
670
892
  }
671
- contents.push({
672
- role: msg.role === "assistant" ? "model" : "user",
673
- parts: [{ text }],
674
- });
893
+ if (!msg.content || msg.content.trim().length === 0) {
894
+ continue;
895
+ }
896
+ // Increment turn counter BEFORE pushing the segment so any tool_calls
897
+ // that follow this message get a fresh (turnCounter, stepIndex) namespace.
898
+ turnCounter++;
899
+ const textPart = { text: msg.content };
900
+ if (msg.metadata?.thoughtSignature) {
901
+ textPart.thoughtSignature = msg.metadata.thoughtSignature;
902
+ }
903
+ segments.push({ type: "regular", role, parts: [textPart] });
904
+ }
905
+ // Emit in order: each ToolStep → model turn (calls) + user turn (results)
906
+ // — same ordering @google/genai's automaticFunctionCalling produces.
907
+ for (const seg of segments) {
908
+ if (seg.type === "regular") {
909
+ contents.push({ role: seg.role, parts: seg.parts });
910
+ continue;
911
+ }
912
+ if (seg.callParts.length === 0) {
913
+ if (seg.resultParts.length > 0) {
914
+ logger.debug("[GoogleNativeGemini3] Dropping orphan tool_result segment with no matching tool_call rows", { resultCount: seg.resultParts.length });
915
+ }
916
+ continue;
917
+ }
918
+ contents.push({ role: "model", parts: seg.callParts });
919
+ if (seg.resultParts.length > 0) {
920
+ contents.push({ role: "user", parts: seg.resultParts });
921
+ }
675
922
  }
676
923
  }
677
924
  /**