@vybestack/llxprt-code-core 0.1.12 → 0.1.13

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 (329) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +256 -0
  3. package/dist/src/adapters/IStreamAdapter.d.ts +18 -0
  4. package/dist/src/adapters/IStreamAdapter.js +7 -0
  5. package/dist/src/adapters/IStreamAdapter.js.map +1 -0
  6. package/dist/src/code_assist/oauth2.d.ts +1 -1
  7. package/dist/src/code_assist/oauth2.js +51 -29
  8. package/dist/src/code_assist/oauth2.js.map +1 -1
  9. package/dist/src/code_assist/oauth2.test.js +36 -7
  10. package/dist/src/code_assist/oauth2.test.js.map +1 -1
  11. package/dist/src/code_assist/server.test.js +10 -2
  12. package/dist/src/code_assist/server.test.js.map +1 -1
  13. package/dist/src/config/config.d.ts +28 -5
  14. package/dist/src/config/config.js +29 -4
  15. package/dist/src/config/config.js.map +1 -1
  16. package/dist/src/config/config.test.js +2 -3
  17. package/dist/src/config/config.test.js.map +1 -1
  18. package/dist/src/core/client.d.ts +4 -2
  19. package/dist/src/core/client.js +68 -7
  20. package/dist/src/core/client.js.map +1 -1
  21. package/dist/src/core/client.test.js +8 -0
  22. package/dist/src/core/client.test.js.map +1 -1
  23. package/dist/src/core/contentGenerator.d.ts +3 -2
  24. package/dist/src/core/contentGenerator.js +6 -8
  25. package/dist/src/core/contentGenerator.js.map +1 -1
  26. package/dist/src/core/contentGenerator.test.js +12 -5
  27. package/dist/src/core/contentGenerator.test.js.map +1 -1
  28. package/dist/src/core/coreToolScheduler.test.js +4 -2
  29. package/dist/src/core/coreToolScheduler.test.js.map +1 -1
  30. package/dist/src/core/geminiChat.js +50 -3
  31. package/dist/src/core/geminiChat.js.map +1 -1
  32. package/dist/src/core/modelCheck.d.ts +1 -1
  33. package/dist/src/core/modelCheck.js +10 -3
  34. package/dist/src/core/modelCheck.js.map +1 -1
  35. package/dist/src/core/nonInteractiveToolExecutor.test.js +3 -0
  36. package/dist/src/core/nonInteractiveToolExecutor.test.js.map +1 -1
  37. package/dist/src/core/prompts.d.ts +1 -1
  38. package/dist/src/core/prompts.js +14 -2
  39. package/dist/src/core/prompts.js.map +1 -1
  40. package/dist/src/core/turn.js +6 -0
  41. package/dist/src/core/turn.js.map +1 -1
  42. package/dist/src/index.d.ts +29 -1
  43. package/dist/src/index.js +33 -0
  44. package/dist/src/index.js.map +1 -1
  45. package/dist/src/mcp/oauth-provider.d.ts +142 -0
  46. package/dist/src/mcp/oauth-provider.js +446 -0
  47. package/dist/src/mcp/oauth-provider.js.map +1 -0
  48. package/dist/src/mcp/oauth-provider.test.js +520 -0
  49. package/dist/src/mcp/oauth-provider.test.js.map +1 -0
  50. package/dist/src/mcp/oauth-token-storage.d.ts +81 -0
  51. package/dist/src/mcp/oauth-token-storage.js +149 -0
  52. package/dist/src/mcp/oauth-token-storage.js.map +1 -0
  53. package/dist/src/mcp/oauth-token-storage.test.d.ts +6 -0
  54. package/dist/src/mcp/oauth-token-storage.test.js +205 -0
  55. package/dist/src/mcp/oauth-token-storage.test.js.map +1 -0
  56. package/dist/src/mcp/oauth-utils.d.ts +109 -0
  57. package/dist/src/mcp/oauth-utils.js +183 -0
  58. package/dist/src/mcp/oauth-utils.js.map +1 -0
  59. package/dist/src/mcp/oauth-utils.test.d.ts +6 -0
  60. package/dist/src/mcp/oauth-utils.test.js +144 -0
  61. package/dist/src/mcp/oauth-utils.test.js.map +1 -0
  62. package/dist/src/parsers/TextToolCallParser.d.ts +35 -0
  63. package/dist/src/parsers/TextToolCallParser.js +248 -0
  64. package/dist/src/parsers/TextToolCallParser.js.map +1 -0
  65. package/dist/src/parsers/TextToolCallParser.test.d.ts +1 -0
  66. package/dist/src/parsers/TextToolCallParser.test.js +225 -0
  67. package/dist/src/parsers/TextToolCallParser.test.js.map +1 -0
  68. package/dist/src/providers/ContentGeneratorRole.d.ts +14 -0
  69. package/dist/src/providers/ContentGeneratorRole.js +16 -0
  70. package/dist/src/providers/ContentGeneratorRole.js.map +1 -0
  71. package/dist/src/providers/IMessage.d.ts +38 -0
  72. package/dist/src/providers/IMessage.js +17 -0
  73. package/dist/src/providers/IMessage.js.map +1 -0
  74. package/dist/src/providers/IModel.d.ts +23 -0
  75. package/dist/src/providers/IModel.js +17 -0
  76. package/dist/src/providers/IModel.js.map +1 -0
  77. package/dist/src/providers/IProvider.d.ts +36 -0
  78. package/dist/src/providers/IProvider.js +17 -0
  79. package/dist/src/providers/IProvider.js.map +1 -0
  80. package/dist/src/providers/IProviderConfig.d.ts +31 -0
  81. package/dist/src/providers/IProviderConfig.js +7 -0
  82. package/dist/src/providers/IProviderConfig.js.map +1 -0
  83. package/dist/src/providers/IProviderManager.d.ts +53 -0
  84. package/dist/src/providers/IProviderManager.js +7 -0
  85. package/dist/src/providers/IProviderManager.js.map +1 -0
  86. package/dist/src/providers/ITool.d.ts +23 -0
  87. package/dist/src/providers/ITool.js +17 -0
  88. package/dist/src/providers/ITool.js.map +1 -0
  89. package/dist/src/providers/ProviderContentGenerator.d.ts +1 -1
  90. package/dist/src/providers/ProviderManager.d.ts +24 -0
  91. package/dist/src/providers/ProviderManager.gemini-switch.test.d.ts +6 -0
  92. package/dist/src/providers/ProviderManager.gemini-switch.test.js +57 -0
  93. package/dist/src/providers/ProviderManager.gemini-switch.test.js.map +1 -0
  94. package/dist/src/providers/ProviderManager.js +116 -0
  95. package/dist/src/providers/ProviderManager.js.map +1 -0
  96. package/dist/src/providers/ProviderManager.test.d.ts +6 -0
  97. package/dist/src/providers/ProviderManager.test.js +284 -0
  98. package/dist/src/providers/ProviderManager.test.js.map +1 -0
  99. package/dist/src/providers/adapters/GeminiCompatibleWrapper.d.ts +2 -1
  100. package/dist/src/providers/adapters/GeminiCompatibleWrapper.js +15 -2
  101. package/dist/src/providers/adapters/GeminiCompatibleWrapper.js.map +1 -1
  102. package/dist/src/providers/adapters/GeminiCompatibleWrapper.test.js +20 -0
  103. package/dist/src/providers/adapters/GeminiCompatibleWrapper.test.js.map +1 -1
  104. package/dist/src/providers/anthropic/AnthropicProvider.d.ts +57 -0
  105. package/dist/src/providers/anthropic/AnthropicProvider.js +490 -0
  106. package/dist/src/providers/anthropic/AnthropicProvider.js.map +1 -0
  107. package/dist/src/providers/anthropic/AnthropicProvider.test.d.ts +1 -0
  108. package/dist/src/providers/anthropic/AnthropicProvider.test.js +486 -0
  109. package/dist/src/providers/anthropic/AnthropicProvider.test.js.map +1 -0
  110. package/dist/src/providers/errors.d.ts +13 -0
  111. package/dist/src/providers/errors.js +19 -0
  112. package/dist/src/providers/errors.js.map +1 -0
  113. package/dist/src/providers/gemini/GeminiProvider.d.ts +97 -0
  114. package/dist/src/providers/gemini/GeminiProvider.integration.test.d.ts +6 -0
  115. package/dist/src/providers/gemini/GeminiProvider.integration.test.js +90 -0
  116. package/dist/src/providers/gemini/GeminiProvider.integration.test.js.map +1 -0
  117. package/dist/src/providers/gemini/GeminiProvider.js +937 -0
  118. package/dist/src/providers/gemini/GeminiProvider.js.map +1 -0
  119. package/dist/src/providers/gemini/GeminiProvider.test.d.ts +6 -0
  120. package/dist/src/providers/gemini/GeminiProvider.test.js +136 -0
  121. package/dist/src/providers/gemini/GeminiProvider.test.js.map +1 -0
  122. package/dist/src/providers/integration/TEST_INSTRUCTIONS.md +197 -0
  123. package/dist/src/providers/integration/multi-provider.integration.test.d.ts +6 -0
  124. package/dist/src/providers/integration/multi-provider.integration.test.js +292 -0
  125. package/dist/src/providers/integration/multi-provider.integration.test.js.map +1 -0
  126. package/dist/src/providers/openai/ConversationCache.accumTokens.test.d.ts +1 -0
  127. package/dist/src/providers/openai/ConversationCache.accumTokens.test.js +97 -0
  128. package/dist/src/providers/openai/ConversationCache.accumTokens.test.js.map +1 -0
  129. package/dist/src/providers/openai/ConversationCache.d.ts +20 -0
  130. package/dist/src/providers/openai/ConversationCache.js +109 -0
  131. package/dist/src/providers/openai/ConversationCache.js.map +1 -0
  132. package/dist/src/providers/openai/ConversationCache.test.d.ts +1 -0
  133. package/dist/src/providers/openai/ConversationCache.test.js +113 -0
  134. package/dist/src/providers/openai/ConversationCache.test.js.map +1 -0
  135. package/dist/src/providers/openai/IChatGenerateParams.d.ts +11 -0
  136. package/dist/src/providers/openai/IChatGenerateParams.js +2 -0
  137. package/dist/src/providers/openai/IChatGenerateParams.js.map +1 -0
  138. package/dist/src/providers/openai/OpenAIProvider.callResponses.stateless.test.d.ts +1 -0
  139. package/dist/src/providers/openai/OpenAIProvider.callResponses.stateless.test.js +189 -0
  140. package/dist/src/providers/openai/OpenAIProvider.callResponses.stateless.test.js.map +1 -0
  141. package/dist/src/providers/openai/OpenAIProvider.d.ts +80 -0
  142. package/dist/src/providers/openai/OpenAIProvider.integration.test.d.ts +6 -0
  143. package/dist/src/providers/openai/OpenAIProvider.integration.test.js +125 -0
  144. package/dist/src/providers/openai/OpenAIProvider.integration.test.js.map +1 -0
  145. package/dist/src/providers/openai/OpenAIProvider.js +518 -0
  146. package/dist/src/providers/openai/OpenAIProvider.js.map +1 -0
  147. package/dist/src/providers/openai/OpenAIProvider.responses.test.d.ts +1 -0
  148. package/dist/src/providers/openai/OpenAIProvider.responses.test.js +326 -0
  149. package/dist/src/providers/openai/OpenAIProvider.responses.test.js.map +1 -0
  150. package/dist/src/providers/openai/OpenAIProvider.responsesIntegration.test.d.ts +1 -0
  151. package/dist/src/providers/openai/OpenAIProvider.responsesIntegration.test.js +213 -0
  152. package/dist/src/providers/openai/OpenAIProvider.responsesIntegration.test.js.map +1 -0
  153. package/dist/src/providers/openai/OpenAIProvider.shouldUseResponses.test.d.ts +1 -0
  154. package/dist/src/providers/openai/OpenAIProvider.shouldUseResponses.test.js +58 -0
  155. package/dist/src/providers/openai/OpenAIProvider.shouldUseResponses.test.js.map +1 -0
  156. package/dist/src/providers/openai/OpenAIProvider.stateful.integration.test.d.ts +6 -0
  157. package/dist/src/providers/openai/OpenAIProvider.stateful.integration.test.js +105 -0
  158. package/dist/src/providers/openai/OpenAIProvider.stateful.integration.test.js.map +1 -0
  159. package/dist/src/providers/openai/OpenAIProvider.switch.test.d.ts +1 -0
  160. package/dist/src/providers/openai/OpenAIProvider.switch.test.js +256 -0
  161. package/dist/src/providers/openai/OpenAIProvider.switch.test.js.map +1 -0
  162. package/dist/src/providers/openai/OpenAIProvider.test.d.ts +16 -0
  163. package/dist/src/providers/openai/OpenAIProvider.test.js +214 -0
  164. package/dist/src/providers/openai/OpenAIProvider.test.js.map +1 -0
  165. package/dist/src/providers/openai/RESPONSES_API_MODELS.d.ts +2 -0
  166. package/dist/src/providers/openai/RESPONSES_API_MODELS.js +14 -0
  167. package/dist/src/providers/openai/RESPONSES_API_MODELS.js.map +1 -0
  168. package/dist/src/providers/openai/ResponsesContextTrim.integration.test.d.ts +1 -0
  169. package/dist/src/providers/openai/ResponsesContextTrim.integration.test.js +210 -0
  170. package/dist/src/providers/openai/ResponsesContextTrim.integration.test.js.map +1 -0
  171. package/dist/src/providers/openai/__tests__/formatArrayResponse.test.d.ts +1 -0
  172. package/dist/src/providers/openai/__tests__/formatArrayResponse.test.js +65 -0
  173. package/dist/src/providers/openai/__tests__/formatArrayResponse.test.js.map +1 -0
  174. package/dist/src/providers/openai/buildResponsesRequest.d.ts +73 -0
  175. package/dist/src/providers/openai/buildResponsesRequest.js +165 -0
  176. package/dist/src/providers/openai/buildResponsesRequest.js.map +1 -0
  177. package/dist/src/providers/openai/buildResponsesRequest.stripToolCalls.test.d.ts +1 -0
  178. package/dist/src/providers/openai/buildResponsesRequest.stripToolCalls.test.js +129 -0
  179. package/dist/src/providers/openai/buildResponsesRequest.stripToolCalls.test.js.map +1 -0
  180. package/dist/src/providers/openai/buildResponsesRequest.test.d.ts +1 -0
  181. package/dist/src/providers/openai/buildResponsesRequest.test.js +406 -0
  182. package/dist/src/providers/openai/buildResponsesRequest.test.js.map +1 -0
  183. package/dist/src/providers/openai/buildResponsesRequest.undefined.test.d.ts +1 -0
  184. package/dist/src/providers/openai/buildResponsesRequest.undefined.test.js +50 -0
  185. package/dist/src/providers/openai/buildResponsesRequest.undefined.test.js.map +1 -0
  186. package/dist/src/providers/openai/docs/accessing-provider-info.md +172 -0
  187. package/dist/src/providers/openai/docs/params-mapping.md +91 -0
  188. package/dist/src/providers/openai/docs/responses-api-tool-calls.md +96 -0
  189. package/dist/src/providers/openai/estimateRemoteTokens.d.ts +26 -0
  190. package/dist/src/providers/openai/estimateRemoteTokens.js +75 -0
  191. package/dist/src/providers/openai/estimateRemoteTokens.js.map +1 -0
  192. package/dist/src/providers/openai/estimateRemoteTokens.test.d.ts +1 -0
  193. package/dist/src/providers/openai/estimateRemoteTokens.test.js +125 -0
  194. package/dist/src/providers/openai/estimateRemoteTokens.test.js.map +1 -0
  195. package/dist/src/providers/openai/getOpenAIProviderInfo.d.ts +46 -0
  196. package/dist/src/providers/openai/getOpenAIProviderInfo.js +75 -0
  197. package/dist/src/providers/openai/getOpenAIProviderInfo.js.map +1 -0
  198. package/dist/src/providers/openai/parseResponsesStream.d.ts +3 -0
  199. package/dist/src/providers/openai/parseResponsesStream.js +462 -0
  200. package/dist/src/providers/openai/parseResponsesStream.js.map +1 -0
  201. package/dist/src/providers/openai/parseResponsesStream.responsesToolCalls.test.d.ts +1 -0
  202. package/dist/src/providers/openai/parseResponsesStream.responsesToolCalls.test.js +192 -0
  203. package/dist/src/providers/openai/parseResponsesStream.responsesToolCalls.test.js.map +1 -0
  204. package/dist/src/providers/openai/parseResponsesStream.test.d.ts +1 -0
  205. package/dist/src/providers/openai/parseResponsesStream.test.js +151 -0
  206. package/dist/src/providers/openai/parseResponsesStream.test.js.map +1 -0
  207. package/dist/src/providers/tokenizers/AnthropicTokenizer.d.ts +19 -0
  208. package/dist/src/providers/tokenizers/AnthropicTokenizer.js +37 -0
  209. package/dist/src/providers/tokenizers/AnthropicTokenizer.js.map +1 -0
  210. package/dist/src/providers/tokenizers/ITokenizer.d.ts +18 -0
  211. package/dist/src/providers/tokenizers/ITokenizer.js +17 -0
  212. package/dist/src/providers/tokenizers/ITokenizer.js.map +1 -0
  213. package/dist/src/providers/tokenizers/OpenAITokenizer.d.ts +24 -0
  214. package/dist/src/providers/tokenizers/OpenAITokenizer.js +56 -0
  215. package/dist/src/providers/tokenizers/OpenAITokenizer.js.map +1 -0
  216. package/dist/src/providers/types/IProviderConfig.d.ts +102 -0
  217. package/dist/src/providers/types/IProviderConfig.js +17 -0
  218. package/dist/src/providers/types/IProviderConfig.js.map +1 -0
  219. package/dist/src/providers/types.d.ts +4 -69
  220. package/dist/src/services/ideContext.d.ts +2 -0
  221. package/dist/src/services/ideContext.js +8 -0
  222. package/dist/src/services/ideContext.js.map +1 -1
  223. package/dist/src/services/ideContext.test.js +10 -0
  224. package/dist/src/services/ideContext.test.js.map +1 -1
  225. package/dist/src/services/loopDetectionService.d.ts +17 -1
  226. package/dist/src/services/loopDetectionService.js +117 -2
  227. package/dist/src/services/loopDetectionService.js.map +1 -1
  228. package/dist/src/services/loopDetectionService.test.js +109 -2
  229. package/dist/src/services/loopDetectionService.test.js.map +1 -1
  230. package/dist/src/telemetry/clearcut-logger/clearcut-logger.d.ts +2 -0
  231. package/dist/src/telemetry/clearcut-logger/clearcut-logger.js +40 -2
  232. package/dist/src/telemetry/clearcut-logger/clearcut-logger.js.map +1 -1
  233. package/dist/src/telemetry/clearcut-logger/event-metadata-key.d.ts +2 -0
  234. package/dist/src/telemetry/clearcut-logger/event-metadata-key.js +4 -0
  235. package/dist/src/telemetry/clearcut-logger/event-metadata-key.js.map +1 -1
  236. package/dist/src/telemetry/sdk.js +0 -2
  237. package/dist/src/telemetry/sdk.js.map +1 -1
  238. package/dist/src/telemetry/types.d.ts +2 -1
  239. package/dist/src/telemetry/types.js +1 -0
  240. package/dist/src/telemetry/types.js.map +1 -1
  241. package/dist/src/telemetry/uiTelemetry.d.ts +1 -0
  242. package/dist/src/telemetry/uiTelemetry.js +7 -0
  243. package/dist/src/telemetry/uiTelemetry.js.map +1 -1
  244. package/dist/src/telemetry/uiTelemetry.test.js +92 -0
  245. package/dist/src/telemetry/uiTelemetry.test.js.map +1 -1
  246. package/dist/src/tools/IToolFormatter.d.ts +40 -0
  247. package/dist/src/tools/IToolFormatter.js +17 -0
  248. package/dist/src/tools/IToolFormatter.js.map +1 -0
  249. package/dist/src/tools/ToolFormatter.d.ts +45 -0
  250. package/dist/src/tools/ToolFormatter.js +216 -0
  251. package/dist/src/tools/ToolFormatter.js.map +1 -0
  252. package/dist/src/tools/ToolFormatter.test.d.ts +16 -0
  253. package/dist/src/tools/ToolFormatter.test.js +349 -0
  254. package/dist/src/tools/ToolFormatter.test.js.map +1 -0
  255. package/dist/src/tools/ToolFormatter.toResponsesTool.test.d.ts +1 -0
  256. package/dist/src/tools/ToolFormatter.toResponsesTool.test.js +241 -0
  257. package/dist/src/tools/ToolFormatter.toResponsesTool.test.js.map +1 -0
  258. package/dist/src/tools/edit.d.ts +7 -1
  259. package/dist/src/tools/edit.js +19 -7
  260. package/dist/src/tools/edit.js.map +1 -1
  261. package/dist/src/tools/glob.js +2 -2
  262. package/dist/src/tools/glob.js.map +1 -1
  263. package/dist/src/tools/grep.js +2 -2
  264. package/dist/src/tools/grep.js.map +1 -1
  265. package/dist/src/tools/ls.js +2 -2
  266. package/dist/src/tools/ls.js.map +1 -1
  267. package/dist/src/tools/mcp-client.d.ts +0 -2
  268. package/dist/src/tools/mcp-client.js +8 -20
  269. package/dist/src/tools/mcp-client.js.map +1 -1
  270. package/dist/src/tools/mcp-client.test.js +1 -72
  271. package/dist/src/tools/mcp-client.test.js.map +1 -1
  272. package/dist/src/tools/mcp-tool.d.ts +11 -5
  273. package/dist/src/tools/mcp-tool.js +33 -9
  274. package/dist/src/tools/mcp-tool.js.map +1 -1
  275. package/dist/src/tools/mcp-tool.test.js +40 -24
  276. package/dist/src/tools/mcp-tool.test.js.map +1 -1
  277. package/dist/src/tools/memoryTool.js +2 -2
  278. package/dist/src/tools/memoryTool.js.map +1 -1
  279. package/dist/src/tools/read-file.d.ts +2 -1
  280. package/dist/src/tools/read-file.js +5 -2
  281. package/dist/src/tools/read-file.js.map +1 -1
  282. package/dist/src/tools/read-many-files.js +2 -2
  283. package/dist/src/tools/read-many-files.js.map +1 -1
  284. package/dist/src/tools/shell.js +2 -2
  285. package/dist/src/tools/shell.js.map +1 -1
  286. package/dist/src/tools/todo-read.js +2 -2
  287. package/dist/src/tools/todo-read.js.map +1 -1
  288. package/dist/src/tools/todo-write.js +2 -2
  289. package/dist/src/tools/todo-write.js.map +1 -1
  290. package/dist/src/tools/tool-registry.d.ts +0 -1
  291. package/dist/src/tools/tool-registry.js +11 -8
  292. package/dist/src/tools/tool-registry.js.map +1 -1
  293. package/dist/src/tools/tool-registry.test.js +36 -10
  294. package/dist/src/tools/tool-registry.test.js.map +1 -1
  295. package/dist/src/tools/tools.d.ts +37 -2
  296. package/dist/src/tools/tools.js +25 -2
  297. package/dist/src/tools/tools.js.map +1 -1
  298. package/dist/src/tools/web-fetch.integration.test.d.ts +6 -0
  299. package/dist/src/tools/web-fetch.integration.test.js +532 -0
  300. package/dist/src/tools/web-fetch.integration.test.js.map +1 -0
  301. package/dist/src/tools/web-fetch.js +57 -50
  302. package/dist/src/tools/web-fetch.js.map +1 -1
  303. package/dist/src/tools/web-search.js +34 -6
  304. package/dist/src/tools/web-search.js.map +1 -1
  305. package/dist/src/tools/web-search.test.d.ts +6 -0
  306. package/dist/src/tools/web-search.test.js +229 -0
  307. package/dist/src/tools/web-search.test.js.map +1 -0
  308. package/dist/src/tools/write-file.js +12 -5
  309. package/dist/src/tools/write-file.js.map +1 -1
  310. package/dist/src/utils/browser.d.ts +13 -0
  311. package/dist/src/utils/browser.js +49 -0
  312. package/dist/src/utils/browser.js.map +1 -0
  313. package/dist/src/utils/errors.js +4 -4
  314. package/dist/src/utils/errors.js.map +1 -1
  315. package/dist/src/utils/memoryDiscovery.js +5 -1
  316. package/dist/src/utils/memoryDiscovery.js.map +1 -1
  317. package/dist/src/utils/quotaErrorDetection.js +0 -2
  318. package/dist/src/utils/quotaErrorDetection.js.map +1 -1
  319. package/dist/src/utils/retry.d.ts +6 -0
  320. package/dist/src/utils/retry.js +1 -1
  321. package/dist/src/utils/retry.js.map +1 -1
  322. package/dist/src/utils/user_account.js +6 -1
  323. package/dist/src/utils/user_account.js.map +1 -1
  324. package/dist/tsconfig.tsbuildinfo +1 -1
  325. package/package.json +6 -3
  326. package/dist/src/tools/web-fetch.test.js +0 -70
  327. package/dist/src/tools/web-fetch.test.js.map +0 -1
  328. package/dist/vybestack-llxprt-code-core-0.1.12.tgz +0 -0
  329. /package/dist/src/{tools/web-fetch.test.d.ts → mcp/oauth-provider.test.d.ts} +0 -0
@@ -0,0 +1,937 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Vybestack LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import { AuthType, ContentGeneratorRole, AuthenticationRequiredError, getCoreSystemPrompt, createCodeAssistContentGenerator, } from '@vybestack/llxprt-code-core';
7
+ export class GeminiProvider {
8
+ name = 'gemini';
9
+ apiKey;
10
+ authMode = 'none';
11
+ config;
12
+ currentModel = 'gemini-2.5-pro';
13
+ modelExplicitlySet = false;
14
+ authDetermined = false;
15
+ toolSchemas;
16
+ constructor() {
17
+ // Do not determine auth mode on instantiation.
18
+ // This will be done lazily when a chat completion is requested.
19
+ }
20
+ /**
21
+ * Determines the best available authentication method based on environment variables
22
+ * and existing configuration. Follows the hierarchy: Vertex AI → Gemini API key → OAuth
23
+ */
24
+ determineBestAuth() {
25
+ // Skip if already determined
26
+ if (this.authDetermined) {
27
+ return;
28
+ }
29
+ // Mark as determined early to prevent concurrent determinations
30
+ this.authDetermined = true;
31
+ // Check if user explicitly selected USE_NONE via the content generator config
32
+ const authType = this.config?.getContentGeneratorConfig()?.authType;
33
+ if (authType === AuthType.USE_NONE) {
34
+ this.authMode = 'none';
35
+ return;
36
+ }
37
+ // If authType is USE_PROVIDER and no credentials exist, fall back to OAuth
38
+ if (authType === AuthType.USE_PROVIDER) {
39
+ if (!this.hasVertexAICredentials() && !this.hasGeminiAPIKey()) {
40
+ this.authMode = 'oauth';
41
+ return;
42
+ }
43
+ }
44
+ // Check for Vertex AI credentials first
45
+ if (this.hasVertexAICredentials()) {
46
+ this.authMode = 'vertex-ai';
47
+ this.setupVertexAIAuth();
48
+ }
49
+ // Check for Gemini API key second
50
+ else if (this.hasGeminiAPIKey()) {
51
+ this.authMode = 'gemini-api-key';
52
+ // API key is already in environment, no additional setup needed
53
+ }
54
+ // Fall back to OAuth (will prompt user if needed)
55
+ else {
56
+ this.authMode = 'oauth';
57
+ // OAuth will be handled by the existing auth system
58
+ }
59
+ }
60
+ /**
61
+ * Checks if Vertex AI credentials are available
62
+ */
63
+ hasVertexAICredentials() {
64
+ const hasProjectAndLocation = !!process.env.GOOGLE_CLOUD_PROJECT && !!process.env.GOOGLE_CLOUD_LOCATION;
65
+ const hasGoogleApiKey = !!process.env.GOOGLE_API_KEY;
66
+ return hasProjectAndLocation || hasGoogleApiKey;
67
+ }
68
+ /**
69
+ * Checks if Gemini API key is available
70
+ */
71
+ hasGeminiAPIKey() {
72
+ return !!this.apiKey || !!process.env.GEMINI_API_KEY;
73
+ }
74
+ /**
75
+ * Sets up environment variables for Vertex AI authentication
76
+ */
77
+ setupVertexAIAuth() {
78
+ process.env.GOOGLE_GENAI_USE_VERTEXAI = 'true';
79
+ // Other Vertex AI env vars are already set, no need to duplicate
80
+ }
81
+ /**
82
+ * Sets the config instance for reading OAuth credentials
83
+ */
84
+ setConfig(config) {
85
+ this.config = config;
86
+ // Sync with config model if user hasn't explicitly set a model
87
+ // This ensures consistency between config and provider state
88
+ const configModel = config.getModel();
89
+ if (!this.modelExplicitlySet && configModel) {
90
+ this.currentModel = configModel;
91
+ }
92
+ // Clear auth cache when config changes to allow re-determination
93
+ this.authDetermined = false;
94
+ // Re-determine auth after config is set
95
+ this.determineBestAuth();
96
+ }
97
+ async getModels() {
98
+ // For OAuth mode, return fixed list of models
99
+ if (this.authMode === 'oauth') {
100
+ return [
101
+ {
102
+ id: 'gemini-2.5-pro',
103
+ name: 'Gemini 2.5 Pro',
104
+ provider: this.name,
105
+ supportedToolFormats: [],
106
+ },
107
+ {
108
+ id: 'gemini-2.5-flash',
109
+ name: 'Gemini 2.5 Flash',
110
+ provider: this.name,
111
+ supportedToolFormats: [],
112
+ },
113
+ {
114
+ id: 'gemini-2.5-flash-exp',
115
+ name: 'Gemini 2.5 Flash Experimental',
116
+ provider: this.name,
117
+ supportedToolFormats: [],
118
+ },
119
+ ];
120
+ }
121
+ // For API key modes (gemini-api-key or vertex-ai), try to fetch real models
122
+ if (this.authMode === 'gemini-api-key' || this.authMode === 'vertex-ai') {
123
+ const apiKey = this.apiKey || process.env.GEMINI_API_KEY;
124
+ if (apiKey) {
125
+ try {
126
+ const response = await fetch(`https://generativelanguage.googleapis.com/v1beta/models?key=${apiKey}`, {
127
+ method: 'GET',
128
+ headers: {
129
+ 'Content-Type': 'application/json',
130
+ },
131
+ });
132
+ if (response.ok) {
133
+ const data = (await response.json());
134
+ if (data.models && data.models.length > 0) {
135
+ return data.models.map((model) => ({
136
+ id: model.name.replace('models/', ''), // Remove 'models/' prefix
137
+ name: model.displayName || model.name,
138
+ provider: this.name,
139
+ supportedToolFormats: [],
140
+ }));
141
+ }
142
+ }
143
+ }
144
+ catch (_error) {
145
+ // Fall through to default models
146
+ }
147
+ }
148
+ }
149
+ // Return default models as fallback
150
+ return [
151
+ {
152
+ id: 'gemini-2.5-pro',
153
+ name: 'Gemini 2.5 Pro',
154
+ provider: this.name,
155
+ supportedToolFormats: [],
156
+ },
157
+ {
158
+ id: 'gemini-2.5-flash',
159
+ name: 'Gemini 2.5 Flash',
160
+ provider: this.name,
161
+ supportedToolFormats: [],
162
+ },
163
+ {
164
+ id: 'gemini-2.5-flash-exp',
165
+ name: 'Gemini 2.5 Flash Experimental',
166
+ provider: this.name,
167
+ supportedToolFormats: [],
168
+ },
169
+ ];
170
+ }
171
+ /**
172
+ * Checks if OAuth authentication is still valid
173
+ */
174
+ async isOAuthValid() {
175
+ if (this.authMode !== 'oauth')
176
+ return true;
177
+ // Check if we have valid OAuth tokens
178
+ // This would need to interact with the core auth system
179
+ try {
180
+ // For now, assume OAuth is valid if we've already determined auth
181
+ // A more robust check would query the auth status from the config
182
+ return this.authDetermined;
183
+ }
184
+ catch {
185
+ return false;
186
+ }
187
+ }
188
+ async *generateChatCompletion(messages, tools, _toolFormat) {
189
+ // Comprehensive debug logging
190
+ if (process.env.DEBUG) {
191
+ console.log('[GEMINI] generateChatCompletion called with:');
192
+ console.log('[GEMINI] messages:', JSON.stringify(messages, null, 2));
193
+ console.log('[GEMINI] messages length:', messages.length);
194
+ console.log('[GEMINI] first message:', messages[0] ? JSON.stringify(messages[0], null, 2) : 'NO FIRST MESSAGE');
195
+ console.log('[GEMINI] tools:', tools ? JSON.stringify(tools.map((t) => t.function.name)) : 'NO TOOLS');
196
+ if (process.env.DEBUG) {
197
+ console.log('DEBUG: GeminiProvider.generateChatCompletion called with messages:', JSON.stringify(messages, null, 2));
198
+ }
199
+ }
200
+ // Check if we need to re-determine auth
201
+ const oauthValid = await this.isOAuthValid();
202
+ if (!oauthValid) {
203
+ this.authDetermined = false;
204
+ }
205
+ // Lazily determine the best auth method now that it's needed.
206
+ this.determineBestAuth();
207
+ // Early authentication validation - check if we have the required credentials
208
+ // for the determined auth mode BEFORE processing messages
209
+ switch (this.authMode) {
210
+ case 'gemini-api-key':
211
+ if (!this.apiKey && !process.env.GEMINI_API_KEY) {
212
+ throw new AuthenticationRequiredError('Gemini API key required but not found. Please set GEMINI_API_KEY environment variable or use /auth to login with Google OAuth.', this.authMode, ['GEMINI_API_KEY']);
213
+ }
214
+ break;
215
+ case 'vertex-ai':
216
+ if (!process.env.GOOGLE_API_KEY) {
217
+ throw new AuthenticationRequiredError('Google API key required for Vertex AI. Please set GOOGLE_API_KEY environment variable or use /auth to login with Google OAuth.', this.authMode, ['GOOGLE_API_KEY', 'GOOGLE_CLOUD_PROJECT', 'GOOGLE_CLOUD_LOCATION']);
218
+ }
219
+ break;
220
+ case 'oauth':
221
+ // OAuth auth will be validated when creating the content generator
222
+ break;
223
+ case 'none':
224
+ // In 'none' mode, check if ANY credentials are available
225
+ if (!this.hasGeminiAPIKey() && !this.hasVertexAICredentials()) {
226
+ throw new AuthenticationRequiredError('No authentication credentials found. Please use /auth to login with Google OAuth, set GEMINI_API_KEY, or configure Vertex AI credentials.', this.authMode, ['GEMINI_API_KEY', 'GOOGLE_API_KEY']);
227
+ }
228
+ break;
229
+ default:
230
+ // For any other auth mode, proceed without validation
231
+ break;
232
+ }
233
+ // Import the necessary modules dynamically to avoid circular dependencies
234
+ const { GoogleGenAI } = await import('@google/genai');
235
+ // Create the appropriate client based on auth mode
236
+ let genAI;
237
+ const httpOptions = {
238
+ headers: {
239
+ 'User-Agent': `GeminiCLI/${process.env.CLI_VERSION || process.version} (${process.platform}; ${process.arch})`,
240
+ },
241
+ };
242
+ switch (this.authMode) {
243
+ case 'gemini-api-key':
244
+ if (!this.apiKey && !process.env.GEMINI_API_KEY) {
245
+ throw new Error('Gemini API key required but not found');
246
+ }
247
+ genAI = new GoogleGenAI({
248
+ apiKey: this.apiKey || process.env.GEMINI_API_KEY,
249
+ httpOptions,
250
+ });
251
+ break;
252
+ case 'vertex-ai':
253
+ if (!process.env.GOOGLE_API_KEY) {
254
+ throw new Error('Google API key required for Vertex AI');
255
+ }
256
+ genAI = new GoogleGenAI({
257
+ apiKey: process.env.GOOGLE_API_KEY,
258
+ vertexai: true,
259
+ httpOptions,
260
+ });
261
+ break;
262
+ case 'oauth': {
263
+ // For OAuth, we need to use the code assist server
264
+ const contentGenerator = await createCodeAssistContentGenerator(httpOptions, AuthType.LOGIN_WITH_GOOGLE, this.config);
265
+ // Convert messages to Gemini request format
266
+ // Use config model in OAuth mode to ensure synchronization
267
+ const oauthModel = this.modelExplicitlySet
268
+ ? this.currentModel
269
+ : this.config?.getModel() || this.currentModel;
270
+ // Generate systemInstruction using getCoreSystemPrompt
271
+ // Get user memory from config if available
272
+ const userMemory = this.config?.getUserMemory
273
+ ? this.config.getUserMemory()
274
+ : '';
275
+ const systemInstruction = getCoreSystemPrompt(userMemory, oauthModel);
276
+ // Store tools if provided
277
+ if (tools && tools.length > 0) {
278
+ this.toolSchemas = this.convertToolsToGeminiFormat(tools);
279
+ }
280
+ // Use provided tools or stored tools
281
+ let geminiTools = tools
282
+ ? this.convertToolsToGeminiFormat(tools)
283
+ : this.toolSchemas;
284
+ // For Flash models, always include tools if available
285
+ if (oauthModel.includes('flash') && !geminiTools && this.toolSchemas) {
286
+ geminiTools = this.toolSchemas;
287
+ }
288
+ const request = {
289
+ model: oauthModel,
290
+ contents: this.convertMessagesToGeminiFormat(messages),
291
+ systemInstruction,
292
+ config: {
293
+ tools: geminiTools,
294
+ },
295
+ };
296
+ // Use the content generator stream
297
+ const streamResult = await contentGenerator.generateContentStream(request);
298
+ // Convert the stream to our format
299
+ for await (const response of streamResult) {
300
+ // Extract text from the response
301
+ const text = response.candidates?.[0]?.content?.parts
302
+ ?.filter((part) => 'text' in part)
303
+ ?.map((part) => part.text)
304
+ ?.join('') || '';
305
+ // Extract function calls from the response
306
+ const functionCalls = response.candidates?.[0]?.content?.parts
307
+ ?.filter((part) => 'functionCall' in part)
308
+ ?.map((part) => part.functionCall) || [];
309
+ // Build response message
310
+ const message = {
311
+ role: ContentGeneratorRole.ASSISTANT,
312
+ content: text,
313
+ };
314
+ // Add function calls if any
315
+ if (functionCalls && functionCalls.length > 0) {
316
+ message.tool_calls = functionCalls.map((call) => ({
317
+ id: call.id ||
318
+ `call_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
319
+ type: 'function',
320
+ function: {
321
+ name: call.name || 'unknown_function',
322
+ arguments: JSON.stringify(call.args || {}),
323
+ },
324
+ }));
325
+ }
326
+ // Only yield if there's content or tool calls
327
+ if (text || (functionCalls && functionCalls.length > 0)) {
328
+ yield message;
329
+ }
330
+ }
331
+ return;
332
+ }
333
+ case 'none':
334
+ // For 'none' mode, check what credentials are available
335
+ if (this.hasGeminiAPIKey()) {
336
+ genAI = new GoogleGenAI({
337
+ apiKey: this.apiKey || process.env.GEMINI_API_KEY,
338
+ httpOptions,
339
+ });
340
+ }
341
+ else if (this.hasVertexAICredentials()) {
342
+ this.setupVertexAIAuth();
343
+ genAI = new GoogleGenAI({
344
+ apiKey: process.env.GOOGLE_API_KEY,
345
+ vertexai: true,
346
+ httpOptions,
347
+ });
348
+ }
349
+ else {
350
+ throw new Error('No authentication credentials found. Please use /auth to login with Google OAuth, set GEMINI_API_KEY, or configure Vertex AI credentials.');
351
+ }
352
+ break;
353
+ default:
354
+ throw new Error(`Unsupported auth mode: ${this.authMode}`);
355
+ }
356
+ // Get the models interface (which is a ContentGenerator)
357
+ const contentGenerator = genAI.models;
358
+ // Store tools if provided
359
+ if (tools && tools.length > 0) {
360
+ this.toolSchemas = this.convertToolsToGeminiFormat(tools);
361
+ }
362
+ // Convert IMessage[] to Gemini format - do this after storing tools so priming can access them
363
+ const contents = this.convertMessagesToGeminiFormat(messages);
364
+ // Use provided tools or stored tools
365
+ let geminiTools = tools
366
+ ? this.convertToolsToGeminiFormat(tools)
367
+ : this.toolSchemas;
368
+ // Create the request - ContentGenerator expects model in the request
369
+ // Use explicit model if set, otherwise fall back to config model
370
+ const modelToUse = this.modelExplicitlySet
371
+ ? this.currentModel
372
+ : this.config?.getModel() || this.currentModel;
373
+ // For Flash models, always include tools if available
374
+ if (modelToUse.includes('flash') && !geminiTools && this.toolSchemas) {
375
+ geminiTools = this.toolSchemas;
376
+ }
377
+ // Generate systemInstruction using getCoreSystemPrompt
378
+ // Get user memory from config if available
379
+ const userMemory = this.config?.getUserMemory
380
+ ? this.config.getUserMemory()
381
+ : '';
382
+ const systemInstruction = getCoreSystemPrompt(userMemory, modelToUse);
383
+ const request = {
384
+ model: modelToUse,
385
+ contents,
386
+ systemInstruction,
387
+ config: {
388
+ tools: geminiTools,
389
+ },
390
+ };
391
+ // Generate content stream using the ContentGenerator interface
392
+ const stream = await contentGenerator.generateContentStream(request);
393
+ // Stream the response
394
+ for await (const response of stream) {
395
+ // Extract text from the response
396
+ const text = response.candidates?.[0]?.content?.parts
397
+ ?.filter((part) => 'text' in part)
398
+ ?.map((part) => part.text)
399
+ ?.join('') || '';
400
+ // Extract function calls from the response
401
+ const functionCalls = response.candidates?.[0]?.content?.parts
402
+ ?.filter((part) => 'functionCall' in part)
403
+ ?.map((part) => part.functionCall) || [];
404
+ // Build response message
405
+ const message = {
406
+ role: ContentGeneratorRole.ASSISTANT,
407
+ content: text || '',
408
+ };
409
+ // Add function calls if any
410
+ if (functionCalls && functionCalls.length > 0) {
411
+ message.tool_calls = functionCalls.map((call) => ({
412
+ id: call.id ||
413
+ `call_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
414
+ type: 'function',
415
+ function: {
416
+ name: call.name || 'unknown_function',
417
+ arguments: JSON.stringify(call.args || {}),
418
+ },
419
+ }));
420
+ }
421
+ // Only yield if there's content or tool calls
422
+ if (text || (functionCalls && functionCalls.length > 0)) {
423
+ yield message;
424
+ }
425
+ }
426
+ }
427
+ convertMessagesToGeminiFormat(messages) {
428
+ const contents = [];
429
+ // Enhanced tracking with more details
430
+ const functionCalls = new Map();
431
+ const functionResponses = new Map();
432
+ for (let i = 0; i < messages.length; i++) {
433
+ const msg = messages[i];
434
+ // Handle tool responses - each in its own Content object
435
+ if (msg.role === ContentGeneratorRole.TOOL) {
436
+ if (!msg.tool_call_id) {
437
+ if (process.env.DEBUG) {
438
+ console.warn(`Tool response at index ${i} missing tool_call_id, skipping:`, msg);
439
+ }
440
+ continue;
441
+ }
442
+ functionResponses.set(msg.tool_call_id, {
443
+ name: msg.tool_name || 'unknown_function',
444
+ contentIndex: contents.length,
445
+ messageIndex: i,
446
+ });
447
+ // Add each tool response as a separate content immediately
448
+ contents.push({
449
+ role: 'user',
450
+ parts: [
451
+ {
452
+ functionResponse: {
453
+ id: msg.tool_call_id,
454
+ name: msg.tool_name || 'unknown_function',
455
+ response: {
456
+ output: msg.content || '',
457
+ },
458
+ },
459
+ },
460
+ ],
461
+ });
462
+ continue;
463
+ }
464
+ // For non-tool messages, convert normally
465
+ const parts = [];
466
+ // Check for parts first (for messages with PDF/image parts but no text content)
467
+ if (msg.parts && msg.parts.length > 0) {
468
+ parts.push(...msg.parts);
469
+ }
470
+ else if (msg.content) {
471
+ // Handle PartListUnion: string | Part | Part[]
472
+ // In practice, content can be PartListUnion even though IMessage types it as string
473
+ const content = msg.content;
474
+ if (typeof content === 'string') {
475
+ // Try to parse string in case it's a stringified Part or Part[]
476
+ if ((content.startsWith('{') && content.endsWith('}')) ||
477
+ (content.startsWith('[') && content.endsWith(']'))) {
478
+ try {
479
+ const parsed = JSON.parse(content);
480
+ if (Array.isArray(parsed)) {
481
+ parts.push(...parsed);
482
+ }
483
+ else {
484
+ parts.push(parsed);
485
+ }
486
+ }
487
+ catch (_e) {
488
+ // Not valid JSON, treat as text
489
+ parts.push({ text: content });
490
+ }
491
+ }
492
+ else {
493
+ parts.push({ text: content });
494
+ }
495
+ }
496
+ else if (Array.isArray(content)) {
497
+ // Content is Part[]
498
+ parts.push(...content);
499
+ }
500
+ else {
501
+ // Content is a single Part
502
+ parts.push(content);
503
+ }
504
+ }
505
+ // Handle tool calls
506
+ if (msg.tool_calls && msg.tool_calls.length > 0) {
507
+ // Check if function calls were already added via parts
508
+ const existingFunctionCallIds = new Set();
509
+ if (msg.parts && msg.parts.length > 0) {
510
+ for (const part of parts) {
511
+ if ('functionCall' in part) {
512
+ const fc = part;
513
+ if (fc.functionCall.id) {
514
+ existingFunctionCallIds.add(fc.functionCall.id);
515
+ }
516
+ }
517
+ }
518
+ }
519
+ for (const toolCall of msg.tool_calls) {
520
+ // Skip if this function call was already added via parts
521
+ if (toolCall.id && existingFunctionCallIds.has(toolCall.id)) {
522
+ continue;
523
+ }
524
+ // Ensure tool call has an ID
525
+ if (!toolCall.id) {
526
+ if (process.env.DEBUG) {
527
+ console.warn(`Tool call at message ${i} missing ID, generating one:`, toolCall);
528
+ }
529
+ // Generate a unique ID for the function call
530
+ toolCall.id = `generated_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
531
+ }
532
+ const partIndex = parts.length;
533
+ parts.push({
534
+ functionCall: {
535
+ id: toolCall.id,
536
+ name: toolCall.function.name,
537
+ args: JSON.parse(toolCall.function.arguments),
538
+ },
539
+ });
540
+ // Track this function call with its position
541
+ functionCalls.set(toolCall.id, {
542
+ name: toolCall.function.name,
543
+ contentIndex: contents.length,
544
+ partIndex,
545
+ messageIndex: i,
546
+ });
547
+ }
548
+ }
549
+ // Map roles
550
+ let role = 'user';
551
+ if (msg.role === ContentGeneratorRole.ASSISTANT) {
552
+ role = 'model';
553
+ }
554
+ else if (msg.role === ContentGeneratorRole.USER) {
555
+ role = 'user';
556
+ }
557
+ else if (msg.role === 'system') {
558
+ // Gemini doesn't have system role in contents, handle separately
559
+ role = 'user';
560
+ }
561
+ if (parts.length > 0) {
562
+ contents.push({
563
+ role,
564
+ parts,
565
+ });
566
+ }
567
+ }
568
+ // Validate and add missing function responses
569
+ for (const [callId, callInfo] of Array.from(functionCalls.entries())) {
570
+ if (!functionResponses.has(callId)) {
571
+ // Create a placeholder response for missing function response
572
+ if (process.env.DEBUG) {
573
+ console.warn(`Function call ${callInfo.name} (id: ${callId}) has no matching response, adding placeholder`);
574
+ }
575
+ // Add each function response as a separate content object (same as regular tool responses)
576
+ contents.push({
577
+ role: 'user',
578
+ parts: [
579
+ {
580
+ functionResponse: {
581
+ id: callId,
582
+ name: callInfo.name,
583
+ response: {
584
+ output: JSON.stringify({
585
+ error: 'Function call was interrupted or no response received',
586
+ message: `The function "${callInfo.name}" was called but did not receive a response. This may occur if the function execution was interrupted, requires authentication, or encountered an error.`,
587
+ callId,
588
+ functionName: callInfo.name,
589
+ }),
590
+ },
591
+ },
592
+ },
593
+ ],
594
+ });
595
+ // Mark this response as added
596
+ functionResponses.set(callId, {
597
+ name: callInfo.name,
598
+ contentIndex: contents.length - 1,
599
+ messageIndex: -1, // Placeholder response doesn't have original message index
600
+ });
601
+ }
602
+ }
603
+ // Final validation - count function calls and responses
604
+ let totalFunctionCalls = 0;
605
+ let totalFunctionResponses = 0;
606
+ const callsDetail = [];
607
+ const responsesDetail = [];
608
+ const unmatchedCalls = new Set();
609
+ const unmatchedResponses = new Set();
610
+ // First pass: collect all function calls and responses with their IDs
611
+ for (let i = 0; i < contents.length; i++) {
612
+ const content = contents[i];
613
+ for (const part of content.parts) {
614
+ if ('functionCall' in part) {
615
+ totalFunctionCalls++;
616
+ const fc = part;
617
+ callsDetail.push(`${i}: ${fc.functionCall.name} (${fc.functionCall.id})`);
618
+ if (fc.functionCall.id) {
619
+ unmatchedCalls.add(fc.functionCall.id);
620
+ }
621
+ }
622
+ else if ('functionResponse' in part) {
623
+ totalFunctionResponses++;
624
+ const fr = part;
625
+ responsesDetail.push(`${i}: ${fr.functionResponse.name} (${fr.functionResponse.id})`);
626
+ if (fr.functionResponse.id) {
627
+ unmatchedResponses.add(fr.functionResponse.id);
628
+ }
629
+ }
630
+ }
631
+ }
632
+ // Second pass: match calls with responses
633
+ for (const id of unmatchedCalls) {
634
+ if (unmatchedResponses.has(id)) {
635
+ unmatchedCalls.delete(id);
636
+ unmatchedResponses.delete(id);
637
+ }
638
+ }
639
+ if (totalFunctionCalls !== totalFunctionResponses) {
640
+ if (process.env.DEBUG) {
641
+ console.warn(`Function parts count mismatch: ${totalFunctionCalls} calls vs ${totalFunctionResponses} responses`);
642
+ console.warn('Function calls:', callsDetail);
643
+ console.warn('Function responses:', responsesDetail);
644
+ console.warn('Unmatched call IDs:', Array.from(unmatchedCalls));
645
+ console.warn('Unmatched response IDs:', Array.from(unmatchedResponses));
646
+ }
647
+ // This is now just a warning, not an error, since we've added placeholders
648
+ // The Gemini API should handle this gracefully
649
+ }
650
+ return contents;
651
+ }
652
+ convertToolsToGeminiFormat(tools) {
653
+ const result = [
654
+ {
655
+ functionDeclarations: tools.map((tool) => ({
656
+ name: tool.function.name,
657
+ description: tool.function.description,
658
+ parameters: tool.function.parameters,
659
+ })),
660
+ },
661
+ ];
662
+ if (process.env.DEBUG) {
663
+ console.log('DEBUG [GeminiProvider]: Converted tools to Gemini format:', JSON.stringify(result, null, 2));
664
+ }
665
+ return result;
666
+ }
667
+ setApiKey(apiKey) {
668
+ this.apiKey = apiKey;
669
+ // Set the API key as an environment variable so it can be used by the core library
670
+ process.env.GEMINI_API_KEY = apiKey;
671
+ // Clear auth cache when API key changes
672
+ this.authDetermined = false;
673
+ // Re-determine auth after API key is set
674
+ this.determineBestAuth();
675
+ }
676
+ /**
677
+ * Gets the current authentication mode
678
+ */
679
+ getAuthMode() {
680
+ return this.authMode;
681
+ }
682
+ /**
683
+ * Gets the appropriate AuthType for the core library
684
+ */
685
+ getCoreAuthType() {
686
+ switch (this.authMode) {
687
+ case 'oauth':
688
+ return AuthType.LOGIN_WITH_GOOGLE;
689
+ case 'gemini-api-key':
690
+ return AuthType.USE_GEMINI;
691
+ case 'vertex-ai':
692
+ return AuthType.USE_VERTEX_AI;
693
+ default:
694
+ return AuthType.LOGIN_WITH_GOOGLE; // Default to OAuth
695
+ }
696
+ }
697
+ /**
698
+ * Gets the current model ID
699
+ */
700
+ getCurrentModel() {
701
+ return this.currentModel;
702
+ }
703
+ /**
704
+ * Sets the current model ID
705
+ */
706
+ setModel(modelId) {
707
+ this.currentModel = modelId;
708
+ this.modelExplicitlySet = true;
709
+ // Always update config if available, not just in OAuth mode
710
+ // This ensures the model is properly synchronized
711
+ if (this.config) {
712
+ this.config.setModel(modelId);
713
+ }
714
+ }
715
+ /**
716
+ * Checks if the current auth mode requires payment
717
+ */
718
+ isPaidMode() {
719
+ return this.authMode === 'gemini-api-key' || this.authMode === 'vertex-ai';
720
+ }
721
+ /**
722
+ * Clears provider state but preserves explicitly set model
723
+ */
724
+ clearState() {
725
+ // Clear auth-related state
726
+ this.authMode = 'none';
727
+ this.authDetermined = false;
728
+ // Only reset model if it wasn't explicitly set by user
729
+ if (!this.modelExplicitlySet) {
730
+ this.currentModel = 'gemini-2.5-pro';
731
+ }
732
+ // Note: We don't clear config or apiKey as they might be needed
733
+ }
734
+ /**
735
+ * Forces re-determination of auth method
736
+ */
737
+ clearAuthCache() {
738
+ this.authDetermined = false;
739
+ // Don't clear the auth mode itself, just the determination flag
740
+ // This allows for smoother transitions
741
+ }
742
+ /**
743
+ * Get the list of server tools supported by this provider
744
+ */
745
+ getServerTools() {
746
+ return ['web_search', 'web_fetch'];
747
+ }
748
+ /**
749
+ * Invoke a server tool (native provider tool)
750
+ */
751
+ async invokeServerTool(toolName, params, _config) {
752
+ if (toolName === 'web_search') {
753
+ // Import the necessary modules dynamically
754
+ const { GoogleGenAI } = await import('@google/genai');
755
+ // Create the appropriate client based on auth mode
756
+ const httpOptions = {
757
+ headers: {
758
+ 'User-Agent': `GeminiCLI/${process.env.CLI_VERSION || process.version} (${process.platform}; ${process.arch})`,
759
+ },
760
+ };
761
+ let genAI;
762
+ switch (this.authMode) {
763
+ case 'gemini-api-key': {
764
+ if (!this.apiKey && !process.env.GEMINI_API_KEY) {
765
+ throw new Error('Gemini API key required for web search');
766
+ }
767
+ genAI = new GoogleGenAI({
768
+ apiKey: this.apiKey || process.env.GEMINI_API_KEY,
769
+ httpOptions,
770
+ });
771
+ // Get the models interface (which is a ContentGenerator)
772
+ const contentGenerator = genAI.models;
773
+ const apiKeyRequest = {
774
+ model: this.currentModel,
775
+ contents: [
776
+ {
777
+ role: 'user',
778
+ parts: [{ text: params.query }],
779
+ },
780
+ ],
781
+ config: {
782
+ tools: [{ googleSearch: {} }],
783
+ },
784
+ };
785
+ const apiKeyResult = await contentGenerator.generateContent(apiKeyRequest);
786
+ return apiKeyResult;
787
+ }
788
+ case 'vertex-ai': {
789
+ if (!process.env.GOOGLE_API_KEY) {
790
+ throw new Error('Google API key required for web search');
791
+ }
792
+ genAI = new GoogleGenAI({
793
+ apiKey: process.env.GOOGLE_API_KEY,
794
+ vertexai: true,
795
+ httpOptions,
796
+ });
797
+ // Get the models interface (which is a ContentGenerator)
798
+ const vertexContentGenerator = genAI.models;
799
+ const vertexRequest = {
800
+ model: this.currentModel,
801
+ contents: [
802
+ {
803
+ role: 'user',
804
+ parts: [{ text: params.query }],
805
+ },
806
+ ],
807
+ config: {
808
+ tools: [{ googleSearch: {} }],
809
+ },
810
+ };
811
+ const vertexResult = await vertexContentGenerator.generateContent(vertexRequest);
812
+ return vertexResult;
813
+ }
814
+ case 'oauth': {
815
+ // For OAuth, use the code assist content generator
816
+ const oauthContentGenerator = await createCodeAssistContentGenerator(httpOptions, AuthType.LOGIN_WITH_GOOGLE, this.config);
817
+ const oauthModel = this.modelExplicitlySet
818
+ ? this.currentModel
819
+ : this.config?.getModel() || this.currentModel;
820
+ // For OAuth, we need to use the ContentGenerator interface
821
+ // which has a different API - it expects tools in the config
822
+ const oauthRequest = {
823
+ model: oauthModel,
824
+ contents: [
825
+ {
826
+ role: 'user',
827
+ parts: [{ text: params.query }],
828
+ },
829
+ ],
830
+ config: {
831
+ tools: [{ googleSearch: {} }],
832
+ },
833
+ };
834
+ const result = await oauthContentGenerator.generateContent(oauthRequest);
835
+ return result;
836
+ }
837
+ default:
838
+ throw new Error(`Web search not supported in auth mode: ${this.authMode}`);
839
+ }
840
+ }
841
+ else if (toolName === 'web_fetch') {
842
+ // Import the necessary modules dynamically
843
+ const { GoogleGenAI } = await import('@google/genai');
844
+ // Get the prompt directly without any processing
845
+ const prompt = params.prompt;
846
+ // Create the appropriate client based on auth mode
847
+ const httpOptions = {
848
+ headers: {
849
+ 'User-Agent': `GeminiCLI/${process.env.CLI_VERSION || process.version} (${process.platform}; ${process.arch})`,
850
+ },
851
+ };
852
+ let genAI;
853
+ switch (this.authMode) {
854
+ case 'gemini-api-key': {
855
+ if (!this.apiKey && !process.env.GEMINI_API_KEY) {
856
+ throw new Error('Gemini API key required for web fetch');
857
+ }
858
+ genAI = new GoogleGenAI({
859
+ apiKey: this.apiKey || process.env.GEMINI_API_KEY,
860
+ httpOptions,
861
+ });
862
+ // Get the models interface (which is a ContentGenerator)
863
+ const contentGenerator = genAI.models;
864
+ const apiKeyRequest = {
865
+ model: this.currentModel,
866
+ contents: [
867
+ {
868
+ role: 'user',
869
+ parts: [{ text: prompt }],
870
+ },
871
+ ],
872
+ config: {
873
+ tools: [{ urlContext: {} }],
874
+ },
875
+ };
876
+ const apiKeyResult = await contentGenerator.generateContent(apiKeyRequest);
877
+ return apiKeyResult;
878
+ }
879
+ case 'vertex-ai': {
880
+ if (!process.env.GOOGLE_API_KEY) {
881
+ throw new Error('Google API key required for web fetch');
882
+ }
883
+ genAI = new GoogleGenAI({
884
+ apiKey: process.env.GOOGLE_API_KEY,
885
+ vertexai: true,
886
+ httpOptions,
887
+ });
888
+ // Get the models interface (which is a ContentGenerator)
889
+ const vertexContentGenerator = genAI.models;
890
+ const vertexRequest = {
891
+ model: this.currentModel,
892
+ contents: [
893
+ {
894
+ role: 'user',
895
+ parts: [{ text: prompt }],
896
+ },
897
+ ],
898
+ config: {
899
+ tools: [{ urlContext: {} }],
900
+ },
901
+ };
902
+ const vertexResult = await vertexContentGenerator.generateContent(vertexRequest);
903
+ return vertexResult;
904
+ }
905
+ case 'oauth': {
906
+ // For OAuth, use the code assist content generator
907
+ const oauthContentGenerator = await createCodeAssistContentGenerator(httpOptions, AuthType.LOGIN_WITH_GOOGLE, this.config);
908
+ const oauthModel = this.modelExplicitlySet
909
+ ? this.currentModel
910
+ : this.config?.getModel() || this.currentModel;
911
+ // For OAuth, we need to use the ContentGenerator interface
912
+ // which has a different API - it expects tools in the config
913
+ const oauthRequest = {
914
+ model: oauthModel,
915
+ contents: [
916
+ {
917
+ role: 'user',
918
+ parts: [{ text: prompt }],
919
+ },
920
+ ],
921
+ config: {
922
+ tools: [{ urlContext: {} }],
923
+ },
924
+ };
925
+ const result = await oauthContentGenerator.generateContent(oauthRequest);
926
+ return result;
927
+ }
928
+ default:
929
+ throw new Error(`Web fetch not supported in auth mode: ${this.authMode}`);
930
+ }
931
+ }
932
+ else {
933
+ throw new Error(`Unknown server tool: ${toolName}`);
934
+ }
935
+ }
936
+ }
937
+ //# sourceMappingURL=GeminiProvider.js.map