@machina.ai/cell-cli-core 1.0.21-rc4 → 1.4.0-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 (505) hide show
  1. package/dist/index.d.ts +6 -2
  2. package/dist/index.js +6 -2
  3. package/dist/index.js.map +1 -1
  4. package/dist/package.json +25 -11
  5. package/dist/src/code_assist/codeAssist.d.ts +6 -3
  6. package/dist/src/code_assist/codeAssist.js +12 -0
  7. package/dist/src/code_assist/codeAssist.js.map +1 -1
  8. package/dist/src/code_assist/converter.d.ts +3 -1
  9. package/dist/src/code_assist/converter.js +37 -5
  10. package/dist/src/code_assist/converter.js.map +1 -1
  11. package/dist/src/code_assist/converter.test.js +83 -0
  12. package/dist/src/code_assist/converter.test.js.map +1 -1
  13. package/dist/src/code_assist/oauth2.d.ts +2 -1
  14. package/dist/src/code_assist/oauth2.js +85 -49
  15. package/dist/src/code_assist/oauth2.js.map +1 -1
  16. package/dist/src/code_assist/oauth2.test.js +317 -15
  17. package/dist/src/code_assist/oauth2.test.js.map +1 -1
  18. package/dist/src/code_assist/server.d.ts +5 -5
  19. package/dist/src/code_assist/server.js +1 -1
  20. package/dist/src/code_assist/server.js.map +1 -1
  21. package/dist/src/code_assist/setup.d.ts +1 -1
  22. package/dist/src/code_assist/setup.js +1 -1
  23. package/dist/src/code_assist/setup.js.map +1 -1
  24. package/dist/src/code_assist/setup.test.js.map +1 -1
  25. package/dist/src/config/config.d.ts +53 -15
  26. package/dist/src/config/config.js +127 -47
  27. package/dist/src/config/config.js.map +1 -1
  28. package/dist/src/config/config.test.js +151 -6
  29. package/dist/src/config/config.test.js.map +1 -1
  30. package/dist/src/config/models.d.ts +1 -0
  31. package/dist/src/config/models.js +2 -0
  32. package/dist/src/config/models.js.map +1 -1
  33. package/dist/src/config/storage.d.ts +32 -0
  34. package/dist/src/config/storage.js +90 -0
  35. package/dist/src/config/storage.js.map +1 -0
  36. package/dist/src/config/storage.test.js +43 -0
  37. package/dist/src/config/storage.test.js.map +1 -0
  38. package/dist/src/core/client.d.ts +21 -11
  39. package/dist/src/core/client.js +83 -26
  40. package/dist/src/core/client.js.map +1 -1
  41. package/dist/src/core/client.test.js +398 -88
  42. package/dist/src/core/client.test.js.map +1 -1
  43. package/dist/src/core/contentGenerator.d.ts +6 -6
  44. package/dist/src/core/contentGenerator.js +4 -3
  45. package/dist/src/core/contentGenerator.js.map +1 -1
  46. package/dist/src/core/contentGenerator.test.js.map +1 -1
  47. package/dist/src/core/coreToolScheduler.d.ts +14 -5
  48. package/dist/src/core/coreToolScheduler.js +120 -49
  49. package/dist/src/core/coreToolScheduler.js.map +1 -1
  50. package/dist/src/core/coreToolScheduler.test.js +383 -72
  51. package/dist/src/core/coreToolScheduler.test.js.map +1 -1
  52. package/dist/src/core/geminiChat.d.ts +48 -15
  53. package/dist/src/core/geminiChat.js +327 -154
  54. package/dist/src/core/geminiChat.js.map +1 -1
  55. package/dist/src/core/geminiChat.test.js +1041 -257
  56. package/dist/src/core/geminiChat.test.js.map +1 -1
  57. package/dist/src/core/geminiRequest.js +1 -0
  58. package/dist/src/core/geminiRequest.js.map +1 -1
  59. package/dist/src/core/logger.d.ts +4 -2
  60. package/dist/src/core/logger.js +4 -3
  61. package/dist/src/core/logger.js.map +1 -1
  62. package/dist/src/core/logger.test.js +19 -18
  63. package/dist/src/core/logger.test.js.map +1 -1
  64. package/dist/src/core/loggingContentGenerator.d.ts +3 -3
  65. package/dist/src/core/loggingContentGenerator.js +11 -9
  66. package/dist/src/core/loggingContentGenerator.js.map +1 -1
  67. package/dist/src/core/nonInteractiveToolExecutor.d.ts +3 -5
  68. package/dist/src/core/nonInteractiveToolExecutor.js +15 -123
  69. package/dist/src/core/nonInteractiveToolExecutor.js.map +1 -1
  70. package/dist/src/core/nonInteractiveToolExecutor.test.js +116 -90
  71. package/dist/src/core/nonInteractiveToolExecutor.test.js.map +1 -1
  72. package/dist/src/core/prompts.js +8 -7
  73. package/dist/src/core/prompts.js.map +1 -1
  74. package/dist/src/core/prompts.test.js +21 -21
  75. package/dist/src/core/prompts.test.js.map +1 -1
  76. package/dist/src/core/subagent.d.ts +24 -18
  77. package/dist/src/core/subagent.js +126 -89
  78. package/dist/src/core/subagent.js.map +1 -1
  79. package/dist/src/core/subagent.test.js +51 -35
  80. package/dist/src/core/subagent.test.js.map +1 -1
  81. package/dist/src/core/turn.d.ts +33 -8
  82. package/dist/src/core/turn.js +59 -14
  83. package/dist/src/core/turn.js.map +1 -1
  84. package/dist/src/core/turn.test.js +349 -90
  85. package/dist/src/core/turn.test.js.map +1 -1
  86. package/dist/src/generated/git-commit.d.ts +2 -2
  87. package/dist/src/generated/git-commit.js +2 -2
  88. package/dist/src/generated/git-commit.js.map +1 -1
  89. package/dist/src/ide/constants.d.ts +1 -1
  90. package/dist/src/ide/constants.js +1 -1
  91. package/dist/src/ide/constants.js.map +1 -1
  92. package/dist/src/ide/detect-ide.d.ts +8 -3
  93. package/dist/src/ide/detect-ide.js +29 -11
  94. package/dist/src/ide/detect-ide.js.map +1 -1
  95. package/dist/src/ide/detect-ide.test.js +96 -52
  96. package/dist/src/ide/detect-ide.test.js.map +1 -1
  97. package/dist/src/ide/ide-client.d.ts +18 -9
  98. package/dist/src/ide/ide-client.js +151 -33
  99. package/dist/src/ide/ide-client.js.map +1 -1
  100. package/dist/src/ide/ide-client.test.js +147 -25
  101. package/dist/src/ide/ide-client.test.js.map +1 -1
  102. package/dist/src/ide/ide-installer.d.ts +1 -1
  103. package/dist/src/ide/ide-installer.js +31 -22
  104. package/dist/src/ide/ide-installer.js.map +1 -1
  105. package/dist/src/ide/ide-installer.test.js +82 -22
  106. package/dist/src/ide/ide-installer.test.js.map +1 -1
  107. package/dist/src/ide/ideContext.d.ts +12 -0
  108. package/dist/src/ide/ideContext.js +1 -0
  109. package/dist/src/ide/ideContext.js.map +1 -1
  110. package/dist/src/ide/process-utils.d.ts +13 -6
  111. package/dist/src/ide/process-utils.js +142 -35
  112. package/dist/src/ide/process-utils.js.map +1 -1
  113. package/dist/src/ide/process-utils.test.js +158 -0
  114. package/dist/src/ide/process-utils.test.js.map +1 -0
  115. package/dist/src/index.d.ts +12 -2
  116. package/dist/src/index.js +11 -1
  117. package/dist/src/index.js.map +1 -1
  118. package/dist/src/mcp/google-auth-provider.d.ts +3 -3
  119. package/dist/src/mcp/google-auth-provider.test.js.map +1 -1
  120. package/dist/src/mcp/oauth-provider.d.ts +13 -13
  121. package/dist/src/mcp/oauth-provider.js +32 -31
  122. package/dist/src/mcp/oauth-provider.js.map +1 -1
  123. package/dist/src/mcp/oauth-provider.test.js +75 -36
  124. package/dist/src/mcp/oauth-provider.test.js.map +1 -1
  125. package/dist/src/mcp/oauth-token-storage.d.ts +9 -31
  126. package/dist/src/mcp/oauth-token-storage.js +10 -13
  127. package/dist/src/mcp/oauth-token-storage.js.map +1 -1
  128. package/dist/src/mcp/oauth-token-storage.test.js +30 -27
  129. package/dist/src/mcp/oauth-token-storage.test.js.map +1 -1
  130. package/dist/src/mcp/oauth-utils.d.ts +9 -1
  131. package/dist/src/mcp/oauth-utils.js +41 -27
  132. package/dist/src/mcp/oauth-utils.js.map +1 -1
  133. package/dist/src/mcp/oauth-utils.test.js +41 -1
  134. package/dist/src/mcp/oauth-utils.test.js.map +1 -1
  135. package/dist/src/mcp/token-storage/base-token-storage.d.ts +19 -0
  136. package/dist/src/mcp/token-storage/base-token-storage.js +36 -0
  137. package/dist/src/mcp/token-storage/base-token-storage.js.map +1 -0
  138. package/dist/src/mcp/token-storage/base-token-storage.test.d.ts +6 -0
  139. package/dist/src/mcp/token-storage/base-token-storage.test.js +160 -0
  140. package/dist/src/mcp/token-storage/base-token-storage.test.js.map +1 -0
  141. package/dist/src/mcp/token-storage/file-token-storage.d.ts +24 -0
  142. package/dist/src/mcp/token-storage/file-token-storage.js +144 -0
  143. package/dist/src/mcp/token-storage/file-token-storage.js.map +1 -0
  144. package/dist/src/mcp/token-storage/file-token-storage.test.d.ts +6 -0
  145. package/dist/src/mcp/token-storage/file-token-storage.test.js +235 -0
  146. package/dist/src/mcp/token-storage/file-token-storage.test.js.map +1 -0
  147. package/dist/src/mcp/token-storage/hybrid-token-storage.d.ts +23 -0
  148. package/dist/src/mcp/token-storage/hybrid-token-storage.js +78 -0
  149. package/dist/src/mcp/token-storage/hybrid-token-storage.js.map +1 -0
  150. package/dist/src/mcp/token-storage/hybrid-token-storage.test.d.ts +6 -0
  151. package/dist/src/mcp/token-storage/hybrid-token-storage.test.js +193 -0
  152. package/dist/src/mcp/token-storage/hybrid-token-storage.test.js.map +1 -0
  153. package/dist/src/mcp/token-storage/keychain-token-storage.d.ts +31 -0
  154. package/dist/src/mcp/token-storage/keychain-token-storage.js +190 -0
  155. package/dist/src/mcp/token-storage/keychain-token-storage.js.map +1 -0
  156. package/dist/src/mcp/token-storage/keychain-token-storage.test.d.ts +6 -0
  157. package/dist/src/mcp/token-storage/keychain-token-storage.test.js +254 -0
  158. package/dist/src/mcp/token-storage/keychain-token-storage.test.js.map +1 -0
  159. package/dist/src/mcp/token-storage/types.d.ts +38 -0
  160. package/dist/src/mcp/token-storage/types.js +11 -0
  161. package/dist/src/mcp/token-storage/types.js.map +1 -0
  162. package/dist/src/prompts/mcp-prompts.d.ts +2 -2
  163. package/dist/src/prompts/prompt-registry.d.ts +1 -1
  164. package/dist/src/services/chatRecordingService.d.ts +6 -13
  165. package/dist/src/services/chatRecordingService.js +31 -19
  166. package/dist/src/services/chatRecordingService.js.map +1 -1
  167. package/dist/src/services/chatRecordingService.test.js +64 -25
  168. package/dist/src/services/chatRecordingService.test.js.map +1 -1
  169. package/dist/src/services/fileDiscoveryService.js +1 -1
  170. package/dist/src/services/fileDiscoveryService.js.map +1 -1
  171. package/dist/src/services/fileDiscoveryService.test.js +3 -3
  172. package/dist/src/services/fileDiscoveryService.test.js.map +1 -1
  173. package/dist/src/services/fileSystemService.js +1 -1
  174. package/dist/src/services/fileSystemService.js.map +1 -1
  175. package/dist/src/services/fileSystemService.test.js +1 -1
  176. package/dist/src/services/fileSystemService.test.js.map +1 -1
  177. package/dist/src/services/gitService.d.ts +3 -1
  178. package/dist/src/services/gitService.js +21 -12
  179. package/dist/src/services/gitService.js.map +1 -1
  180. package/dist/src/services/gitService.test.js +22 -19
  181. package/dist/src/services/gitService.test.js.map +1 -1
  182. package/dist/src/services/loopDetectionService.d.ts +3 -2
  183. package/dist/src/services/loopDetectionService.js +28 -4
  184. package/dist/src/services/loopDetectionService.js.map +1 -1
  185. package/dist/src/services/loopDetectionService.test.js +23 -1
  186. package/dist/src/services/loopDetectionService.test.js.map +1 -1
  187. package/dist/src/services/shellExecutionService.d.ts +8 -10
  188. package/dist/src/services/shellExecutionService.js +292 -135
  189. package/dist/src/services/shellExecutionService.js.map +1 -1
  190. package/dist/src/services/shellExecutionService.test.js +277 -42
  191. package/dist/src/services/shellExecutionService.test.js.map +1 -1
  192. package/dist/src/telemetry/clearcut-logger/clearcut-logger.d.ts +18 -4
  193. package/dist/src/telemetry/clearcut-logger/clearcut-logger.js +171 -11
  194. package/dist/src/telemetry/clearcut-logger/clearcut-logger.js.map +1 -1
  195. package/dist/src/telemetry/clearcut-logger/clearcut-logger.test.js +103 -11
  196. package/dist/src/telemetry/clearcut-logger/clearcut-logger.test.js.map +1 -1
  197. package/dist/src/telemetry/clearcut-logger/event-metadata-key.d.ts +31 -1
  198. package/dist/src/telemetry/clearcut-logger/event-metadata-key.js +75 -0
  199. package/dist/src/telemetry/clearcut-logger/event-metadata-key.js.map +1 -1
  200. package/dist/src/telemetry/constants.d.ts +9 -0
  201. package/dist/src/telemetry/constants.js +9 -0
  202. package/dist/src/telemetry/constants.js.map +1 -1
  203. package/dist/src/telemetry/file-exporters.d.ts +5 -4
  204. package/dist/src/telemetry/file-exporters.js +1 -1
  205. package/dist/src/telemetry/file-exporters.js.map +1 -1
  206. package/dist/src/telemetry/index.d.ts +5 -2
  207. package/dist/src/telemetry/index.js +3 -2
  208. package/dist/src/telemetry/index.js.map +1 -1
  209. package/dist/src/telemetry/loggers.d.ts +8 -2
  210. package/dist/src/telemetry/loggers.js +130 -2
  211. package/dist/src/telemetry/loggers.js.map +1 -1
  212. package/dist/src/telemetry/loggers.test.circular.js.map +1 -1
  213. package/dist/src/telemetry/loggers.test.js +105 -9
  214. package/dist/src/telemetry/loggers.test.js.map +1 -1
  215. package/dist/src/telemetry/metrics.d.ts +15 -4
  216. package/dist/src/telemetry/metrics.js +46 -8
  217. package/dist/src/telemetry/metrics.js.map +1 -1
  218. package/dist/src/telemetry/metrics.test.js +5 -25
  219. package/dist/src/telemetry/metrics.test.js.map +1 -1
  220. package/dist/src/telemetry/sdk.d.ts +1 -1
  221. package/dist/src/telemetry/sdk.js +3 -3
  222. package/dist/src/telemetry/sdk.js.map +1 -1
  223. package/dist/src/telemetry/telemetry-utils.d.ts +6 -0
  224. package/dist/src/telemetry/telemetry-utils.js +14 -0
  225. package/dist/src/telemetry/telemetry-utils.js.map +1 -0
  226. package/dist/src/telemetry/telemetry-utils.test.d.ts +6 -0
  227. package/dist/src/telemetry/telemetry-utils.test.js +40 -0
  228. package/dist/src/telemetry/telemetry-utils.test.js.map +1 -0
  229. package/dist/src/telemetry/types.d.ts +61 -6
  230. package/dist/src/telemetry/types.js +105 -4
  231. package/dist/src/telemetry/types.js.map +1 -1
  232. package/dist/src/telemetry/uiTelemetry.d.ts +2 -2
  233. package/dist/src/telemetry/uiTelemetry.js +5 -5
  234. package/dist/src/telemetry/uiTelemetry.js.map +1 -1
  235. package/dist/src/telemetry/uiTelemetry.test.js +20 -16
  236. package/dist/src/telemetry/uiTelemetry.test.js.map +1 -1
  237. package/dist/src/test-utils/config.d.ts +2 -1
  238. package/dist/src/test-utils/config.js.map +1 -1
  239. package/dist/src/test-utils/index.d.ts +6 -0
  240. package/dist/src/test-utils/index.js +7 -0
  241. package/dist/src/test-utils/index.js.map +1 -0
  242. package/dist/src/test-utils/mock-tool.d.ts +41 -0
  243. package/dist/src/test-utils/mock-tool.js +51 -0
  244. package/dist/src/test-utils/mock-tool.js.map +1 -0
  245. package/dist/src/test-utils/mockWorkspaceContext.d.ts +1 -1
  246. package/dist/src/test-utils/tools.d.ts +3 -2
  247. package/dist/src/test-utils/tools.js.map +1 -1
  248. package/dist/src/tools/diffOptions.d.ts +1 -1
  249. package/dist/src/tools/diffOptions.js +21 -13
  250. package/dist/src/tools/diffOptions.js.map +1 -1
  251. package/dist/src/tools/diffOptions.test.js +58 -22
  252. package/dist/src/tools/diffOptions.test.js.map +1 -1
  253. package/dist/src/tools/edit.d.ts +6 -5
  254. package/dist/src/tools/edit.js +47 -36
  255. package/dist/src/tools/edit.js.map +1 -1
  256. package/dist/src/tools/edit.test.js +77 -12
  257. package/dist/src/tools/edit.test.js.map +1 -1
  258. package/dist/src/tools/glob.d.ts +3 -2
  259. package/dist/src/tools/glob.js +17 -6
  260. package/dist/src/tools/glob.js.map +1 -1
  261. package/dist/src/tools/glob.test.js +29 -4
  262. package/dist/src/tools/glob.test.js.map +1 -1
  263. package/dist/src/tools/grep.d.ts +3 -2
  264. package/dist/src/tools/grep.js +35 -15
  265. package/dist/src/tools/grep.js.map +1 -1
  266. package/dist/src/tools/grep.test.js +26 -3
  267. package/dist/src/tools/grep.test.js.map +1 -1
  268. package/dist/src/tools/ls.d.ts +3 -2
  269. package/dist/src/tools/ls.js +12 -7
  270. package/dist/src/tools/ls.js.map +1 -1
  271. package/dist/src/tools/ls.test.js +7 -2
  272. package/dist/src/tools/ls.test.js.map +1 -1
  273. package/dist/src/tools/mcp-client-manager.d.ts +8 -6
  274. package/dist/src/tools/mcp-client-manager.js +30 -5
  275. package/dist/src/tools/mcp-client-manager.js.map +1 -1
  276. package/dist/src/tools/mcp-client-manager.test.js +20 -1
  277. package/dist/src/tools/mcp-client-manager.test.js.map +1 -1
  278. package/dist/src/tools/mcp-client.d.ts +18 -11
  279. package/dist/src/tools/mcp-client.js +67 -57
  280. package/dist/src/tools/mcp-client.js.map +1 -1
  281. package/dist/src/tools/mcp-client.test.js +29 -4
  282. package/dist/src/tools/mcp-client.test.js.map +1 -1
  283. package/dist/src/tools/mcp-tool.d.ts +6 -4
  284. package/dist/src/tools/mcp-tool.js +21 -11
  285. package/dist/src/tools/mcp-tool.js.map +1 -1
  286. package/dist/src/tools/mcp-tool.test.js +49 -12
  287. package/dist/src/tools/mcp-tool.test.js.map +1 -1
  288. package/dist/src/tools/memoryTool.d.ts +4 -3
  289. package/dist/src/tools/memoryTool.js +15 -38
  290. package/dist/src/tools/memoryTool.js.map +1 -1
  291. package/dist/src/tools/memoryTool.test.js +24 -12
  292. package/dist/src/tools/memoryTool.test.js.map +1 -1
  293. package/dist/src/tools/modifiable-tool.d.ts +2 -2
  294. package/dist/src/tools/modifiable-tool.js +3 -3
  295. package/dist/src/tools/modifiable-tool.js.map +1 -1
  296. package/dist/src/tools/modifiable-tool.test.js +4 -4
  297. package/dist/src/tools/modifiable-tool.test.js.map +1 -1
  298. package/dist/src/tools/read-file.d.ts +3 -2
  299. package/dist/src/tools/read-file.js +12 -34
  300. package/dist/src/tools/read-file.js.map +1 -1
  301. package/dist/src/tools/read-file.test.js +9 -6
  302. package/dist/src/tools/read-file.test.js.map +1 -1
  303. package/dist/src/tools/read-many-files.d.ts +3 -2
  304. package/dist/src/tools/read-many-files.js +35 -58
  305. package/dist/src/tools/read-many-files.js.map +1 -1
  306. package/dist/src/tools/read-many-files.test.js +64 -11
  307. package/dist/src/tools/read-many-files.test.js.map +1 -1
  308. package/dist/src/tools/ripGrep.d.ts +47 -0
  309. package/dist/src/tools/ripGrep.js +368 -0
  310. package/dist/src/tools/ripGrep.js.map +1 -0
  311. package/dist/src/tools/ripGrep.test.d.ts +6 -0
  312. package/dist/src/tools/ripGrep.test.js +874 -0
  313. package/dist/src/tools/ripGrep.test.js.map +1 -0
  314. package/dist/src/tools/shell.d.ts +3 -2
  315. package/dist/src/tools/shell.js +30 -25
  316. package/dist/src/tools/shell.js.map +1 -1
  317. package/dist/src/tools/shell.test.js +34 -25
  318. package/dist/src/tools/shell.test.js.map +1 -1
  319. package/dist/src/tools/smart-edit.d.ts +73 -0
  320. package/dist/src/tools/smart-edit.js +607 -0
  321. package/dist/src/tools/smart-edit.js.map +1 -0
  322. package/dist/src/tools/smart-edit.test.d.ts +6 -0
  323. package/dist/src/tools/smart-edit.test.js +405 -0
  324. package/dist/src/tools/smart-edit.test.js.map +1 -0
  325. package/dist/src/tools/tool-error.d.ts +17 -1
  326. package/dist/src/tools/tool-error.js +26 -0
  327. package/dist/src/tools/tool-error.js.map +1 -1
  328. package/dist/src/tools/tool-registry.d.ts +10 -4
  329. package/dist/src/tools/tool-registry.js +19 -7
  330. package/dist/src/tools/tool-registry.js.map +1 -1
  331. package/dist/src/tools/tool-registry.test.js +86 -3
  332. package/dist/src/tools/tool-registry.test.js.map +1 -1
  333. package/dist/src/tools/tools.d.ts +15 -9
  334. package/dist/src/tools/tools.js +12 -0
  335. package/dist/src/tools/tools.js.map +1 -1
  336. package/dist/src/tools/tools.test.js +1 -2
  337. package/dist/src/tools/tools.test.js.map +1 -1
  338. package/dist/src/tools/web-fetch.d.ts +3 -2
  339. package/dist/src/tools/web-fetch.js +14 -10
  340. package/dist/src/tools/web-fetch.js.map +1 -1
  341. package/dist/src/tools/web-fetch.test.js +55 -16
  342. package/dist/src/tools/web-fetch.test.js.map +1 -1
  343. package/dist/src/tools/web-search.d.ts +4 -3
  344. package/dist/src/tools/web-search.js +31 -8
  345. package/dist/src/tools/web-search.js.map +1 -1
  346. package/dist/src/tools/web-search.test.js +69 -1
  347. package/dist/src/tools/web-search.test.js.map +1 -1
  348. package/dist/src/tools/write-file.d.ts +4 -3
  349. package/dist/src/tools/write-file.js +14 -14
  350. package/dist/src/tools/write-file.js.map +1 -1
  351. package/dist/src/tools/write-file.test.js +14 -14
  352. package/dist/src/tools/write-file.test.js.map +1 -1
  353. package/dist/src/utils/bfsFileSearch.d.ts +2 -2
  354. package/dist/src/utils/bfsFileSearch.js +2 -2
  355. package/dist/src/utils/bfsFileSearch.js.map +1 -1
  356. package/dist/src/utils/bfsFileSearch.test.js +3 -3
  357. package/dist/src/utils/bfsFileSearch.test.js.map +1 -1
  358. package/dist/src/utils/editCorrector.d.ts +2 -2
  359. package/dist/src/utils/editCorrector.js +1 -1
  360. package/dist/src/utils/editCorrector.js.map +1 -1
  361. package/dist/src/utils/editCorrector.test.js +3 -3
  362. package/dist/src/utils/editCorrector.test.js.map +1 -1
  363. package/dist/src/utils/editor.js +2 -2
  364. package/dist/src/utils/editor.js.map +1 -1
  365. package/dist/src/utils/editor.test.js +2 -2
  366. package/dist/src/utils/editor.test.js.map +1 -1
  367. package/dist/src/utils/environmentContext.d.ts +2 -2
  368. package/dist/src/utils/environmentContext.js +1 -1
  369. package/dist/src/utils/environmentContext.js.map +1 -1
  370. package/dist/src/utils/environmentContext.test.js +1 -1
  371. package/dist/src/utils/environmentContext.test.js.map +1 -1
  372. package/dist/src/utils/errorReporting.d.ts +1 -1
  373. package/dist/src/utils/errors.d.ts +19 -0
  374. package/dist/src/utils/errors.js +32 -0
  375. package/dist/src/utils/errors.js.map +1 -1
  376. package/dist/src/utils/fetch.js +1 -1
  377. package/dist/src/utils/fetch.js.map +1 -1
  378. package/dist/src/utils/fileUtils.d.ts +23 -12
  379. package/dist/src/utils/fileUtils.js +160 -79
  380. package/dist/src/utils/fileUtils.js.map +1 -1
  381. package/dist/src/utils/fileUtils.test.js +314 -21
  382. package/dist/src/utils/fileUtils.test.js.map +1 -1
  383. package/dist/src/utils/filesearch/crawler.d.ts +1 -1
  384. package/dist/src/utils/filesearch/crawler.test.js +2 -2
  385. package/dist/src/utils/filesearch/crawler.test.js.map +1 -1
  386. package/dist/src/utils/filesearch/fileSearch.d.ts +1 -0
  387. package/dist/src/utils/filesearch/fileSearch.js +14 -9
  388. package/dist/src/utils/filesearch/fileSearch.js.map +1 -1
  389. package/dist/src/utils/filesearch/fileSearch.test.js +90 -0
  390. package/dist/src/utils/filesearch/fileSearch.test.js.map +1 -1
  391. package/dist/src/utils/generateContentResponseUtilities.d.ts +1 -2
  392. package/dist/src/utils/generateContentResponseUtilities.js +1 -13
  393. package/dist/src/utils/generateContentResponseUtilities.js.map +1 -1
  394. package/dist/src/utils/generateContentResponseUtilities.test.js +2 -40
  395. package/dist/src/utils/generateContentResponseUtilities.test.js.map +1 -1
  396. package/dist/src/utils/getFolderStructure.d.ts +2 -2
  397. package/dist/src/utils/getFolderStructure.js +2 -2
  398. package/dist/src/utils/getFolderStructure.js.map +1 -1
  399. package/dist/src/utils/getFolderStructure.test.js +13 -13
  400. package/dist/src/utils/getFolderStructure.test.js.map +1 -1
  401. package/dist/src/utils/getPty.d.ts +19 -0
  402. package/dist/src/utils/getPty.js +23 -0
  403. package/dist/src/utils/getPty.js.map +1 -0
  404. package/dist/src/utils/gitIgnoreParser.d.ts +1 -0
  405. package/dist/src/utils/gitIgnoreParser.js +104 -13
  406. package/dist/src/utils/gitIgnoreParser.js.map +1 -1
  407. package/dist/src/utils/gitIgnoreParser.test.js +69 -3
  408. package/dist/src/utils/gitIgnoreParser.test.js.map +1 -1
  409. package/dist/src/utils/gitUtils.js +2 -2
  410. package/dist/src/utils/gitUtils.js.map +1 -1
  411. package/dist/src/utils/ide-trust.d.ts +10 -0
  412. package/dist/src/utils/ide-trust.js +14 -0
  413. package/dist/src/utils/ide-trust.js.map +1 -0
  414. package/dist/src/utils/ignorePatterns.d.ts +103 -0
  415. package/dist/src/utils/ignorePatterns.js +220 -0
  416. package/dist/src/utils/ignorePatterns.js.map +1 -0
  417. package/dist/src/utils/ignorePatterns.test.d.ts +6 -0
  418. package/dist/src/utils/ignorePatterns.test.js +250 -0
  419. package/dist/src/utils/ignorePatterns.test.js.map +1 -0
  420. package/dist/src/utils/installationManager.d.ts +16 -0
  421. package/dist/src/utils/installationManager.js +50 -0
  422. package/dist/src/utils/installationManager.js.map +1 -0
  423. package/dist/src/utils/installationManager.test.d.ts +6 -0
  424. package/dist/src/utils/installationManager.test.js +83 -0
  425. package/dist/src/utils/installationManager.test.js.map +1 -0
  426. package/dist/src/utils/language-detection.d.ts +6 -0
  427. package/dist/src/utils/language-detection.js +101 -0
  428. package/dist/src/utils/language-detection.js.map +1 -0
  429. package/dist/src/utils/llm-edit-fixer.d.ts +25 -0
  430. package/dist/src/utils/llm-edit-fixer.js +112 -0
  431. package/dist/src/utils/llm-edit-fixer.js.map +1 -0
  432. package/dist/src/utils/memoryDiscovery.d.ts +7 -6
  433. package/dist/src/utils/memoryDiscovery.js +68 -33
  434. package/dist/src/utils/memoryDiscovery.js.map +1 -1
  435. package/dist/src/utils/memoryDiscovery.test.js +76 -20
  436. package/dist/src/utils/memoryDiscovery.test.js.map +1 -1
  437. package/dist/src/utils/memoryImportProcessor.js +2 -2
  438. package/dist/src/utils/memoryImportProcessor.js.map +1 -1
  439. package/dist/src/utils/memoryImportProcessor.test.js +2 -141
  440. package/dist/src/utils/memoryImportProcessor.test.js.map +1 -1
  441. package/dist/src/utils/messageInspectors.d.ts +1 -1
  442. package/dist/src/utils/nextSpeakerChecker.d.ts +2 -2
  443. package/dist/src/utils/nextSpeakerChecker.test.js +33 -0
  444. package/dist/src/utils/nextSpeakerChecker.test.js.map +1 -1
  445. package/dist/src/utils/partUtils.d.ts +22 -1
  446. package/dist/src/utils/partUtils.js +68 -0
  447. package/dist/src/utils/partUtils.js.map +1 -1
  448. package/dist/src/utils/partUtils.test.js +112 -1
  449. package/dist/src/utils/partUtils.test.js.map +1 -1
  450. package/dist/src/utils/pathReader.d.ts +17 -0
  451. package/dist/src/utils/pathReader.js +92 -0
  452. package/dist/src/utils/pathReader.js.map +1 -0
  453. package/dist/src/utils/pathReader.test.d.ts +6 -0
  454. package/dist/src/utils/pathReader.test.js +363 -0
  455. package/dist/src/utils/pathReader.test.js.map +1 -0
  456. package/dist/src/utils/paths.d.ts +1 -18
  457. package/dist/src/utils/paths.js +3 -29
  458. package/dist/src/utils/paths.js.map +1 -1
  459. package/dist/src/utils/quotaErrorDetection.d.ts +1 -1
  460. package/dist/src/utils/retry.test.js +4 -1
  461. package/dist/src/utils/retry.test.js.map +1 -1
  462. package/dist/src/utils/schemaValidator.js +4 -0
  463. package/dist/src/utils/schemaValidator.js.map +1 -1
  464. package/dist/src/utils/session.js +1 -1
  465. package/dist/src/utils/session.js.map +1 -1
  466. package/dist/src/utils/shell-utils.d.ts +1 -1
  467. package/dist/src/utils/shell-utils.js +23 -29
  468. package/dist/src/utils/shell-utils.js.map +1 -1
  469. package/dist/src/utils/shell-utils.test.js +7 -0
  470. package/dist/src/utils/shell-utils.test.js.map +1 -1
  471. package/dist/src/utils/summarizer.d.ts +2 -2
  472. package/dist/src/utils/summarizer.test.js.map +1 -1
  473. package/dist/src/utils/systemEncoding.js +2 -2
  474. package/dist/src/utils/systemEncoding.js.map +1 -1
  475. package/dist/src/utils/systemEncoding.test.js +2 -2
  476. package/dist/src/utils/systemEncoding.test.js.map +1 -1
  477. package/dist/src/utils/tool-utils.d.ts +19 -0
  478. package/dist/src/utils/tool-utils.js +58 -0
  479. package/dist/src/utils/tool-utils.js.map +1 -0
  480. package/dist/src/utils/tool-utils.test.d.ts +6 -0
  481. package/dist/src/utils/tool-utils.test.js +61 -0
  482. package/dist/src/utils/tool-utils.test.js.map +1 -0
  483. package/dist/src/utils/userAccountManager.d.ts +20 -0
  484. package/dist/src/utils/userAccountManager.js +114 -0
  485. package/dist/src/utils/userAccountManager.js.map +1 -0
  486. package/dist/src/utils/userAccountManager.test.d.ts +6 -0
  487. package/dist/src/utils/{user_account.test.js → userAccountManager.test.js} +33 -30
  488. package/dist/src/utils/userAccountManager.test.js.map +1 -0
  489. package/dist/src/utils/workspaceContext.js +13 -7
  490. package/dist/src/utils/workspaceContext.js.map +1 -1
  491. package/dist/src/utils/workspaceContext.test.js +41 -16
  492. package/dist/src/utils/workspaceContext.test.js.map +1 -1
  493. package/dist/tsconfig.tsbuildinfo +1 -1
  494. package/package.json +27 -13
  495. package/dist/src/utils/user_account.d.ts +0 -9
  496. package/dist/src/utils/user_account.js +0 -109
  497. package/dist/src/utils/user_account.js.map +0 -1
  498. package/dist/src/utils/user_account.test.js.map +0 -1
  499. package/dist/src/utils/user_id.d.ts +0 -11
  500. package/dist/src/utils/user_id.js +0 -49
  501. package/dist/src/utils/user_id.js.map +0 -1
  502. package/dist/src/utils/user_id.test.js +0 -21
  503. package/dist/src/utils/user_id.test.js.map +0 -1
  504. /package/dist/src/{utils/user_account.test.d.ts → config/storage.test.d.ts} +0 -0
  505. /package/dist/src/{utils/user_id.test.d.ts → ide/process-utils.test.d.ts} +0 -0
@@ -3,14 +3,29 @@
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 { createUserContent, } from '@google/genai';
6
+ import { toParts } from '../code_assist/converter.js';
7
+ import { createUserContent } from '@google/genai';
9
8
  import { retryWithBackoff } from '../utils/retry.js';
10
- import { isFunctionResponse } from '../utils/messageInspectors.js';
11
9
  import { AuthType } from './contentGenerator.js';
12
10
  import { DEFAULT_GEMINI_FLASH_MODEL } from '../config/models.js';
13
11
  import { hasCycleInSchema } from '../tools/tools.js';
12
+ import { logContentRetry, logContentRetryFailure, logInvalidChunk, } from '../telemetry/loggers.js';
13
+ import { ChatRecordingService } from '../services/chatRecordingService.js';
14
+ import { ContentRetryEvent, ContentRetryFailureEvent, InvalidChunkEvent, } from '../telemetry/types.js';
15
+ import { isFunctionResponse } from '../utils/messageInspectors.js';
16
+ import { partListUnionToString } from './geminiRequest.js';
17
+ export var StreamEventType;
18
+ (function (StreamEventType) {
19
+ /** A regular content chunk from the API. */
20
+ StreamEventType["CHUNK"] = "chunk";
21
+ /** A signal that a retry is about to happen. The UI should discard any partial
22
+ * content from the attempt that just failed. */
23
+ StreamEventType["RETRY"] = "retry";
24
+ })(StreamEventType || (StreamEventType = {}));
25
+ const INVALID_CONTENT_RETRY_OPTIONS = {
26
+ maxAttempts: 3, // 1 initial call + 2 retries
27
+ initialDelayMs: 500,
28
+ };
14
29
  /**
15
30
  * Returns true if the response is valid, false otherwise.
16
31
  */
@@ -84,14 +99,20 @@ function extractCuratedHistory(comprehensiveHistory) {
84
99
  if (isValid) {
85
100
  curatedHistory.push(...modelOutput);
86
101
  }
87
- else {
88
- // Remove the last user input when model content is invalid.
89
- curatedHistory.pop();
90
- }
91
102
  }
92
103
  }
93
104
  return curatedHistory;
94
105
  }
106
+ /**
107
+ * Custom error to signal that a stream completed without valid content,
108
+ * which should trigger a retry.
109
+ */
110
+ export class EmptyStreamError extends Error {
111
+ constructor(message) {
112
+ super(message);
113
+ this.name = 'EmptyStreamError';
114
+ }
115
+ }
95
116
  /**
96
117
  * Chat session that enables sending messages to the model with previous
97
118
  * conversation context.
@@ -107,12 +128,15 @@ export class GeminiChat {
107
128
  // A promise to represent the current state of the message being sent to the
108
129
  // model.
109
130
  sendPromise = Promise.resolve();
131
+ chatRecordingService;
110
132
  constructor(config, contentGenerator, generationConfig = {}, history = []) {
111
133
  this.config = config;
112
134
  this.contentGenerator = contentGenerator;
113
135
  this.generationConfig = generationConfig;
114
136
  this.history = history;
115
137
  validateHistory(history);
138
+ this.chatRecordingService = new ChatRecordingService(config);
139
+ this.chatRecordingService.initialize();
116
140
  }
117
141
  /**
118
142
  * Handles falling back to Flash model when persistent 429 errors occur for OAuth users.
@@ -168,7 +192,7 @@ export class GeminiChat {
168
192
  * ```ts
169
193
  * const chat = ai.chats.create({model: 'gemini-2.0-flash'});
170
194
  * const response = await chat.sendMessage({
171
- * message: 'Why is the sky blue?'
195
+ * message: 'Why is the sky blue?'
172
196
  * });
173
197
  * console.log(response.text);
174
198
  * ```
@@ -176,6 +200,17 @@ export class GeminiChat {
176
200
  async sendMessage(params, prompt_id) {
177
201
  await this.sendPromise;
178
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
+ }
179
214
  const requestContents = this.getHistory(true).concat(userContent);
180
215
  let response;
181
216
  try {
@@ -210,6 +245,7 @@ export class GeminiChat {
210
245
  });
211
246
  this.sendPromise = (async () => {
212
247
  const outputContent = response.candidates?.[0]?.content;
248
+ const modelOutput = outputContent ? [outputContent] : [];
213
249
  // Because the AFC input contains the entire curated chat history in
214
250
  // addition to the new user input, we need to truncate the AFC history
215
251
  // to deduplicate the existing chat history.
@@ -220,12 +256,13 @@ export class GeminiChat {
220
256
  automaticFunctionCallingHistory =
221
257
  fullAutomaticFunctionCallingHistory.slice(index) ?? [];
222
258
  }
223
- const modelOutput = outputContent ? [outputContent] : [];
224
259
  this.recordHistory(userContent, modelOutput, automaticFunctionCallingHistory);
225
260
  })();
226
- await this.sendPromise.catch(() => {
261
+ await this.sendPromise.catch((error) => {
227
262
  // Resets sendPromise to avoid subsequent calls failing
228
263
  this.sendPromise = Promise.resolve();
264
+ // Re-throw the error so the caller knows something went wrong.
265
+ throw error;
229
266
  });
230
267
  return response;
231
268
  }
@@ -249,64 +286,113 @@ export class GeminiChat {
249
286
  * ```ts
250
287
  * const chat = ai.chats.create({model: 'gemini-2.0-flash'});
251
288
  * const response = await chat.sendMessageStream({
252
- * message: 'Why is the sky blue?'
289
+ * message: 'Why is the sky blue?'
253
290
  * });
254
291
  * for await (const chunk of response) {
255
- * console.log(chunk.text);
292
+ * console.log(chunk.text);
256
293
  * }
257
294
  * ```
258
295
  */
259
296
  async sendMessageStream(params, prompt_id) {
260
297
  await this.sendPromise;
298
+ let streamDoneResolver;
299
+ const streamDonePromise = new Promise((resolve) => {
300
+ streamDoneResolver = resolve;
301
+ });
302
+ this.sendPromise = streamDonePromise;
261
303
  const userContent = createUserContent(params.message);
262
- const requestContents = this.getHistory(true).concat(userContent);
263
- try {
264
- const apiCall = () => {
265
- const modelToUse = this.config.getModel();
266
- // Prevent Flash model calls immediately after quota error
267
- if (this.config.getQuotaErrorOccurred() &&
268
- modelToUse === DEFAULT_GEMINI_FLASH_MODEL) {
269
- throw new Error('Please submit a new query to continue with the Flash model.');
270
- }
271
- return this.contentGenerator.generateContentStream({
272
- model: modelToUse,
273
- contents: requestContents,
274
- config: { ...this.generationConfig, ...params.config },
275
- }, prompt_id);
276
- };
277
- // Note: Retrying streams can be complex. If generateContentStream itself doesn't handle retries
278
- // for transient issues internally before yielding the async generator, this retry will re-initiate
279
- // the stream. For simple 429/500 errors on initial call, this is fine.
280
- // If errors occur mid-stream, this setup won't resume the stream; it will restart it.
281
- const streamResponse = await retryWithBackoff(apiCall, {
282
- shouldRetry: (error) => {
283
- // Check for known error messages and codes.
284
- if (error instanceof Error && error.message) {
285
- if (isSchemaDepthError(error.message))
286
- return false;
287
- if (error.message.includes('429'))
288
- return true;
289
- if (error.message.match(/5\d{2}/))
290
- return true;
291
- }
292
- return false; // Don't retry other errors by default
293
- },
294
- onPersistent429: async (authType, error) => await this.handleFlashFallback(authType, error),
295
- authType: this.config.getContentGeneratorConfig()?.authType,
304
+ // Record user input - capture complete message with all parts (text, files, images, etc.)
305
+ // but skip recording function responses (tool call results) as they should be stored in tool call records
306
+ if (!isFunctionResponse(userContent)) {
307
+ const userMessage = Array.isArray(params.message)
308
+ ? params.message
309
+ : [params.message];
310
+ const userMessageContent = partListUnionToString(toParts(userMessage));
311
+ this.chatRecordingService.recordMessage({
312
+ type: 'user',
313
+ content: userMessageContent,
296
314
  });
297
- // Resolve the internal tracking of send completion promise - `sendPromise`
298
- // for both success and failure response. The actual failure is still
299
- // propagated by the `await streamResponse`.
300
- this.sendPromise = Promise.resolve(streamResponse)
301
- .then(() => undefined)
302
- .catch(() => undefined);
303
- const result = this.processStreamResponse(streamResponse, userContent);
304
- return result;
305
- }
306
- catch (error) {
307
- this.sendPromise = Promise.resolve();
308
- throw error;
309
315
  }
316
+ // Add user content to history ONCE before any attempts.
317
+ this.history.push(userContent);
318
+ const requestContents = this.getHistory(true);
319
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
320
+ const self = this;
321
+ return (async function* () {
322
+ try {
323
+ let lastError = new Error('Request failed after all retries.');
324
+ for (let attempt = 0; attempt < INVALID_CONTENT_RETRY_OPTIONS.maxAttempts; attempt++) {
325
+ try {
326
+ if (attempt > 0) {
327
+ yield { type: StreamEventType.RETRY };
328
+ }
329
+ const stream = await self.makeApiCallAndProcessStream(requestContents, params, prompt_id, userContent);
330
+ for await (const chunk of stream) {
331
+ yield { type: StreamEventType.CHUNK, value: chunk };
332
+ }
333
+ lastError = null;
334
+ break;
335
+ }
336
+ catch (error) {
337
+ lastError = error;
338
+ const isContentError = error instanceof EmptyStreamError;
339
+ if (isContentError) {
340
+ // Check if we have more attempts left.
341
+ if (attempt < INVALID_CONTENT_RETRY_OPTIONS.maxAttempts - 1) {
342
+ logContentRetry(self.config, new ContentRetryEvent(attempt, 'EmptyStreamError', INVALID_CONTENT_RETRY_OPTIONS.initialDelayMs));
343
+ await new Promise((res) => setTimeout(res, INVALID_CONTENT_RETRY_OPTIONS.initialDelayMs *
344
+ (attempt + 1)));
345
+ continue;
346
+ }
347
+ }
348
+ break;
349
+ }
350
+ }
351
+ if (lastError) {
352
+ if (lastError instanceof EmptyStreamError) {
353
+ logContentRetryFailure(self.config, new ContentRetryFailureEvent(INVALID_CONTENT_RETRY_OPTIONS.maxAttempts, 'EmptyStreamError'));
354
+ }
355
+ // If the stream fails, remove the user message that was added.
356
+ if (self.history[self.history.length - 1] === userContent) {
357
+ self.history.pop();
358
+ }
359
+ throw lastError;
360
+ }
361
+ }
362
+ finally {
363
+ streamDoneResolver();
364
+ }
365
+ })();
366
+ }
367
+ async makeApiCallAndProcessStream(requestContents, params, prompt_id, userContent) {
368
+ const apiCall = () => {
369
+ const modelToUse = this.config.getModel();
370
+ if (this.config.getQuotaErrorOccurred() &&
371
+ modelToUse === DEFAULT_GEMINI_FLASH_MODEL) {
372
+ throw new Error('Please submit a new query to continue with the Flash model.');
373
+ }
374
+ return this.contentGenerator.generateContentStream({
375
+ model: modelToUse,
376
+ contents: requestContents,
377
+ config: { ...this.generationConfig, ...params.config },
378
+ }, prompt_id);
379
+ };
380
+ const streamResponse = await retryWithBackoff(apiCall, {
381
+ shouldRetry: (error) => {
382
+ if (error instanceof Error && error.message) {
383
+ if (isSchemaDepthError(error.message))
384
+ return false;
385
+ if (error.message.includes('429'))
386
+ return true;
387
+ if (error.message.match(/5\d{2}/))
388
+ return true;
389
+ }
390
+ return false;
391
+ },
392
+ onPersistent429: async (authType, error) => await this.handleFlashFallback(authType, error),
393
+ authType: this.config.getContentGeneratorConfig()?.authType,
394
+ });
395
+ return this.processStreamResponse(streamResponse, userContent);
310
396
  }
311
397
  /**
312
398
  * Returns the chat history.
@@ -318,7 +404,7 @@ export class GeminiChat {
318
404
  * - The `curated history` contains only the valid turns between user and
319
405
  * model, which will be included in the subsequent requests sent to the model.
320
406
  * - The `comprehensive history` contains all turns, including invalid or
321
- * empty model outputs, providing a complete record of the history.
407
+ * empty model outputs, providing a complete record of the history.
322
408
  *
323
409
  * The history is updated after receiving the response from the model,
324
410
  * for streaming response, it means receiving the last chunk of the response.
@@ -327,9 +413,9 @@ export class GeminiChat {
327
413
  * history`, set the `curated` parameter to `true`.
328
414
  *
329
415
  * @param curated - whether to return the curated history or the comprehensive
330
- * history.
416
+ * history.
331
417
  * @return History contents alternating between user and model for the entire
332
- * chat session.
418
+ * chat session.
333
419
  */
334
420
  getHistory(curated = false) {
335
421
  const history = curated
@@ -347,8 +433,6 @@ export class GeminiChat {
347
433
  }
348
434
  /**
349
435
  * Adds a new entry to the chat history.
350
- *
351
- * @param content - The content to add to the history.
352
436
  */
353
437
  addHistory(content) {
354
438
  this.history.push(content);
@@ -364,7 +448,7 @@ export class GeminiChat {
364
448
  // and include a recommendation to remove potentially problematic tools.
365
449
  if (isSchemaDepthError(error.message) ||
366
450
  isInvalidArgumentError(error.message)) {
367
- const tools = (await this.config.getToolRegistry()).getAllTools();
451
+ const tools = this.config.getToolRegistry().getAllTools();
368
452
  const cyclicSchemaTools = [];
369
453
  for (const tool of tools) {
370
454
  if ((tool.schema.parametersJsonSchema &&
@@ -381,122 +465,211 @@ export class GeminiChat {
381
465
  }
382
466
  }
383
467
  }
384
- async *processStreamResponse(streamResponse, inputContent) {
385
- const outputContent = [];
386
- const chunks = [];
387
- let errorOccurred = false;
388
- try {
389
- for await (const chunk of streamResponse) {
390
- if (isValidResponse(chunk)) {
391
- chunks.push(chunk);
392
- const content = chunk.candidates?.[0]?.content;
393
- if (content !== undefined) {
394
- if (this.isThoughtContent(content)) {
395
- yield chunk;
396
- continue;
397
- }
398
- outputContent.push(content);
468
+ async *processStreamResponse(streamResponse, userInput) {
469
+ const modelResponseParts = [];
470
+ let hasReceivedAnyChunk = false;
471
+ let hasReceivedValidChunk = false;
472
+ let hasToolCall = false;
473
+ let lastChunk = null;
474
+ let lastChunkIsInvalid = false;
475
+ for await (const chunk of streamResponse) {
476
+ hasReceivedAnyChunk = true;
477
+ lastChunk = chunk;
478
+ if (isValidResponse(chunk)) {
479
+ hasReceivedValidChunk = true;
480
+ lastChunkIsInvalid = false;
481
+ const content = chunk.candidates?.[0]?.content;
482
+ if (content?.parts) {
483
+ if (content.parts.some((part) => part.thought)) {
484
+ // Record thoughts
485
+ this.recordThoughtFromContent(content);
486
+ }
487
+ if (content.parts.some((part) => part.functionCall)) {
488
+ hasToolCall = true;
489
+ }
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;
399
494
  }
400
495
  }
401
- yield chunk;
402
496
  }
403
- }
404
- catch (error) {
405
- errorOccurred = true;
406
- throw error;
407
- }
408
- if (!errorOccurred) {
409
- const allParts = [];
410
- for (const content of outputContent) {
411
- if (content.parts) {
412
- allParts.push(...content.parts);
413
- }
497
+ else {
498
+ logInvalidChunk(this.config, new InvalidChunkEvent('Invalid chunk received from stream.'));
499
+ lastChunkIsInvalid = true;
414
500
  }
501
+ // Record token usage if this chunk has usageMetadata
502
+ if (chunk.usageMetadata) {
503
+ this.chatRecordingService.recordMessageTokens(chunk.usageMetadata);
504
+ }
505
+ yield chunk; // Yield every chunk to the UI immediately.
415
506
  }
416
- this.recordHistory(inputContent, outputContent);
417
- }
418
- recordHistory(userInput, modelOutput, automaticFunctionCallingHistory) {
419
- const nonThoughtModelOutput = modelOutput.filter((content) => !this.isThoughtContent(content));
420
- let outputContents = [];
421
- if (nonThoughtModelOutput.length > 0 &&
422
- nonThoughtModelOutput.every((content) => content.role !== undefined)) {
423
- outputContents = nonThoughtModelOutput;
507
+ if (!hasReceivedAnyChunk) {
508
+ throw new EmptyStreamError('Model stream completed without any chunks.');
424
509
  }
425
- else if (nonThoughtModelOutput.length === 0 && modelOutput.length > 0) {
426
- // This case handles when the model returns only a thought.
427
- // We don't want to add an empty model response in this case.
510
+ const hasFinishReason = lastChunk?.candidates?.some((candidate) => candidate.finishReason);
511
+ // Stream validation logic: A stream is considered successful if:
512
+ // 1. There's a tool call (tool calls can end without explicit finish reasons), OR
513
+ // 2. There's a finish reason AND the last chunk is valid (or we haven't received any valid chunks)
514
+ //
515
+ // We throw an error only when there's no tool call AND:
516
+ // - No finish reason, OR
517
+ // - Last chunk is invalid after receiving valid content
518
+ if (!hasToolCall &&
519
+ (!hasFinishReason || (lastChunkIsInvalid && !hasReceivedValidChunk))) {
520
+ throw new EmptyStreamError('Model stream ended with an invalid chunk or missing finish reason.');
428
521
  }
429
- else {
430
- // When not a function response appends an empty content when model returns empty response, so that the
431
- // history is always alternating between user and model.
432
- // Workaround for: https://b.corp.google.com/issues/420354090
433
- if (!isFunctionResponse(userInput)) {
434
- outputContents.push({
435
- role: 'model',
436
- parts: [],
522
+ // Record model response text from the collected parts
523
+ if (modelResponseParts.length > 0) {
524
+ const responseText = modelResponseParts
525
+ .filter((part) => part.text && !part.thought)
526
+ .map((part) => part.text)
527
+ .join('');
528
+ if (responseText.trim()) {
529
+ this.chatRecordingService.recordMessage({
530
+ type: 'gemini',
531
+ content: responseText,
437
532
  });
438
533
  }
439
534
  }
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.
440
544
  if (automaticFunctionCallingHistory &&
441
545
  automaticFunctionCallingHistory.length > 0) {
442
546
  this.history.push(...extractCuratedHistory(automaticFunctionCallingHistory));
443
547
  }
444
548
  else {
445
- this.history.push(userInput);
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
+ }
446
562
  }
447
- // Consolidate adjacent model roles in outputContents
448
- const consolidatedOutputContents = [];
449
- for (const content of outputContents) {
450
- if (this.isThoughtContent(content)) {
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);
451
569
  continue;
452
570
  }
453
- const lastContent = consolidatedOutputContents[consolidatedOutputContents.length - 1];
454
- if (this.isTextContent(lastContent) && this.isTextContent(content)) {
455
- // If both current and last are text, combine their text into the lastContent's first part
456
- // and append any other parts from the current content.
457
- lastContent.parts[0].text += content.parts[0].text || '';
458
- if (content.parts.length > 1) {
459
- lastContent.parts.push(...content.parts.slice(1));
460
- }
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);
461
582
  }
462
583
  else {
463
- consolidatedOutputContents.push(content);
584
+ finalModelTurns.push(newTurn);
464
585
  }
465
586
  }
466
- if (consolidatedOutputContents.length > 0) {
467
- const lastHistoryEntry = this.history[this.history.length - 1];
468
- const canMergeWithLastHistory = !automaticFunctionCallingHistory ||
469
- automaticFunctionCallingHistory.length === 0;
470
- if (canMergeWithLastHistory &&
471
- this.isTextContent(lastHistoryEntry) &&
472
- this.isTextContent(consolidatedOutputContents[0])) {
473
- // If both current and last are text, combine their text into the lastHistoryEntry's first part
474
- // and append any other parts from the current content.
475
- lastHistoryEntry.parts[0].text +=
476
- consolidatedOutputContents[0].parts[0].text || '';
477
- if (consolidatedOutputContents[0].parts.length > 1) {
478
- lastHistoryEntry.parts.push(...consolidatedOutputContents[0].parts.slice(1));
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;
479
617
  }
480
- consolidatedOutputContents.shift(); // Remove the first element as it's merged
481
618
  }
482
- this.history.push(...consolidatedOutputContents);
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: [] });
483
624
  }
484
625
  }
485
- isTextContent(content) {
486
- return !!(content &&
487
- content.role === 'model' &&
488
- content.parts &&
489
- content.parts.length > 0 &&
490
- typeof content.parts[0].text === 'string' &&
491
- content.parts[0].text !== '');
626
+ /**
627
+ * Gets the chat recording service instance.
628
+ */
629
+ getChatRecordingService() {
630
+ return this.chatRecordingService;
631
+ }
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);
492
651
  }
493
- isThoughtContent(content) {
494
- return !!(content &&
495
- content.role === 'model' &&
496
- content.parts &&
497
- content.parts.length > 0 &&
498
- typeof content.parts[0].thought === 'boolean' &&
499
- content.parts[0].thought === true);
652
+ /**
653
+ * Extracts and records thought from thought content.
654
+ */
655
+ recordThoughtFromContent(content) {
656
+ if (!content.parts || content.parts.length === 0) {
657
+ return;
658
+ }
659
+ const thoughtPart = content.parts[0];
660
+ if (thoughtPart.text) {
661
+ // Extract subject and description using the same logic as turn.ts
662
+ const rawText = thoughtPart.text;
663
+ const subjectStringMatches = rawText.match(/\*\*(.*?)\*\*/s);
664
+ const subject = subjectStringMatches
665
+ ? subjectStringMatches[1].trim()
666
+ : '';
667
+ const description = rawText.replace(/\*\*(.*?)\*\*/s, '').trim();
668
+ this.chatRecordingService.recordThought({
669
+ subject,
670
+ description,
671
+ });
672
+ }
500
673
  }
501
674
  }
502
675
  /** Visible for Testing */