@machina.ai/cell-cli 1.11.0-rc1 → 1.13.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 (538) hide show
  1. package/dist/package.json +12 -10
  2. package/dist/src/commands/extensions/disable.d.ts +1 -1
  3. package/dist/src/commands/extensions/disable.js +15 -7
  4. package/dist/src/commands/extensions/disable.js.map +1 -1
  5. package/dist/src/commands/extensions/enable.d.ts +1 -1
  6. package/dist/src/commands/extensions/enable.js +15 -7
  7. package/dist/src/commands/extensions/enable.js.map +1 -1
  8. package/dist/src/commands/extensions/install.js +14 -3
  9. package/dist/src/commands/extensions/install.js.map +1 -1
  10. package/dist/src/commands/extensions/install.test.js +39 -19
  11. package/dist/src/commands/extensions/install.test.js.map +1 -1
  12. package/dist/src/commands/extensions/link.js +14 -3
  13. package/dist/src/commands/extensions/link.js.map +1 -1
  14. package/dist/src/commands/extensions/list.js +13 -4
  15. package/dist/src/commands/extensions/list.js.map +1 -1
  16. package/dist/src/commands/extensions/uninstall.js +13 -2
  17. package/dist/src/commands/extensions/uninstall.js.map +1 -1
  18. package/dist/src/commands/extensions/update.js +18 -13
  19. package/dist/src/commands/extensions/update.js.map +1 -1
  20. package/dist/src/commands/extensions/validate.d.ts +12 -0
  21. package/dist/src/commands/extensions/validate.js +83 -0
  22. package/dist/src/commands/extensions/validate.js.map +1 -0
  23. package/dist/src/commands/extensions/validate.test.js +93 -0
  24. package/dist/src/commands/extensions/validate.test.js.map +1 -0
  25. package/dist/src/commands/extensions.js +3 -0
  26. package/dist/src/commands/extensions.js.map +1 -1
  27. package/dist/src/commands/mcp/add.test.js +3 -0
  28. package/dist/src/commands/mcp/add.test.js.map +1 -1
  29. package/dist/src/commands/mcp/list.js +10 -3
  30. package/dist/src/commands/mcp/list.js.map +1 -1
  31. package/dist/src/commands/mcp/list.test.js +37 -27
  32. package/dist/src/commands/mcp/list.test.js.map +1 -1
  33. package/dist/src/config/auth.js +0 -5
  34. package/dist/src/config/auth.js.map +1 -1
  35. package/dist/src/config/config.d.ts +6 -3
  36. package/dist/src/config/config.js +65 -80
  37. package/dist/src/config/config.js.map +1 -1
  38. package/dist/src/config/config.test.js +235 -212
  39. package/dist/src/config/config.test.js.map +1 -1
  40. package/dist/src/config/extension-manager.d.ts +63 -0
  41. package/dist/src/config/extension-manager.js +450 -0
  42. package/dist/src/config/extension-manager.js.map +1 -0
  43. package/dist/src/config/extension.d.ts +4 -51
  44. package/dist/src/config/extension.js +1 -535
  45. package/dist/src/config/extension.js.map +1 -1
  46. package/dist/src/config/extension.test.js +525 -201
  47. package/dist/src/config/extension.test.js.map +1 -1
  48. package/dist/src/config/extensions/consent.d.ts +38 -0
  49. package/dist/src/config/extensions/consent.js +123 -0
  50. package/dist/src/config/extensions/consent.js.map +1 -0
  51. package/dist/src/config/extensions/extensionEnablement.d.ts +1 -1
  52. package/dist/src/config/extensions/extensionEnablement.js +4 -3
  53. package/dist/src/config/extensions/extensionEnablement.js.map +1 -1
  54. package/dist/src/config/extensions/extensionEnablement.test.js +10 -10
  55. package/dist/src/config/extensions/extensionEnablement.test.js.map +1 -1
  56. package/dist/src/config/extensions/extensionSettings.d.ts +15 -0
  57. package/dist/src/config/extensions/extensionSettings.js +113 -0
  58. package/dist/src/config/extensions/extensionSettings.js.map +1 -0
  59. package/dist/src/config/extensions/extensionSettings.test.d.ts +6 -0
  60. package/dist/src/config/extensions/extensionSettings.test.js +254 -0
  61. package/dist/src/config/extensions/extensionSettings.test.js.map +1 -0
  62. package/dist/src/config/extensions/github.d.ts +2 -2
  63. package/dist/src/config/extensions/github.js +5 -10
  64. package/dist/src/config/extensions/github.js.map +1 -1
  65. package/dist/src/config/extensions/github.test.js +153 -167
  66. package/dist/src/config/extensions/github.test.js.map +1 -1
  67. package/dist/src/config/extensions/github_fetch.d.ts +1 -1
  68. package/dist/src/config/extensions/github_fetch.js +13 -1
  69. package/dist/src/config/extensions/github_fetch.js.map +1 -1
  70. package/dist/src/config/extensions/github_fetch.test.d.ts +6 -0
  71. package/dist/src/config/extensions/github_fetch.test.js +169 -0
  72. package/dist/src/config/extensions/github_fetch.test.js.map +1 -0
  73. package/dist/src/config/extensions/storage.d.ts +14 -0
  74. package/dist/src/config/extensions/storage.js +32 -0
  75. package/dist/src/config/extensions/storage.js.map +1 -0
  76. package/dist/src/config/extensions/update.d.ts +4 -4
  77. package/dist/src/config/extensions/update.js +39 -39
  78. package/dist/src/config/extensions/update.js.map +1 -1
  79. package/dist/src/config/extensions/update.test.js +72 -74
  80. package/dist/src/config/extensions/update.test.js.map +1 -1
  81. package/dist/src/config/extensions/variableSchema.d.ts +0 -6
  82. package/dist/src/config/extensions/variableSchema.js.map +1 -1
  83. package/dist/src/config/extensions/variables.d.ts +4 -0
  84. package/dist/src/config/extensions/variables.js +6 -0
  85. package/dist/src/config/extensions/variables.js.map +1 -1
  86. package/dist/src/config/keyBindings.d.ts +3 -0
  87. package/dist/src/config/keyBindings.js +30 -8
  88. package/dist/src/config/keyBindings.js.map +1 -1
  89. package/dist/src/config/keyBindings.test.js +17 -0
  90. package/dist/src/config/keyBindings.test.js.map +1 -1
  91. package/dist/src/config/policies/read-only.toml +56 -0
  92. package/dist/src/config/policies/write.toml +63 -0
  93. package/dist/src/config/policies/yolo.toml +31 -0
  94. package/dist/src/config/policy-engine.integration.test.js +41 -38
  95. package/dist/src/config/policy-engine.integration.test.js.map +1 -1
  96. package/dist/src/config/policy.d.ts +2 -2
  97. package/dist/src/config/policy.js +10 -148
  98. package/dist/src/config/policy.js.map +1 -1
  99. package/dist/src/config/sandboxConfig.d.ts +1 -1
  100. package/dist/src/config/sandboxConfig.js +6 -3
  101. package/dist/src/config/sandboxConfig.js.map +1 -1
  102. package/dist/src/config/settings.d.ts +2 -1
  103. package/dist/src/config/settings.js +58 -18
  104. package/dist/src/config/settings.js.map +1 -1
  105. package/dist/src/config/settings.test.js +128 -69
  106. package/dist/src/config/settings.test.js.map +1 -1
  107. package/dist/src/config/settingsSchema.d.ts +170 -28
  108. package/dist/src/config/settingsSchema.js +418 -27
  109. package/dist/src/config/settingsSchema.js.map +1 -1
  110. package/dist/src/config/settingsSchema.test.js +42 -1
  111. package/dist/src/config/settingsSchema.test.js.map +1 -1
  112. package/dist/src/config/trustedFolders.d.ts +1 -1
  113. package/dist/src/config/trustedFolders.js +4 -2
  114. package/dist/src/config/trustedFolders.js.map +1 -1
  115. package/dist/src/core/initializer.js +2 -1
  116. package/dist/src/core/initializer.js.map +1 -1
  117. package/dist/src/gemini.d.ts +1 -1
  118. package/dist/src/gemini.js +46 -16
  119. package/dist/src/gemini.js.map +1 -1
  120. package/dist/src/gemini.test.js +88 -30
  121. package/dist/src/gemini.test.js.map +1 -1
  122. package/dist/src/generated/git-commit.d.ts +2 -2
  123. package/dist/src/generated/git-commit.js +2 -2
  124. package/dist/src/nonInteractiveCli.d.ts +9 -1
  125. package/dist/src/nonInteractiveCli.js +114 -7
  126. package/dist/src/nonInteractiveCli.js.map +1 -1
  127. package/dist/src/nonInteractiveCli.test.js +355 -112
  128. package/dist/src/nonInteractiveCli.test.js.map +1 -1
  129. package/dist/src/services/BuiltinCommandLoader.js +4 -0
  130. package/dist/src/services/BuiltinCommandLoader.js.map +1 -1
  131. package/dist/src/services/BuiltinCommandLoader.test.js +22 -0
  132. package/dist/src/services/BuiltinCommandLoader.test.js.map +1 -1
  133. package/dist/src/services/FeedbackService.js +2 -2
  134. package/dist/src/services/FeedbackService.js.map +1 -1
  135. package/dist/src/services/McpPromptLoader.js +2 -2
  136. package/dist/src/services/McpPromptLoader.js.map +1 -1
  137. package/dist/src/services/McpPromptLoader.test.js +4 -2
  138. package/dist/src/services/McpPromptLoader.test.js.map +1 -1
  139. package/dist/src/test-utils/async.d.ts +9 -0
  140. package/dist/src/test-utils/async.js +29 -0
  141. package/dist/src/test-utils/async.js.map +1 -0
  142. package/dist/src/test-utils/createExtension.d.ts +3 -1
  143. package/dist/src/test-utils/createExtension.js +3 -3
  144. package/dist/src/test-utils/createExtension.js.map +1 -1
  145. package/dist/src/test-utils/render.d.ts +16 -2
  146. package/dist/src/test-utils/render.js +66 -4
  147. package/dist/src/test-utils/render.js.map +1 -1
  148. package/dist/src/test-utils/render.test.d.ts +6 -0
  149. package/dist/src/test-utils/render.test.js +79 -0
  150. package/dist/src/test-utils/render.test.js.map +1 -0
  151. package/dist/src/ui/App.test.js +1 -1
  152. package/dist/src/ui/App.test.js.map +1 -1
  153. package/dist/src/ui/AppContainer.js +181 -65
  154. package/dist/src/ui/AppContainer.js.map +1 -1
  155. package/dist/src/ui/AppContainer.test.js +505 -147
  156. package/dist/src/ui/AppContainer.test.js.map +1 -1
  157. package/dist/src/ui/IdeIntegrationNudge.js +1 -1
  158. package/dist/src/ui/IdeIntegrationNudge.js.map +1 -1
  159. package/dist/src/ui/auth/ApiAuthDialog.d.ts +14 -0
  160. package/dist/src/ui/auth/ApiAuthDialog.js +26 -0
  161. package/dist/src/ui/auth/ApiAuthDialog.js.map +1 -0
  162. package/dist/src/ui/auth/ApiAuthDialog.test.d.ts +6 -0
  163. package/dist/src/ui/auth/ApiAuthDialog.test.js +91 -0
  164. package/dist/src/ui/auth/ApiAuthDialog.test.js.map +1 -0
  165. package/dist/src/ui/auth/AuthDialog.js +7 -3
  166. package/dist/src/ui/auth/AuthDialog.js.map +1 -1
  167. package/dist/src/ui/auth/useAuth.d.ts +2 -0
  168. package/dist/src/ui/auth/useAuth.js +31 -2
  169. package/dist/src/ui/auth/useAuth.js.map +1 -1
  170. package/dist/src/ui/colors.js +3 -0
  171. package/dist/src/ui/colors.js.map +1 -1
  172. package/dist/src/ui/commands/directoryCommand.js +1 -1
  173. package/dist/src/ui/commands/directoryCommand.js.map +1 -1
  174. package/dist/src/ui/commands/extensionsCommand.js +64 -11
  175. package/dist/src/ui/commands/extensionsCommand.js.map +1 -1
  176. package/dist/src/ui/commands/extensionsCommand.test.js +72 -1
  177. package/dist/src/ui/commands/extensionsCommand.test.js.map +1 -1
  178. package/dist/src/ui/commands/mcpCommand.js +14 -14
  179. package/dist/src/ui/commands/mcpCommand.js.map +1 -1
  180. package/dist/src/ui/commands/mcpCommand.test.js +4 -0
  181. package/dist/src/ui/commands/mcpCommand.test.js.map +1 -1
  182. package/dist/src/ui/commands/memoryCommand.js +1 -1
  183. package/dist/src/ui/commands/memoryCommand.js.map +1 -1
  184. package/dist/src/ui/commands/memoryCommand.test.js +3 -1
  185. package/dist/src/ui/commands/memoryCommand.test.js.map +1 -1
  186. package/dist/src/ui/commands/policiesCommand.d.ts +7 -0
  187. package/dist/src/ui/commands/policiesCommand.js +59 -0
  188. package/dist/src/ui/commands/policiesCommand.js.map +1 -0
  189. package/dist/src/ui/commands/policiesCommand.test.d.ts +6 -0
  190. package/dist/src/ui/commands/policiesCommand.test.js +83 -0
  191. package/dist/src/ui/commands/policiesCommand.test.js.map +1 -0
  192. package/dist/src/ui/components/AnsiOutput.test.js +1 -1
  193. package/dist/src/ui/components/AnsiOutput.test.js.map +1 -1
  194. package/dist/src/ui/components/AsciiArt.d.ts +3 -3
  195. package/dist/src/ui/components/AsciiArt.js +3 -3
  196. package/dist/src/ui/components/Composer.js +1 -1
  197. package/dist/src/ui/components/Composer.js.map +1 -1
  198. package/dist/src/ui/components/Composer.test.js +5 -2
  199. package/dist/src/ui/components/Composer.test.js.map +1 -1
  200. package/dist/src/ui/components/ConfigInitDisplay.js +4 -6
  201. package/dist/src/ui/components/ConfigInitDisplay.js.map +1 -1
  202. package/dist/src/ui/components/ConsentPrompt.test.js +18 -8
  203. package/dist/src/ui/components/ConsentPrompt.test.js.map +1 -1
  204. package/dist/src/ui/components/ConsoleSummaryDisplay.js +1 -1
  205. package/dist/src/ui/components/ConsoleSummaryDisplay.js.map +1 -1
  206. package/dist/src/ui/components/ContextSummaryDisplay.test.js +11 -6
  207. package/dist/src/ui/components/ContextSummaryDisplay.test.js.map +1 -1
  208. package/dist/src/ui/components/DetailedMessagesDisplay.js +1 -1
  209. package/dist/src/ui/components/DetailedMessagesDisplay.js.map +1 -1
  210. package/dist/src/ui/components/DialogManager.js +4 -0
  211. package/dist/src/ui/components/DialogManager.js.map +1 -1
  212. package/dist/src/ui/components/FolderTrustDialog.test.js +2 -1
  213. package/dist/src/ui/components/FolderTrustDialog.test.js.map +1 -1
  214. package/dist/src/ui/components/Footer.js +4 -3
  215. package/dist/src/ui/components/Footer.js.map +1 -1
  216. package/dist/src/ui/components/Footer.test.js +83 -0
  217. package/dist/src/ui/components/Footer.test.js.map +1 -1
  218. package/dist/src/ui/components/Header.test.js +13 -5
  219. package/dist/src/ui/components/Header.test.js.map +1 -1
  220. package/dist/src/ui/components/Help.test.js +5 -4
  221. package/dist/src/ui/components/Help.test.js.map +1 -1
  222. package/dist/src/ui/components/HistoryItemDisplay.js +1 -1
  223. package/dist/src/ui/components/HistoryItemDisplay.js.map +1 -1
  224. package/dist/src/ui/components/InputPrompt.js +27 -8
  225. package/dist/src/ui/components/InputPrompt.js.map +1 -1
  226. package/dist/src/ui/components/InputPrompt.test.js +776 -727
  227. package/dist/src/ui/components/InputPrompt.test.js.map +1 -1
  228. package/dist/src/ui/components/LoadingIndicator.js +2 -2
  229. package/dist/src/ui/components/LoadingIndicator.js.map +1 -1
  230. package/dist/src/ui/components/LoadingIndicator.test.js +28 -15
  231. package/dist/src/ui/components/LoadingIndicator.test.js.map +1 -1
  232. package/dist/src/ui/components/LoopDetectionConfirmation.js +1 -1
  233. package/dist/src/ui/components/LoopDetectionConfirmation.js.map +1 -1
  234. package/dist/src/ui/components/LoopDetectionConfirmation.test.js +2 -2
  235. package/dist/src/ui/components/LoopDetectionConfirmation.test.js.map +1 -1
  236. package/dist/src/ui/components/MainContent.js +15 -4
  237. package/dist/src/ui/components/MainContent.js.map +1 -1
  238. package/dist/src/ui/components/ModelDialog.js +1 -1
  239. package/dist/src/ui/components/ModelDialog.js.map +1 -1
  240. package/dist/src/ui/components/ModelDialog.test.js +23 -13
  241. package/dist/src/ui/components/ModelDialog.test.js.map +1 -1
  242. package/dist/src/ui/components/ModelStatsDisplay.test.js +1 -1
  243. package/dist/src/ui/components/ModelStatsDisplay.test.js.map +1 -1
  244. package/dist/src/ui/components/Notifications.js +38 -5
  245. package/dist/src/ui/components/Notifications.js.map +1 -1
  246. package/dist/src/ui/components/PermissionsModifyTrustDialog.test.js +2 -2
  247. package/dist/src/ui/components/PermissionsModifyTrustDialog.test.js.map +1 -1
  248. package/dist/src/ui/components/PrepareLabel.test.js +14 -8
  249. package/dist/src/ui/components/PrepareLabel.test.js.map +1 -1
  250. package/dist/src/ui/components/ProQuotaDialog.test.js +14 -6
  251. package/dist/src/ui/components/ProQuotaDialog.test.js.map +1 -1
  252. package/dist/src/ui/components/QueuedMessageDisplay.test.js +11 -6
  253. package/dist/src/ui/components/QueuedMessageDisplay.test.js.map +1 -1
  254. package/dist/src/ui/components/SessionSummaryDisplay.test.js +1 -1
  255. package/dist/src/ui/components/SessionSummaryDisplay.test.js.map +1 -1
  256. package/dist/src/ui/components/SettingsDialog.js +32 -25
  257. package/dist/src/ui/components/SettingsDialog.js.map +1 -1
  258. package/dist/src/ui/components/SettingsDialog.test.js +428 -532
  259. package/dist/src/ui/components/SettingsDialog.test.js.map +1 -1
  260. package/dist/src/ui/components/ShellConfirmationDialog.js +1 -1
  261. package/dist/src/ui/components/ShellConfirmationDialog.js.map +1 -1
  262. package/dist/src/ui/components/ShellConfirmationDialog.test.js +2 -2
  263. package/dist/src/ui/components/ShellConfirmationDialog.test.js.map +1 -1
  264. package/dist/src/ui/components/StatsDisplay.test.js +1 -1
  265. package/dist/src/ui/components/StatsDisplay.test.js.map +1 -1
  266. package/dist/src/ui/components/SuggestionsDisplay.js +1 -1
  267. package/dist/src/ui/components/SuggestionsDisplay.js.map +1 -1
  268. package/dist/src/ui/components/ThemeDialog.test.js +2 -2
  269. package/dist/src/ui/components/ThemeDialog.test.js.map +1 -1
  270. package/dist/src/ui/components/ToolStatsDisplay.test.js +1 -1
  271. package/dist/src/ui/components/ToolStatsDisplay.test.js.map +1 -1
  272. package/dist/src/ui/components/messages/CompressionMessage.test.js +25 -17
  273. package/dist/src/ui/components/messages/CompressionMessage.test.js.map +1 -1
  274. package/dist/src/ui/components/messages/DiffRenderer.test.js +1 -1
  275. package/dist/src/ui/components/messages/DiffRenderer.test.js.map +1 -1
  276. package/dist/src/ui/components/messages/InfoMessage.js +1 -1
  277. package/dist/src/ui/components/messages/InfoMessage.js.map +1 -1
  278. package/dist/src/ui/components/messages/Todo.js +27 -5
  279. package/dist/src/ui/components/messages/Todo.js.map +1 -1
  280. package/dist/src/ui/components/messages/Todo.test.js +20 -8
  281. package/dist/src/ui/components/messages/Todo.test.js.map +1 -1
  282. package/dist/src/ui/components/messages/ToolConfirmationMessage.js +1 -1
  283. package/dist/src/ui/components/messages/ToolConfirmationMessage.js.map +1 -1
  284. package/dist/src/ui/components/messages/ToolGroupMessage.test.js +29 -15
  285. package/dist/src/ui/components/messages/ToolGroupMessage.test.js.map +1 -1
  286. package/dist/src/ui/components/messages/WarningMessage.js +2 -2
  287. package/dist/src/ui/components/messages/WarningMessage.js.map +1 -1
  288. package/dist/src/ui/components/shared/BaseSelectionList.test.js +1 -1
  289. package/dist/src/ui/components/shared/BaseSelectionList.test.js.map +1 -1
  290. package/dist/src/ui/components/shared/MaxSizedBox.test.js +43 -22
  291. package/dist/src/ui/components/shared/MaxSizedBox.test.js.map +1 -1
  292. package/dist/src/ui/components/shared/TextInput.d.ts +15 -0
  293. package/dist/src/ui/components/shared/TextInput.js +38 -0
  294. package/dist/src/ui/components/shared/TextInput.js.map +1 -0
  295. package/dist/src/ui/components/shared/TextInput.test.d.ts +6 -0
  296. package/dist/src/ui/components/shared/TextInput.test.js +242 -0
  297. package/dist/src/ui/components/shared/TextInput.test.js.map +1 -0
  298. package/dist/src/ui/components/shared/text-buffer.d.ts +9 -2
  299. package/dist/src/ui/components/shared/text-buffer.js +51 -13
  300. package/dist/src/ui/components/shared/text-buffer.js.map +1 -1
  301. package/dist/src/ui/components/shared/text-buffer.test.js +385 -202
  302. package/dist/src/ui/components/shared/text-buffer.test.js.map +1 -1
  303. package/dist/src/ui/components/views/ChatList.test.js +7 -4
  304. package/dist/src/ui/components/views/ChatList.test.js.map +1 -1
  305. package/dist/src/ui/components/views/ExtensionsList.d.ts +7 -1
  306. package/dist/src/ui/components/views/ExtensionsList.js +9 -11
  307. package/dist/src/ui/components/views/ExtensionsList.js.map +1 -1
  308. package/dist/src/ui/components/views/ExtensionsList.test.js +43 -22
  309. package/dist/src/ui/components/views/ExtensionsList.test.js.map +1 -1
  310. package/dist/src/ui/components/views/McpStatus.test.js +23 -12
  311. package/dist/src/ui/components/views/McpStatus.test.js.map +1 -1
  312. package/dist/src/ui/contexts/KeypressContext.d.ts +3 -2
  313. package/dist/src/ui/contexts/KeypressContext.js +610 -540
  314. package/dist/src/ui/contexts/KeypressContext.js.map +1 -1
  315. package/dist/src/ui/contexts/KeypressContext.test.js +438 -718
  316. package/dist/src/ui/contexts/KeypressContext.test.js.map +1 -1
  317. package/dist/src/ui/contexts/MouseContext.d.ts +21 -0
  318. package/dist/src/ui/contexts/MouseContext.js +89 -0
  319. package/dist/src/ui/contexts/MouseContext.js.map +1 -0
  320. package/dist/src/ui/contexts/MouseContext.test.d.ts +6 -0
  321. package/dist/src/ui/contexts/MouseContext.test.js +164 -0
  322. package/dist/src/ui/contexts/MouseContext.test.js.map +1 -0
  323. package/dist/src/ui/contexts/SessionContext.test.js +35 -17
  324. package/dist/src/ui/contexts/SessionContext.test.js.map +1 -1
  325. package/dist/src/ui/contexts/UIActionsContext.d.ts +2 -0
  326. package/dist/src/ui/contexts/UIActionsContext.js.map +1 -1
  327. package/dist/src/ui/contexts/UIStateContext.d.ts +2 -0
  328. package/dist/src/ui/contexts/UIStateContext.js.map +1 -1
  329. package/dist/src/ui/hooks/atCommandProcessor.js +31 -9
  330. package/dist/src/ui/hooks/atCommandProcessor.js.map +1 -1
  331. package/dist/src/ui/hooks/atCommandProcessor.test.js +163 -64
  332. package/dist/src/ui/hooks/atCommandProcessor.test.js.map +1 -1
  333. package/dist/src/ui/hooks/shellCommandProcessor.test.js +64 -35
  334. package/dist/src/ui/hooks/shellCommandProcessor.test.js.map +1 -1
  335. package/dist/src/ui/hooks/slashCommandProcessor.test.js +193 -165
  336. package/dist/src/ui/hooks/slashCommandProcessor.test.js.map +1 -1
  337. package/dist/src/ui/hooks/useAtCompletion.test.js +16 -5
  338. package/dist/src/ui/hooks/useAtCompletion.test.js.map +1 -1
  339. package/dist/src/ui/hooks/useAutoAcceptIndicator.js +10 -0
  340. package/dist/src/ui/hooks/useAutoAcceptIndicator.js.map +1 -1
  341. package/dist/src/ui/hooks/useAutoAcceptIndicator.test.js +32 -1
  342. package/dist/src/ui/hooks/useAutoAcceptIndicator.test.js.map +1 -1
  343. package/dist/src/ui/hooks/useCommandCompletion.test.js +66 -64
  344. package/dist/src/ui/hooks/useCommandCompletion.test.js.map +1 -1
  345. package/dist/src/ui/hooks/useConsoleMessages.test.js +26 -9
  346. package/dist/src/ui/hooks/useConsoleMessages.test.js.map +1 -1
  347. package/dist/src/ui/hooks/useEditorSettings.test.js +40 -34
  348. package/dist/src/ui/hooks/useEditorSettings.test.js.map +1 -1
  349. package/dist/src/ui/hooks/useExtensionUpdates.d.ts +14 -5
  350. package/dist/src/ui/hooks/useExtensionUpdates.js +18 -13
  351. package/dist/src/ui/hooks/useExtensionUpdates.js.map +1 -1
  352. package/dist/src/ui/hooks/useExtensionUpdates.test.js +49 -44
  353. package/dist/src/ui/hooks/useExtensionUpdates.test.js.map +1 -1
  354. package/dist/src/ui/hooks/useFlickerDetector.test.js +9 -5
  355. package/dist/src/ui/hooks/useFlickerDetector.test.js.map +1 -1
  356. package/dist/src/ui/hooks/useFocus.test.js +25 -9
  357. package/dist/src/ui/hooks/useFocus.test.js.map +1 -1
  358. package/dist/src/ui/hooks/useFolderTrust.test.js +46 -22
  359. package/dist/src/ui/hooks/useFolderTrust.test.js.map +1 -1
  360. package/dist/src/ui/hooks/useGeminiStream.js +56 -19
  361. package/dist/src/ui/hooks/useGeminiStream.js.map +1 -1
  362. package/dist/src/ui/hooks/useGeminiStream.test.js +260 -411
  363. package/dist/src/ui/hooks/useGeminiStream.test.js.map +1 -1
  364. package/dist/src/ui/hooks/useGitBranchName.js +4 -0
  365. package/dist/src/ui/hooks/useGitBranchName.js.map +1 -1
  366. package/dist/src/ui/hooks/useGitBranchName.test.js +46 -34
  367. package/dist/src/ui/hooks/useGitBranchName.test.js.map +1 -1
  368. package/dist/src/ui/hooks/useHistoryManager.test.js +2 -1
  369. package/dist/src/ui/hooks/useHistoryManager.test.js.map +1 -1
  370. package/dist/src/ui/hooks/useIdeTrustListener.test.js +40 -9
  371. package/dist/src/ui/hooks/useIdeTrustListener.test.js.map +1 -1
  372. package/dist/src/ui/hooks/useInputHistory.test.js +2 -1
  373. package/dist/src/ui/hooks/useInputHistory.test.js.map +1 -1
  374. package/dist/src/ui/hooks/useInputHistoryStore.test.js +2 -1
  375. package/dist/src/ui/hooks/useInputHistoryStore.test.js.map +1 -1
  376. package/dist/src/ui/hooks/useKeypress.test.js +103 -114
  377. package/dist/src/ui/hooks/useKeypress.test.js.map +1 -1
  378. package/dist/src/ui/hooks/useLoadingIndicator.test.js +24 -6
  379. package/dist/src/ui/hooks/useLoadingIndicator.test.js.map +1 -1
  380. package/dist/src/ui/hooks/useMemoryMonitor.test.js +10 -5
  381. package/dist/src/ui/hooks/useMemoryMonitor.test.js.map +1 -1
  382. package/dist/src/ui/hooks/useMessageQueue.test.js +62 -45
  383. package/dist/src/ui/hooks/useMessageQueue.test.js.map +1 -1
  384. package/dist/src/ui/hooks/useModelCommand.test.js +21 -11
  385. package/dist/src/ui/hooks/useModelCommand.test.js.map +1 -1
  386. package/dist/src/ui/hooks/useMouse.d.ts +17 -0
  387. package/dist/src/ui/hooks/useMouse.js +27 -0
  388. package/dist/src/ui/hooks/useMouse.js.map +1 -0
  389. package/dist/src/ui/hooks/useMouse.test.d.ts +6 -0
  390. package/dist/src/ui/hooks/useMouse.test.js +57 -0
  391. package/dist/src/ui/hooks/useMouse.test.js.map +1 -0
  392. package/dist/src/ui/hooks/usePermissionsModifyTrust.test.js +2 -2
  393. package/dist/src/ui/hooks/usePermissionsModifyTrust.test.js.map +1 -1
  394. package/dist/src/ui/hooks/usePhraseCycler.js +1 -1
  395. package/dist/src/ui/hooks/usePhraseCycler.js.map +1 -1
  396. package/dist/src/ui/hooks/usePhraseCycler.test.js +109 -106
  397. package/dist/src/ui/hooks/usePhraseCycler.test.js.map +1 -1
  398. package/dist/src/ui/hooks/usePrivacySettings.test.js +26 -6
  399. package/dist/src/ui/hooks/usePrivacySettings.test.js.map +1 -1
  400. package/dist/src/ui/hooks/usePromptCompletion.js +2 -2
  401. package/dist/src/ui/hooks/usePromptCompletion.js.map +1 -1
  402. package/dist/src/ui/hooks/useQuotaAndFallback.js +13 -14
  403. package/dist/src/ui/hooks/useQuotaAndFallback.js.map +1 -1
  404. package/dist/src/ui/hooks/useQuotaAndFallback.test.js +55 -48
  405. package/dist/src/ui/hooks/useQuotaAndFallback.test.js.map +1 -1
  406. package/dist/src/ui/hooks/useReactToolScheduler.d.ts +8 -1
  407. package/dist/src/ui/hooks/useReactToolScheduler.js +59 -34
  408. package/dist/src/ui/hooks/useReactToolScheduler.js.map +1 -1
  409. package/dist/src/ui/hooks/useReactToolScheduler.test.d.ts +6 -0
  410. package/dist/src/ui/hooks/useReactToolScheduler.test.js +65 -0
  411. package/dist/src/ui/hooks/useReactToolScheduler.test.js.map +1 -0
  412. package/dist/src/ui/hooks/useReverseSearchCompletion.test.js +2 -2
  413. package/dist/src/ui/hooks/useReverseSearchCompletion.test.js.map +1 -1
  414. package/dist/src/ui/hooks/useSelectionList.js +5 -4
  415. package/dist/src/ui/hooks/useSelectionList.js.map +1 -1
  416. package/dist/src/ui/hooks/useSelectionList.test.js +272 -183
  417. package/dist/src/ui/hooks/useSelectionList.test.js.map +1 -1
  418. package/dist/src/ui/hooks/useShellHistory.test.js +52 -20
  419. package/dist/src/ui/hooks/useShellHistory.test.js.map +1 -1
  420. package/dist/src/ui/hooks/useSlashCompletion.js +18 -7
  421. package/dist/src/ui/hooks/useSlashCompletion.js.map +1 -1
  422. package/dist/src/ui/hooks/useSlashCompletion.test.js +275 -137
  423. package/dist/src/ui/hooks/useSlashCompletion.test.js.map +1 -1
  424. package/dist/src/ui/hooks/useTimer.test.js +43 -14
  425. package/dist/src/ui/hooks/useTimer.test.js.map +1 -1
  426. package/dist/src/ui/hooks/useToolScheduler.test.js +226 -242
  427. package/dist/src/ui/hooks/useToolScheduler.test.js.map +1 -1
  428. package/dist/src/ui/hooks/vim.test.js +235 -355
  429. package/dist/src/ui/hooks/vim.test.js.map +1 -1
  430. package/dist/src/ui/keyMatchers.test.js +30 -3
  431. package/dist/src/ui/keyMatchers.test.js.map +1 -1
  432. package/dist/src/ui/state/extensions.d.ts +1 -0
  433. package/dist/src/ui/state/extensions.js +1 -0
  434. package/dist/src/ui/state/extensions.js.map +1 -1
  435. package/dist/src/ui/themes/ansi-light.js +1 -0
  436. package/dist/src/ui/themes/ansi-light.js.map +1 -1
  437. package/dist/src/ui/themes/ansi.js +1 -0
  438. package/dist/src/ui/themes/ansi.js.map +1 -1
  439. package/dist/src/ui/themes/atom-one-dark.js +2 -0
  440. package/dist/src/ui/themes/atom-one-dark.js.map +1 -1
  441. package/dist/src/ui/themes/ayu-light.js +2 -0
  442. package/dist/src/ui/themes/ayu-light.js.map +1 -1
  443. package/dist/src/ui/themes/ayu.js +2 -0
  444. package/dist/src/ui/themes/ayu.js.map +1 -1
  445. package/dist/src/ui/themes/color-utils.d.ts +1 -0
  446. package/dist/src/ui/themes/color-utils.js +6 -0
  447. package/dist/src/ui/themes/color-utils.js.map +1 -1
  448. package/dist/src/ui/themes/color-utils.test.js +13 -1
  449. package/dist/src/ui/themes/color-utils.test.js.map +1 -1
  450. package/dist/src/ui/themes/dracula.js +2 -0
  451. package/dist/src/ui/themes/dracula.js.map +1 -1
  452. package/dist/src/ui/themes/github-dark.js +2 -0
  453. package/dist/src/ui/themes/github-dark.js.map +1 -1
  454. package/dist/src/ui/themes/github-light.js +2 -0
  455. package/dist/src/ui/themes/github-light.js.map +1 -1
  456. package/dist/src/ui/themes/googlecode.js +2 -0
  457. package/dist/src/ui/themes/googlecode.js.map +1 -1
  458. package/dist/src/ui/themes/no-color.js +3 -0
  459. package/dist/src/ui/themes/no-color.js.map +1 -1
  460. package/dist/src/ui/themes/semantic-tokens.d.ts +2 -0
  461. package/dist/src/ui/themes/semantic-tokens.js +6 -0
  462. package/dist/src/ui/themes/semantic-tokens.js.map +1 -1
  463. package/dist/src/ui/themes/shades-of-purple.js +2 -0
  464. package/dist/src/ui/themes/shades-of-purple.js.map +1 -1
  465. package/dist/src/ui/themes/theme.d.ts +3 -0
  466. package/dist/src/ui/themes/theme.js +14 -3
  467. package/dist/src/ui/themes/theme.js.map +1 -1
  468. package/dist/src/ui/themes/theme.test.js +67 -1
  469. package/dist/src/ui/themes/theme.test.js.map +1 -1
  470. package/dist/src/ui/themes/xcode.js +2 -0
  471. package/dist/src/ui/themes/xcode.js.map +1 -1
  472. package/dist/src/ui/types.d.ts +3 -1
  473. package/dist/src/ui/types.js +2 -0
  474. package/dist/src/ui/types.js.map +1 -1
  475. package/dist/src/ui/utils/CodeColorizer.js +2 -1
  476. package/dist/src/ui/utils/CodeColorizer.js.map +1 -1
  477. package/dist/src/ui/utils/InlineMarkdownRenderer.d.ts +1 -0
  478. package/dist/src/ui/utils/InlineMarkdownRenderer.js +11 -10
  479. package/dist/src/ui/utils/InlineMarkdownRenderer.js.map +1 -1
  480. package/dist/src/ui/utils/MarkdownDisplay.js +11 -9
  481. package/dist/src/ui/utils/MarkdownDisplay.js.map +1 -1
  482. package/dist/src/ui/utils/clipboardUtils.js +2 -2
  483. package/dist/src/ui/utils/clipboardUtils.js.map +1 -1
  484. package/dist/src/ui/utils/input.d.ts +17 -0
  485. package/dist/src/ui/utils/input.js +51 -0
  486. package/dist/src/ui/utils/input.js.map +1 -0
  487. package/dist/src/ui/utils/input.test.d.ts +6 -0
  488. package/dist/src/ui/utils/input.test.js +44 -0
  489. package/dist/src/ui/utils/input.test.js.map +1 -0
  490. package/dist/src/ui/utils/kittyProtocolDetector.js +13 -4
  491. package/dist/src/ui/utils/kittyProtocolDetector.js.map +1 -1
  492. package/dist/src/ui/utils/mouse.d.ts +31 -0
  493. package/dist/src/ui/utils/mouse.js +164 -0
  494. package/dist/src/ui/utils/mouse.js.map +1 -0
  495. package/dist/src/ui/utils/mouse.test.d.ts +6 -0
  496. package/dist/src/ui/utils/mouse.test.js +131 -0
  497. package/dist/src/ui/utils/mouse.test.js.map +1 -0
  498. package/dist/src/ui/utils/textOutput.d.ts +25 -0
  499. package/dist/src/ui/utils/textOutput.js +49 -0
  500. package/dist/src/ui/utils/textOutput.js.map +1 -0
  501. package/dist/src/ui/utils/textOutput.test.d.ts +6 -0
  502. package/dist/src/ui/utils/textOutput.test.js +79 -0
  503. package/dist/src/ui/utils/textOutput.test.js.map +1 -0
  504. package/dist/src/ui/utils/updateCheck.d.ts +7 -1
  505. package/dist/src/ui/utils/updateCheck.js +33 -29
  506. package/dist/src/ui/utils/updateCheck.js.map +1 -1
  507. package/dist/src/ui/utils/updateCheck.test.js +24 -50
  508. package/dist/src/ui/utils/updateCheck.test.js.map +1 -1
  509. package/dist/src/utils/commentJson.js +2 -2
  510. package/dist/src/utils/commentJson.js.map +1 -1
  511. package/dist/src/utils/commentJson.test.js +7 -6
  512. package/dist/src/utils/commentJson.test.js.map +1 -1
  513. package/dist/src/utils/envVarResolver.d.ts +2 -2
  514. package/dist/src/utils/envVarResolver.js +10 -7
  515. package/dist/src/utils/envVarResolver.js.map +1 -1
  516. package/dist/src/utils/events.d.ts +11 -2
  517. package/dist/src/utils/events.js +1 -0
  518. package/dist/src/utils/events.js.map +1 -1
  519. package/dist/src/utils/handleAutoUpdate.js +9 -3
  520. package/dist/src/utils/handleAutoUpdate.js.map +1 -1
  521. package/dist/src/utils/sandbox.js +16 -18
  522. package/dist/src/utils/sandbox.js.map +1 -1
  523. package/dist/src/utils/version.js +6 -2
  524. package/dist/src/utils/version.js.map +1 -1
  525. package/dist/src/zed-integration/acp.js +2 -1
  526. package/dist/src/zed-integration/acp.js.map +1 -1
  527. package/dist/src/zed-integration/schema.d.ts +4 -4
  528. package/dist/src/zed-integration/zedIntegration.d.ts +2 -2
  529. package/dist/src/zed-integration/zedIntegration.js +12 -19
  530. package/dist/src/zed-integration/zedIntegration.js.map +1 -1
  531. package/dist/tsconfig.tsbuildinfo +1 -1
  532. package/package.json +14 -14
  533. package/dist/src/config/policy.test.js +0 -360
  534. package/dist/src/config/policy.test.js.map +0 -1
  535. package/dist/src/utils/package.d.ts +0 -12
  536. package/dist/src/utils/package.js +0 -24
  537. package/dist/src/utils/package.js.map +0 -1
  538. /package/dist/src/{config/policy.test.d.ts → commands/extensions/validate.test.d.ts} +0 -0
@@ -4,12 +4,14 @@
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
6
  import { describe, it, expect, vi, beforeEach } from 'vitest';
7
- import { renderHook, act, waitFor } from '@testing-library/react';
7
+ import { act } from 'react';
8
+ import { renderHook } from '../../test-utils/render.js';
9
+ import { waitFor } from '../../test-utils/async.js';
8
10
  import { useGeminiStream } from './useGeminiStream.js';
9
11
  import { useKeypress } from './useKeypress.js';
10
12
  import * as atCommandProcessor from './atCommandProcessor.js';
11
13
  import { useReactToolScheduler } from './useReactToolScheduler.js';
12
- import { ApprovalMode, AuthType, GeminiEventType as ServerGeminiEventType, ToolErrorType, ToolConfirmationOutcome, tokenLimit, } from '@google/gemini-cli-core';
14
+ import { ApprovalMode, AuthType, GeminiEventType as ServerGeminiEventType, ToolErrorType, ToolConfirmationOutcome, tokenLimit, debugLogger, } from '@google/gemini-cli-core';
13
15
  import { MessageType, StreamingState } from '../types.js';
14
16
  // --- MOCKS ---
15
17
  const mockSendMessageStream = vi
@@ -175,8 +177,9 @@ describe('useGeminiStream', () => {
175
177
  mockUseReactToolScheduler.mockReturnValue([
176
178
  [], // Default to empty array for toolCalls
177
179
  mockScheduleToolCalls,
178
- mockCancelAllToolCalls,
179
180
  mockMarkToolsAsSubmitted,
181
+ vi.fn(), // setToolCallsForDisplay
182
+ mockCancelAllToolCalls,
180
183
  ]);
181
184
  // Reset mocks for GeminiClient instance methods (startChat and sendMessageStream)
182
185
  // The GeminiClient constructor itself is mocked at the module level.
@@ -197,35 +200,62 @@ describe('useGeminiStream', () => {
197
200
  setValue: vi.fn(),
198
201
  };
199
202
  const renderTestHook = (initialToolCalls = [], geminiClient) => {
200
- let currentToolCalls = initialToolCalls;
201
- const setToolCalls = (newToolCalls) => {
202
- currentToolCalls = newToolCalls;
203
- };
204
- mockUseReactToolScheduler.mockImplementation(() => [
205
- currentToolCalls,
206
- mockScheduleToolCalls,
207
- mockCancelAllToolCalls,
208
- mockMarkToolsAsSubmitted,
209
- ]);
210
203
  const client = geminiClient || mockConfig.getGeminiClient();
204
+ const initialProps = {
205
+ client,
206
+ history: [],
207
+ addItem: mockAddItem,
208
+ config: mockConfig,
209
+ onDebugMessage: mockOnDebugMessage,
210
+ handleSlashCommand: mockHandleSlashCommand,
211
+ shellModeActive: false,
212
+ loadedSettings: mockLoadedSettings,
213
+ toolCalls: initialToolCalls,
214
+ };
211
215
  const { result, rerender } = renderHook((props) => {
212
- // Update the mock's return value if new toolCalls are passed in props
213
- if (props.toolCalls) {
214
- setToolCalls(props.toolCalls);
215
- }
216
+ // This mock needs to be stateful. When setToolCallsForDisplay is called,
217
+ // it should trigger a rerender with the new state.
218
+ const mockSetToolCallsForDisplay = vi.fn((updater) => {
219
+ const newToolCalls = typeof updater === 'function' ? updater(props.toolCalls) : updater;
220
+ rerender({ ...props, toolCalls: newToolCalls });
221
+ });
222
+ // Create a stateful mock for cancellation that updates the toolCalls state.
223
+ const statefulCancelAllToolCalls = vi.fn((...args) => {
224
+ // Call the original spy so `toHaveBeenCalled` checks still work.
225
+ mockCancelAllToolCalls(...args);
226
+ const newToolCalls = props.toolCalls.map((tc) => {
227
+ // Only cancel tools that are in a cancellable state.
228
+ if (tc.status === 'awaiting_approval' ||
229
+ tc.status === 'executing' ||
230
+ tc.status === 'scheduled' ||
231
+ tc.status === 'validating') {
232
+ // A real cancelled tool call has a response object.
233
+ // We need to simulate this to avoid type errors downstream.
234
+ return {
235
+ ...tc,
236
+ status: 'cancelled',
237
+ response: {
238
+ callId: tc.request.callId,
239
+ responseParts: [],
240
+ resultDisplay: 'Request cancelled.',
241
+ },
242
+ responseSubmittedToGemini: true, // Mark as "processed"
243
+ };
244
+ }
245
+ return tc;
246
+ });
247
+ rerender({ ...props, toolCalls: newToolCalls });
248
+ });
249
+ mockUseReactToolScheduler.mockImplementation(() => [
250
+ props.toolCalls,
251
+ mockScheduleToolCalls,
252
+ mockMarkToolsAsSubmitted,
253
+ mockSetToolCallsForDisplay,
254
+ statefulCancelAllToolCalls, // Use the stateful mock
255
+ ]);
216
256
  return useGeminiStream(props.client, props.history, props.addItem, props.config, props.loadedSettings, props.onDebugMessage, props.handleSlashCommand, props.shellModeActive, () => 'vscode', () => { }, () => Promise.resolve(), false, () => { }, () => { }, () => { }, () => { }, 80, 24);
217
257
  }, {
218
- initialProps: {
219
- client,
220
- history: [],
221
- addItem: mockAddItem,
222
- config: mockConfig,
223
- onDebugMessage: mockOnDebugMessage,
224
- handleSlashCommand: mockHandleSlashCommand,
225
- shellModeActive: false,
226
- loadedSettings: mockLoadedSettings,
227
- toolCalls: initialToolCalls,
228
- },
258
+ initialProps,
229
259
  });
230
260
  return {
231
261
  result,
@@ -235,6 +265,49 @@ describe('useGeminiStream', () => {
235
265
  client,
236
266
  };
237
267
  };
268
+ // Helper to create mock tool calls - reduces boilerplate
269
+ const createMockToolCall = (toolName, callId, confirmationType, mockOnConfirm, status = 'awaiting_approval') => ({
270
+ request: {
271
+ callId,
272
+ name: toolName,
273
+ args: {},
274
+ isClientInitiated: false,
275
+ prompt_id: 'prompt-id-1',
276
+ },
277
+ status: status,
278
+ responseSubmittedToGemini: false,
279
+ confirmationDetails: confirmationType === 'edit'
280
+ ? {
281
+ type: 'edit',
282
+ title: 'Confirm Edit',
283
+ onConfirm: mockOnConfirm,
284
+ fileName: 'file.txt',
285
+ filePath: '/test/file.txt',
286
+ fileDiff: 'fake diff',
287
+ originalContent: 'old',
288
+ newContent: 'new',
289
+ }
290
+ : {
291
+ type: 'info',
292
+ title: `${toolName} confirmation`,
293
+ onConfirm: mockOnConfirm,
294
+ prompt: `Execute ${toolName}?`,
295
+ },
296
+ tool: {
297
+ name: toolName,
298
+ displayName: toolName,
299
+ description: `${toolName} description`,
300
+ build: vi.fn(),
301
+ },
302
+ invocation: {
303
+ getDescription: () => 'Mock description',
304
+ },
305
+ });
306
+ // Helper to render hook with default parameters - reduces boilerplate
307
+ const renderHookWithDefaults = (options = {}) => {
308
+ const { shellModeActive = false, onCancelSubmit = () => { }, setShellInputFocused = () => { }, performMemoryRefresh = () => Promise.resolve(), onAuthError = () => { }, onEditorClose = () => { }, setModelSwitched = vi.fn(), modelSwitched = false, } = options;
309
+ return renderHook(() => useGeminiStream(new MockedGeminiClientClass(mockConfig), [], mockAddItem, mockConfig, mockLoadedSettings, mockOnDebugMessage, mockHandleSlashCommand, shellModeActive, () => 'vscode', onAuthError, performMemoryRefresh, modelSwitched, setModelSwitched, onEditorClose, onCancelSubmit, setShellInputFocused, 80, 24));
310
+ };
238
311
  it('should not submit tool responses if not all tool calls are completed', () => {
239
312
  const toolCalls = [
240
313
  {
@@ -341,7 +414,7 @@ describe('useGeminiStream', () => {
341
414
  let capturedOnComplete = null;
342
415
  mockUseReactToolScheduler.mockImplementation((onComplete) => {
343
416
  capturedOnComplete = onComplete;
344
- return [[], mockScheduleToolCalls, mockMarkToolsAsSubmitted];
417
+ return [[], mockScheduleToolCalls, mockMarkToolsAsSubmitted, vi.fn()];
345
418
  });
346
419
  renderHook(() => useGeminiStream(new MockedGeminiClientClass(mockConfig), [], mockAddItem, mockConfig, mockLoadedSettings, mockOnDebugMessage, mockHandleSlashCommand, false, () => 'vscode', () => { }, () => Promise.resolve(), false, () => { }, () => { }, () => { }, () => { }, 80, 24));
347
420
  // Trigger the onComplete callback with completed tools
@@ -390,7 +463,7 @@ describe('useGeminiStream', () => {
390
463
  let capturedOnComplete = null;
391
464
  mockUseReactToolScheduler.mockImplementation((onComplete) => {
392
465
  capturedOnComplete = onComplete;
393
- return [[], mockScheduleToolCalls, mockMarkToolsAsSubmitted];
466
+ return [[], mockScheduleToolCalls, mockMarkToolsAsSubmitted, vi.fn()];
394
467
  });
395
468
  renderHook(() => useGeminiStream(client, [], mockAddItem, mockConfig, mockLoadedSettings, mockOnDebugMessage, mockHandleSlashCommand, false, () => 'vscode', () => { }, () => Promise.resolve(), false, () => { }, () => { }, () => { }, () => { }, 80, 24));
396
469
  // Trigger the onComplete callback with cancelled tools
@@ -473,7 +546,7 @@ describe('useGeminiStream', () => {
473
546
  let capturedOnComplete = null;
474
547
  mockUseReactToolScheduler.mockImplementation((onComplete) => {
475
548
  capturedOnComplete = onComplete;
476
- return [[], mockScheduleToolCalls, mockMarkToolsAsSubmitted];
549
+ return [[], mockScheduleToolCalls, mockMarkToolsAsSubmitted, vi.fn()];
477
550
  });
478
551
  renderHook(() => useGeminiStream(client, [], mockAddItem, mockConfig, mockLoadedSettings, mockOnDebugMessage, mockHandleSlashCommand, false, () => 'vscode', () => { }, () => Promise.resolve(), false, () => { }, () => { }, () => { }, () => { }, 80, 24));
479
552
  // Trigger the onComplete callback with multiple cancelled tools
@@ -552,6 +625,7 @@ describe('useGeminiStream', () => {
552
625
  currentToolCalls,
553
626
  mockScheduleToolCalls,
554
627
  mockMarkToolsAsSubmitted,
628
+ vi.fn(), // setToolCallsForDisplay
555
629
  ];
556
630
  });
557
631
  const { result, rerender } = renderHook(() => useGeminiStream(new MockedGeminiClientClass(mockConfig), [], mockAddItem, mockConfig, mockLoadedSettings, mockOnDebugMessage, mockHandleSlashCommand, false, () => 'vscode', () => { }, () => Promise.resolve(), false, () => { }, () => { }, () => { }, () => { }, 80, 24));
@@ -565,6 +639,7 @@ describe('useGeminiStream', () => {
565
639
  completedToolCalls,
566
640
  mockScheduleToolCalls,
567
641
  mockMarkToolsAsSubmitted,
642
+ vi.fn(), // setToolCallsForDisplay
568
643
  ];
569
644
  });
570
645
  act(() => {
@@ -696,18 +771,18 @@ describe('useGeminiStream', () => {
696
771
  // Cancel the request
697
772
  simulateEscapeKeyPress();
698
773
  // Allow the stream to continue
699
- act(() => {
774
+ await act(async () => {
700
775
  continueStream();
776
+ // Wait a bit to see if the second part is processed
777
+ await new Promise((resolve) => setTimeout(resolve, 50));
701
778
  });
702
- // Wait a bit to see if the second part is processed
703
- await new Promise((resolve) => setTimeout(resolve, 50));
704
779
  // The text should not have been updated with " Canceled"
705
780
  const lastCall = mockAddItem.mock.calls.find((call) => call[0].type === 'gemini');
706
781
  expect(lastCall?.[0].text).toBe('Initial');
707
782
  // The final state should be idle after cancellation
708
783
  expect(result.current.streamingState).toBe(StreamingState.Idle);
709
784
  });
710
- it('should not cancel if a tool call is in progress (not just responding)', async () => {
785
+ it('should cancel if a tool call is in progress', async () => {
711
786
  const toolCalls = [
712
787
  {
713
788
  request: { callId: 'call1', name: 'tool1', args: {} },
@@ -727,14 +802,64 @@ describe('useGeminiStream', () => {
727
802
  liveOutput: '...',
728
803
  },
729
804
  ];
730
- const abortSpy = vi.spyOn(AbortController.prototype, 'abort');
731
805
  const { result } = renderTestHook(toolCalls);
732
806
  // State is `Responding` because a tool is running
733
807
  expect(result.current.streamingState).toBe(StreamingState.Responding);
734
808
  // Try to cancel
735
809
  simulateEscapeKeyPress();
736
- // Nothing should happen because the state is not `Responding`
737
- expect(abortSpy).not.toHaveBeenCalled();
810
+ // The cancel function should be called
811
+ expect(mockCancelAllToolCalls).toHaveBeenCalled();
812
+ });
813
+ it('should cancel a request when a tool is awaiting confirmation', async () => {
814
+ const mockOnConfirm = vi.fn().mockResolvedValue(undefined);
815
+ const toolCalls = [
816
+ {
817
+ request: {
818
+ callId: 'confirm-call',
819
+ name: 'some_tool',
820
+ args: {},
821
+ isClientInitiated: false,
822
+ prompt_id: 'prompt-id-1',
823
+ },
824
+ status: 'awaiting_approval',
825
+ responseSubmittedToGemini: false,
826
+ tool: {
827
+ name: 'some_tool',
828
+ description: 'a tool',
829
+ build: vi.fn().mockImplementation((_) => ({
830
+ getDescription: () => `Mock description`,
831
+ })),
832
+ },
833
+ invocation: {
834
+ getDescription: () => `Mock description`,
835
+ },
836
+ confirmationDetails: {
837
+ type: 'edit',
838
+ title: 'Confirm Edit',
839
+ onConfirm: mockOnConfirm,
840
+ fileName: 'file.txt',
841
+ filePath: '/test/file.txt',
842
+ fileDiff: 'fake diff',
843
+ originalContent: 'old',
844
+ newContent: 'new',
845
+ },
846
+ },
847
+ ];
848
+ const { result } = renderTestHook(toolCalls);
849
+ // State is `WaitingForConfirmation` because a tool is awaiting approval
850
+ expect(result.current.streamingState).toBe(StreamingState.WaitingForConfirmation);
851
+ // Try to cancel
852
+ simulateEscapeKeyPress();
853
+ // The imperative cancel function should be called on the scheduler
854
+ expect(mockCancelAllToolCalls).toHaveBeenCalled();
855
+ // A cancellation message should be added to history
856
+ await waitFor(() => {
857
+ expect(mockAddItem).toHaveBeenCalledWith(expect.objectContaining({
858
+ text: 'Request cancelled.',
859
+ }), expect.any(Number));
860
+ });
861
+ // The final state should be idle
862
+ expect(result.current.streamingState).toBe(StreamingState.Idle);
738
863
  });
739
864
  });
740
865
  describe('Slash Command Handling', () => {
@@ -871,7 +996,7 @@ describe('useGeminiStream', () => {
871
996
  let capturedOnComplete = null;
872
997
  mockUseReactToolScheduler.mockImplementation((onComplete) => {
873
998
  capturedOnComplete = onComplete;
874
- return [[], mockScheduleToolCalls, mockMarkToolsAsSubmitted];
999
+ return [[], mockScheduleToolCalls, mockMarkToolsAsSubmitted, vi.fn()];
875
1000
  });
876
1001
  renderHook(() => useGeminiStream(new MockedGeminiClientClass(mockConfig), [], mockAddItem, mockConfig, mockLoadedSettings, mockOnDebugMessage, mockHandleSlashCommand, false, () => 'vscode', () => { }, mockPerformMemoryRefresh, false, () => { }, () => { }, () => { }, () => { }, 80, 24));
877
1002
  // Trigger the onComplete callback with the completed save_memory tool
@@ -917,62 +1042,8 @@ describe('useGeminiStream', () => {
917
1042
  it('should auto-approve all pending tool calls when switching to YOLO mode', async () => {
918
1043
  const mockOnConfirm = vi.fn().mockResolvedValue(undefined);
919
1044
  const awaitingApprovalToolCalls = [
920
- {
921
- request: {
922
- callId: 'call1',
923
- name: 'replace',
924
- args: { old_string: 'old', new_string: 'new' },
925
- isClientInitiated: false,
926
- prompt_id: 'prompt-id-1',
927
- },
928
- status: 'awaiting_approval',
929
- responseSubmittedToGemini: false,
930
- confirmationDetails: {
931
- type: 'edit',
932
- title: 'Confirm Edit',
933
- onConfirm: mockOnConfirm,
934
- fileName: 'file.txt',
935
- filePath: '/test/file.txt',
936
- fileDiff: 'fake diff',
937
- originalContent: 'old',
938
- newContent: 'new',
939
- },
940
- tool: {
941
- name: 'replace',
942
- displayName: 'replace',
943
- description: 'Replace text',
944
- build: vi.fn(),
945
- },
946
- invocation: {
947
- getDescription: () => 'Mock description',
948
- },
949
- },
950
- {
951
- request: {
952
- callId: 'call2',
953
- name: 'read_file',
954
- args: { path: '/test/file.txt' },
955
- isClientInitiated: false,
956
- prompt_id: 'prompt-id-1',
957
- },
958
- status: 'awaiting_approval',
959
- responseSubmittedToGemini: false,
960
- confirmationDetails: {
961
- type: 'info',
962
- title: 'Read File',
963
- onConfirm: mockOnConfirm,
964
- prompt: 'Read /test/file.txt?',
965
- },
966
- tool: {
967
- name: 'read_file',
968
- displayName: 'read_file',
969
- description: 'Read file',
970
- build: vi.fn(),
971
- },
972
- invocation: {
973
- getDescription: () => 'Mock description',
974
- },
975
- },
1045
+ createMockToolCall('replace', 'call1', 'edit', mockOnConfirm),
1046
+ createMockToolCall('read_file', 'call2', 'info', mockOnConfirm),
976
1047
  ];
977
1048
  const { result } = renderTestHook(awaitingApprovalToolCalls);
978
1049
  await act(async () => {
@@ -980,109 +1051,23 @@ describe('useGeminiStream', () => {
980
1051
  });
981
1052
  // Both tool calls should be auto-approved
982
1053
  expect(mockOnConfirm).toHaveBeenCalledTimes(2);
983
- expect(mockOnConfirm).toHaveBeenNthCalledWith(1, ToolConfirmationOutcome.ProceedOnce);
984
- expect(mockOnConfirm).toHaveBeenNthCalledWith(2, ToolConfirmationOutcome.ProceedOnce);
1054
+ expect(mockOnConfirm).toHaveBeenCalledWith(ToolConfirmationOutcome.ProceedOnce);
985
1055
  });
986
1056
  it('should only auto-approve edit tools when switching to AUTO_EDIT mode', async () => {
987
1057
  const mockOnConfirmReplace = vi.fn().mockResolvedValue(undefined);
988
1058
  const mockOnConfirmWrite = vi.fn().mockResolvedValue(undefined);
989
1059
  const mockOnConfirmRead = vi.fn().mockResolvedValue(undefined);
990
1060
  const awaitingApprovalToolCalls = [
991
- {
992
- request: {
993
- callId: 'call1',
994
- name: 'replace',
995
- args: { old_string: 'old', new_string: 'new' },
996
- isClientInitiated: false,
997
- prompt_id: 'prompt-id-1',
998
- },
999
- status: 'awaiting_approval',
1000
- responseSubmittedToGemini: false,
1001
- confirmationDetails: {
1002
- type: 'edit',
1003
- title: 'Confirm Edit',
1004
- onConfirm: mockOnConfirmReplace,
1005
- fileName: 'file.txt',
1006
- filePath: '/test/file.txt',
1007
- fileDiff: 'fake diff',
1008
- originalContent: 'old',
1009
- newContent: 'new',
1010
- },
1011
- tool: {
1012
- name: 'replace',
1013
- displayName: 'replace',
1014
- description: 'Replace text',
1015
- build: vi.fn(),
1016
- },
1017
- invocation: {
1018
- getDescription: () => 'Mock description',
1019
- },
1020
- },
1021
- {
1022
- request: {
1023
- callId: 'call2',
1024
- name: 'write_file',
1025
- args: { path: '/test/new.txt', content: 'content' },
1026
- isClientInitiated: false,
1027
- prompt_id: 'prompt-id-1',
1028
- },
1029
- status: 'awaiting_approval',
1030
- responseSubmittedToGemini: false,
1031
- confirmationDetails: {
1032
- type: 'edit',
1033
- title: 'Confirm Edit',
1034
- onConfirm: mockOnConfirmWrite,
1035
- fileName: 'new.txt',
1036
- filePath: '/test/new.txt',
1037
- fileDiff: 'fake diff',
1038
- originalContent: null,
1039
- newContent: 'content',
1040
- },
1041
- tool: {
1042
- name: 'write_file',
1043
- displayName: 'write_file',
1044
- description: 'Write file',
1045
- build: vi.fn(),
1046
- },
1047
- invocation: {
1048
- getDescription: () => 'Mock description',
1049
- },
1050
- },
1051
- {
1052
- request: {
1053
- callId: 'call3',
1054
- name: 'read_file',
1055
- args: { path: '/test/file.txt' },
1056
- isClientInitiated: false,
1057
- prompt_id: 'prompt-id-1',
1058
- },
1059
- status: 'awaiting_approval',
1060
- responseSubmittedToGemini: false,
1061
- confirmationDetails: {
1062
- type: 'info',
1063
- title: 'Read File',
1064
- onConfirm: mockOnConfirmRead,
1065
- prompt: 'Read /test/file.txt?',
1066
- },
1067
- tool: {
1068
- name: 'read_file',
1069
- displayName: 'read_file',
1070
- description: 'Read file',
1071
- build: vi.fn(),
1072
- },
1073
- invocation: {
1074
- getDescription: () => 'Mock description',
1075
- },
1076
- },
1061
+ createMockToolCall('replace', 'call1', 'edit', mockOnConfirmReplace),
1062
+ createMockToolCall('write_file', 'call2', 'edit', mockOnConfirmWrite),
1063
+ createMockToolCall('read_file', 'call3', 'info', mockOnConfirmRead),
1077
1064
  ];
1078
1065
  const { result } = renderTestHook(awaitingApprovalToolCalls);
1079
1066
  await act(async () => {
1080
1067
  await result.current.handleApprovalModeChange(ApprovalMode.AUTO_EDIT);
1081
1068
  });
1082
1069
  // Only replace and write_file should be auto-approved
1083
- expect(mockOnConfirmReplace).toHaveBeenCalledTimes(1);
1084
1070
  expect(mockOnConfirmReplace).toHaveBeenCalledWith(ToolConfirmationOutcome.ProceedOnce);
1085
- expect(mockOnConfirmWrite).toHaveBeenCalledTimes(1);
1086
1071
  expect(mockOnConfirmWrite).toHaveBeenCalledWith(ToolConfirmationOutcome.ProceedOnce);
1087
1072
  // read_file should not be auto-approved
1088
1073
  expect(mockOnConfirmRead).not.toHaveBeenCalled();
@@ -1090,36 +1075,7 @@ describe('useGeminiStream', () => {
1090
1075
  it('should not auto-approve any tools when switching to REQUIRE_CONFIRMATION mode', async () => {
1091
1076
  const mockOnConfirm = vi.fn().mockResolvedValue(undefined);
1092
1077
  const awaitingApprovalToolCalls = [
1093
- {
1094
- request: {
1095
- callId: 'call1',
1096
- name: 'replace',
1097
- args: { old_string: 'old', new_string: 'new' },
1098
- isClientInitiated: false,
1099
- prompt_id: 'prompt-id-1',
1100
- },
1101
- status: 'awaiting_approval',
1102
- responseSubmittedToGemini: false,
1103
- confirmationDetails: {
1104
- type: 'edit',
1105
- title: 'Confirm Edit',
1106
- onConfirm: mockOnConfirm,
1107
- fileName: 'file.txt',
1108
- filePath: '/test/file.txt',
1109
- fileDiff: 'fake diff',
1110
- originalContent: 'old',
1111
- newContent: 'new',
1112
- },
1113
- tool: {
1114
- name: 'replace',
1115
- displayName: 'replace',
1116
- description: 'Replace text',
1117
- build: vi.fn(),
1118
- },
1119
- invocation: {
1120
- getDescription: () => 'Mock description',
1121
- },
1122
- },
1078
+ createMockToolCall('replace', 'call1', 'edit', mockOnConfirm),
1123
1079
  ];
1124
1080
  const { result } = renderTestHook(awaitingApprovalToolCalls);
1125
1081
  await act(async () => {
@@ -1129,85 +1085,27 @@ describe('useGeminiStream', () => {
1129
1085
  expect(mockOnConfirm).not.toHaveBeenCalled();
1130
1086
  });
1131
1087
  it('should handle errors gracefully when auto-approving tool calls', async () => {
1132
- const consoleSpy = vi
1133
- .spyOn(console, 'error')
1088
+ const debuggerSpy = vi
1089
+ .spyOn(debugLogger, 'warn')
1134
1090
  .mockImplementation(() => { });
1135
1091
  const mockOnConfirmSuccess = vi.fn().mockResolvedValue(undefined);
1136
1092
  const mockOnConfirmError = vi
1137
1093
  .fn()
1138
1094
  .mockRejectedValue(new Error('Approval failed'));
1139
1095
  const awaitingApprovalToolCalls = [
1140
- {
1141
- request: {
1142
- callId: 'call1',
1143
- name: 'replace',
1144
- args: { old_string: 'old', new_string: 'new' },
1145
- isClientInitiated: false,
1146
- prompt_id: 'prompt-id-1',
1147
- },
1148
- status: 'awaiting_approval',
1149
- responseSubmittedToGemini: false,
1150
- confirmationDetails: {
1151
- type: 'edit',
1152
- title: 'Confirm Edit',
1153
- onConfirm: mockOnConfirmSuccess,
1154
- fileName: 'file.txt',
1155
- filePath: '/test/file.txt',
1156
- fileDiff: 'fake diff',
1157
- originalContent: 'old',
1158
- newContent: 'new',
1159
- },
1160
- tool: {
1161
- name: 'replace',
1162
- displayName: 'replace',
1163
- description: 'Replace text',
1164
- build: vi.fn(),
1165
- },
1166
- invocation: {
1167
- getDescription: () => 'Mock description',
1168
- },
1169
- },
1170
- {
1171
- request: {
1172
- callId: 'call2',
1173
- name: 'write_file',
1174
- args: { path: '/test/file.txt', content: 'content' },
1175
- isClientInitiated: false,
1176
- prompt_id: 'prompt-id-1',
1177
- },
1178
- status: 'awaiting_approval',
1179
- responseSubmittedToGemini: false,
1180
- confirmationDetails: {
1181
- type: 'edit',
1182
- title: 'Confirm Edit',
1183
- onConfirm: mockOnConfirmError,
1184
- fileName: 'file.txt',
1185
- filePath: '/test/file.txt',
1186
- fileDiff: 'fake diff',
1187
- originalContent: null,
1188
- newContent: 'content',
1189
- },
1190
- tool: {
1191
- name: 'write_file',
1192
- displayName: 'write_file',
1193
- description: 'Write file',
1194
- build: vi.fn(),
1195
- },
1196
- invocation: {
1197
- getDescription: () => 'Mock description',
1198
- },
1199
- },
1096
+ createMockToolCall('replace', 'call1', 'edit', mockOnConfirmSuccess),
1097
+ createMockToolCall('write_file', 'call2', 'edit', mockOnConfirmError),
1200
1098
  ];
1201
1099
  const { result } = renderTestHook(awaitingApprovalToolCalls);
1202
1100
  await act(async () => {
1203
1101
  await result.current.handleApprovalModeChange(ApprovalMode.YOLO);
1204
1102
  });
1205
1103
  // Both confirmation methods should be called
1206
- expect(mockOnConfirmSuccess).toHaveBeenCalledTimes(1);
1207
- expect(mockOnConfirmError).toHaveBeenCalledTimes(1);
1104
+ expect(mockOnConfirmSuccess).toHaveBeenCalled();
1105
+ expect(mockOnConfirmError).toHaveBeenCalled();
1208
1106
  // Error should be logged
1209
- expect(consoleSpy).toHaveBeenCalledWith('Failed to auto-approve tool call call2:', expect.any(Error));
1210
- consoleSpy.mockRestore();
1107
+ expect(debuggerSpy).toHaveBeenCalledWith('Failed to auto-approve tool call call2:', expect.any(Error));
1108
+ debuggerSpy.mockRestore();
1211
1109
  });
1212
1110
  it('should skip tool calls without confirmationDetails', async () => {
1213
1111
  const awaitingApprovalToolCalls = [
@@ -1374,53 +1272,37 @@ describe('useGeminiStream', () => {
1374
1272
  beforeEach(() => {
1375
1273
  vi.mocked(tokenLimit).mockReturnValue(100);
1376
1274
  });
1377
- it('should add message without suggestion when remaining tokens are > 75% of limit', async () => {
1378
- // Setup mock to return a stream with ContextWindowWillOverflow event
1379
- // Limit is 100, remaining is 80 (> 75)
1380
- mockSendMessageStream.mockReturnValue((async function* () {
1381
- yield {
1382
- type: ServerGeminiEventType.ContextWindowWillOverflow,
1383
- value: {
1384
- estimatedRequestTokenCount: 20,
1385
- remainingTokenCount: 80,
1386
- },
1387
- };
1388
- })());
1389
- const { result } = renderHook(() => useGeminiStream(new MockedGeminiClientClass(mockConfig), [], mockAddItem, mockConfig, mockLoadedSettings, mockOnDebugMessage, mockHandleSlashCommand, false, () => 'vscode', () => { }, () => Promise.resolve(), false, () => { }, () => { }, () => { }, () => { }, 80, 24));
1390
- // Submit a query
1391
- await act(async () => {
1392
- await result.current.submitQuery('Test overflow');
1393
- });
1394
- // Check that the message was added without suggestion
1395
- await waitFor(() => {
1396
- expect(mockAddItem).toHaveBeenCalledWith({
1397
- type: 'info',
1398
- text: `Sending this message (20 tokens) might exceed the remaining context window limit (80 tokens).`,
1399
- }, expect.any(Number));
1400
- });
1401
- });
1402
- it('should add message with suggestion when remaining tokens are < 75% of limit', async () => {
1403
- // Setup mock to return a stream with ContextWindowWillOverflow event
1404
- // Limit is 100, remaining is 70 (< 75)
1275
+ it.each([
1276
+ {
1277
+ name: 'without suggestion when remaining tokens are > 75% of limit',
1278
+ requestTokens: 20,
1279
+ remainingTokens: 80,
1280
+ expectedMessage: 'Sending this message (20 tokens) might exceed the remaining context window limit (80 tokens).',
1281
+ },
1282
+ {
1283
+ name: 'with suggestion when remaining tokens are < 75% of limit',
1284
+ requestTokens: 30,
1285
+ remainingTokens: 70,
1286
+ expectedMessage: 'Sending this message (30 tokens) might exceed the remaining context window limit (70 tokens). Please try reducing the size of your message or use the `/compress` command to compress the chat history.',
1287
+ },
1288
+ ])('should add message $name', async ({ requestTokens, remainingTokens, expectedMessage }) => {
1405
1289
  mockSendMessageStream.mockReturnValue((async function* () {
1406
1290
  yield {
1407
1291
  type: ServerGeminiEventType.ContextWindowWillOverflow,
1408
1292
  value: {
1409
- estimatedRequestTokenCount: 30,
1410
- remainingTokenCount: 70,
1293
+ estimatedRequestTokenCount: requestTokens,
1294
+ remainingTokenCount: remainingTokens,
1411
1295
  },
1412
1296
  };
1413
1297
  })());
1414
- const { result } = renderHook(() => useGeminiStream(new MockedGeminiClientClass(mockConfig), [], mockAddItem, mockConfig, mockLoadedSettings, mockOnDebugMessage, mockHandleSlashCommand, false, () => 'vscode', () => { }, () => Promise.resolve(), false, () => { }, () => { }, () => { }, () => { }, 80, 24));
1415
- // Submit a query
1298
+ const { result } = renderHookWithDefaults();
1416
1299
  await act(async () => {
1417
1300
  await result.current.submitQuery('Test overflow');
1418
1301
  });
1419
- // Check that the message was added with suggestion
1420
1302
  await waitFor(() => {
1421
1303
  expect(mockAddItem).toHaveBeenCalledWith({
1422
1304
  type: 'info',
1423
- text: `Sending this message (30 tokens) might exceed the remaining context window limit (70 tokens). Please try reducing the size of your message or use the \`/compress\` command to compress the chat history.`,
1305
+ text: expectedMessage,
1424
1306
  }, expect.any(Number));
1425
1307
  });
1426
1308
  });
@@ -1447,112 +1329,71 @@ describe('useGeminiStream', () => {
1447
1329
  expect(onCancelSubmitSpy).toHaveBeenCalled();
1448
1330
  });
1449
1331
  });
1450
- it('should not add message for STOP finish reason', async () => {
1451
- // Setup mock to return a stream with STOP finish reason
1452
- mockSendMessageStream.mockReturnValue((async function* () {
1453
- yield {
1454
- type: ServerGeminiEventType.Content,
1455
- value: 'Complete response',
1456
- };
1457
- yield {
1458
- type: ServerGeminiEventType.Finished,
1459
- value: { reason: 'STOP', usageMetadata: undefined },
1460
- };
1461
- })());
1462
- const { result } = renderHook(() => useGeminiStream(new MockedGeminiClientClass(mockConfig), [], mockAddItem, mockConfig, mockLoadedSettings, mockOnDebugMessage, mockHandleSlashCommand, false, () => 'vscode', () => { }, () => Promise.resolve(), false, () => { }, () => { }, () => { }, () => { }, 80, 24));
1463
- // Submit a query
1464
- await act(async () => {
1465
- await result.current.submitQuery('Test normal completion');
1466
- });
1467
- // Wait a bit to ensure no message is added
1468
- await new Promise((resolve) => setTimeout(resolve, 100));
1469
- // Check that no info message was added for STOP
1470
- const infoMessages = mockAddItem.mock.calls.filter((call) => call[0].type === 'info');
1471
- expect(infoMessages).toHaveLength(0);
1472
- });
1473
- it('should not add message for FINISH_REASON_UNSPECIFIED', async () => {
1474
- // Setup mock to return a stream with FINISH_REASON_UNSPECIFIED
1332
+ it.each([
1333
+ {
1334
+ reason: 'STOP',
1335
+ shouldAddMessage: false,
1336
+ },
1337
+ {
1338
+ reason: 'FINISH_REASON_UNSPECIFIED',
1339
+ shouldAddMessage: false,
1340
+ },
1341
+ {
1342
+ reason: 'SAFETY',
1343
+ message: '⚠️ Response stopped due to safety reasons.',
1344
+ },
1345
+ {
1346
+ reason: 'RECITATION',
1347
+ message: '⚠️ Response stopped due to recitation policy.',
1348
+ },
1349
+ {
1350
+ reason: 'LANGUAGE',
1351
+ message: '⚠️ Response stopped due to unsupported language.',
1352
+ },
1353
+ {
1354
+ reason: 'BLOCKLIST',
1355
+ message: '⚠️ Response stopped due to forbidden terms.',
1356
+ },
1357
+ {
1358
+ reason: 'PROHIBITED_CONTENT',
1359
+ message: '⚠️ Response stopped due to prohibited content.',
1360
+ },
1361
+ {
1362
+ reason: 'SPII',
1363
+ message: '⚠️ Response stopped due to sensitive personally identifiable information.',
1364
+ },
1365
+ {
1366
+ reason: 'OTHER',
1367
+ message: '⚠️ Response stopped for other reasons.',
1368
+ },
1369
+ {
1370
+ reason: 'MALFORMED_FUNCTION_CALL',
1371
+ message: '⚠️ Response stopped due to malformed function call.',
1372
+ },
1373
+ {
1374
+ reason: 'IMAGE_SAFETY',
1375
+ message: '⚠️ Response stopped due to image safety violations.',
1376
+ },
1377
+ {
1378
+ reason: 'UNEXPECTED_TOOL_CALL',
1379
+ message: '⚠️ Response stopped due to unexpected tool call.',
1380
+ },
1381
+ ])('should handle $reason finish reason correctly', async ({ reason, shouldAddMessage = true, message }) => {
1475
1382
  mockSendMessageStream.mockReturnValue((async function* () {
1476
1383
  yield {
1477
1384
  type: ServerGeminiEventType.Content,
1478
- value: 'Response with unspecified finish',
1385
+ value: `Response for ${reason}`,
1479
1386
  };
1480
1387
  yield {
1481
1388
  type: ServerGeminiEventType.Finished,
1482
- value: {
1483
- reason: 'FINISH_REASON_UNSPECIFIED',
1484
- usageMetadata: undefined,
1485
- },
1389
+ value: { reason, usageMetadata: undefined },
1486
1390
  };
1487
1391
  })());
1488
- const { result } = renderHook(() => useGeminiStream(new MockedGeminiClientClass(mockConfig), [], mockAddItem, mockConfig, mockLoadedSettings, mockOnDebugMessage, mockHandleSlashCommand, false, () => 'vscode', () => { }, () => Promise.resolve(), false, () => { }, () => { }, () => { }, () => { }, 80, 24));
1489
- // Submit a query
1392
+ const { result } = renderHookWithDefaults();
1490
1393
  await act(async () => {
1491
- await result.current.submitQuery('Test unspecified finish');
1394
+ await result.current.submitQuery(`Test ${reason}`);
1492
1395
  });
1493
- // Wait a bit to ensure no message is added
1494
- await new Promise((resolve) => setTimeout(resolve, 100));
1495
- // Check that no info message was added
1496
- const infoMessages = mockAddItem.mock.calls.filter((call) => call[0].type === 'info');
1497
- expect(infoMessages).toHaveLength(0);
1498
- });
1499
- it('should add appropriate messages for other finish reasons', async () => {
1500
- const testCases = [
1501
- {
1502
- reason: 'SAFETY',
1503
- message: '⚠️ Response stopped due to safety reasons.',
1504
- },
1505
- {
1506
- reason: 'RECITATION',
1507
- message: '⚠️ Response stopped due to recitation policy.',
1508
- },
1509
- {
1510
- reason: 'LANGUAGE',
1511
- message: '⚠️ Response stopped due to unsupported language.',
1512
- },
1513
- {
1514
- reason: 'BLOCKLIST',
1515
- message: '⚠️ Response stopped due to forbidden terms.',
1516
- },
1517
- {
1518
- reason: 'PROHIBITED_CONTENT',
1519
- message: '⚠️ Response stopped due to prohibited content.',
1520
- },
1521
- {
1522
- reason: 'SPII',
1523
- message: '⚠️ Response stopped due to sensitive personally identifiable information.',
1524
- },
1525
- { reason: 'OTHER', message: '⚠️ Response stopped for other reasons.' },
1526
- {
1527
- reason: 'MALFORMED_FUNCTION_CALL',
1528
- message: '⚠️ Response stopped due to malformed function call.',
1529
- },
1530
- {
1531
- reason: 'IMAGE_SAFETY',
1532
- message: '⚠️ Response stopped due to image safety violations.',
1533
- },
1534
- {
1535
- reason: 'UNEXPECTED_TOOL_CALL',
1536
- message: '⚠️ Response stopped due to unexpected tool call.',
1537
- },
1538
- ];
1539
- for (const { reason, message } of testCases) {
1540
- // Reset mocks for each test case
1541
- mockAddItem.mockClear();
1542
- mockSendMessageStream.mockReturnValue((async function* () {
1543
- yield {
1544
- type: ServerGeminiEventType.Content,
1545
- value: `Response for ${reason}`,
1546
- };
1547
- yield {
1548
- type: ServerGeminiEventType.Finished,
1549
- value: { reason, usageMetadata: undefined },
1550
- };
1551
- })());
1552
- const { result } = renderHook(() => useGeminiStream(new MockedGeminiClientClass(mockConfig), [], mockAddItem, mockConfig, mockLoadedSettings, mockOnDebugMessage, mockHandleSlashCommand, false, () => 'vscode', () => { }, () => Promise.resolve(), false, () => { }, () => { }, () => { }, vi.fn(), 80, 24));
1553
- await act(async () => {
1554
- await result.current.submitQuery(`Test ${reason}`);
1555
- });
1396
+ if (shouldAddMessage) {
1556
1397
  await waitFor(() => {
1557
1398
  expect(mockAddItem).toHaveBeenCalledWith({
1558
1399
  type: 'info',
@@ -1560,6 +1401,14 @@ describe('useGeminiStream', () => {
1560
1401
  }, expect.any(Number));
1561
1402
  });
1562
1403
  }
1404
+ else {
1405
+ // Verify state returns to idle without any info messages
1406
+ await waitFor(() => {
1407
+ expect(result.current.streamingState).toBe(StreamingState.Idle);
1408
+ });
1409
+ const infoMessages = mockAddItem.mock.calls.filter((call) => call[0].type === 'info');
1410
+ expect(infoMessages).toHaveLength(0);
1411
+ }
1563
1412
  });
1564
1413
  });
1565
1414
  it('should process @include commands, adding user turn after processing to prevent race conditions', async () => {