@machina.ai/cell-cli-core 1.0.17-rc1 → 1.0.21-rc2

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 (399) hide show
  1. package/dist/index.d.ts +3 -0
  2. package/dist/index.js +3 -0
  3. package/dist/index.js.map +1 -1
  4. package/dist/package.json +9 -2
  5. package/dist/src/code_assist/converter.d.ts +4 -2
  6. package/dist/src/code_assist/converter.js +2 -1
  7. package/dist/src/code_assist/converter.js.map +1 -1
  8. package/dist/src/code_assist/converter.test.js +48 -1
  9. package/dist/src/code_assist/converter.test.js.map +1 -1
  10. package/dist/src/code_assist/oauth2.d.ts +1 -0
  11. package/dist/src/code_assist/oauth2.js +38 -21
  12. package/dist/src/code_assist/oauth2.js.map +1 -1
  13. package/dist/src/code_assist/oauth2.test.js +51 -8
  14. package/dist/src/code_assist/oauth2.test.js.map +1 -1
  15. package/dist/src/code_assist/server.js +1 -1
  16. package/dist/src/code_assist/server.js.map +1 -1
  17. package/dist/src/code_assist/server.test.js +4 -1
  18. package/dist/src/code_assist/server.test.js.map +1 -1
  19. package/dist/src/code_assist/setup.js +48 -17
  20. package/dist/src/code_assist/setup.js.map +1 -1
  21. package/dist/src/code_assist/setup.test.js +114 -8
  22. package/dist/src/code_assist/setup.test.js.map +1 -1
  23. package/dist/src/config/config.d.ts +41 -11
  24. package/dist/src/config/config.js +85 -27
  25. package/dist/src/config/config.js.map +1 -1
  26. package/dist/src/config/config.test.js +117 -3
  27. package/dist/src/config/config.test.js.map +1 -1
  28. package/dist/src/config/flashFallback.test.js +0 -4
  29. package/dist/src/config/flashFallback.test.js.map +1 -1
  30. package/dist/src/core/client.d.ts +12 -16
  31. package/dist/src/core/client.js +218 -135
  32. package/dist/src/core/client.js.map +1 -1
  33. package/dist/src/core/client.test.js +636 -37
  34. package/dist/src/core/client.test.js.map +1 -1
  35. package/dist/src/core/contentGenerator.js +23 -21
  36. package/dist/src/core/contentGenerator.js.map +1 -1
  37. package/dist/src/core/contentGenerator.test.js +30 -126
  38. package/dist/src/core/contentGenerator.test.js.map +1 -1
  39. package/dist/src/core/coreToolScheduler.d.ts +23 -8
  40. package/dist/src/core/coreToolScheduler.js +179 -64
  41. package/dist/src/core/coreToolScheduler.js.map +1 -1
  42. package/dist/src/core/coreToolScheduler.test.js +250 -97
  43. package/dist/src/core/coreToolScheduler.test.js.map +1 -1
  44. package/dist/src/core/geminiChat.d.ts +7 -6
  45. package/dist/src/core/geminiChat.js +43 -43
  46. package/dist/src/core/geminiChat.js.map +1 -1
  47. package/dist/src/core/logger.d.ts +23 -1
  48. package/dist/src/core/logger.js +115 -11
  49. package/dist/src/core/logger.js.map +1 -1
  50. package/dist/src/core/logger.test.js +112 -17
  51. package/dist/src/core/logger.test.js.map +1 -1
  52. package/dist/src/core/loggingContentGenerator.d.ts +25 -0
  53. package/dist/src/core/loggingContentGenerator.js +95 -0
  54. package/dist/src/core/loggingContentGenerator.js.map +1 -0
  55. package/dist/src/core/nonInteractiveToolExecutor.js +28 -1
  56. package/dist/src/core/nonInteractiveToolExecutor.js.map +1 -1
  57. package/dist/src/core/nonInteractiveToolExecutor.test.js +85 -62
  58. package/dist/src/core/nonInteractiveToolExecutor.test.js.map +1 -1
  59. package/dist/src/core/prompts.js +4 -4
  60. package/dist/src/core/prompts.js.map +1 -1
  61. package/dist/src/core/subagent.d.ts +230 -0
  62. package/dist/src/core/subagent.js +447 -0
  63. package/dist/src/core/subagent.js.map +1 -0
  64. package/dist/src/core/subagent.test.d.ts +6 -0
  65. package/dist/src/core/subagent.test.js +515 -0
  66. package/dist/src/core/subagent.test.js.map +1 -0
  67. package/dist/src/core/turn.js +1 -0
  68. package/dist/src/core/turn.js.map +1 -1
  69. package/dist/src/core/turn.test.js +4 -0
  70. package/dist/src/core/turn.test.js.map +1 -1
  71. package/dist/src/generated/git-commit.d.ts +7 -0
  72. package/dist/src/generated/git-commit.js +10 -0
  73. package/dist/src/generated/git-commit.js.map +1 -0
  74. package/dist/src/ide/constants.d.ts +6 -0
  75. package/dist/src/ide/constants.js +7 -0
  76. package/dist/src/ide/constants.js.map +1 -0
  77. package/dist/src/ide/detect-ide.d.ts +12 -2
  78. package/dist/src/ide/detect-ide.js +64 -5
  79. package/dist/src/ide/detect-ide.js.map +1 -1
  80. package/dist/src/ide/detect-ide.test.d.ts +6 -0
  81. package/dist/src/ide/detect-ide.test.js +65 -0
  82. package/dist/src/ide/detect-ide.test.js.map +1 -0
  83. package/dist/src/ide/ide-client.d.ts +34 -11
  84. package/dist/src/ide/ide-client.js +230 -62
  85. package/dist/src/ide/ide-client.js.map +1 -1
  86. package/dist/src/ide/ide-client.test.d.ts +6 -0
  87. package/dist/src/ide/ide-client.test.js +43 -0
  88. package/dist/src/ide/ide-client.test.js.map +1 -0
  89. package/dist/src/ide/ide-installer.js +23 -34
  90. package/dist/src/ide/ide-installer.js.map +1 -1
  91. package/dist/src/ide/ide-installer.test.js +6 -8
  92. package/dist/src/ide/ide-installer.test.js.map +1 -1
  93. package/dist/src/ide/ideContext.d.ts +97 -2
  94. package/dist/src/ide/ideContext.js +45 -0
  95. package/dist/src/ide/ideContext.js.map +1 -1
  96. package/dist/src/ide/process-utils.d.ts +14 -0
  97. package/dist/src/ide/process-utils.js +57 -0
  98. package/dist/src/ide/process-utils.js.map +1 -0
  99. package/dist/src/index.d.ts +7 -1
  100. package/dist/src/index.js +7 -1
  101. package/dist/src/index.js.map +1 -1
  102. package/dist/src/mcp/google-auth-provider.js +9 -0
  103. package/dist/src/mcp/google-auth-provider.js.map +1 -1
  104. package/dist/src/mcp/google-auth-provider.test.js +45 -10
  105. package/dist/src/mcp/google-auth-provider.test.js.map +1 -1
  106. package/dist/src/mcp/oauth-provider.d.ts +1 -1
  107. package/dist/src/mcp/oauth-provider.js +185 -59
  108. package/dist/src/mcp/oauth-provider.js.map +1 -1
  109. package/dist/src/mcp/oauth-provider.test.js +134 -62
  110. package/dist/src/mcp/oauth-provider.test.js.map +1 -1
  111. package/dist/src/mcp/oauth-utils.d.ts +3 -1
  112. package/dist/src/mcp/oauth-utils.js +50 -12
  113. package/dist/src/mcp/oauth-utils.js.map +1 -1
  114. package/dist/src/mcp/oauth-utils.test.js +17 -2
  115. package/dist/src/mcp/oauth-utils.test.js.map +1 -1
  116. package/dist/src/mocks/msw.d.ts +6 -0
  117. package/dist/src/mocks/msw.js +8 -0
  118. package/dist/src/mocks/msw.js.map +1 -0
  119. package/dist/src/prompts/prompt-registry.d.ts +8 -0
  120. package/dist/src/prompts/prompt-registry.js +16 -0
  121. package/dist/src/prompts/prompt-registry.js.map +1 -1
  122. package/dist/src/services/chatRecordingService.d.ts +150 -0
  123. package/dist/src/services/chatRecordingService.js +318 -0
  124. package/dist/src/services/chatRecordingService.js.map +1 -0
  125. package/dist/src/services/chatRecordingService.test.d.ts +6 -0
  126. package/dist/src/services/chatRecordingService.test.js +288 -0
  127. package/dist/src/services/chatRecordingService.test.js.map +1 -0
  128. package/dist/src/services/fileSystemService.d.ts +31 -0
  129. package/dist/src/services/fileSystemService.js +18 -0
  130. package/dist/src/services/fileSystemService.js.map +1 -0
  131. package/dist/src/services/fileSystemService.test.d.ts +6 -0
  132. package/dist/src/services/fileSystemService.test.js +41 -0
  133. package/dist/src/services/fileSystemService.test.js.map +1 -0
  134. package/dist/src/services/loopDetectionService.js +19 -16
  135. package/dist/src/services/loopDetectionService.js.map +1 -1
  136. package/dist/src/services/loopDetectionService.test.js +191 -0
  137. package/dist/src/services/loopDetectionService.test.js.map +1 -1
  138. package/dist/src/services/shellExecutionService.js +29 -9
  139. package/dist/src/services/shellExecutionService.js.map +1 -1
  140. package/dist/src/services/shellExecutionService.test.js +21 -3
  141. package/dist/src/services/shellExecutionService.test.js.map +1 -1
  142. package/dist/src/telemetry/clearcut-logger/clearcut-logger.d.ts +78 -9
  143. package/dist/src/telemetry/clearcut-logger/clearcut-logger.js +316 -205
  144. package/dist/src/telemetry/clearcut-logger/clearcut-logger.js.map +1 -1
  145. package/dist/src/telemetry/clearcut-logger/clearcut-logger.test.d.ts +17 -0
  146. package/dist/src/telemetry/clearcut-logger/clearcut-logger.test.js +342 -0
  147. package/dist/src/telemetry/clearcut-logger/clearcut-logger.test.js.map +1 -0
  148. package/dist/src/telemetry/clearcut-logger/event-metadata-key.d.ts +14 -2
  149. package/dist/src/telemetry/clearcut-logger/event-metadata-key.js +32 -9
  150. package/dist/src/telemetry/clearcut-logger/event-metadata-key.js.map +1 -1
  151. package/dist/src/telemetry/constants.d.ts +2 -0
  152. package/dist/src/telemetry/constants.js +2 -0
  153. package/dist/src/telemetry/constants.js.map +1 -1
  154. package/dist/src/telemetry/index.d.ts +2 -2
  155. package/dist/src/telemetry/index.js +2 -2
  156. package/dist/src/telemetry/index.js.map +1 -1
  157. package/dist/src/telemetry/integration.test.circular.js +1 -0
  158. package/dist/src/telemetry/integration.test.circular.js.map +1 -1
  159. package/dist/src/telemetry/loggers.d.ts +4 -1
  160. package/dist/src/telemetry/loggers.js +55 -6
  161. package/dist/src/telemetry/loggers.js.map +1 -1
  162. package/dist/src/telemetry/loggers.test.circular.js +7 -2
  163. package/dist/src/telemetry/loggers.test.circular.js.map +1 -1
  164. package/dist/src/telemetry/loggers.test.js +43 -8
  165. package/dist/src/telemetry/loggers.test.js.map +1 -1
  166. package/dist/src/telemetry/metrics.d.ts +7 -2
  167. package/dist/src/telemetry/metrics.js +26 -6
  168. package/dist/src/telemetry/metrics.js.map +1 -1
  169. package/dist/src/telemetry/metrics.test.js +81 -1
  170. package/dist/src/telemetry/metrics.test.js.map +1 -1
  171. package/dist/src/telemetry/sdk.d.ts +1 -1
  172. package/dist/src/telemetry/sdk.js +77 -41
  173. package/dist/src/telemetry/sdk.js.map +1 -1
  174. package/dist/src/telemetry/sdk.test.d.ts +6 -0
  175. package/dist/src/telemetry/sdk.test.js +82 -0
  176. package/dist/src/telemetry/sdk.test.js.map +1 -0
  177. package/dist/src/telemetry/telemetry.test.js +2 -4
  178. package/dist/src/telemetry/telemetry.test.js.map +1 -1
  179. package/dist/src/telemetry/tool-call-decision.d.ts +13 -0
  180. package/dist/src/telemetry/tool-call-decision.js +29 -0
  181. package/dist/src/telemetry/tool-call-decision.js.map +1 -0
  182. package/dist/src/telemetry/types.d.ts +55 -21
  183. package/dist/src/telemetry/types.js +83 -43
  184. package/dist/src/telemetry/types.js.map +1 -1
  185. package/dist/src/telemetry/uiTelemetry.d.ts +8 -1
  186. package/dist/src/telemetry/uiTelemetry.js +17 -2
  187. package/dist/src/telemetry/uiTelemetry.js.map +1 -1
  188. package/dist/src/telemetry/uiTelemetry.test.js +57 -10
  189. package/dist/src/telemetry/uiTelemetry.test.js.map +1 -1
  190. package/dist/src/test-utils/config.d.ts +16 -0
  191. package/dist/src/test-utils/config.js +32 -0
  192. package/dist/src/test-utils/config.js.map +1 -0
  193. package/dist/src/test-utils/tools.d.ts +44 -0
  194. package/dist/src/test-utils/tools.js +105 -0
  195. package/dist/src/test-utils/tools.js.map +1 -0
  196. package/dist/src/tools/diffOptions.d.ts +2 -0
  197. package/dist/src/tools/diffOptions.js +28 -0
  198. package/dist/src/tools/diffOptions.js.map +1 -1
  199. package/dist/src/tools/diffOptions.test.d.ts +6 -0
  200. package/dist/src/tools/diffOptions.test.js +119 -0
  201. package/dist/src/tools/diffOptions.test.js.map +1 -0
  202. package/dist/src/tools/edit.d.ts +10 -34
  203. package/dist/src/tools/edit.js +152 -136
  204. package/dist/src/tools/edit.js.map +1 -1
  205. package/dist/src/tools/edit.test.js +125 -51
  206. package/dist/src/tools/edit.test.js.map +1 -1
  207. package/dist/src/tools/glob.d.ts +4 -11
  208. package/dist/src/tools/glob.js +87 -91
  209. package/dist/src/tools/glob.js.map +1 -1
  210. package/dist/src/tools/glob.test.js +42 -12
  211. package/dist/src/tools/glob.test.js.map +1 -1
  212. package/dist/src/tools/grep.d.ts +4 -36
  213. package/dist/src/tools/grep.js +107 -84
  214. package/dist/src/tools/grep.js.map +1 -1
  215. package/dist/src/tools/grep.test.js +40 -23
  216. package/dist/src/tools/grep.test.js.map +1 -1
  217. package/dist/src/tools/ls.d.ts +4 -23
  218. package/dist/src/tools/ls.js +77 -79
  219. package/dist/src/tools/ls.js.map +1 -1
  220. package/dist/src/tools/ls.test.js +62 -34
  221. package/dist/src/tools/ls.test.js.map +1 -1
  222. package/dist/src/tools/mcp-client-manager.d.ts +38 -0
  223. package/dist/src/tools/mcp-client-manager.js +74 -0
  224. package/dist/src/tools/mcp-client-manager.js.map +1 -0
  225. package/dist/src/tools/mcp-client-manager.test.d.ts +6 -0
  226. package/dist/src/tools/mcp-client-manager.test.js +39 -0
  227. package/dist/src/tools/mcp-client-manager.test.js.map +1 -0
  228. package/dist/src/tools/mcp-client.d.ts +57 -3
  229. package/dist/src/tools/mcp-client.js +235 -10
  230. package/dist/src/tools/mcp-client.js.map +1 -1
  231. package/dist/src/tools/mcp-client.test.js +234 -87
  232. package/dist/src/tools/mcp-client.test.js.map +1 -1
  233. package/dist/src/tools/mcp-tool.d.ts +6 -13
  234. package/dist/src/tools/mcp-tool.js +62 -34
  235. package/dist/src/tools/mcp-tool.js.map +1 -1
  236. package/dist/src/tools/mcp-tool.test.js +118 -59
  237. package/dist/src/tools/mcp-tool.test.js.map +1 -1
  238. package/dist/src/tools/memoryTool.d.ts +10 -14
  239. package/dist/src/tools/memoryTool.js +123 -121
  240. package/dist/src/tools/memoryTool.js.map +1 -1
  241. package/dist/src/tools/memoryTool.test.js +38 -18
  242. package/dist/src/tools/memoryTool.test.js.map +1 -1
  243. package/dist/src/tools/modifiable-tool.d.ts +9 -6
  244. package/dist/src/tools/modifiable-tool.js +6 -3
  245. package/dist/src/tools/modifiable-tool.js.map +1 -1
  246. package/dist/src/tools/modifiable-tool.test.js +12 -12
  247. package/dist/src/tools/modifiable-tool.test.js.map +1 -1
  248. package/dist/src/tools/read-file.d.ts +4 -6
  249. package/dist/src/tools/read-file.js +95 -50
  250. package/dist/src/tools/read-file.js.map +1 -1
  251. package/dist/src/tools/read-file.test.js +199 -129
  252. package/dist/src/tools/read-file.test.js.map +1 -1
  253. package/dist/src/tools/read-many-files.d.ts +3 -5
  254. package/dist/src/tools/read-many-files.js +204 -138
  255. package/dist/src/tools/read-many-files.js.map +1 -1
  256. package/dist/src/tools/read-many-files.test.js +197 -33
  257. package/dist/src/tools/read-many-files.test.js.map +1 -1
  258. package/dist/src/tools/shell.d.ts +4 -6
  259. package/dist/src/tools/shell.js +112 -102
  260. package/dist/src/tools/shell.js.map +1 -1
  261. package/dist/src/tools/shell.test.js +55 -28
  262. package/dist/src/tools/shell.test.js.map +1 -1
  263. package/dist/src/tools/tool-error.d.ts +5 -0
  264. package/dist/src/tools/tool-error.js +5 -0
  265. package/dist/src/tools/tool-error.js.map +1 -1
  266. package/dist/src/tools/tool-registry.d.ts +25 -22
  267. package/dist/src/tools/tool-registry.js +100 -116
  268. package/dist/src/tools/tool-registry.js.map +1 -1
  269. package/dist/src/tools/tool-registry.test.js +29 -212
  270. package/dist/src/tools/tool-registry.test.js.map +1 -1
  271. package/dist/src/tools/tools.d.ts +137 -92
  272. package/dist/src/tools/tools.js +188 -61
  273. package/dist/src/tools/tools.js.map +1 -1
  274. package/dist/src/tools/tools.test.d.ts +6 -0
  275. package/dist/src/tools/tools.test.js +206 -0
  276. package/dist/src/tools/tools.test.js.map +1 -0
  277. package/dist/src/tools/web-fetch.d.ts +4 -7
  278. package/dist/src/tools/web-fetch.js +58 -64
  279. package/dist/src/tools/web-fetch.js.map +1 -1
  280. package/dist/src/tools/web-fetch.test.js +8 -4
  281. package/dist/src/tools/web-fetch.test.js.map +1 -1
  282. package/dist/src/tools/web-search.d.ts +4 -5
  283. package/dist/src/tools/web-search.js +47 -51
  284. package/dist/src/tools/web-search.js.map +1 -1
  285. package/dist/src/tools/web-search.test.d.ts +6 -0
  286. package/dist/src/tools/web-search.test.js +139 -0
  287. package/dist/src/tools/web-search.test.js.map +1 -0
  288. package/dist/src/tools/write-file.d.ts +20 -11
  289. package/dist/src/tools/write-file.js +197 -142
  290. package/dist/src/tools/write-file.js.map +1 -1
  291. package/dist/src/tools/write-file.test.js +156 -96
  292. package/dist/src/tools/write-file.test.js.map +1 -1
  293. package/dist/src/utils/bfsFileSearch.test.js +28 -56
  294. package/dist/src/utils/bfsFileSearch.test.js.map +1 -1
  295. package/dist/src/utils/browser.js +4 -3
  296. package/dist/src/utils/browser.js.map +1 -1
  297. package/dist/src/utils/editCorrector.js +21 -22
  298. package/dist/src/utils/editCorrector.js.map +1 -1
  299. package/dist/src/utils/editor.d.ts +1 -1
  300. package/dist/src/utils/editor.js +14 -6
  301. package/dist/src/utils/editor.js.map +1 -1
  302. package/dist/src/utils/editor.test.js +48 -16
  303. package/dist/src/utils/editor.test.js.map +1 -1
  304. package/dist/src/utils/environmentContext.d.ts +21 -0
  305. package/dist/src/utils/environmentContext.js +90 -0
  306. package/dist/src/utils/environmentContext.js.map +1 -0
  307. package/dist/src/utils/environmentContext.test.d.ts +6 -0
  308. package/dist/src/utils/environmentContext.test.js +140 -0
  309. package/dist/src/utils/environmentContext.test.js.map +1 -0
  310. package/dist/src/utils/errorParsing.d.ts +8 -0
  311. package/dist/src/utils/errorParsing.js +93 -0
  312. package/dist/src/utils/errorParsing.js.map +1 -0
  313. package/dist/src/utils/errorParsing.test.d.ts +6 -0
  314. package/dist/src/utils/errorParsing.test.js +172 -0
  315. package/dist/src/utils/errorParsing.test.js.map +1 -0
  316. package/dist/src/utils/fileUtils.d.ts +9 -1
  317. package/dist/src/utils/fileUtils.js +18 -15
  318. package/dist/src/utils/fileUtils.js.map +1 -1
  319. package/dist/src/utils/fileUtils.test.js +24 -22
  320. package/dist/src/utils/fileUtils.test.js.map +1 -1
  321. package/dist/src/utils/filesearch/crawlCache.d.ts +25 -0
  322. package/dist/src/utils/filesearch/crawlCache.js +57 -0
  323. package/dist/src/utils/filesearch/crawlCache.js.map +1 -0
  324. package/dist/src/utils/filesearch/crawlCache.test.d.ts +6 -0
  325. package/dist/src/utils/filesearch/crawlCache.test.js +103 -0
  326. package/dist/src/utils/filesearch/crawlCache.test.js.map +1 -0
  327. package/dist/src/utils/filesearch/crawler.d.ts +15 -0
  328. package/dist/src/utils/filesearch/crawler.js +50 -0
  329. package/dist/src/utils/filesearch/crawler.js.map +1 -0
  330. package/dist/src/utils/filesearch/crawler.test.d.ts +6 -0
  331. package/dist/src/utils/filesearch/crawler.test.js +468 -0
  332. package/dist/src/utils/filesearch/crawler.test.js.map +1 -0
  333. package/dist/src/utils/filesearch/fileSearch.d.ts +37 -0
  334. package/dist/src/utils/filesearch/fileSearch.js +186 -0
  335. package/dist/src/utils/filesearch/fileSearch.js.map +1 -0
  336. package/dist/src/utils/filesearch/fileSearch.test.d.ts +6 -0
  337. package/dist/src/utils/filesearch/fileSearch.test.js +552 -0
  338. package/dist/src/utils/filesearch/fileSearch.test.js.map +1 -0
  339. package/dist/src/utils/filesearch/ignore.d.ts +42 -0
  340. package/dist/src/utils/filesearch/ignore.js +106 -0
  341. package/dist/src/utils/filesearch/ignore.js.map +1 -0
  342. package/dist/src/utils/filesearch/ignore.test.d.ts +6 -0
  343. package/dist/src/utils/filesearch/ignore.test.js +144 -0
  344. package/dist/src/utils/filesearch/ignore.test.js.map +1 -0
  345. package/dist/src/utils/filesearch/result-cache.d.ts +33 -0
  346. package/dist/src/utils/filesearch/result-cache.js +59 -0
  347. package/dist/src/utils/filesearch/result-cache.js.map +1 -0
  348. package/dist/src/utils/filesearch/result-cache.test.d.ts +6 -0
  349. package/dist/src/utils/filesearch/result-cache.test.js +46 -0
  350. package/dist/src/utils/filesearch/result-cache.test.js.map +1 -0
  351. package/dist/src/utils/flashFallback.integration.test.js +0 -2
  352. package/dist/src/utils/flashFallback.integration.test.js.map +1 -1
  353. package/dist/src/utils/memoryDiscovery.js +7 -4
  354. package/dist/src/utils/memoryDiscovery.js.map +1 -1
  355. package/dist/src/utils/memoryDiscovery.test.js +3 -2
  356. package/dist/src/utils/memoryDiscovery.test.js.map +1 -1
  357. package/dist/src/utils/memoryImportProcessor.js +3 -7
  358. package/dist/src/utils/memoryImportProcessor.js.map +1 -1
  359. package/dist/src/utils/memoryImportProcessor.test.js +17 -20
  360. package/dist/src/utils/memoryImportProcessor.test.js.map +1 -1
  361. package/dist/src/utils/nextSpeakerChecker.js +5 -6
  362. package/dist/src/utils/nextSpeakerChecker.js.map +1 -1
  363. package/dist/src/utils/nextSpeakerChecker.test.js +2 -2
  364. package/dist/src/utils/nextSpeakerChecker.test.js.map +1 -1
  365. package/dist/src/utils/paths.d.ts +7 -0
  366. package/dist/src/utils/paths.js +15 -0
  367. package/dist/src/utils/paths.js.map +1 -1
  368. package/dist/src/utils/paths.test.js +74 -2
  369. package/dist/src/utils/paths.test.js.map +1 -1
  370. package/dist/src/utils/quotaErrorDetection.d.ts +1 -5
  371. package/dist/src/utils/quotaErrorDetection.js.map +1 -1
  372. package/dist/src/utils/schemaValidator.d.ts +1 -8
  373. package/dist/src/utils/schemaValidator.js +1 -32
  374. package/dist/src/utils/schemaValidator.js.map +1 -1
  375. package/dist/src/utils/secure-browser-launcher.js +4 -3
  376. package/dist/src/utils/secure-browser-launcher.js.map +1 -1
  377. package/dist/src/utils/shell-utils.d.ts +39 -0
  378. package/dist/src/utils/shell-utils.js +72 -2
  379. package/dist/src/utils/shell-utils.js.map +1 -1
  380. package/dist/src/utils/shell-utils.test.js +132 -4
  381. package/dist/src/utils/shell-utils.test.js.map +1 -1
  382. package/dist/src/utils/systemEncoding.js +1 -1
  383. package/dist/src/utils/systemEncoding.js.map +1 -1
  384. package/dist/src/utils/systemEncoding.test.js +23 -23
  385. package/dist/src/utils/systemEncoding.test.js.map +1 -1
  386. package/dist/src/utils/user_account.js +58 -48
  387. package/dist/src/utils/user_account.js.map +1 -1
  388. package/dist/src/utils/user_account.test.js +76 -9
  389. package/dist/src/utils/user_account.test.js.map +1 -1
  390. package/dist/src/utils/workspaceContext.d.ts +22 -7
  391. package/dist/src/utils/workspaceContext.js +81 -55
  392. package/dist/src/utils/workspaceContext.js.map +1 -1
  393. package/dist/src/utils/workspaceContext.test.js +221 -137
  394. package/dist/src/utils/workspaceContext.test.js.map +1 -1
  395. package/dist/tsconfig.tsbuildinfo +1 -1
  396. package/package.json +11 -2
  397. package/dist/src/core/modelCheck.d.ts +0 -14
  398. package/dist/src/core/modelCheck.js +0 -62
  399. package/dist/src/core/modelCheck.js.map +0 -1
@@ -15,6 +15,7 @@ import { FileDiscoveryService } from '../services/fileDiscoveryService.js';
15
15
  import { setSimulate429 } from '../utils/testUtils.js';
16
16
  import { tokenLimit } from './tokenLimits.js';
17
17
  import { ideContext } from '../ide/ideContext.js';
18
+ import { ClearcutLogger } from '../telemetry/clearcut-logger/clearcut-logger.js';
18
19
  // --- Mocks ---
19
20
  const mockChatCreateFn = vi.fn();
20
21
  const mockGenerateContentFn = vi.fn();
@@ -170,11 +171,13 @@ describe('Gemini Client (client.ts)', () => {
170
171
  getUsageStatisticsEnabled: vi.fn().mockReturnValue(true),
171
172
  getIdeModeFeature: vi.fn().mockReturnValue(false),
172
173
  getIdeMode: vi.fn().mockReturnValue(true),
174
+ getDebugMode: vi.fn().mockReturnValue(false),
173
175
  getWorkspaceContext: vi.fn().mockReturnValue({
174
176
  getDirectories: vi.fn().mockReturnValue(['/test/dir']),
175
177
  }),
176
178
  getGeminiClient: vi.fn(),
177
179
  setFallbackMode: vi.fn(),
180
+ getChatCompression: vi.fn().mockReturnValue(undefined),
178
181
  };
179
182
  const MockedConfig = vi.mocked(Config, true);
180
183
  MockedConfig.mockImplementation(() => mockConfigObject);
@@ -314,7 +317,7 @@ describe('Gemini Client (client.ts)', () => {
314
317
  systemInstruction: getCoreSystemPrompt(''),
315
318
  temperature: 0,
316
319
  topP: 1,
317
- responseSchema: schema,
320
+ responseJsonSchema: schema,
318
321
  responseMimeType: 'application/json',
319
322
  },
320
323
  contents,
@@ -340,7 +343,7 @@ describe('Gemini Client (client.ts)', () => {
340
343
  temperature: 0.9,
341
344
  topP: 1, // from default
342
345
  topK: 20,
343
- responseSchema: schema,
346
+ responseJsonSchema: schema,
344
347
  responseMimeType: 'application/json',
345
348
  },
346
349
  contents,
@@ -352,7 +355,6 @@ describe('Gemini Client (client.ts)', () => {
352
355
  const mockChat = {
353
356
  addHistory: vi.fn(),
354
357
  };
355
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
356
358
  client['chat'] = mockChat;
357
359
  const newContent = {
358
360
  role: 'user',
@@ -418,13 +420,44 @@ describe('Gemini Client (client.ts)', () => {
418
420
  expect(result).toBeNull();
419
421
  expect(newChat).toBe(initialChat);
420
422
  });
421
- it('should trigger summarization if token count is at threshold', async () => {
423
+ it('logs a telemetry event when compressing', async () => {
424
+ vi.spyOn(ClearcutLogger.prototype, 'logChatCompressionEvent');
422
425
  const MOCKED_TOKEN_LIMIT = 1000;
426
+ const MOCKED_CONTEXT_PERCENTAGE_THRESHOLD = 0.5;
423
427
  vi.mocked(tokenLimit).mockReturnValue(MOCKED_TOKEN_LIMIT);
428
+ vi.spyOn(client['config'], 'getChatCompression').mockReturnValue({
429
+ contextPercentageThreshold: MOCKED_CONTEXT_PERCENTAGE_THRESHOLD,
430
+ });
424
431
  mockGetHistory.mockReturnValue([
425
432
  { role: 'user', parts: [{ text: '...history...' }] },
426
433
  ]);
427
- const originalTokenCount = 1000 * 0.7;
434
+ const originalTokenCount = MOCKED_TOKEN_LIMIT * MOCKED_CONTEXT_PERCENTAGE_THRESHOLD;
435
+ const newTokenCount = 100;
436
+ mockCountTokens
437
+ .mockResolvedValueOnce({ totalTokens: originalTokenCount }) // First call for the check
438
+ .mockResolvedValueOnce({ totalTokens: newTokenCount }); // Second call for the new history
439
+ // Mock the summary response from the chat
440
+ mockSendMessage.mockResolvedValue({
441
+ role: 'model',
442
+ parts: [{ text: 'This is a summary.' }],
443
+ });
444
+ await client.tryCompressChat('prompt-id-3');
445
+ expect(ClearcutLogger.prototype.logChatCompressionEvent).toHaveBeenCalledWith(expect.objectContaining({
446
+ tokens_before: originalTokenCount,
447
+ tokens_after: newTokenCount,
448
+ }));
449
+ });
450
+ it('should trigger summarization if token count is at threshold with contextPercentageThreshold setting', async () => {
451
+ const MOCKED_TOKEN_LIMIT = 1000;
452
+ const MOCKED_CONTEXT_PERCENTAGE_THRESHOLD = 0.5;
453
+ vi.mocked(tokenLimit).mockReturnValue(MOCKED_TOKEN_LIMIT);
454
+ vi.spyOn(client['config'], 'getChatCompression').mockReturnValue({
455
+ contextPercentageThreshold: MOCKED_CONTEXT_PERCENTAGE_THRESHOLD,
456
+ });
457
+ mockGetHistory.mockReturnValue([
458
+ { role: 'user', parts: [{ text: '...history...' }] },
459
+ ]);
460
+ const originalTokenCount = MOCKED_TOKEN_LIMIT * MOCKED_CONTEXT_PERCENTAGE_THRESHOLD;
428
461
  const newTokenCount = 100;
429
462
  mockCountTokens
430
463
  .mockResolvedValueOnce({ totalTokens: originalTokenCount }) // First call for the check
@@ -524,7 +557,7 @@ describe('Gemini Client (client.ts)', () => {
524
557
  });
525
558
  });
526
559
  describe('sendMessageStream', () => {
527
- it('should include IDE context when ideModeFeature is enabled', async () => {
560
+ it('should include editor context when ideMode is enabled', async () => {
528
561
  // Arrange
529
562
  vi.mocked(ideContext.getIdeContext).mockReturnValue({
530
563
  workspaceState: {
@@ -547,7 +580,7 @@ describe('Gemini Client (client.ts)', () => {
547
580
  ],
548
581
  },
549
582
  });
550
- vi.spyOn(client['config'], 'getIdeModeFeature').mockReturnValue(true);
583
+ vi.spyOn(client['config'], 'getIdeMode').mockReturnValue(true);
551
584
  const mockStream = (async function* () {
552
585
  yield { type: 'content', value: 'Hello' };
553
586
  })();
@@ -571,27 +604,35 @@ describe('Gemini Client (client.ts)', () => {
571
604
  // Assert
572
605
  expect(ideContext.getIdeContext).toHaveBeenCalled();
573
606
  const expectedContext = `
574
- This is the file that the user is looking at:
575
- - Path: /path/to/active/file.ts
576
- This is the cursor position in the file:
577
- - Cursor Position: Line 5, Character 10
578
- This is the selected text in the file:
579
- - hello
580
- Here are some other files the user has open, with the most recent at the top:
581
- - /path/to/recent/file1.ts
582
- - /path/to/recent/file2.ts
607
+ Here is the user's editor context as a JSON object. This is for your information only.
608
+ \`\`\`json
609
+ ${JSON.stringify({
610
+ activeFile: {
611
+ path: '/path/to/active/file.ts',
612
+ cursor: {
613
+ line: 5,
614
+ character: 10,
615
+ },
616
+ selectedText: 'hello',
617
+ },
618
+ otherOpenFiles: ['/path/to/recent/file1.ts', '/path/to/recent/file2.ts'],
619
+ }, null, 2)}
620
+ \`\`\`
583
621
  `.trim();
584
- const expectedRequest = [{ text: expectedContext }, ...initialRequest];
585
- expect(mockTurnRunFn).toHaveBeenCalledWith(expectedRequest, expect.any(Object));
622
+ const expectedRequest = [{ text: expectedContext }];
623
+ expect(mockChat.addHistory).toHaveBeenCalledWith({
624
+ role: 'user',
625
+ parts: expectedRequest,
626
+ });
586
627
  });
587
- it('should not add context if ideModeFeature is enabled but no open files', async () => {
628
+ it('should not add context if ideMode is enabled but no open files', async () => {
588
629
  // Arrange
589
630
  vi.mocked(ideContext.getIdeContext).mockReturnValue({
590
631
  workspaceState: {
591
632
  openFiles: [],
592
633
  },
593
634
  });
594
- vi.spyOn(client['config'], 'getIdeModeFeature').mockReturnValue(true);
635
+ vi.spyOn(client['config'], 'getIdeMode').mockReturnValue(true);
595
636
  const mockStream = (async function* () {
596
637
  yield { type: 'content', value: 'Hello' };
597
638
  })();
@@ -616,7 +657,7 @@ Here are some other files the user has open, with the most recent at the top:
616
657
  expect(ideContext.getIdeContext).toHaveBeenCalled();
617
658
  expect(mockTurnRunFn).toHaveBeenCalledWith(initialRequest, expect.any(Object));
618
659
  });
619
- it('should add context if ideModeFeature is enabled and there is one active file', async () => {
660
+ it('should add context if ideMode is enabled and there is one active file', async () => {
620
661
  // Arrange
621
662
  vi.mocked(ideContext.getIdeContext).mockReturnValue({
622
663
  workspaceState: {
@@ -631,7 +672,7 @@ Here are some other files the user has open, with the most recent at the top:
631
672
  ],
632
673
  },
633
674
  });
634
- vi.spyOn(client['config'], 'getIdeModeFeature').mockReturnValue(true);
675
+ vi.spyOn(client['config'], 'getIdeMode').mockReturnValue(true);
635
676
  const mockStream = (async function* () {
636
677
  yield { type: 'content', value: 'Hello' };
637
678
  })();
@@ -655,17 +696,27 @@ Here are some other files the user has open, with the most recent at the top:
655
696
  // Assert
656
697
  expect(ideContext.getIdeContext).toHaveBeenCalled();
657
698
  const expectedContext = `
658
- This is the file that the user is looking at:
659
- - Path: /path/to/active/file.ts
660
- This is the cursor position in the file:
661
- - Cursor Position: Line 5, Character 10
662
- This is the selected text in the file:
663
- - hello
699
+ Here is the user's editor context as a JSON object. This is for your information only.
700
+ \`\`\`json
701
+ ${JSON.stringify({
702
+ activeFile: {
703
+ path: '/path/to/active/file.ts',
704
+ cursor: {
705
+ line: 5,
706
+ character: 10,
707
+ },
708
+ selectedText: 'hello',
709
+ },
710
+ }, null, 2)}
711
+ \`\`\`
664
712
  `.trim();
665
- const expectedRequest = [{ text: expectedContext }, ...initialRequest];
666
- expect(mockTurnRunFn).toHaveBeenCalledWith(expectedRequest, expect.any(Object));
713
+ const expectedRequest = [{ text: expectedContext }];
714
+ expect(mockChat.addHistory).toHaveBeenCalledWith({
715
+ role: 'user',
716
+ parts: expectedRequest,
717
+ });
667
718
  });
668
- it('should add context if ideModeFeature is enabled and there are open files but no active file', async () => {
719
+ it('should add context if ideMode is enabled and there are open files but no active file', async () => {
669
720
  // Arrange
670
721
  vi.mocked(ideContext.getIdeContext).mockReturnValue({
671
722
  workspaceState: {
@@ -681,7 +732,7 @@ This is the selected text in the file:
681
732
  ],
682
733
  },
683
734
  });
684
- vi.spyOn(client['config'], 'getIdeModeFeature').mockReturnValue(true);
735
+ vi.spyOn(client['config'], 'getIdeMode').mockReturnValue(true);
685
736
  const mockStream = (async function* () {
686
737
  yield { type: 'content', value: 'Hello' };
687
738
  })();
@@ -705,12 +756,18 @@ This is the selected text in the file:
705
756
  // Assert
706
757
  expect(ideContext.getIdeContext).toHaveBeenCalled();
707
758
  const expectedContext = `
708
- Here are some files the user has open, with the most recent at the top:
709
- - /path/to/recent/file1.ts
710
- - /path/to/recent/file2.ts
759
+ Here is the user's editor context as a JSON object. This is for your information only.
760
+ \`\`\`json
761
+ ${JSON.stringify({
762
+ otherOpenFiles: ['/path/to/recent/file1.ts', '/path/to/recent/file2.ts'],
763
+ }, null, 2)}
764
+ \`\`\`
711
765
  `.trim();
712
- const expectedRequest = [{ text: expectedContext }, ...initialRequest];
713
- expect(mockTurnRunFn).toHaveBeenCalledWith(expectedRequest, expect.any(Object));
766
+ const expectedRequest = [{ text: expectedContext }];
767
+ expect(mockChat.addHistory).toHaveBeenCalledWith({
768
+ role: 'user',
769
+ parts: expectedRequest,
770
+ });
714
771
  });
715
772
  it('should return the turn instance after the stream is complete', async () => {
716
773
  // Arrange
@@ -913,6 +970,488 @@ Here are some files the user has open, with the most recent at the top:
913
970
  console.log(`Infinite loop protection working: checkNextSpeaker called ${callCount} times, ` +
914
971
  `${eventCount} events generated (properly bounded by MAX_TURNS)`);
915
972
  });
973
+ describe('Editor context delta', () => {
974
+ const mockStream = (async function* () {
975
+ yield { type: 'content', value: 'Hello' };
976
+ })();
977
+ beforeEach(() => {
978
+ client['forceFullIdeContext'] = false; // Reset before each delta test
979
+ vi.spyOn(client, 'tryCompressChat').mockResolvedValue(null);
980
+ vi.spyOn(client['config'], 'getIdeMode').mockReturnValue(true);
981
+ mockTurnRunFn.mockReturnValue(mockStream);
982
+ const mockChat = {
983
+ addHistory: vi.fn(),
984
+ setHistory: vi.fn(),
985
+ sendMessage: vi.fn().mockResolvedValue({ text: 'summary' }),
986
+ // Assume history is not empty for delta checks
987
+ getHistory: vi
988
+ .fn()
989
+ .mockReturnValue([
990
+ { role: 'user', parts: [{ text: 'previous message' }] },
991
+ ]),
992
+ };
993
+ client['chat'] = mockChat;
994
+ const mockGenerator = {
995
+ countTokens: vi.fn().mockResolvedValue({ totalTokens: 0 }),
996
+ generateContent: mockGenerateContentFn,
997
+ };
998
+ client['contentGenerator'] = mockGenerator;
999
+ });
1000
+ const testCases = [
1001
+ {
1002
+ description: 'sends delta when active file changes',
1003
+ previousActiveFile: {
1004
+ path: '/path/to/old/file.ts',
1005
+ cursor: { line: 5, character: 10 },
1006
+ selectedText: 'hello',
1007
+ },
1008
+ currentActiveFile: {
1009
+ path: '/path/to/active/file.ts',
1010
+ cursor: { line: 5, character: 10 },
1011
+ selectedText: 'hello',
1012
+ },
1013
+ shouldSendContext: true,
1014
+ },
1015
+ {
1016
+ description: 'sends delta when cursor line changes',
1017
+ previousActiveFile: {
1018
+ path: '/path/to/active/file.ts',
1019
+ cursor: { line: 1, character: 10 },
1020
+ selectedText: 'hello',
1021
+ },
1022
+ currentActiveFile: {
1023
+ path: '/path/to/active/file.ts',
1024
+ cursor: { line: 5, character: 10 },
1025
+ selectedText: 'hello',
1026
+ },
1027
+ shouldSendContext: true,
1028
+ },
1029
+ {
1030
+ description: 'sends delta when cursor character changes',
1031
+ previousActiveFile: {
1032
+ path: '/path/to/active/file.ts',
1033
+ cursor: { line: 5, character: 1 },
1034
+ selectedText: 'hello',
1035
+ },
1036
+ currentActiveFile: {
1037
+ path: '/path/to/active/file.ts',
1038
+ cursor: { line: 5, character: 10 },
1039
+ selectedText: 'hello',
1040
+ },
1041
+ shouldSendContext: true,
1042
+ },
1043
+ {
1044
+ description: 'sends delta when selected text changes',
1045
+ previousActiveFile: {
1046
+ path: '/path/to/active/file.ts',
1047
+ cursor: { line: 5, character: 10 },
1048
+ selectedText: 'world',
1049
+ },
1050
+ currentActiveFile: {
1051
+ path: '/path/to/active/file.ts',
1052
+ cursor: { line: 5, character: 10 },
1053
+ selectedText: 'hello',
1054
+ },
1055
+ shouldSendContext: true,
1056
+ },
1057
+ {
1058
+ description: 'sends delta when selected text is added',
1059
+ previousActiveFile: {
1060
+ path: '/path/to/active/file.ts',
1061
+ cursor: { line: 5, character: 10 },
1062
+ },
1063
+ currentActiveFile: {
1064
+ path: '/path/to/active/file.ts',
1065
+ cursor: { line: 5, character: 10 },
1066
+ selectedText: 'hello',
1067
+ },
1068
+ shouldSendContext: true,
1069
+ },
1070
+ {
1071
+ description: 'sends delta when selected text is removed',
1072
+ previousActiveFile: {
1073
+ path: '/path/to/active/file.ts',
1074
+ cursor: { line: 5, character: 10 },
1075
+ selectedText: 'hello',
1076
+ },
1077
+ currentActiveFile: {
1078
+ path: '/path/to/active/file.ts',
1079
+ cursor: { line: 5, character: 10 },
1080
+ },
1081
+ shouldSendContext: true,
1082
+ },
1083
+ {
1084
+ description: 'does not send context when nothing changes',
1085
+ previousActiveFile: {
1086
+ path: '/path/to/active/file.ts',
1087
+ cursor: { line: 5, character: 10 },
1088
+ selectedText: 'hello',
1089
+ },
1090
+ currentActiveFile: {
1091
+ path: '/path/to/active/file.ts',
1092
+ cursor: { line: 5, character: 10 },
1093
+ selectedText: 'hello',
1094
+ },
1095
+ shouldSendContext: false,
1096
+ },
1097
+ ];
1098
+ it.each(testCases)('$description', async ({ previousActiveFile, currentActiveFile, shouldSendContext, }) => {
1099
+ // Setup previous context
1100
+ client['lastSentIdeContext'] = {
1101
+ workspaceState: {
1102
+ openFiles: [
1103
+ {
1104
+ path: previousActiveFile.path,
1105
+ cursor: previousActiveFile.cursor,
1106
+ selectedText: previousActiveFile.selectedText,
1107
+ isActive: true,
1108
+ timestamp: Date.now() - 1000,
1109
+ },
1110
+ ],
1111
+ },
1112
+ };
1113
+ // Setup current context
1114
+ vi.mocked(ideContext.getIdeContext).mockReturnValue({
1115
+ workspaceState: {
1116
+ openFiles: [
1117
+ { ...currentActiveFile, isActive: true, timestamp: Date.now() },
1118
+ ],
1119
+ },
1120
+ });
1121
+ const stream = client.sendMessageStream([{ text: 'Hi' }], new AbortController().signal, 'prompt-id-delta');
1122
+ for await (const _ of stream) {
1123
+ // consume stream
1124
+ }
1125
+ const mockChat = client['chat'];
1126
+ if (shouldSendContext) {
1127
+ expect(mockChat.addHistory).toHaveBeenCalledWith(expect.objectContaining({
1128
+ parts: expect.arrayContaining([
1129
+ expect.objectContaining({
1130
+ text: expect.stringContaining("Here is a summary of changes in the user's editor context"),
1131
+ }),
1132
+ ]),
1133
+ }));
1134
+ }
1135
+ else {
1136
+ expect(mockChat.addHistory).not.toHaveBeenCalled();
1137
+ }
1138
+ });
1139
+ it('sends full context when history is cleared, even if editor state is unchanged', async () => {
1140
+ const activeFile = {
1141
+ path: '/path/to/active/file.ts',
1142
+ cursor: { line: 5, character: 10 },
1143
+ selectedText: 'hello',
1144
+ };
1145
+ // Setup previous context
1146
+ client['lastSentIdeContext'] = {
1147
+ workspaceState: {
1148
+ openFiles: [
1149
+ {
1150
+ path: activeFile.path,
1151
+ cursor: activeFile.cursor,
1152
+ selectedText: activeFile.selectedText,
1153
+ isActive: true,
1154
+ timestamp: Date.now() - 1000,
1155
+ },
1156
+ ],
1157
+ },
1158
+ };
1159
+ // Setup current context (same as previous)
1160
+ vi.mocked(ideContext.getIdeContext).mockReturnValue({
1161
+ workspaceState: {
1162
+ openFiles: [
1163
+ { ...activeFile, isActive: true, timestamp: Date.now() },
1164
+ ],
1165
+ },
1166
+ });
1167
+ // Make history empty
1168
+ const mockChat = client['chat'];
1169
+ mockChat.getHistory.mockReturnValue([]);
1170
+ const stream = client.sendMessageStream([{ text: 'Hi' }], new AbortController().signal, 'prompt-id-history-cleared');
1171
+ for await (const _ of stream) {
1172
+ // consume stream
1173
+ }
1174
+ expect(mockChat.addHistory).toHaveBeenCalledWith(expect.objectContaining({
1175
+ parts: expect.arrayContaining([
1176
+ expect.objectContaining({
1177
+ text: expect.stringContaining("Here is the user's editor context"),
1178
+ }),
1179
+ ]),
1180
+ }));
1181
+ // Also verify it's the full context, not a delta.
1182
+ const call = mockChat.addHistory.mock.calls[0][0];
1183
+ const contextText = call.parts[0].text;
1184
+ const contextJson = JSON.parse(contextText.match(/```json\n(.*)\n```/s)[1]);
1185
+ expect(contextJson).toHaveProperty('activeFile');
1186
+ expect(contextJson.activeFile.path).toBe('/path/to/active/file.ts');
1187
+ });
1188
+ });
1189
+ describe('IDE context with pending tool calls', () => {
1190
+ let mockChat;
1191
+ beforeEach(() => {
1192
+ vi.spyOn(client, 'tryCompressChat').mockResolvedValue(null);
1193
+ const mockStream = (async function* () {
1194
+ yield { type: 'content', value: 'response' };
1195
+ })();
1196
+ mockTurnRunFn.mockReturnValue(mockStream);
1197
+ mockChat = {
1198
+ addHistory: vi.fn(),
1199
+ getHistory: vi.fn().mockReturnValue([]), // Default empty history
1200
+ setHistory: vi.fn(),
1201
+ sendMessage: vi.fn().mockResolvedValue({ text: 'summary' }),
1202
+ };
1203
+ client['chat'] = mockChat;
1204
+ const mockGenerator = {
1205
+ countTokens: vi.fn().mockResolvedValue({ totalTokens: 0 }),
1206
+ };
1207
+ client['contentGenerator'] = mockGenerator;
1208
+ vi.spyOn(client['config'], 'getIdeMode').mockReturnValue(true);
1209
+ vi.mocked(ideContext.getIdeContext).mockReturnValue({
1210
+ workspaceState: {
1211
+ openFiles: [{ path: '/path/to/file.ts', timestamp: Date.now() }],
1212
+ },
1213
+ });
1214
+ });
1215
+ it('should NOT add IDE context when a tool call is pending', async () => {
1216
+ // Arrange: History ends with a functionCall from the model
1217
+ const historyWithPendingCall = [
1218
+ { role: 'user', parts: [{ text: 'Please use a tool.' }] },
1219
+ {
1220
+ role: 'model',
1221
+ parts: [{ functionCall: { name: 'some_tool', args: {} } }],
1222
+ },
1223
+ ];
1224
+ vi.mocked(mockChat.getHistory).mockReturnValue(historyWithPendingCall);
1225
+ // Act: Simulate sending the tool's response back
1226
+ const stream = client.sendMessageStream([
1227
+ {
1228
+ functionResponse: {
1229
+ name: 'some_tool',
1230
+ response: { success: true },
1231
+ },
1232
+ },
1233
+ ], new AbortController().signal, 'prompt-id-tool-response');
1234
+ for await (const _ of stream) {
1235
+ // consume stream to complete the call
1236
+ }
1237
+ // Assert: The IDE context message should NOT have been added to the history.
1238
+ expect(mockChat.addHistory).not.toHaveBeenCalledWith(expect.objectContaining({
1239
+ parts: expect.arrayContaining([
1240
+ expect.objectContaining({
1241
+ text: expect.stringContaining("user's editor context"),
1242
+ }),
1243
+ ]),
1244
+ }));
1245
+ });
1246
+ it('should add IDE context when no tool call is pending', async () => {
1247
+ // Arrange: History is normal, no pending calls
1248
+ const normalHistory = [
1249
+ { role: 'user', parts: [{ text: 'A normal message.' }] },
1250
+ { role: 'model', parts: [{ text: 'A normal response.' }] },
1251
+ ];
1252
+ vi.mocked(mockChat.getHistory).mockReturnValue(normalHistory);
1253
+ // Act
1254
+ const stream = client.sendMessageStream([{ text: 'Another normal message' }], new AbortController().signal, 'prompt-id-normal');
1255
+ for await (const _ of stream) {
1256
+ // consume stream
1257
+ }
1258
+ // Assert: The IDE context message SHOULD have been added.
1259
+ expect(mockChat.addHistory).toHaveBeenCalledWith(expect.objectContaining({
1260
+ role: 'user',
1261
+ parts: expect.arrayContaining([
1262
+ expect.objectContaining({
1263
+ text: expect.stringContaining("user's editor context"),
1264
+ }),
1265
+ ]),
1266
+ }));
1267
+ });
1268
+ it('should send the latest IDE context on the next message after a skipped context', async () => {
1269
+ // --- Step 1: A tool call is pending, context should be skipped ---
1270
+ // Arrange: History ends with a functionCall
1271
+ const historyWithPendingCall = [
1272
+ { role: 'user', parts: [{ text: 'Please use a tool.' }] },
1273
+ {
1274
+ role: 'model',
1275
+ parts: [{ functionCall: { name: 'some_tool', args: {} } }],
1276
+ },
1277
+ ];
1278
+ vi.mocked(mockChat.getHistory).mockReturnValue(historyWithPendingCall);
1279
+ // Arrange: Set the initial IDE context
1280
+ const initialIdeContext = {
1281
+ workspaceState: {
1282
+ openFiles: [{ path: '/path/to/fileA.ts', timestamp: Date.now() }],
1283
+ },
1284
+ };
1285
+ vi.mocked(ideContext.getIdeContext).mockReturnValue(initialIdeContext);
1286
+ // Act: Send the tool response
1287
+ let stream = client.sendMessageStream([
1288
+ {
1289
+ functionResponse: {
1290
+ name: 'some_tool',
1291
+ response: { success: true },
1292
+ },
1293
+ },
1294
+ ], new AbortController().signal, 'prompt-id-tool-response');
1295
+ for await (const _ of stream) {
1296
+ /* consume */
1297
+ }
1298
+ // Assert: The initial context was NOT sent
1299
+ expect(mockChat.addHistory).not.toHaveBeenCalledWith(expect.objectContaining({
1300
+ parts: expect.arrayContaining([
1301
+ expect.objectContaining({
1302
+ text: expect.stringContaining("user's editor context"),
1303
+ }),
1304
+ ]),
1305
+ }));
1306
+ // --- Step 2: A new message is sent, latest context should be included ---
1307
+ // Arrange: The model has responded to the tool, and the user is sending a new message.
1308
+ const historyAfterToolResponse = [
1309
+ ...historyWithPendingCall,
1310
+ {
1311
+ role: 'user',
1312
+ parts: [
1313
+ {
1314
+ functionResponse: {
1315
+ name: 'some_tool',
1316
+ response: { success: true },
1317
+ },
1318
+ },
1319
+ ],
1320
+ },
1321
+ { role: 'model', parts: [{ text: 'The tool ran successfully.' }] },
1322
+ ];
1323
+ vi.mocked(mockChat.getHistory).mockReturnValue(historyAfterToolResponse);
1324
+ vi.mocked(mockChat.addHistory).mockClear(); // Clear previous calls for the next assertion
1325
+ // Arrange: The IDE context has now changed
1326
+ const newIdeContext = {
1327
+ workspaceState: {
1328
+ openFiles: [{ path: '/path/to/fileB.ts', timestamp: Date.now() }],
1329
+ },
1330
+ };
1331
+ vi.mocked(ideContext.getIdeContext).mockReturnValue(newIdeContext);
1332
+ // Act: Send a new, regular user message
1333
+ stream = client.sendMessageStream([{ text: 'Thanks!' }], new AbortController().signal, 'prompt-id-final');
1334
+ for await (const _ of stream) {
1335
+ /* consume */
1336
+ }
1337
+ // Assert: The NEW context was sent as a FULL context because there was no previously sent context.
1338
+ const addHistoryCalls = vi.mocked(mockChat.addHistory).mock.calls;
1339
+ const contextCall = addHistoryCalls.find((call) => JSON.stringify(call[0]).includes("user's editor context"));
1340
+ expect(contextCall).toBeDefined();
1341
+ expect(JSON.stringify(contextCall[0])).toContain("Here is the user's editor context as a JSON object");
1342
+ // Check that the sent context is the new one (fileB.ts)
1343
+ expect(JSON.stringify(contextCall[0])).toContain('fileB.ts');
1344
+ // Check that the sent context is NOT the old one (fileA.ts)
1345
+ expect(JSON.stringify(contextCall[0])).not.toContain('fileA.ts');
1346
+ });
1347
+ it('should send a context DELTA on the next message after a skipped context', async () => {
1348
+ // --- Step 0: Establish an initial context ---
1349
+ vi.mocked(mockChat.getHistory).mockReturnValue([]); // Start with empty history
1350
+ const contextA = {
1351
+ workspaceState: {
1352
+ openFiles: [
1353
+ {
1354
+ path: '/path/to/fileA.ts',
1355
+ isActive: true,
1356
+ timestamp: Date.now(),
1357
+ },
1358
+ ],
1359
+ },
1360
+ };
1361
+ vi.mocked(ideContext.getIdeContext).mockReturnValue(contextA);
1362
+ // Act: Send a regular message to establish the initial context
1363
+ let stream = client.sendMessageStream([{ text: 'Initial message' }], new AbortController().signal, 'prompt-id-initial');
1364
+ for await (const _ of stream) {
1365
+ /* consume */
1366
+ }
1367
+ // Assert: Full context for fileA.ts was sent and stored.
1368
+ const initialCall = vi.mocked(mockChat.addHistory).mock.calls[0][0];
1369
+ expect(JSON.stringify(initialCall)).toContain("user's editor context as a JSON object");
1370
+ expect(JSON.stringify(initialCall)).toContain('fileA.ts');
1371
+ // This implicitly tests that `lastSentIdeContext` is now set internally by the client.
1372
+ vi.mocked(mockChat.addHistory).mockClear();
1373
+ // --- Step 1: A tool call is pending, context should be skipped ---
1374
+ const historyWithPendingCall = [
1375
+ { role: 'user', parts: [{ text: 'Please use a tool.' }] },
1376
+ {
1377
+ role: 'model',
1378
+ parts: [{ functionCall: { name: 'some_tool', args: {} } }],
1379
+ },
1380
+ ];
1381
+ vi.mocked(mockChat.getHistory).mockReturnValue(historyWithPendingCall);
1382
+ // Arrange: IDE context changes, but this should be skipped
1383
+ const contextB = {
1384
+ workspaceState: {
1385
+ openFiles: [
1386
+ {
1387
+ path: '/path/to/fileB.ts',
1388
+ isActive: true,
1389
+ timestamp: Date.now(),
1390
+ },
1391
+ ],
1392
+ },
1393
+ };
1394
+ vi.mocked(ideContext.getIdeContext).mockReturnValue(contextB);
1395
+ // Act: Send the tool response
1396
+ stream = client.sendMessageStream([
1397
+ {
1398
+ functionResponse: {
1399
+ name: 'some_tool',
1400
+ response: { success: true },
1401
+ },
1402
+ },
1403
+ ], new AbortController().signal, 'prompt-id-tool-response');
1404
+ for await (const _ of stream) {
1405
+ /* consume */
1406
+ }
1407
+ // Assert: No context was sent
1408
+ expect(mockChat.addHistory).not.toHaveBeenCalled();
1409
+ // --- Step 2: A new message is sent, latest context DELTA should be included ---
1410
+ const historyAfterToolResponse = [
1411
+ ...historyWithPendingCall,
1412
+ {
1413
+ role: 'user',
1414
+ parts: [
1415
+ {
1416
+ functionResponse: {
1417
+ name: 'some_tool',
1418
+ response: { success: true },
1419
+ },
1420
+ },
1421
+ ],
1422
+ },
1423
+ { role: 'model', parts: [{ text: 'The tool ran successfully.' }] },
1424
+ ];
1425
+ vi.mocked(mockChat.getHistory).mockReturnValue(historyAfterToolResponse);
1426
+ // Arrange: The IDE context has changed again
1427
+ const contextC = {
1428
+ workspaceState: {
1429
+ openFiles: [
1430
+ // fileA is now closed, fileC is open
1431
+ {
1432
+ path: '/path/to/fileC.ts',
1433
+ isActive: true,
1434
+ timestamp: Date.now(),
1435
+ },
1436
+ ],
1437
+ },
1438
+ };
1439
+ vi.mocked(ideContext.getIdeContext).mockReturnValue(contextC);
1440
+ // Act: Send a new, regular user message
1441
+ stream = client.sendMessageStream([{ text: 'Thanks!' }], new AbortController().signal, 'prompt-id-final');
1442
+ for await (const _ of stream) {
1443
+ /* consume */
1444
+ }
1445
+ // Assert: The DELTA context was sent
1446
+ const finalCall = vi.mocked(mockChat.addHistory).mock.calls[0][0];
1447
+ expect(JSON.stringify(finalCall)).toContain('summary of changes');
1448
+ // The delta should reflect fileA being closed and fileC being opened.
1449
+ expect(JSON.stringify(finalCall)).toContain('filesClosed');
1450
+ expect(JSON.stringify(finalCall)).toContain('fileA.ts');
1451
+ expect(JSON.stringify(finalCall)).toContain('activeFileChanged');
1452
+ expect(JSON.stringify(finalCall)).toContain('fileC.ts');
1453
+ });
1454
+ });
916
1455
  });
917
1456
  describe('generateContent', () => {
918
1457
  it('should use current model from config for content generation', async () => {
@@ -999,5 +1538,65 @@ Here are some files the user has open, with the most recent at the top:
999
1538
  expect(mockFallbackHandler).toHaveBeenCalledWith(currentModel, fallbackModel, undefined);
1000
1539
  });
1001
1540
  });
1541
+ describe('setHistory', () => {
1542
+ it('should strip thought signatures when stripThoughts is true', () => {
1543
+ const mockChat = {
1544
+ setHistory: vi.fn(),
1545
+ };
1546
+ client['chat'] = mockChat;
1547
+ const historyWithThoughts = [
1548
+ {
1549
+ role: 'user',
1550
+ parts: [{ text: 'hello' }],
1551
+ },
1552
+ {
1553
+ role: 'model',
1554
+ parts: [
1555
+ { text: 'thinking...', thoughtSignature: 'thought-123' },
1556
+ {
1557
+ functionCall: { name: 'test', args: {} },
1558
+ thoughtSignature: 'thought-456',
1559
+ },
1560
+ ],
1561
+ },
1562
+ ];
1563
+ client.setHistory(historyWithThoughts, { stripThoughts: true });
1564
+ const expectedHistory = [
1565
+ {
1566
+ role: 'user',
1567
+ parts: [{ text: 'hello' }],
1568
+ },
1569
+ {
1570
+ role: 'model',
1571
+ parts: [
1572
+ { text: 'thinking...' },
1573
+ { functionCall: { name: 'test', args: {} } },
1574
+ ],
1575
+ },
1576
+ ];
1577
+ expect(mockChat.setHistory).toHaveBeenCalledWith(expectedHistory);
1578
+ });
1579
+ it('should not strip thought signatures when stripThoughts is false', () => {
1580
+ const mockChat = {
1581
+ setHistory: vi.fn(),
1582
+ };
1583
+ client['chat'] = mockChat;
1584
+ const historyWithThoughts = [
1585
+ {
1586
+ role: 'user',
1587
+ parts: [{ text: 'hello' }],
1588
+ },
1589
+ {
1590
+ role: 'model',
1591
+ parts: [
1592
+ { text: 'thinking...', thoughtSignature: 'thought-123' },
1593
+ { text: 'ok', thoughtSignature: 'thought-456' },
1594
+ ],
1595
+ },
1596
+ ];
1597
+ client.setHistory(historyWithThoughts, { stripThoughts: false });
1598
+ expect(mockChat.setHistory).toHaveBeenCalledWith(historyWithThoughts);
1599
+ });
1600
+ });
1002
1601
  });
1003
1602
  //# sourceMappingURL=client.test.js.map