@machina.ai/cell-cli-core 1.4.0-rc2 → 1.6.1-rc1

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 (421) hide show
  1. package/dist/index.d.ts +6 -4
  2. package/dist/index.js +6 -4
  3. package/dist/index.js.map +1 -1
  4. package/dist/package.json +6 -2
  5. package/dist/src/code_assist/codeAssist.js +1 -1
  6. package/dist/src/code_assist/codeAssist.js.map +1 -1
  7. package/dist/src/code_assist/converter.d.ts +1 -0
  8. package/dist/src/code_assist/converter.js +1 -0
  9. package/dist/src/code_assist/converter.js.map +1 -1
  10. package/dist/src/code_assist/converter.test.js +10 -0
  11. package/dist/src/code_assist/converter.test.js.map +1 -1
  12. package/dist/src/code_assist/oauth-credential-storage.d.ts +25 -0
  13. package/dist/src/code_assist/oauth-credential-storage.js +109 -0
  14. package/dist/src/code_assist/oauth-credential-storage.js.map +1 -0
  15. package/dist/src/code_assist/oauth-credential-storage.test.js +136 -0
  16. package/dist/src/code_assist/oauth-credential-storage.test.js.map +1 -0
  17. package/dist/src/code_assist/oauth2.js +28 -2
  18. package/dist/src/code_assist/oauth2.js.map +1 -1
  19. package/dist/src/code_assist/oauth2.test.js +674 -536
  20. package/dist/src/code_assist/oauth2.test.js.map +1 -1
  21. package/dist/src/code_assist/server.d.ts +1 -1
  22. package/dist/src/code_assist/server.js +24 -1
  23. package/dist/src/code_assist/server.js.map +1 -1
  24. package/dist/src/code_assist/server.test.js +25 -0
  25. package/dist/src/code_assist/server.test.js.map +1 -1
  26. package/dist/src/code_assist/types.d.ts +17 -2
  27. package/dist/src/config/config.d.ts +62 -5
  28. package/dist/src/config/config.js +154 -42
  29. package/dist/src/config/config.js.map +1 -1
  30. package/dist/src/config/config.test.js +235 -137
  31. package/dist/src/config/config.test.js.map +1 -1
  32. package/dist/src/config/models.d.ts +15 -0
  33. package/dist/src/config/models.js +27 -0
  34. package/dist/src/config/models.js.map +1 -1
  35. package/dist/src/config/models.test.d.ts +6 -0
  36. package/dist/src/config/models.test.js +55 -0
  37. package/dist/src/config/models.test.js.map +1 -0
  38. package/dist/src/config/storage.d.ts +2 -0
  39. package/dist/src/config/storage.js +6 -1
  40. package/dist/src/config/storage.js.map +1 -1
  41. package/dist/src/config/storage.test.js +4 -0
  42. package/dist/src/config/storage.test.js.map +1 -1
  43. package/dist/src/confirmation-bus/index.d.ts +7 -0
  44. package/dist/src/confirmation-bus/index.js +8 -0
  45. package/dist/src/confirmation-bus/index.js.map +1 -0
  46. package/dist/src/confirmation-bus/message-bus.d.ts +17 -0
  47. package/dist/src/confirmation-bus/message-bus.js +81 -0
  48. package/dist/src/confirmation-bus/message-bus.js.map +1 -0
  49. package/dist/src/confirmation-bus/message-bus.test.d.ts +6 -0
  50. package/dist/src/confirmation-bus/message-bus.test.js +164 -0
  51. package/dist/src/confirmation-bus/message-bus.test.js.map +1 -0
  52. package/dist/src/confirmation-bus/types.d.ts +38 -0
  53. package/dist/src/confirmation-bus/types.js +15 -0
  54. package/dist/src/confirmation-bus/types.js.map +1 -0
  55. package/dist/src/core/baseLlmClient.d.ts +46 -0
  56. package/dist/src/core/baseLlmClient.js +112 -0
  57. package/dist/src/core/baseLlmClient.js.map +1 -0
  58. package/dist/src/core/baseLlmClient.test.d.ts +6 -0
  59. package/dist/src/core/baseLlmClient.test.js +253 -0
  60. package/dist/src/core/baseLlmClient.test.js.map +1 -0
  61. package/dist/src/core/client.d.ts +8 -18
  62. package/dist/src/core/client.js +108 -227
  63. package/dist/src/core/client.js.map +1 -1
  64. package/dist/src/core/client.test.js +269 -491
  65. package/dist/src/core/client.test.js.map +1 -1
  66. package/dist/src/core/contentGenerator.d.ts +0 -1
  67. package/dist/src/core/contentGenerator.js +0 -4
  68. package/dist/src/core/contentGenerator.js.map +1 -1
  69. package/dist/src/core/contentGenerator.test.js +1 -3
  70. package/dist/src/core/contentGenerator.test.js.map +1 -1
  71. package/dist/src/core/coreToolScheduler.d.ts +8 -3
  72. package/dist/src/core/coreToolScheduler.js +118 -8
  73. package/dist/src/core/coreToolScheduler.js.map +1 -1
  74. package/dist/src/core/coreToolScheduler.test.js +314 -5
  75. package/dist/src/core/coreToolScheduler.test.js.map +1 -1
  76. package/dist/src/core/geminiChat.d.ts +15 -38
  77. package/dist/src/core/geminiChat.js +108 -257
  78. package/dist/src/core/geminiChat.js.map +1 -1
  79. package/dist/src/core/geminiChat.test.js +429 -491
  80. package/dist/src/core/geminiChat.test.js.map +1 -1
  81. package/dist/src/core/loggingContentGenerator.js +7 -10
  82. package/dist/src/core/loggingContentGenerator.js.map +1 -1
  83. package/dist/src/core/nonInteractiveToolExecutor.test.js +57 -1
  84. package/dist/src/core/nonInteractiveToolExecutor.test.js.map +1 -1
  85. package/dist/src/core/prompts.d.ts +5 -0
  86. package/dist/src/core/prompts.js +64 -43
  87. package/dist/src/core/prompts.js.map +1 -1
  88. package/dist/src/core/prompts.test.js +146 -17
  89. package/dist/src/core/prompts.test.js.map +1 -1
  90. package/dist/src/core/subagent.js +2 -4
  91. package/dist/src/core/subagent.js.map +1 -1
  92. package/dist/src/core/subagent.test.js +12 -13
  93. package/dist/src/core/subagent.test.js.map +1 -1
  94. package/dist/src/core/turn.d.ts +3 -1
  95. package/dist/src/core/turn.js +2 -2
  96. package/dist/src/core/turn.js.map +1 -1
  97. package/dist/src/core/turn.test.js +18 -18
  98. package/dist/src/core/turn.test.js.map +1 -1
  99. package/dist/src/fallback/handler.d.ts +7 -0
  100. package/dist/src/fallback/handler.js +51 -0
  101. package/dist/src/fallback/handler.js.map +1 -0
  102. package/dist/src/fallback/handler.test.d.ts +6 -0
  103. package/dist/src/fallback/handler.test.js +130 -0
  104. package/dist/src/fallback/handler.test.js.map +1 -0
  105. package/dist/src/fallback/types.d.ts +14 -0
  106. package/dist/src/fallback/types.js +7 -0
  107. package/dist/src/fallback/types.js.map +1 -0
  108. package/dist/src/generated/git-commit.d.ts +2 -2
  109. package/dist/src/generated/git-commit.js +2 -2
  110. package/dist/src/generated/git-commit.js.map +1 -1
  111. package/dist/src/ide/constants.d.ts +3 -0
  112. package/dist/src/ide/constants.js +3 -0
  113. package/dist/src/ide/constants.js.map +1 -1
  114. package/dist/src/ide/ide-client.d.ts +51 -13
  115. package/dist/src/ide/ide-client.js +241 -35
  116. package/dist/src/ide/ide-client.js.map +1 -1
  117. package/dist/src/ide/ide-client.test.js +236 -0
  118. package/dist/src/ide/ide-client.test.js.map +1 -1
  119. package/dist/src/ide/ide-installer.js +8 -2
  120. package/dist/src/ide/ide-installer.js.map +1 -1
  121. package/dist/src/ide/ide-installer.test.js +13 -2
  122. package/dist/src/ide/ide-installer.test.js.map +1 -1
  123. package/dist/src/ide/ideContext.d.ts +35 -377
  124. package/dist/src/ide/ideContext.js +60 -107
  125. package/dist/src/ide/ideContext.js.map +1 -1
  126. package/dist/src/ide/ideContext.test.js +152 -24
  127. package/dist/src/ide/ideContext.test.js.map +1 -1
  128. package/dist/src/ide/process-utils.js +8 -1
  129. package/dist/src/ide/process-utils.js.map +1 -1
  130. package/dist/src/ide/types.d.ts +486 -0
  131. package/dist/src/ide/types.js +138 -0
  132. package/dist/src/ide/types.js.map +1 -0
  133. package/dist/src/index.d.ts +6 -1
  134. package/dist/src/index.js +6 -1
  135. package/dist/src/index.js.map +1 -1
  136. package/dist/src/mcp/oauth-provider.d.ts +1 -0
  137. package/dist/src/mcp/oauth-provider.js +22 -17
  138. package/dist/src/mcp/oauth-provider.js.map +1 -1
  139. package/dist/src/mcp/oauth-provider.test.js +149 -13
  140. package/dist/src/mcp/oauth-provider.test.js.map +1 -1
  141. package/dist/src/mcp/oauth-token-storage.d.ts +10 -6
  142. package/dist/src/mcp/oauth-token-storage.js +48 -16
  143. package/dist/src/mcp/oauth-token-storage.js.map +1 -1
  144. package/dist/src/mcp/oauth-token-storage.test.js +254 -163
  145. package/dist/src/mcp/oauth-token-storage.test.js.map +1 -1
  146. package/dist/src/mcp/oauth-utils.js +1 -0
  147. package/dist/src/mcp/oauth-utils.js.map +1 -1
  148. package/dist/src/mcp/token-storage/index.d.ts +11 -0
  149. package/dist/src/mcp/token-storage/index.js +12 -0
  150. package/dist/src/mcp/token-storage/index.js.map +1 -0
  151. package/dist/src/output/json-formatter.d.ts +11 -0
  152. package/dist/src/output/json-formatter.js +30 -0
  153. package/dist/src/output/json-formatter.js.map +1 -0
  154. package/dist/src/output/json-formatter.test.d.ts +6 -0
  155. package/dist/src/output/json-formatter.test.js +266 -0
  156. package/dist/src/output/json-formatter.test.js.map +1 -0
  157. package/dist/src/output/types.d.ts +20 -0
  158. package/dist/src/output/types.js +11 -0
  159. package/dist/src/output/types.js.map +1 -0
  160. package/dist/src/policy/index.d.ts +7 -0
  161. package/dist/src/policy/index.js +8 -0
  162. package/dist/src/policy/index.js.map +1 -0
  163. package/dist/src/policy/policy-engine.d.ts +30 -0
  164. package/dist/src/policy/policy-engine.js +83 -0
  165. package/dist/src/policy/policy-engine.js.map +1 -0
  166. package/dist/src/policy/policy-engine.test.d.ts +6 -0
  167. package/dist/src/policy/policy-engine.test.js +470 -0
  168. package/dist/src/policy/policy-engine.test.js.map +1 -0
  169. package/dist/src/policy/stable-stringify.d.ts +58 -0
  170. package/dist/src/policy/stable-stringify.js +122 -0
  171. package/dist/src/policy/stable-stringify.js.map +1 -0
  172. package/dist/src/policy/types.d.ts +47 -0
  173. package/dist/src/policy/types.js +12 -0
  174. package/dist/src/policy/types.js.map +1 -0
  175. package/dist/src/routing/modelRouterService.d.ts +23 -0
  176. package/dist/src/routing/modelRouterService.js +70 -0
  177. package/dist/src/routing/modelRouterService.js.map +1 -0
  178. package/dist/src/routing/modelRouterService.test.d.ts +6 -0
  179. package/dist/src/routing/modelRouterService.test.js +98 -0
  180. package/dist/src/routing/modelRouterService.test.js.map +1 -0
  181. package/dist/src/routing/routingStrategy.d.ts +62 -0
  182. package/dist/src/routing/routingStrategy.js +7 -0
  183. package/dist/src/routing/routingStrategy.js.map +1 -0
  184. package/dist/src/routing/strategies/classifierStrategy.d.ts +12 -0
  185. package/dist/src/routing/strategies/classifierStrategy.js +173 -0
  186. package/dist/src/routing/strategies/classifierStrategy.js.map +1 -0
  187. package/dist/src/routing/strategies/classifierStrategy.test.d.ts +6 -0
  188. package/dist/src/routing/strategies/classifierStrategy.test.js +192 -0
  189. package/dist/src/routing/strategies/classifierStrategy.test.js.map +1 -0
  190. package/dist/src/routing/strategies/compositeStrategy.d.ts +26 -0
  191. package/dist/src/routing/strategies/compositeStrategy.js +67 -0
  192. package/dist/src/routing/strategies/compositeStrategy.js.map +1 -0
  193. package/dist/src/routing/strategies/compositeStrategy.test.d.ts +6 -0
  194. package/dist/src/routing/strategies/compositeStrategy.test.js +123 -0
  195. package/dist/src/routing/strategies/compositeStrategy.test.js.map +1 -0
  196. package/dist/src/routing/strategies/defaultStrategy.d.ts +12 -0
  197. package/dist/src/routing/strategies/defaultStrategy.js +20 -0
  198. package/dist/src/routing/strategies/defaultStrategy.js.map +1 -0
  199. package/dist/src/routing/strategies/defaultStrategy.test.d.ts +6 -0
  200. package/dist/src/routing/strategies/defaultStrategy.test.js +26 -0
  201. package/dist/src/routing/strategies/defaultStrategy.test.js.map +1 -0
  202. package/dist/src/routing/strategies/fallbackStrategy.d.ts +12 -0
  203. package/dist/src/routing/strategies/fallbackStrategy.js +25 -0
  204. package/dist/src/routing/strategies/fallbackStrategy.js.map +1 -0
  205. package/dist/src/routing/strategies/fallbackStrategy.test.d.ts +6 -0
  206. package/dist/src/routing/strategies/fallbackStrategy.test.js +55 -0
  207. package/dist/src/routing/strategies/fallbackStrategy.test.js.map +1 -0
  208. package/dist/src/routing/strategies/overrideStrategy.d.ts +15 -0
  209. package/dist/src/routing/strategies/overrideStrategy.js +28 -0
  210. package/dist/src/routing/strategies/overrideStrategy.js.map +1 -0
  211. package/dist/src/routing/strategies/overrideStrategy.test.d.ts +6 -0
  212. package/dist/src/routing/strategies/overrideStrategy.test.js +42 -0
  213. package/dist/src/routing/strategies/overrideStrategy.test.js.map +1 -0
  214. package/dist/src/services/chatRecordingService.d.ts +2 -1
  215. package/dist/src/services/chatRecordingService.js +3 -3
  216. package/dist/src/services/chatRecordingService.js.map +1 -1
  217. package/dist/src/services/chatRecordingService.test.js +8 -3
  218. package/dist/src/services/chatRecordingService.test.js.map +1 -1
  219. package/dist/src/services/fileDiscoveryService.d.ts +10 -0
  220. package/dist/src/services/fileDiscoveryService.js +31 -17
  221. package/dist/src/services/fileDiscoveryService.js.map +1 -1
  222. package/dist/src/services/gitService.js +9 -12
  223. package/dist/src/services/gitService.js.map +1 -1
  224. package/dist/src/services/gitService.test.js +10 -20
  225. package/dist/src/services/gitService.test.js.map +1 -1
  226. package/dist/src/services/loopDetectionService.d.ts +5 -0
  227. package/dist/src/services/loopDetectionService.js +36 -20
  228. package/dist/src/services/loopDetectionService.js.map +1 -1
  229. package/dist/src/services/loopDetectionService.test.js +41 -12
  230. package/dist/src/services/loopDetectionService.test.js.map +1 -1
  231. package/dist/src/services/shellExecutionService.d.ts +34 -2
  232. package/dist/src/services/shellExecutionService.js +192 -43
  233. package/dist/src/services/shellExecutionService.js.map +1 -1
  234. package/dist/src/services/shellExecutionService.test.js +184 -55
  235. package/dist/src/services/shellExecutionService.test.js.map +1 -1
  236. package/dist/src/telemetry/clearcut-logger/clearcut-logger.d.ts +14 -2
  237. package/dist/src/telemetry/clearcut-logger/clearcut-logger.js +107 -5
  238. package/dist/src/telemetry/clearcut-logger/clearcut-logger.js.map +1 -1
  239. package/dist/src/telemetry/clearcut-logger/clearcut-logger.test.js +82 -5
  240. package/dist/src/telemetry/clearcut-logger/clearcut-logger.test.js.map +1 -1
  241. package/dist/src/telemetry/clearcut-logger/event-metadata-key.d.ts +13 -2
  242. package/dist/src/telemetry/clearcut-logger/event-metadata-key.js +33 -2
  243. package/dist/src/telemetry/clearcut-logger/event-metadata-key.js.map +1 -1
  244. package/dist/src/telemetry/constants.d.ts +7 -0
  245. package/dist/src/telemetry/constants.js +7 -0
  246. package/dist/src/telemetry/constants.js.map +1 -1
  247. package/dist/src/telemetry/gcp-exporters.d.ts +34 -0
  248. package/dist/src/telemetry/gcp-exporters.js +117 -0
  249. package/dist/src/telemetry/gcp-exporters.js.map +1 -0
  250. package/dist/src/telemetry/gcp-exporters.test.d.ts +6 -0
  251. package/dist/src/telemetry/gcp-exporters.test.js +318 -0
  252. package/dist/src/telemetry/gcp-exporters.test.js.map +1 -0
  253. package/dist/src/telemetry/high-water-mark-tracker.d.ts +43 -0
  254. package/dist/src/telemetry/high-water-mark-tracker.js +88 -0
  255. package/dist/src/telemetry/high-water-mark-tracker.js.map +1 -0
  256. package/dist/src/telemetry/high-water-mark-tracker.test.d.ts +6 -0
  257. package/dist/src/telemetry/high-water-mark-tracker.test.js +152 -0
  258. package/dist/src/telemetry/high-water-mark-tracker.test.js.map +1 -0
  259. package/dist/src/telemetry/index.d.ts +5 -2
  260. package/dist/src/telemetry/index.js +5 -2
  261. package/dist/src/telemetry/index.js.map +1 -1
  262. package/dist/src/telemetry/loggers.d.ts +8 -1
  263. package/dist/src/telemetry/loggers.js +114 -7
  264. package/dist/src/telemetry/loggers.js.map +1 -1
  265. package/dist/src/telemetry/loggers.test.js +232 -39
  266. package/dist/src/telemetry/loggers.test.js.map +1 -1
  267. package/dist/src/telemetry/metrics.d.ts +3 -1
  268. package/dist/src/telemetry/metrics.js +32 -3
  269. package/dist/src/telemetry/metrics.js.map +1 -1
  270. package/dist/src/telemetry/metrics.test.js +42 -0
  271. package/dist/src/telemetry/metrics.test.js.map +1 -1
  272. package/dist/src/telemetry/rate-limiter.d.ts +48 -0
  273. package/dist/src/telemetry/rate-limiter.js +100 -0
  274. package/dist/src/telemetry/rate-limiter.js.map +1 -0
  275. package/dist/src/telemetry/rate-limiter.test.d.ts +6 -0
  276. package/dist/src/telemetry/rate-limiter.test.js +207 -0
  277. package/dist/src/telemetry/rate-limiter.test.js.map +1 -0
  278. package/dist/src/telemetry/sdk.js +19 -1
  279. package/dist/src/telemetry/sdk.js.map +1 -1
  280. package/dist/src/telemetry/sdk.test.js +95 -0
  281. package/dist/src/telemetry/sdk.test.js.map +1 -1
  282. package/dist/src/telemetry/types.d.ts +60 -3
  283. package/dist/src/telemetry/types.js +93 -3
  284. package/dist/src/telemetry/types.js.map +1 -1
  285. package/dist/src/tools/edit.js +12 -5
  286. package/dist/src/tools/edit.js.map +1 -1
  287. package/dist/src/tools/edit.test.js +120 -9
  288. package/dist/src/tools/edit.test.js.map +1 -1
  289. package/dist/src/tools/glob.d.ts +5 -1
  290. package/dist/src/tools/glob.js +24 -17
  291. package/dist/src/tools/glob.js.map +1 -1
  292. package/dist/src/tools/glob.test.js +51 -0
  293. package/dist/src/tools/glob.test.js.map +1 -1
  294. package/dist/src/tools/ls.js +19 -32
  295. package/dist/src/tools/ls.js.map +1 -1
  296. package/dist/src/tools/ls.test.js +140 -280
  297. package/dist/src/tools/ls.test.js.map +1 -1
  298. package/dist/src/tools/mcp-client-manager.js +5 -21
  299. package/dist/src/tools/mcp-client-manager.js.map +1 -1
  300. package/dist/src/tools/mcp-client.js +5 -5
  301. package/dist/src/tools/mcp-client.js.map +1 -1
  302. package/dist/src/tools/mcp-tool.js +30 -2
  303. package/dist/src/tools/mcp-tool.js.map +1 -1
  304. package/dist/src/tools/mcp-tool.test.js +117 -0
  305. package/dist/src/tools/mcp-tool.test.js.map +1 -1
  306. package/dist/src/tools/read-file.js +7 -2
  307. package/dist/src/tools/read-file.js.map +1 -1
  308. package/dist/src/tools/read-file.test.js +29 -0
  309. package/dist/src/tools/read-file.test.js.map +1 -1
  310. package/dist/src/tools/read-many-files.d.ts +1 -1
  311. package/dist/src/tools/read-many-files.js +17 -49
  312. package/dist/src/tools/read-many-files.js.map +1 -1
  313. package/dist/src/tools/ripGrep.d.ts +8 -0
  314. package/dist/src/tools/ripGrep.js +26 -1
  315. package/dist/src/tools/ripGrep.js.map +1 -1
  316. package/dist/src/tools/ripGrep.test.js +107 -5
  317. package/dist/src/tools/ripGrep.test.js.map +1 -1
  318. package/dist/src/tools/shell.d.ts +12 -2
  319. package/dist/src/tools/shell.js +20 -27
  320. package/dist/src/tools/shell.js.map +1 -1
  321. package/dist/src/tools/shell.test.js +33 -68
  322. package/dist/src/tools/shell.test.js.map +1 -1
  323. package/dist/src/tools/smart-edit.d.ts +0 -1
  324. package/dist/src/tools/smart-edit.js +12 -19
  325. package/dist/src/tools/smart-edit.js.map +1 -1
  326. package/dist/src/tools/smart-edit.test.js +68 -9
  327. package/dist/src/tools/smart-edit.test.js.map +1 -1
  328. package/dist/src/tools/tool-registry.js +1 -0
  329. package/dist/src/tools/tool-registry.js.map +1 -1
  330. package/dist/src/tools/tools.d.ts +8 -5
  331. package/dist/src/tools/tools.js +9 -2
  332. package/dist/src/tools/tools.js.map +1 -1
  333. package/dist/src/tools/write-file.js +4 -5
  334. package/dist/src/tools/write-file.js.map +1 -1
  335. package/dist/src/tools/write-file.test.js +94 -10
  336. package/dist/src/tools/write-file.test.js.map +1 -1
  337. package/dist/src/utils/bfsFileSearch.js +11 -5
  338. package/dist/src/utils/bfsFileSearch.js.map +1 -1
  339. package/dist/src/utils/editCorrector.d.ts +7 -6
  340. package/dist/src/utils/editCorrector.js +61 -18
  341. package/dist/src/utils/editCorrector.js.map +1 -1
  342. package/dist/src/utils/editCorrector.test.js +30 -79
  343. package/dist/src/utils/editCorrector.test.js.map +1 -1
  344. package/dist/src/utils/editor.js +31 -44
  345. package/dist/src/utils/editor.js.map +1 -1
  346. package/dist/src/utils/editor.test.js +61 -75
  347. package/dist/src/utils/editor.test.js.map +1 -1
  348. package/dist/src/utils/errorParsing.js +2 -2
  349. package/dist/src/utils/errorParsing.js.map +1 -1
  350. package/dist/src/utils/errorParsing.test.js +7 -7
  351. package/dist/src/utils/errorParsing.test.js.map +1 -1
  352. package/dist/src/utils/errors.d.ts +6 -0
  353. package/dist/src/utils/errors.js +10 -0
  354. package/dist/src/utils/errors.js.map +1 -1
  355. package/dist/src/utils/fileUtils.d.ts +1 -0
  356. package/dist/src/utils/fileUtils.js +10 -0
  357. package/dist/src/utils/fileUtils.js.map +1 -1
  358. package/dist/src/utils/fileUtils.test.js +34 -9
  359. package/dist/src/utils/fileUtils.test.js.map +1 -1
  360. package/dist/src/utils/filesearch/crawler.test.js +1 -1
  361. package/dist/src/utils/filesearch/crawler.test.js.map +1 -1
  362. package/dist/src/utils/filesearch/fileSearch.test.js +1 -1
  363. package/dist/src/utils/filesearch/fileSearch.test.js.map +1 -1
  364. package/dist/src/utils/filesearch/ignore.test.js +1 -1
  365. package/dist/src/utils/filesearch/ignore.test.js.map +1 -1
  366. package/dist/src/utils/flashFallback.test.d.ts +6 -0
  367. package/dist/src/utils/{flashFallback.integration.test.js → flashFallback.test.js} +31 -27
  368. package/dist/src/utils/flashFallback.test.js.map +1 -0
  369. package/dist/src/utils/geminiIgnoreParser.d.ts +18 -0
  370. package/dist/src/utils/geminiIgnoreParser.js +61 -0
  371. package/dist/src/utils/geminiIgnoreParser.js.map +1 -0
  372. package/dist/src/utils/geminiIgnoreParser.test.d.ts +6 -0
  373. package/dist/src/utils/geminiIgnoreParser.test.js +50 -0
  374. package/dist/src/utils/geminiIgnoreParser.test.js.map +1 -0
  375. package/dist/src/utils/gitIgnoreParser.d.ts +3 -8
  376. package/dist/src/utils/gitIgnoreParser.js +60 -60
  377. package/dist/src/utils/gitIgnoreParser.js.map +1 -1
  378. package/dist/src/utils/gitIgnoreParser.test.js +18 -53
  379. package/dist/src/utils/gitIgnoreParser.test.js.map +1 -1
  380. package/dist/src/utils/installationManager.test.js +1 -1
  381. package/dist/src/utils/installationManager.test.js.map +1 -1
  382. package/dist/src/utils/llm-edit-fixer.d.ts +4 -3
  383. package/dist/src/utils/llm-edit-fixer.js +19 -10
  384. package/dist/src/utils/llm-edit-fixer.js.map +1 -1
  385. package/dist/src/utils/llm-edit-fixer.test.d.ts +6 -0
  386. package/dist/src/utils/llm-edit-fixer.test.js +105 -0
  387. package/dist/src/utils/llm-edit-fixer.test.js.map +1 -0
  388. package/dist/src/utils/memoryDiscovery.test.js +12 -6
  389. package/dist/src/utils/memoryDiscovery.test.js.map +1 -1
  390. package/dist/src/utils/nextSpeakerChecker.d.ts +2 -2
  391. package/dist/src/utils/nextSpeakerChecker.js +8 -2
  392. package/dist/src/utils/nextSpeakerChecker.js.map +1 -1
  393. package/dist/src/utils/nextSpeakerChecker.test.js +52 -74
  394. package/dist/src/utils/nextSpeakerChecker.test.js.map +1 -1
  395. package/dist/src/utils/promptIdContext.d.ts +7 -0
  396. package/dist/src/utils/promptIdContext.js +8 -0
  397. package/dist/src/utils/promptIdContext.js.map +1 -0
  398. package/dist/src/utils/shell-utils.d.ts +5 -0
  399. package/dist/src/utils/shell-utils.js +23 -0
  400. package/dist/src/utils/shell-utils.js.map +1 -1
  401. package/dist/src/utils/terminalSerializer.d.ts +28 -0
  402. package/dist/src/utils/terminalSerializer.js +432 -0
  403. package/dist/src/utils/terminalSerializer.js.map +1 -0
  404. package/dist/src/utils/terminalSerializer.test.d.ts +6 -0
  405. package/dist/src/utils/terminalSerializer.test.js +176 -0
  406. package/dist/src/utils/terminalSerializer.test.js.map +1 -0
  407. package/dist/src/utils/textUtils.d.ts +5 -0
  408. package/dist/src/utils/textUtils.js +14 -0
  409. package/dist/src/utils/textUtils.js.map +1 -1
  410. package/dist/src/utils/textUtils.test.d.ts +6 -0
  411. package/dist/src/utils/textUtils.test.js +59 -0
  412. package/dist/src/utils/textUtils.test.js.map +1 -0
  413. package/dist/src/utils/userAccountManager.test.js +1 -1
  414. package/dist/src/utils/userAccountManager.test.js.map +1 -1
  415. package/dist/tsconfig.tsbuildinfo +1 -1
  416. package/package.json +7 -2
  417. package/dist/src/utils/flashFallback.integration.test.js.map +0 -1
  418. package/dist/src/utils/ide-trust.d.ts +0 -10
  419. package/dist/src/utils/ide-trust.js +0 -14
  420. package/dist/src/utils/ide-trust.js.map +0 -1
  421. /package/dist/src/{utils/flashFallback.integration.test.d.ts → code_assist/oauth-credential-storage.test.d.ts} +0 -0
@@ -3,11 +3,9 @@
3
3
  * Copyright 2025 Google LLC
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
- import type { GenerateContentResponse, Content, GenerateContentConfig, SendMessageParameters, Tool } from '@google/genai';
7
- import type { ContentGenerator } from './contentGenerator.js';
6
+ import { GenerateContentResponse, type Content, type GenerateContentConfig, type SendMessageParameters, type Part, type Tool } from '@google/genai';
8
7
  import type { Config } from '../config/config.js';
9
8
  import type { StructuredError } from './turn.js';
10
- import type { CompletedToolCall } from './coreToolScheduler.js';
11
9
  import { ChatRecordingService } from '../services/chatRecordingService.js';
12
10
  export declare enum StreamEventType {
13
11
  /** A regular content chunk from the API. */
@@ -22,6 +20,7 @@ export type StreamEvent = {
22
20
  } | {
23
21
  type: StreamEventType.RETRY;
24
22
  };
23
+ export declare function isValidNonThoughtTextPart(part: Part): boolean;
25
24
  /**
26
25
  * Custom error to signal that a stream completed without valid content,
27
26
  * which should trigger a retry.
@@ -38,39 +37,12 @@ export declare class EmptyStreamError extends Error {
38
37
  */
39
38
  export declare class GeminiChat {
40
39
  private readonly config;
41
- private readonly contentGenerator;
42
40
  private readonly generationConfig;
43
41
  private history;
44
42
  private sendPromise;
45
43
  private readonly chatRecordingService;
46
- constructor(config: Config, contentGenerator: ContentGenerator, generationConfig?: GenerateContentConfig, history?: Content[]);
47
- /**
48
- * Handles falling back to Flash model when persistent 429 errors occur for OAuth users.
49
- * Uses a fallback handler if provided by the config; otherwise, returns null.
50
- */
51
- private handleFlashFallback;
44
+ constructor(config: Config, generationConfig?: GenerateContentConfig, history?: Content[]);
52
45
  setSystemInstruction(sysInstr: string): void;
53
- /**
54
- * Sends a message to the model and returns the response.
55
- *
56
- * @remarks
57
- * This method will wait for the previous message to be processed before
58
- * sending the next message.
59
- *
60
- * @see {@link Chat#sendMessageStream} for streaming method.
61
- * @param params - parameters for sending messages within a chat session.
62
- * @returns The model's response.
63
- *
64
- * @example
65
- * ```ts
66
- * const chat = ai.chats.create({model: 'gemini-2.0-flash'});
67
- * const response = await chat.sendMessage({
68
- * message: 'Why is the sky blue?'
69
- * });
70
- * console.log(response.text);
71
- * ```
72
- */
73
- sendMessage(params: SendMessageParameters, prompt_id: string): Promise<GenerateContentResponse>;
74
46
  /**
75
47
  * Sends a message to the model and returns the response in chunks.
76
48
  *
@@ -93,7 +65,7 @@ export declare class GeminiChat {
93
65
  * }
94
66
  * ```
95
67
  */
96
- sendMessageStream(params: SendMessageParameters, prompt_id: string): Promise<AsyncGenerator<StreamEvent>>;
68
+ sendMessageStream(model: string, params: SendMessageParameters, prompt_id: string): Promise<AsyncGenerator<StreamEvent>>;
97
69
  private makeApiCallAndProcessStream;
98
70
  /**
99
71
  * Returns the chat history.
@@ -128,23 +100,28 @@ export declare class GeminiChat {
128
100
  */
129
101
  addHistory(content: Content): void;
130
102
  setHistory(history: Content[]): void;
103
+ stripThoughtsFromHistory(): void;
131
104
  setTools(tools: Tool[]): void;
132
105
  maybeIncludeSchemaDepthContext(error: StructuredError): Promise<void>;
133
106
  private processStreamResponse;
134
- private recordHistory;
135
107
  /**
136
108
  * Gets the chat recording service instance.
137
109
  */
138
110
  getChatRecordingService(): ChatRecordingService;
139
- /**
140
- * Records completed tool calls with full metadata.
141
- * This is called by external components when tool calls complete, before sending responses to Gemini.
142
- */
143
- recordCompletedToolCalls(toolCalls: CompletedToolCall[]): void;
144
111
  /**
145
112
  * Extracts and records thought from thought content.
146
113
  */
147
114
  private recordThoughtFromContent;
115
+ /**
116
+ * Truncates the chunkStream right before the second function call to a
117
+ * function that mutates state. This may involve trimming parts from a chunk
118
+ * as well as omtting some chunks altogether.
119
+ *
120
+ * We do this because it improves tool call quality if the model gets
121
+ * feedback from one mutating function call before it makes the next one.
122
+ */
123
+ private stopBeforeSecondMutator;
124
+ private isMutatorFunctionCall;
148
125
  }
149
126
  /** Visible for Testing */
150
127
  export declare function isSchemaDepthError(errorMessage: string): boolean;
@@ -3,15 +3,18 @@
3
3
  * Copyright 2025 Google LLC
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
+ // DISCLAIMER: This is a copied version of https://github.com/googleapis/js-genai/blob/main/src/chats.ts with the intention of working around a key bug
7
+ // where function responses are not treated as "valid" responses: https://b.corp.google.com/issues/420354090
8
+ import { GenerateContentResponse, FinishReason, } from '@google/genai';
6
9
  import { toParts } from '../code_assist/converter.js';
7
10
  import { createUserContent } from '@google/genai';
8
11
  import { retryWithBackoff } from '../utils/retry.js';
9
- import { AuthType } from './contentGenerator.js';
10
- import { DEFAULT_GEMINI_FLASH_MODEL } from '../config/models.js';
11
- import { hasCycleInSchema } from '../tools/tools.js';
12
+ import { DEFAULT_GEMINI_FLASH_MODEL, getEffectiveModel, } from '../config/models.js';
13
+ import { hasCycleInSchema, MUTATOR_KINDS } from '../tools/tools.js';
12
14
  import { logContentRetry, logContentRetryFailure, logInvalidChunk, } from '../telemetry/loggers.js';
13
15
  import { ChatRecordingService } from '../services/chatRecordingService.js';
14
16
  import { ContentRetryEvent, ContentRetryFailureEvent, InvalidChunkEvent, } from '../telemetry/types.js';
17
+ import { handleFallback } from '../fallback/handler.js';
15
18
  import { isFunctionResponse } from '../utils/messageInspectors.js';
16
19
  import { partListUnionToString } from './geminiRequest.js';
17
20
  export var StreamEventType;
@@ -39,6 +42,16 @@ function isValidResponse(response) {
39
42
  }
40
43
  return isValidContent(content);
41
44
  }
45
+ export function isValidNonThoughtTextPart(part) {
46
+ return (typeof part.text === 'string' &&
47
+ !part.thought &&
48
+ // Technically, the model should never generate parts that have text and
49
+ // any of these but we don't trust them so check anyways.
50
+ !part.functionCall &&
51
+ !part.functionResponse &&
52
+ !part.inlineData &&
53
+ !part.fileData);
54
+ }
42
55
  function isValidContent(content) {
43
56
  if (content.parts === undefined || content.parts.length === 0) {
44
57
  return false;
@@ -122,155 +135,23 @@ export class EmptyStreamError extends Error {
122
135
  */
123
136
  export class GeminiChat {
124
137
  config;
125
- contentGenerator;
126
138
  generationConfig;
127
139
  history;
128
140
  // A promise to represent the current state of the message being sent to the
129
141
  // model.
130
142
  sendPromise = Promise.resolve();
131
143
  chatRecordingService;
132
- constructor(config, contentGenerator, generationConfig = {}, history = []) {
144
+ constructor(config, generationConfig = {}, history = []) {
133
145
  this.config = config;
134
- this.contentGenerator = contentGenerator;
135
146
  this.generationConfig = generationConfig;
136
147
  this.history = history;
137
148
  validateHistory(history);
138
149
  this.chatRecordingService = new ChatRecordingService(config);
139
150
  this.chatRecordingService.initialize();
140
151
  }
141
- /**
142
- * Handles falling back to Flash model when persistent 429 errors occur for OAuth users.
143
- * Uses a fallback handler if provided by the config; otherwise, returns null.
144
- */
145
- async handleFlashFallback(authType, error) {
146
- // Only handle fallback for OAuth users
147
- if (authType !== AuthType.LOGIN_WITH_GOOGLE) {
148
- return null;
149
- }
150
- const currentModel = this.config.getModel();
151
- const fallbackModel = DEFAULT_GEMINI_FLASH_MODEL;
152
- // Don't fallback if already using Flash model
153
- if (currentModel === fallbackModel) {
154
- return null;
155
- }
156
- // Check if config has a fallback handler (set by CLI package)
157
- const fallbackHandler = this.config.flashFallbackHandler;
158
- if (typeof fallbackHandler === 'function') {
159
- try {
160
- const accepted = await fallbackHandler(currentModel, fallbackModel, error);
161
- if (accepted !== false && accepted !== null) {
162
- this.config.setModel(fallbackModel);
163
- this.config.setFallbackMode(true);
164
- return fallbackModel;
165
- }
166
- // Check if the model was switched manually in the handler
167
- if (this.config.getModel() === fallbackModel) {
168
- return null; // Model was switched but don't continue with current prompt
169
- }
170
- }
171
- catch (error) {
172
- console.warn('Flash fallback handler failed:', error);
173
- }
174
- }
175
- return null;
176
- }
177
152
  setSystemInstruction(sysInstr) {
178
153
  this.generationConfig.systemInstruction = sysInstr;
179
154
  }
180
- /**
181
- * Sends a message to the model and returns the response.
182
- *
183
- * @remarks
184
- * This method will wait for the previous message to be processed before
185
- * sending the next message.
186
- *
187
- * @see {@link Chat#sendMessageStream} for streaming method.
188
- * @param params - parameters for sending messages within a chat session.
189
- * @returns The model's response.
190
- *
191
- * @example
192
- * ```ts
193
- * const chat = ai.chats.create({model: 'gemini-2.0-flash'});
194
- * const response = await chat.sendMessage({
195
- * message: 'Why is the sky blue?'
196
- * });
197
- * console.log(response.text);
198
- * ```
199
- */
200
- async sendMessage(params, prompt_id) {
201
- await this.sendPromise;
202
- const userContent = createUserContent(params.message);
203
- // Record user input - capture complete message with all parts (text, files, images, etc.)
204
- // but skip recording function responses (tool call results) as they should be stored in tool call records
205
- if (!isFunctionResponse(userContent)) {
206
- const userMessage = Array.isArray(params.message)
207
- ? params.message
208
- : [params.message];
209
- this.chatRecordingService.recordMessage({
210
- type: 'user',
211
- content: userMessage,
212
- });
213
- }
214
- const requestContents = this.getHistory(true).concat(userContent);
215
- let response;
216
- try {
217
- const apiCall = () => {
218
- const modelToUse = this.config.getModel() || DEFAULT_GEMINI_FLASH_MODEL;
219
- // Prevent Flash model calls immediately after quota error
220
- if (this.config.getQuotaErrorOccurred() &&
221
- modelToUse === DEFAULT_GEMINI_FLASH_MODEL) {
222
- throw new Error('Please submit a new query to continue with the Flash model.');
223
- }
224
- return this.contentGenerator.generateContent({
225
- model: modelToUse,
226
- contents: requestContents,
227
- config: { ...this.generationConfig, ...params.config },
228
- }, prompt_id);
229
- };
230
- response = await retryWithBackoff(apiCall, {
231
- shouldRetry: (error) => {
232
- // Check for known error messages and codes.
233
- if (error instanceof Error && error.message) {
234
- if (isSchemaDepthError(error.message))
235
- return false;
236
- if (error.message.includes('429'))
237
- return true;
238
- if (error.message.match(/5\d{2}/))
239
- return true;
240
- }
241
- return false; // Don't retry other errors by default
242
- },
243
- onPersistent429: async (authType, error) => await this.handleFlashFallback(authType, error),
244
- authType: this.config.getContentGeneratorConfig()?.authType,
245
- });
246
- this.sendPromise = (async () => {
247
- const outputContent = response.candidates?.[0]?.content;
248
- const modelOutput = outputContent ? [outputContent] : [];
249
- // Because the AFC input contains the entire curated chat history in
250
- // addition to the new user input, we need to truncate the AFC history
251
- // to deduplicate the existing chat history.
252
- const fullAutomaticFunctionCallingHistory = response.automaticFunctionCallingHistory;
253
- const index = this.getHistory(true).length;
254
- let automaticFunctionCallingHistory = [];
255
- if (fullAutomaticFunctionCallingHistory != null) {
256
- automaticFunctionCallingHistory =
257
- fullAutomaticFunctionCallingHistory.slice(index) ?? [];
258
- }
259
- this.recordHistory(userContent, modelOutput, automaticFunctionCallingHistory);
260
- })();
261
- await this.sendPromise.catch((error) => {
262
- // Resets sendPromise to avoid subsequent calls failing
263
- this.sendPromise = Promise.resolve();
264
- // Re-throw the error so the caller knows something went wrong.
265
- throw error;
266
- });
267
- return response;
268
- }
269
- catch (error) {
270
- this.sendPromise = Promise.resolve();
271
- throw error;
272
- }
273
- }
274
155
  /**
275
156
  * Sends a message to the model and returns the response in chunks.
276
157
  *
@@ -293,7 +174,7 @@ export class GeminiChat {
293
174
  * }
294
175
  * ```
295
176
  */
296
- async sendMessageStream(params, prompt_id) {
177
+ async sendMessageStream(model, params, prompt_id) {
297
178
  await this.sendPromise;
298
179
  let streamDoneResolver;
299
180
  const streamDonePromise = new Promise((resolve) => {
@@ -309,6 +190,7 @@ export class GeminiChat {
309
190
  : [params.message];
310
191
  const userMessageContent = partListUnionToString(toParts(userMessage));
311
192
  this.chatRecordingService.recordMessage({
193
+ model,
312
194
  type: 'user',
313
195
  content: userMessageContent,
314
196
  });
@@ -326,7 +208,7 @@ export class GeminiChat {
326
208
  if (attempt > 0) {
327
209
  yield { type: StreamEventType.RETRY };
328
210
  }
329
- const stream = await self.makeApiCallAndProcessStream(requestContents, params, prompt_id, userContent);
211
+ const stream = await self.makeApiCallAndProcessStream(model, requestContents, params, prompt_id);
330
212
  for await (const chunk of stream) {
331
213
  yield { type: StreamEventType.CHUNK, value: chunk };
332
214
  }
@@ -364,19 +246,20 @@ export class GeminiChat {
364
246
  }
365
247
  })();
366
248
  }
367
- async makeApiCallAndProcessStream(requestContents, params, prompt_id, userContent) {
249
+ async makeApiCallAndProcessStream(model, requestContents, params, prompt_id) {
368
250
  const apiCall = () => {
369
- const modelToUse = this.config.getModel();
251
+ const modelToUse = getEffectiveModel(this.config.isInFallbackMode(), model);
370
252
  if (this.config.getQuotaErrorOccurred() &&
371
253
  modelToUse === DEFAULT_GEMINI_FLASH_MODEL) {
372
254
  throw new Error('Please submit a new query to continue with the Flash model.');
373
255
  }
374
- return this.contentGenerator.generateContentStream({
256
+ return this.config.getContentGenerator().generateContentStream({
375
257
  model: modelToUse,
376
258
  contents: requestContents,
377
259
  config: { ...this.generationConfig, ...params.config },
378
260
  }, prompt_id);
379
261
  };
262
+ const onPersistent429Callback = async (authType, error) => await handleFallback(this.config, model, authType, error);
380
263
  const streamResponse = await retryWithBackoff(apiCall, {
381
264
  shouldRetry: (error) => {
382
265
  if (error instanceof Error && error.message) {
@@ -389,10 +272,10 @@ export class GeminiChat {
389
272
  }
390
273
  return false;
391
274
  },
392
- onPersistent429: async (authType, error) => await this.handleFlashFallback(authType, error),
275
+ onPersistent429: onPersistent429Callback,
393
276
  authType: this.config.getContentGeneratorConfig()?.authType,
394
277
  });
395
- return this.processStreamResponse(streamResponse, userContent);
278
+ return this.processStreamResponse(model, streamResponse);
396
279
  }
397
280
  /**
398
281
  * Returns the chat history.
@@ -440,6 +323,22 @@ export class GeminiChat {
440
323
  setHistory(history) {
441
324
  this.history = history;
442
325
  }
326
+ stripThoughtsFromHistory() {
327
+ this.history = this.history.map((content) => {
328
+ const newContent = { ...content };
329
+ if (newContent.parts) {
330
+ newContent.parts = newContent.parts.map((part) => {
331
+ if (part && typeof part === 'object' && 'thoughtSignature' in part) {
332
+ const newPart = { ...part };
333
+ delete newPart.thoughtSignature;
334
+ return newPart;
335
+ }
336
+ return part;
337
+ });
338
+ }
339
+ return newContent;
340
+ });
341
+ }
443
342
  setTools(tools) {
444
343
  this.generationConfig.tools = tools;
445
344
  }
@@ -465,14 +364,14 @@ export class GeminiChat {
465
364
  }
466
365
  }
467
366
  }
468
- async *processStreamResponse(streamResponse, userInput) {
367
+ async *processStreamResponse(model, streamResponse) {
469
368
  const modelResponseParts = [];
470
369
  let hasReceivedAnyChunk = false;
471
370
  let hasReceivedValidChunk = false;
472
371
  let hasToolCall = false;
473
372
  let lastChunk = null;
474
373
  let lastChunkIsInvalid = false;
475
- for await (const chunk of streamResponse) {
374
+ for await (const chunk of this.stopBeforeSecondMutator(streamResponse)) {
476
375
  hasReceivedAnyChunk = true;
477
376
  lastChunk = chunk;
478
377
  if (isValidResponse(chunk)) {
@@ -487,11 +386,7 @@ export class GeminiChat {
487
386
  if (content.parts.some((part) => part.functionCall)) {
488
387
  hasToolCall = true;
489
388
  }
490
- // Always add parts - thoughts will be filtered out later in recordHistory
491
- modelResponseParts.push(...content.parts);
492
- if (content.parts.some((part) => part.functionCall)) {
493
- hasToolCall = true;
494
- }
389
+ modelResponseParts.push(...content.parts.filter((part) => !part.thought));
495
390
  }
496
391
  }
497
392
  else {
@@ -522,106 +417,31 @@ export class GeminiChat {
522
417
  // Record model response text from the collected parts
523
418
  if (modelResponseParts.length > 0) {
524
419
  const responseText = modelResponseParts
525
- .filter((part) => part.text && !part.thought)
420
+ .filter((part) => part.text)
526
421
  .map((part) => part.text)
527
422
  .join('');
528
423
  if (responseText.trim()) {
529
424
  this.chatRecordingService.recordMessage({
425
+ model,
530
426
  type: 'gemini',
531
427
  content: responseText,
532
428
  });
533
429
  }
534
430
  }
535
- // Bundle all streamed parts into a single Content object
536
- const modelOutput = modelResponseParts.length > 0
537
- ? [{ role: 'model', parts: modelResponseParts }]
538
- : [];
539
- // Pass the raw, bundled data to the new, robust recordHistory
540
- this.recordHistory(userInput, modelOutput);
541
- }
542
- recordHistory(userInput, modelOutput, automaticFunctionCallingHistory) {
543
- // Part 1: Handle the user's turn.
544
- if (automaticFunctionCallingHistory &&
545
- automaticFunctionCallingHistory.length > 0) {
546
- this.history.push(...extractCuratedHistory(automaticFunctionCallingHistory));
547
- }
548
- else {
549
- if (this.history.length === 0 ||
550
- this.history[this.history.length - 1] !== userInput) {
551
- const lastTurn = this.history[this.history.length - 1];
552
- // The only time we don't push is if it's the *exact same* object,
553
- // which happens in streaming where we add it preemptively.
554
- if (lastTurn !== userInput) {
555
- if (lastTurn?.role === 'user') {
556
- // This is an invalid sequence.
557
- throw new Error('Cannot add a user turn after another user turn.');
558
- }
559
- this.history.push(userInput);
560
- }
561
- }
562
- }
563
- // Part 2: Process the model output into a final, consolidated list of turns.
564
- const finalModelTurns = [];
565
- for (const content of modelOutput) {
566
- // A. Preserve malformed content that has no 'parts' array.
567
- if (!content.parts) {
568
- finalModelTurns.push(content);
569
- continue;
570
- }
571
- // B. Filter out 'thought' parts.
572
- const visibleParts = content.parts.filter((part) => !part.thought);
573
- const newTurn = { ...content, parts: visibleParts };
574
- const lastTurnInFinal = finalModelTurns[finalModelTurns.length - 1];
575
- // Consolidate this new turn with the PREVIOUS turn if they are adjacent model turns.
576
- if (lastTurnInFinal &&
577
- lastTurnInFinal.role === 'model' &&
578
- newTurn.role === 'model' &&
579
- lastTurnInFinal.parts && // SAFETY CHECK: Ensure the destination has a parts array.
580
- newTurn.parts) {
581
- lastTurnInFinal.parts.push(...newTurn.parts);
431
+ // String thoughts and consolidate text parts.
432
+ const consolidatedParts = [];
433
+ for (const part of modelResponseParts) {
434
+ const lastPart = consolidatedParts[consolidatedParts.length - 1];
435
+ if (lastPart?.text &&
436
+ isValidNonThoughtTextPart(lastPart) &&
437
+ isValidNonThoughtTextPart(part)) {
438
+ lastPart.text += part.text;
582
439
  }
583
440
  else {
584
- finalModelTurns.push(newTurn);
585
- }
586
- }
587
- // Part 3: Add the processed model turns to the history, with one final consolidation pass.
588
- if (finalModelTurns.length > 0) {
589
- // Re-consolidate parts within any turns that were merged in the previous step.
590
- for (const turn of finalModelTurns) {
591
- if (turn.parts && turn.parts.length > 1) {
592
- const consolidatedParts = [];
593
- for (const part of turn.parts) {
594
- const lastPart = consolidatedParts[consolidatedParts.length - 1];
595
- if (lastPart &&
596
- // Ensure lastPart is a pure text part
597
- typeof lastPart.text === 'string' &&
598
- !lastPart.functionCall &&
599
- !lastPart.functionResponse &&
600
- !lastPart.inlineData &&
601
- !lastPart.fileData &&
602
- !lastPart.thought &&
603
- // Ensure current part is a pure text part
604
- typeof part.text === 'string' &&
605
- !part.functionCall &&
606
- !part.functionResponse &&
607
- !part.inlineData &&
608
- !part.fileData &&
609
- !part.thought) {
610
- lastPart.text += part.text;
611
- }
612
- else {
613
- consolidatedParts.push({ ...part });
614
- }
615
- }
616
- turn.parts = consolidatedParts;
617
- }
441
+ consolidatedParts.push(part);
618
442
  }
619
- this.history.push(...finalModelTurns);
620
- }
621
- else {
622
- // If, after all processing, there's NO model output, add the placeholder.
623
- this.history.push({ role: 'model', parts: [] });
624
443
  }
444
+ this.history.push({ role: 'model', parts: consolidatedParts });
625
445
  }
626
446
  /**
627
447
  * Gets the chat recording service instance.
@@ -629,26 +449,6 @@ export class GeminiChat {
629
449
  getChatRecordingService() {
630
450
  return this.chatRecordingService;
631
451
  }
632
- /**
633
- * Records completed tool calls with full metadata.
634
- * This is called by external components when tool calls complete, before sending responses to Gemini.
635
- */
636
- recordCompletedToolCalls(toolCalls) {
637
- const toolCallRecords = toolCalls.map((call) => {
638
- const resultDisplayRaw = call.response?.resultDisplay;
639
- const resultDisplay = typeof resultDisplayRaw === 'string' ? resultDisplayRaw : undefined;
640
- return {
641
- id: call.request.callId,
642
- name: call.request.name,
643
- args: call.request.args,
644
- result: call.response?.responseParts || null,
645
- status: call.status,
646
- timestamp: new Date().toISOString(),
647
- resultDisplay,
648
- };
649
- });
650
- this.chatRecordingService.recordToolCalls(toolCallRecords);
651
- }
652
452
  /**
653
453
  * Extracts and records thought from thought content.
654
454
  */
@@ -671,6 +471,57 @@ export class GeminiChat {
671
471
  });
672
472
  }
673
473
  }
474
+ /**
475
+ * Truncates the chunkStream right before the second function call to a
476
+ * function that mutates state. This may involve trimming parts from a chunk
477
+ * as well as omtting some chunks altogether.
478
+ *
479
+ * We do this because it improves tool call quality if the model gets
480
+ * feedback from one mutating function call before it makes the next one.
481
+ */
482
+ async *stopBeforeSecondMutator(chunkStream) {
483
+ let foundMutatorFunctionCall = false;
484
+ for await (const chunk of chunkStream) {
485
+ const candidate = chunk.candidates?.[0];
486
+ const content = candidate?.content;
487
+ if (!candidate || !content?.parts) {
488
+ yield chunk;
489
+ continue;
490
+ }
491
+ const truncatedParts = [];
492
+ for (const part of content.parts) {
493
+ if (this.isMutatorFunctionCall(part)) {
494
+ if (foundMutatorFunctionCall) {
495
+ // This is the second mutator call.
496
+ // Truncate and return immedaitely.
497
+ const newChunk = new GenerateContentResponse();
498
+ newChunk.candidates = [
499
+ {
500
+ ...candidate,
501
+ content: {
502
+ ...content,
503
+ parts: truncatedParts,
504
+ },
505
+ finishReason: FinishReason.STOP,
506
+ },
507
+ ];
508
+ yield newChunk;
509
+ return;
510
+ }
511
+ foundMutatorFunctionCall = true;
512
+ }
513
+ truncatedParts.push(part);
514
+ }
515
+ yield chunk;
516
+ }
517
+ }
518
+ isMutatorFunctionCall(part) {
519
+ if (!part?.functionCall?.name) {
520
+ return false;
521
+ }
522
+ const tool = this.config.getToolRegistry().getTool(part.functionCall.name);
523
+ return !!tool && MUTATOR_KINDS.includes(tool.kind);
524
+ }
674
525
  }
675
526
  /** Visible for Testing */
676
527
  export function isSchemaDepthError(errorMessage) {