@machina.ai/cell-cli-core 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 (432) hide show
  1. package/dist/index.d.ts +5 -0
  2. package/dist/index.js +4 -0
  3. package/dist/index.js.map +1 -1
  4. package/dist/package.json +9 -3
  5. package/dist/src/agents/executor.d.ts +19 -0
  6. package/dist/src/agents/executor.js +226 -32
  7. package/dist/src/agents/executor.js.map +1 -1
  8. package/dist/src/agents/executor.test.js +335 -9
  9. package/dist/src/agents/executor.test.js.map +1 -1
  10. package/dist/src/agents/subagent-tool-wrapper.test.js +2 -4
  11. package/dist/src/agents/subagent-tool-wrapper.test.js.map +1 -1
  12. package/dist/src/agents/types.d.ts +2 -1
  13. package/dist/src/agents/types.js +1 -0
  14. package/dist/src/agents/types.js.map +1 -1
  15. package/dist/src/code_assist/experiments/client_metadata.d.ts +12 -0
  16. package/dist/src/code_assist/experiments/client_metadata.js +49 -0
  17. package/dist/src/code_assist/experiments/client_metadata.js.map +1 -0
  18. package/dist/src/code_assist/experiments/experiments.d.ts +17 -0
  19. package/dist/src/code_assist/experiments/experiments.js +36 -0
  20. package/dist/src/code_assist/experiments/experiments.js.map +1 -0
  21. package/dist/src/code_assist/experiments/types.d.ts +35 -0
  22. package/dist/src/code_assist/experiments/types.js +7 -0
  23. package/dist/src/code_assist/experiments/types.js.map +1 -0
  24. package/dist/src/code_assist/oauth-credential-storage.js +5 -4
  25. package/dist/src/code_assist/oauth-credential-storage.js.map +1 -1
  26. package/dist/src/code_assist/oauth-credential-storage.test.js +15 -3
  27. package/dist/src/code_assist/oauth-credential-storage.test.js.map +1 -1
  28. package/dist/src/code_assist/oauth2.d.ts +2 -2
  29. package/dist/src/code_assist/oauth2.js +53 -41
  30. package/dist/src/code_assist/oauth2.js.map +1 -1
  31. package/dist/src/code_assist/oauth2.test.js +65 -33
  32. package/dist/src/code_assist/oauth2.test.js.map +1 -1
  33. package/dist/src/code_assist/server.d.ts +6 -4
  34. package/dist/src/code_assist/server.js +11 -0
  35. package/dist/src/code_assist/server.js.map +1 -1
  36. package/dist/src/code_assist/server.test.js +17 -0
  37. package/dist/src/code_assist/server.test.js.map +1 -1
  38. package/dist/src/code_assist/setup.d.ts +2 -2
  39. package/dist/src/code_assist/setup.js.map +1 -1
  40. package/dist/src/code_assist/types.d.ts +1 -1
  41. package/dist/src/code_assist/types.js.map +1 -1
  42. package/dist/src/commands/extensions.d.ts +7 -0
  43. package/dist/src/commands/extensions.js +9 -0
  44. package/dist/src/commands/extensions.js.map +1 -0
  45. package/dist/src/commands/extensions.test.d.ts +6 -0
  46. package/dist/src/commands/extensions.test.js +19 -0
  47. package/dist/src/commands/extensions.test.js.map +1 -0
  48. package/dist/src/config/config.d.ts +72 -24
  49. package/dist/src/config/config.js +168 -55
  50. package/dist/src/config/config.js.map +1 -1
  51. package/dist/src/config/config.test.js +59 -23
  52. package/dist/src/config/config.test.js.map +1 -1
  53. package/dist/src/config/models.d.ts +1 -1
  54. package/dist/src/config/models.js +2 -2
  55. package/dist/src/config/models.js.map +1 -1
  56. package/dist/src/config/storage.d.ts +3 -0
  57. package/dist/src/config/storage.js +20 -0
  58. package/dist/src/config/storage.js.map +1 -1
  59. package/dist/src/core/apiKeyCredentialStorage.d.ts +17 -0
  60. package/dist/src/core/apiKeyCredentialStorage.js +64 -0
  61. package/dist/src/core/apiKeyCredentialStorage.js.map +1 -0
  62. package/dist/src/core/apiKeyCredentialStorage.test.d.ts +6 -0
  63. package/dist/src/core/apiKeyCredentialStorage.test.js +71 -0
  64. package/dist/src/core/apiKeyCredentialStorage.test.js.map +1 -0
  65. package/dist/src/core/client.d.ts +2 -11
  66. package/dist/src/core/client.js +28 -168
  67. package/dist/src/core/client.js.map +1 -1
  68. package/dist/src/core/client.test.js +107 -409
  69. package/dist/src/core/client.test.js.map +1 -1
  70. package/dist/src/core/contentGenerator.js +64 -59
  71. package/dist/src/core/contentGenerator.js.map +1 -1
  72. package/dist/src/core/contentGenerator.test.js +38 -4
  73. package/dist/src/core/contentGenerator.test.js.map +1 -1
  74. package/dist/src/core/coreToolScheduler.d.ts +7 -8
  75. package/dist/src/core/coreToolScheduler.js +316 -187
  76. package/dist/src/core/coreToolScheduler.js.map +1 -1
  77. package/dist/src/core/coreToolScheduler.test.js +240 -10
  78. package/dist/src/core/coreToolScheduler.test.js.map +1 -1
  79. package/dist/src/core/fakeContentGenerator.d.ts +33 -0
  80. package/dist/src/core/fakeContentGenerator.js +58 -0
  81. package/dist/src/core/fakeContentGenerator.js.map +1 -0
  82. package/dist/src/core/fakeContentGenerator.test.d.ts +6 -0
  83. package/dist/src/core/fakeContentGenerator.test.js +127 -0
  84. package/dist/src/core/fakeContentGenerator.test.js.map +1 -0
  85. package/dist/src/core/geminiChat.d.ts +2 -0
  86. package/dist/src/core/geminiChat.js +6 -2
  87. package/dist/src/core/geminiChat.js.map +1 -1
  88. package/dist/src/core/geminiChat.test.js +15 -3
  89. package/dist/src/core/geminiChat.test.js.map +1 -1
  90. package/dist/src/core/logger.js +10 -9
  91. package/dist/src/core/logger.js.map +1 -1
  92. package/dist/src/core/loggingContentGenerator.d.ts +1 -0
  93. package/dist/src/core/loggingContentGenerator.js +113 -33
  94. package/dist/src/core/loggingContentGenerator.js.map +1 -1
  95. package/dist/src/core/nonInteractiveToolExecutor.js +5 -4
  96. package/dist/src/core/nonInteractiveToolExecutor.js.map +1 -1
  97. package/dist/src/core/prompts.js +104 -55
  98. package/dist/src/core/prompts.js.map +1 -1
  99. package/dist/src/core/prompts.test.js +30 -108
  100. package/dist/src/core/prompts.test.js.map +1 -1
  101. package/dist/src/core/recordingContentGenerator.d.ts +18 -0
  102. package/dist/src/core/recordingContentGenerator.js +77 -0
  103. package/dist/src/core/recordingContentGenerator.js.map +1 -0
  104. package/dist/src/core/recordingContentGenerator.test.d.ts +6 -0
  105. package/dist/src/core/recordingContentGenerator.test.js +101 -0
  106. package/dist/src/core/recordingContentGenerator.test.js.map +1 -0
  107. package/dist/src/fallback/handler.js +2 -0
  108. package/dist/src/fallback/handler.js.map +1 -1
  109. package/dist/src/generated/git-commit.d.ts +2 -2
  110. package/dist/src/generated/git-commit.js +2 -2
  111. package/dist/src/hooks/hookPlanner.d.ts +46 -0
  112. package/dist/src/hooks/hookPlanner.js +108 -0
  113. package/dist/src/hooks/hookPlanner.js.map +1 -0
  114. package/dist/src/hooks/hookPlanner.test.d.ts +6 -0
  115. package/dist/src/hooks/hookPlanner.test.js +255 -0
  116. package/dist/src/hooks/hookPlanner.test.js.map +1 -0
  117. package/dist/src/hooks/hookRegistry.d.ts +87 -0
  118. package/dist/src/hooks/hookRegistry.js +198 -0
  119. package/dist/src/hooks/hookRegistry.js.map +1 -0
  120. package/dist/src/hooks/hookRegistry.test.d.ts +6 -0
  121. package/dist/src/hooks/hookRegistry.test.js +341 -0
  122. package/dist/src/hooks/hookRegistry.test.js.map +1 -0
  123. package/dist/src/hooks/hookTranslator.d.ts +113 -0
  124. package/dist/src/hooks/hookTranslator.js +232 -0
  125. package/dist/src/hooks/hookTranslator.js.map +1 -0
  126. package/dist/src/hooks/hookTranslator.test.d.ts +6 -0
  127. package/dist/src/hooks/hookTranslator.test.js +192 -0
  128. package/dist/src/hooks/hookTranslator.test.js.map +1 -0
  129. package/dist/src/hooks/types.d.ts +384 -0
  130. package/dist/src/hooks/types.js +284 -0
  131. package/dist/src/hooks/types.js.map +1 -0
  132. package/dist/src/hooks/types.test.d.ts +6 -0
  133. package/dist/src/hooks/types.test.js +35 -0
  134. package/dist/src/hooks/types.test.js.map +1 -0
  135. package/dist/src/index.d.ts +11 -0
  136. package/dist/src/index.js +14 -0
  137. package/dist/src/index.js.map +1 -1
  138. package/dist/src/mcp/google-auth-provider.d.ts +2 -0
  139. package/dist/src/mcp/google-auth-provider.js +21 -3
  140. package/dist/src/mcp/google-auth-provider.js.map +1 -1
  141. package/dist/src/mcp/google-auth-provider.test.js +42 -9
  142. package/dist/src/mcp/google-auth-provider.test.js.map +1 -1
  143. package/dist/src/mcp/mcpLauncher.d.ts +26 -0
  144. package/dist/src/mcp/mcpLauncher.js +238 -0
  145. package/dist/src/mcp/mcpLauncher.js.map +1 -0
  146. package/dist/src/mcp/oauth-provider.d.ts +8 -5
  147. package/dist/src/mcp/oauth-provider.js +120 -36
  148. package/dist/src/mcp/oauth-provider.js.map +1 -1
  149. package/dist/src/mcp/oauth-provider.test.js +191 -2
  150. package/dist/src/mcp/oauth-provider.test.js.map +1 -1
  151. package/dist/src/mcp/oauth-token-storage.js +5 -4
  152. package/dist/src/mcp/oauth-token-storage.js.map +1 -1
  153. package/dist/src/mcp/oauth-token-storage.test.js +17 -11
  154. package/dist/src/mcp/oauth-token-storage.test.js.map +1 -1
  155. package/dist/src/mcp/oauth-utils.d.ts +7 -0
  156. package/dist/src/mcp/oauth-utils.js +19 -0
  157. package/dist/src/mcp/oauth-utils.js.map +1 -1
  158. package/dist/src/mcp/oauth-utils.test.js +32 -0
  159. package/dist/src/mcp/oauth-utils.test.js.map +1 -1
  160. package/dist/src/mcp/sa-impersonation-provider.d.ts +0 -6
  161. package/dist/src/mcp/sa-impersonation-provider.js +6 -23
  162. package/dist/src/mcp/sa-impersonation-provider.js.map +1 -1
  163. package/dist/src/mcp/token-storage/base-token-storage.test.js +75 -84
  164. package/dist/src/mcp/token-storage/base-token-storage.test.js.map +1 -1
  165. package/dist/src/mcp/token-storage/file-token-storage.js +1 -1
  166. package/dist/src/mcp/token-storage/file-token-storage.js.map +1 -1
  167. package/dist/src/mcp/token-storage/file-token-storage.test.js +7 -5
  168. package/dist/src/mcp/token-storage/file-token-storage.test.js.map +1 -1
  169. package/dist/src/mcp/token-storage/hybrid-token-storage.js +1 -1
  170. package/dist/src/mcp/token-storage/hybrid-token-storage.js.map +1 -1
  171. package/dist/src/mcp/token-storage/hybrid-token-storage.test.js +2 -2
  172. package/dist/src/mcp/token-storage/hybrid-token-storage.test.js.map +1 -1
  173. package/dist/src/mcp/token-storage/keychain-token-storage.d.ts +6 -2
  174. package/dist/src/mcp/token-storage/keychain-token-storage.js +63 -7
  175. package/dist/src/mcp/token-storage/keychain-token-storage.js.map +1 -1
  176. package/dist/src/mcp/token-storage/keychain-token-storage.test.js +54 -3
  177. package/dist/src/mcp/token-storage/keychain-token-storage.test.js.map +1 -1
  178. package/dist/src/mcp/token-storage/types.d.ts +6 -0
  179. package/dist/src/mcp/token-storage/types.js.map +1 -1
  180. package/dist/src/policy/config.d.ts +31 -0
  181. package/dist/src/policy/config.js +197 -0
  182. package/dist/src/policy/config.js.map +1 -0
  183. package/dist/src/policy/config.test.d.ts +6 -0
  184. package/dist/src/policy/config.test.js +404 -0
  185. package/dist/src/policy/config.test.js.map +1 -0
  186. package/dist/src/policy/index.d.ts +2 -0
  187. package/dist/src/policy/index.js +2 -0
  188. package/dist/src/policy/index.js.map +1 -1
  189. package/dist/src/policy/policies/read-only.toml +56 -0
  190. package/dist/src/policy/policies/write.toml +63 -0
  191. package/dist/src/policy/policies/yolo.toml +31 -0
  192. package/dist/src/policy/policy-engine.js +4 -0
  193. package/dist/src/policy/policy-engine.js.map +1 -1
  194. package/dist/src/policy/toml-loader.d.ts +46 -0
  195. package/dist/src/policy/toml-loader.js +314 -0
  196. package/dist/src/policy/toml-loader.js.map +1 -0
  197. package/dist/src/policy/toml-loader.test.d.ts +6 -0
  198. package/dist/src/policy/toml-loader.test.js +522 -0
  199. package/dist/src/policy/toml-loader.test.js.map +1 -0
  200. package/dist/src/policy/types.d.ts +18 -0
  201. package/dist/src/policy/types.js +6 -0
  202. package/dist/src/policy/types.js.map +1 -1
  203. package/dist/src/services/chatCompressionService.d.ts +32 -0
  204. package/dist/src/services/chatCompressionService.js +162 -0
  205. package/dist/src/services/chatCompressionService.js.map +1 -0
  206. package/dist/src/services/chatCompressionService.test.d.ts +6 -0
  207. package/dist/src/services/chatCompressionService.test.js +209 -0
  208. package/dist/src/services/chatCompressionService.test.js.map +1 -0
  209. package/dist/src/services/chatRecordingService.js +9 -8
  210. package/dist/src/services/chatRecordingService.js.map +1 -1
  211. package/dist/src/services/fileDiscoveryService.d.ts +2 -14
  212. package/dist/src/services/fileDiscoveryService.js +19 -55
  213. package/dist/src/services/fileDiscoveryService.js.map +1 -1
  214. package/dist/src/services/fileDiscoveryService.test.js +91 -11
  215. package/dist/src/services/fileDiscoveryService.test.js.map +1 -1
  216. package/dist/src/services/loopDetectionService.d.ts +1 -1
  217. package/dist/src/services/loopDetectionService.js +26 -13
  218. package/dist/src/services/loopDetectionService.js.map +1 -1
  219. package/dist/src/services/loopDetectionService.test.js +119 -11
  220. package/dist/src/services/loopDetectionService.test.js.map +1 -1
  221. package/dist/src/services/shellExecutionService.js +22 -6
  222. package/dist/src/services/shellExecutionService.js.map +1 -1
  223. package/dist/src/services/shellExecutionService.test.js +32 -6
  224. package/dist/src/services/shellExecutionService.test.js.map +1 -1
  225. package/dist/src/telemetry/clearcut-logger/clearcut-logger.d.ts +4 -1
  226. package/dist/src/telemetry/clearcut-logger/clearcut-logger.js +84 -33
  227. package/dist/src/telemetry/clearcut-logger/clearcut-logger.js.map +1 -1
  228. package/dist/src/telemetry/clearcut-logger/clearcut-logger.test.js +40 -61
  229. package/dist/src/telemetry/clearcut-logger/clearcut-logger.test.js.map +1 -1
  230. package/dist/src/telemetry/clearcut-logger/event-metadata-key.d.ts +4 -1
  231. package/dist/src/telemetry/clearcut-logger/event-metadata-key.js +6 -0
  232. package/dist/src/telemetry/clearcut-logger/event-metadata-key.js.map +1 -1
  233. package/dist/src/telemetry/gcp-exporters.js +0 -1
  234. package/dist/src/telemetry/gcp-exporters.js.map +1 -1
  235. package/dist/src/telemetry/gcp-exporters.test.js +1 -1
  236. package/dist/src/telemetry/gcp-exporters.test.js.map +1 -1
  237. package/dist/src/telemetry/index.d.ts +2 -1
  238. package/dist/src/telemetry/index.js +3 -2
  239. package/dist/src/telemetry/index.js.map +1 -1
  240. package/dist/src/telemetry/loggers.d.ts +2 -1
  241. package/dist/src/telemetry/loggers.js +37 -26
  242. package/dist/src/telemetry/loggers.js.map +1 -1
  243. package/dist/src/telemetry/loggers.test.js +199 -44
  244. package/dist/src/telemetry/loggers.test.js.map +1 -1
  245. package/dist/src/telemetry/metrics.d.ts +55 -6
  246. package/dist/src/telemetry/metrics.js +89 -1
  247. package/dist/src/telemetry/metrics.js.map +1 -1
  248. package/dist/src/telemetry/metrics.test.js +172 -213
  249. package/dist/src/telemetry/metrics.test.js.map +1 -1
  250. package/dist/src/telemetry/semantic.d.ts +82 -0
  251. package/dist/src/telemetry/semantic.js +269 -0
  252. package/dist/src/telemetry/semantic.js.map +1 -0
  253. package/dist/src/telemetry/semantic.test.d.ts +6 -0
  254. package/dist/src/telemetry/semantic.test.js +387 -0
  255. package/dist/src/telemetry/semantic.test.js.map +1 -0
  256. package/dist/src/telemetry/telemetry-utils.test.js +29 -28
  257. package/dist/src/telemetry/telemetry-utils.test.js.map +1 -1
  258. package/dist/src/telemetry/trace.d.ts +46 -0
  259. package/dist/src/telemetry/trace.js +121 -0
  260. package/dist/src/telemetry/trace.js.map +1 -0
  261. package/dist/src/telemetry/types.d.ts +64 -28
  262. package/dist/src/telemetry/types.js +163 -55
  263. package/dist/src/telemetry/types.js.map +1 -1
  264. package/dist/src/telemetry/uiTelemetry.js +6 -6
  265. package/dist/src/telemetry/uiTelemetry.js.map +1 -1
  266. package/dist/src/telemetry/uiTelemetry.test.js +88 -66
  267. package/dist/src/telemetry/uiTelemetry.test.js.map +1 -1
  268. package/dist/src/tools/edit.d.ts +3 -2
  269. package/dist/src/tools/edit.js +16 -12
  270. package/dist/src/tools/edit.js.map +1 -1
  271. package/dist/src/tools/edit.test.js +78 -1
  272. package/dist/src/tools/edit.test.js.map +1 -1
  273. package/dist/src/tools/glob.js +9 -13
  274. package/dist/src/tools/glob.js.map +1 -1
  275. package/dist/src/tools/glob.test.js +203 -199
  276. package/dist/src/tools/glob.test.js.map +1 -1
  277. package/dist/src/tools/grep.js +2 -2
  278. package/dist/src/tools/grep.js.map +1 -1
  279. package/dist/src/tools/ls.js +7 -13
  280. package/dist/src/tools/ls.js.map +1 -1
  281. package/dist/src/tools/ls.test.js +2 -9
  282. package/dist/src/tools/ls.test.js.map +1 -1
  283. package/dist/src/tools/mcp-client-manager.d.ts +49 -4
  284. package/dist/src/tools/mcp-client-manager.js +209 -23
  285. package/dist/src/tools/mcp-client-manager.js.map +1 -1
  286. package/dist/src/tools/mcp-client-manager.test.js +130 -33
  287. package/dist/src/tools/mcp-client-manager.test.js.map +1 -1
  288. package/dist/src/tools/mcp-client.d.ts +5 -1
  289. package/dist/src/tools/mcp-client.js +72 -92
  290. package/dist/src/tools/mcp-client.js.map +1 -1
  291. package/dist/src/tools/mcp-client.test.js +65 -6
  292. package/dist/src/tools/mcp-client.test.js.map +1 -1
  293. package/dist/src/tools/mcp-tool.d.ts +4 -2
  294. package/dist/src/tools/mcp-tool.js +14 -10
  295. package/dist/src/tools/mcp-tool.js.map +1 -1
  296. package/dist/src/tools/memoryTool.d.ts +5 -3
  297. package/dist/src/tools/memoryTool.js +10 -8
  298. package/dist/src/tools/memoryTool.js.map +1 -1
  299. package/dist/src/tools/modifiable-tool.js +3 -2
  300. package/dist/src/tools/modifiable-tool.js.map +1 -1
  301. package/dist/src/tools/read-file.js +7 -3
  302. package/dist/src/tools/read-file.js.map +1 -1
  303. package/dist/src/tools/read-file.test.js +25 -2
  304. package/dist/src/tools/read-file.test.js.map +1 -1
  305. package/dist/src/tools/read-many-files.js +8 -29
  306. package/dist/src/tools/read-many-files.js.map +1 -1
  307. package/dist/src/tools/shell.d.ts +6 -4
  308. package/dist/src/tools/shell.js +23 -16
  309. package/dist/src/tools/shell.js.map +1 -1
  310. package/dist/src/tools/shell.test.js +8 -1
  311. package/dist/src/tools/shell.test.js.map +1 -1
  312. package/dist/src/tools/smart-edit.d.ts +3 -2
  313. package/dist/src/tools/smart-edit.js +23 -12
  314. package/dist/src/tools/smart-edit.js.map +1 -1
  315. package/dist/src/tools/smart-edit.test.js +62 -1
  316. package/dist/src/tools/smart-edit.test.js.map +1 -1
  317. package/dist/src/tools/tool-registry.d.ts +6 -19
  318. package/dist/src/tools/tool-registry.js +11 -47
  319. package/dist/src/tools/tool-registry.js.map +1 -1
  320. package/dist/src/tools/tool-registry.test.js +2 -24
  321. package/dist/src/tools/tool-registry.test.js.map +1 -1
  322. package/dist/src/tools/tools.d.ts +8 -1
  323. package/dist/src/tools/tools.js +32 -18
  324. package/dist/src/tools/tools.js.map +1 -1
  325. package/dist/src/tools/web-fetch.js +4 -18
  326. package/dist/src/tools/web-fetch.js.map +1 -1
  327. package/dist/src/tools/web-fetch.test.js +3 -3
  328. package/dist/src/tools/web-fetch.test.js.map +1 -1
  329. package/dist/src/tools/write-file.d.ts +2 -1
  330. package/dist/src/tools/write-file.js +7 -7
  331. package/dist/src/tools/write-file.js.map +1 -1
  332. package/dist/src/tools/write-file.test.js +1 -1
  333. package/dist/src/tools/write-file.test.js.map +1 -1
  334. package/dist/src/tools/write-todos.d.ts +2 -1
  335. package/dist/src/tools/write-todos.js +5 -2
  336. package/dist/src/tools/write-todos.js.map +1 -1
  337. package/dist/src/utils/channel.d.ts +19 -0
  338. package/dist/src/utils/channel.js +49 -0
  339. package/dist/src/utils/channel.js.map +1 -0
  340. package/dist/src/utils/channel.test.d.ts +6 -0
  341. package/dist/src/utils/channel.test.js +170 -0
  342. package/dist/src/utils/channel.test.js.map +1 -0
  343. package/dist/src/utils/environmentContext.d.ts +2 -1
  344. package/dist/src/utils/environmentContext.js +18 -0
  345. package/dist/src/utils/environmentContext.js.map +1 -1
  346. package/dist/src/utils/errorParsing.d.ts +1 -1
  347. package/dist/src/utils/errorParsing.js +5 -33
  348. package/dist/src/utils/errorParsing.js.map +1 -1
  349. package/dist/src/utils/errorParsing.test.js +0 -88
  350. package/dist/src/utils/errorParsing.test.js.map +1 -1
  351. package/dist/src/utils/errors.d.ts +3 -0
  352. package/dist/src/utils/errors.js +6 -0
  353. package/dist/src/utils/errors.js.map +1 -1
  354. package/dist/src/utils/events.d.ts +88 -0
  355. package/dist/src/utils/events.js +77 -0
  356. package/dist/src/utils/events.js.map +1 -0
  357. package/dist/src/utils/events.test.d.ts +6 -0
  358. package/dist/src/utils/events.test.js +131 -0
  359. package/dist/src/utils/events.test.js.map +1 -0
  360. package/dist/src/utils/extensionLoader.d.ts +78 -0
  361. package/dist/src/utils/extensionLoader.js +162 -0
  362. package/dist/src/utils/extensionLoader.js.map +1 -0
  363. package/dist/src/utils/extensionLoader.test.d.ts +6 -0
  364. package/dist/src/utils/extensionLoader.test.js +90 -0
  365. package/dist/src/utils/extensionLoader.test.js.map +1 -0
  366. package/dist/src/utils/fetch.js +1 -6
  367. package/dist/src/utils/fetch.js.map +1 -1
  368. package/dist/src/utils/flashFallback.test.js +26 -45
  369. package/dist/src/utils/flashFallback.test.js.map +1 -1
  370. package/dist/src/utils/getFolderStructure.js +7 -16
  371. package/dist/src/utils/getFolderStructure.js.map +1 -1
  372. package/dist/src/utils/gitIgnoreParser.d.ts +4 -1
  373. package/dist/src/utils/gitIgnoreParser.js +21 -4
  374. package/dist/src/utils/gitIgnoreParser.js.map +1 -1
  375. package/dist/src/utils/gitIgnoreParser.test.js +28 -0
  376. package/dist/src/utils/gitIgnoreParser.test.js.map +1 -1
  377. package/dist/src/utils/googleErrors.d.ts +104 -0
  378. package/dist/src/utils/googleErrors.js +152 -0
  379. package/dist/src/utils/googleErrors.js.map +1 -0
  380. package/dist/src/utils/googleErrors.test.d.ts +6 -0
  381. package/dist/src/utils/googleErrors.test.js +301 -0
  382. package/dist/src/utils/googleErrors.test.js.map +1 -0
  383. package/dist/src/utils/googleQuotaErrors.d.ts +36 -0
  384. package/dist/src/utils/googleQuotaErrors.js +149 -0
  385. package/dist/src/utils/googleQuotaErrors.js.map +1 -0
  386. package/dist/src/utils/googleQuotaErrors.test.d.ts +6 -0
  387. package/dist/src/utils/googleQuotaErrors.test.js +311 -0
  388. package/dist/src/utils/googleQuotaErrors.test.js.map +1 -0
  389. package/dist/src/utils/ignorePatterns.test.js +26 -30
  390. package/dist/src/utils/ignorePatterns.test.js.map +1 -1
  391. package/dist/src/utils/installationManager.js +2 -1
  392. package/dist/src/utils/installationManager.js.map +1 -1
  393. package/dist/src/utils/installationManager.test.js +3 -3
  394. package/dist/src/utils/installationManager.test.js.map +1 -1
  395. package/dist/src/utils/llm-edit-fixer.d.ts +1 -1
  396. package/dist/src/utils/llm-edit-fixer.js +27 -3
  397. package/dist/src/utils/llm-edit-fixer.js.map +1 -1
  398. package/dist/src/utils/llm-edit-fixer.test.js +21 -0
  399. package/dist/src/utils/llm-edit-fixer.test.js.map +1 -1
  400. package/dist/src/utils/memoryDiscovery.d.ts +11 -2
  401. package/dist/src/utils/memoryDiscovery.js +140 -2
  402. package/dist/src/utils/memoryDiscovery.js.map +1 -1
  403. package/dist/src/utils/memoryDiscovery.test.js +153 -37
  404. package/dist/src/utils/memoryDiscovery.test.js.map +1 -1
  405. package/dist/src/utils/package.d.ts +12 -0
  406. package/dist/src/utils/package.js +15 -0
  407. package/dist/src/utils/package.js.map +1 -0
  408. package/dist/src/utils/paths.js +126 -26
  409. package/dist/src/utils/paths.js.map +1 -1
  410. package/dist/src/utils/paths.test.js +200 -68
  411. package/dist/src/utils/paths.test.js.map +1 -1
  412. package/dist/src/utils/quotaErrorDetection.d.ts +0 -2
  413. package/dist/src/utils/quotaErrorDetection.js +0 -46
  414. package/dist/src/utils/quotaErrorDetection.js.map +1 -1
  415. package/dist/src/utils/retry.js +41 -145
  416. package/dist/src/utils/retry.js.map +1 -1
  417. package/dist/src/utils/retry.test.js +31 -110
  418. package/dist/src/utils/retry.test.js.map +1 -1
  419. package/dist/src/utils/shell-utils.js +29 -1
  420. package/dist/src/utils/shell-utils.js.map +1 -1
  421. package/dist/src/utils/shell-utils.test.js +88 -0
  422. package/dist/src/utils/shell-utils.test.js.map +1 -1
  423. package/dist/src/utils/summarizer.js +2 -1
  424. package/dist/src/utils/summarizer.js.map +1 -1
  425. package/dist/src/utils/summarizer.test.js +0 -1
  426. package/dist/src/utils/summarizer.test.js.map +1 -1
  427. package/dist/src/utils/workspaceContext.js +1 -1
  428. package/dist/src/utils/workspaceContext.js.map +1 -1
  429. package/dist/src/utils/workspaceContext.test.js +2 -2
  430. package/dist/src/utils/workspaceContext.test.js.map +1 -1
  431. package/dist/tsconfig.tsbuildinfo +1 -1
  432. package/package.json +9 -3
@@ -4,7 +4,7 @@
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
6
  import { describe, it, expect, vi, beforeEach, afterEach, } from 'vitest';
7
- import { findCompressSplitPoint, isThinkingDefault, isThinkingSupported, GeminiClient, } from './client.js';
7
+ import { isThinkingDefault, isThinkingSupported, GeminiClient, } from './client.js';
8
8
  import { AuthType, } from './contentGenerator.js';
9
9
  import {} from './geminiChat.js';
10
10
  import { CompressionStatus, GeminiEventType, Turn, } from './turn.js';
@@ -14,8 +14,9 @@ import { FileDiscoveryService } from '../services/fileDiscoveryService.js';
14
14
  import { setSimulate429 } from '../utils/testUtils.js';
15
15
  import { tokenLimit } from './tokenLimits.js';
16
16
  import { ideContextStore } from '../ide/ideContext.js';
17
- import { ClearcutLogger } from '../telemetry/clearcut-logger/clearcut-logger.js';
18
17
  import { uiTelemetryService } from '../telemetry/uiTelemetry.js';
18
+ import { ChatCompressionService } from '../services/chatCompressionService.js';
19
+ vi.mock('../services/chatCompressionService.js');
19
20
  // Mock fs module to prevent actual file system operations during tests
20
21
  const mockFileSystem = new Map();
21
22
  vi.mock('node:fs', () => {
@@ -95,70 +96,6 @@ async function fromAsync(promise) {
95
96
  }
96
97
  return results;
97
98
  }
98
- describe('findCompressSplitPoint', () => {
99
- it('should throw an error for non-positive numbers', () => {
100
- expect(() => findCompressSplitPoint([], 0)).toThrow('Fraction must be between 0 and 1');
101
- });
102
- it('should throw an error for a fraction greater than or equal to 1', () => {
103
- expect(() => findCompressSplitPoint([], 1)).toThrow('Fraction must be between 0 and 1');
104
- });
105
- it('should handle an empty history', () => {
106
- expect(findCompressSplitPoint([], 0.5)).toBe(0);
107
- });
108
- it('should handle a fraction in the middle', () => {
109
- const history = [
110
- { role: 'user', parts: [{ text: 'This is the first message.' }] }, // JSON length: 66 (19%)
111
- { role: 'model', parts: [{ text: 'This is the second message.' }] }, // JSON length: 68 (40%)
112
- { role: 'user', parts: [{ text: 'This is the third message.' }] }, // JSON length: 66 (60%)
113
- { role: 'model', parts: [{ text: 'This is the fourth message.' }] }, // JSON length: 68 (80%)
114
- { role: 'user', parts: [{ text: 'This is the fifth message.' }] }, // JSON length: 65 (100%)
115
- ];
116
- expect(findCompressSplitPoint(history, 0.5)).toBe(4);
117
- });
118
- it('should handle a fraction of last index', () => {
119
- const history = [
120
- { role: 'user', parts: [{ text: 'This is the first message.' }] }, // JSON length: 66 (19%)
121
- { role: 'model', parts: [{ text: 'This is the second message.' }] }, // JSON length: 68 (40%)
122
- { role: 'user', parts: [{ text: 'This is the third message.' }] }, // JSON length: 66 (60%)
123
- { role: 'model', parts: [{ text: 'This is the fourth message.' }] }, // JSON length: 68 (80%)
124
- { role: 'user', parts: [{ text: 'This is the fifth message.' }] }, // JSON length: 65 (100%)
125
- ];
126
- expect(findCompressSplitPoint(history, 0.9)).toBe(4);
127
- });
128
- it('should handle a fraction of after last index', () => {
129
- const history = [
130
- { role: 'user', parts: [{ text: 'This is the first message.' }] }, // JSON length: 66 (24%%)
131
- { role: 'model', parts: [{ text: 'This is the second message.' }] }, // JSON length: 68 (50%)
132
- { role: 'user', parts: [{ text: 'This is the third message.' }] }, // JSON length: 66 (74%)
133
- { role: 'model', parts: [{ text: 'This is the fourth message.' }] }, // JSON length: 68 (100%)
134
- ];
135
- expect(findCompressSplitPoint(history, 0.8)).toBe(4);
136
- });
137
- it('should return earlier splitpoint if no valid ones are after threshhold', () => {
138
- const history = [
139
- { role: 'user', parts: [{ text: 'This is the first message.' }] },
140
- { role: 'model', parts: [{ text: 'This is the second message.' }] },
141
- { role: 'user', parts: [{ text: 'This is the third message.' }] },
142
- { role: 'model', parts: [{ functionCall: {} }] },
143
- ];
144
- // Can't return 4 because the previous item has a function call.
145
- expect(findCompressSplitPoint(history, 0.99)).toBe(2);
146
- });
147
- it('should handle a history with only one item', () => {
148
- const historyWithEmptyParts = [
149
- { role: 'user', parts: [{ text: 'Message 1' }] },
150
- ];
151
- expect(findCompressSplitPoint(historyWithEmptyParts, 0.5)).toBe(0);
152
- });
153
- it('should handle history with weird parts', () => {
154
- const historyWithEmptyParts = [
155
- { role: 'user', parts: [{ text: 'Message 1' }] },
156
- { role: 'model', parts: [{ fileData: { fileUri: 'derp' } }] },
157
- { role: 'user', parts: [{ text: 'Message 2' }] },
158
- ];
159
- expect(findCompressSplitPoint(historyWithEmptyParts, 0.5)).toBe(2);
160
- });
161
- });
162
99
  describe('isThinkingSupported', () => {
163
100
  it('should return true for gemini-2.5', () => {
164
101
  expect(isThinkingSupported('gemini-2.5')).toBe(true);
@@ -194,6 +131,14 @@ describe('Gemini Client (client.ts)', () => {
194
131
  beforeEach(async () => {
195
132
  vi.resetAllMocks();
196
133
  vi.mocked(uiTelemetryService.setLastPromptTokenCount).mockClear();
134
+ vi.mocked(ChatCompressionService.prototype.compress).mockResolvedValue({
135
+ newHistory: null,
136
+ info: {
137
+ originalTokenCount: 0,
138
+ newTokenCount: 0,
139
+ compressionStatus: CompressionStatus.NOOP,
140
+ },
141
+ });
197
142
  mockGenerateContentFn = vi.fn().mockResolvedValue({
198
143
  candidates: [{ content: { parts: [{ text: '{"key": "value"}' }] } }],
199
144
  });
@@ -269,6 +214,7 @@ describe('Gemini Client (client.ts)', () => {
269
214
  client = new GeminiClient(mockConfig);
270
215
  await client.initialize();
271
216
  vi.mocked(mockConfig.getGeminiClient).mockReturnValue(client);
217
+ vi.mocked(uiTelemetryService.setLastPromptTokenCount).mockClear();
272
218
  });
273
219
  afterEach(() => {
274
220
  vi.restoreAllMocks();
@@ -319,87 +265,64 @@ describe('Gemini Client (client.ts)', () => {
319
265
  getHistory: mockGetHistory,
320
266
  addHistory: vi.fn(),
321
267
  setHistory: vi.fn(),
268
+ getLastPromptTokenCount: vi.fn(),
322
269
  };
323
270
  });
324
271
  function setup({ chatHistory = [
325
272
  { role: 'user', parts: [{ text: 'Long conversation' }] },
326
273
  { role: 'model', parts: [{ text: 'Long response' }] },
327
- ], originalTokenCount = 1000, summaryText = 'This is a summary.', } = {}) {
274
+ ], originalTokenCount = 1000, newTokenCount = 500, compressionStatus = CompressionStatus.COMPRESSED, } = {}) {
328
275
  const mockOriginalChat = {
329
276
  getHistory: vi.fn((_curated) => chatHistory),
330
277
  setHistory: vi.fn(),
278
+ getLastPromptTokenCount: vi.fn().mockReturnValue(originalTokenCount),
331
279
  };
332
280
  client['chat'] = mockOriginalChat;
333
281
  vi.mocked(uiTelemetryService.getLastPromptTokenCount).mockReturnValue(originalTokenCount);
334
- mockGenerateContentFn.mockResolvedValue({
335
- candidates: [
336
- {
337
- content: {
338
- role: 'model',
339
- parts: [{ text: summaryText }],
340
- },
341
- },
342
- ],
343
- });
344
- // Calculate what the new history will be
345
- const splitPoint = findCompressSplitPoint(chatHistory, 0.7); // 1 - 0.3
346
- const historyToKeep = chatHistory.slice(splitPoint);
347
- // This is the history that the new chat will have.
348
- // It includes the default startChat history + the extra history from tryCompressChat
349
- const newCompressedHistory = [
350
- // Mocked envParts + canned response from startChat
351
- {
352
- role: 'user',
353
- parts: [{ text: 'Mocked env context' }],
354
- },
355
- {
356
- role: 'model',
357
- parts: [{ text: 'Got it. Thanks for the context!' }],
358
- },
359
- // extraHistory from tryCompressChat
360
- {
361
- role: 'user',
362
- parts: [{ text: summaryText }],
363
- },
364
- {
365
- role: 'model',
366
- parts: [{ text: 'Got it. Thanks for the additional context!' }],
367
- },
368
- ...historyToKeep,
282
+ const newHistory = [
283
+ { role: 'user', parts: [{ text: 'Summary' }] },
284
+ { role: 'model', parts: [{ text: 'Got it' }] },
369
285
  ];
286
+ vi.mocked(ChatCompressionService.prototype.compress).mockResolvedValue({
287
+ newHistory: compressionStatus === CompressionStatus.COMPRESSED
288
+ ? newHistory
289
+ : null,
290
+ info: {
291
+ originalTokenCount,
292
+ newTokenCount,
293
+ compressionStatus,
294
+ },
295
+ });
370
296
  const mockNewChat = {
371
- getHistory: vi.fn().mockReturnValue(newCompressedHistory),
297
+ getHistory: vi.fn().mockReturnValue(newHistory),
372
298
  setHistory: vi.fn(),
299
+ getLastPromptTokenCount: vi.fn().mockReturnValue(newTokenCount),
373
300
  };
374
301
  client['startChat'] = vi
375
302
  .fn()
376
303
  .mockResolvedValue(mockNewChat);
377
- const totalChars = newCompressedHistory.reduce((total, content) => total + JSON.stringify(content).length, 0);
378
- const estimatedNewTokenCount = Math.floor(totalChars / 4);
379
304
  return {
380
305
  client,
381
306
  mockOriginalChat,
382
307
  mockNewChat,
383
- estimatedNewTokenCount,
308
+ estimatedNewTokenCount: newTokenCount,
384
309
  };
385
310
  }
386
311
  describe('when compression inflates the token count', () => {
387
312
  it('allows compression to be forced/manual after a failure', async () => {
388
- // Call 1 (Fails): Setup with a long summary to inflate tokens
389
- const longSummary = 'long summary '.repeat(100);
390
- const { client, estimatedNewTokenCount: inflatedTokenCount } = setup({
313
+ // Call 1 (Fails): Setup with inflated tokens
314
+ setup({
391
315
  originalTokenCount: 100,
392
- summaryText: longSummary,
316
+ newTokenCount: 200,
317
+ compressionStatus: CompressionStatus.COMPRESSION_FAILED_INFLATED_TOKEN_COUNT,
393
318
  });
394
- expect(inflatedTokenCount).toBeGreaterThan(100); // Ensure setup is correct
395
319
  await client.tryCompressChat('prompt-id-4', false); // Fails
396
- // Call 2 (Forced): Re-setup with a short summary
397
- const shortSummary = 'short';
320
+ // Call 2 (Forced): Re-setup with compressed tokens
398
321
  const { estimatedNewTokenCount: compressedTokenCount } = setup({
399
322
  originalTokenCount: 100,
400
- summaryText: shortSummary,
323
+ newTokenCount: 50,
324
+ compressionStatus: CompressionStatus.COMPRESSED,
401
325
  });
402
- expect(compressedTokenCount).toBeLessThanOrEqual(100); // Ensure setup is correct
403
326
  const result = await client.tryCompressChat('prompt-id-4', true); // Forced
404
327
  expect(result).toEqual({
405
328
  compressionStatus: CompressionStatus.COMPRESSED,
@@ -408,12 +331,11 @@ describe('Gemini Client (client.ts)', () => {
408
331
  });
409
332
  });
410
333
  it('yields the result even if the compression inflated the tokens', async () => {
411
- const longSummary = 'long summary '.repeat(100);
412
334
  const { client, estimatedNewTokenCount } = setup({
413
335
  originalTokenCount: 100,
414
- summaryText: longSummary,
336
+ newTokenCount: 200,
337
+ compressionStatus: CompressionStatus.COMPRESSION_FAILED_INFLATED_TOKEN_COUNT,
415
338
  });
416
- expect(estimatedNewTokenCount).toBeGreaterThan(100); // Ensure setup is correct
417
339
  const result = await client.tryCompressChat('prompt-id-4', false);
418
340
  expect(result).toEqual({
419
341
  compressionStatus: CompressionStatus.COMPRESSION_FAILED_INFLATED_TOKEN_COUNT,
@@ -424,47 +346,52 @@ describe('Gemini Client (client.ts)', () => {
424
346
  expect(uiTelemetryService.setLastPromptTokenCount).not.toHaveBeenCalled();
425
347
  });
426
348
  it('does not manipulate the source chat', async () => {
427
- const longSummary = 'long summary '.repeat(100);
428
- const { client, mockOriginalChat, estimatedNewTokenCount } = setup({
349
+ const { client, mockOriginalChat } = setup({
429
350
  originalTokenCount: 100,
430
- summaryText: longSummary,
351
+ newTokenCount: 200,
352
+ compressionStatus: CompressionStatus.COMPRESSION_FAILED_INFLATED_TOKEN_COUNT,
431
353
  });
432
- expect(estimatedNewTokenCount).toBeGreaterThan(100); // Ensure setup is correct
433
354
  await client.tryCompressChat('prompt-id-4', false);
434
355
  // On failure, the chat should NOT be replaced
435
356
  expect(client['chat']).toBe(mockOriginalChat);
436
357
  });
437
- it('will not attempt to compress context after a failure', async () => {
438
- const longSummary = 'long summary '.repeat(100);
439
- const { client, estimatedNewTokenCount } = setup({
358
+ it.skip('will not attempt to compress context after a failure', async () => {
359
+ const { client } = setup({
440
360
  originalTokenCount: 100,
441
- summaryText: longSummary,
361
+ newTokenCount: 200,
362
+ compressionStatus: CompressionStatus.COMPRESSION_FAILED_INFLATED_TOKEN_COUNT,
442
363
  });
443
- expect(estimatedNewTokenCount).toBeGreaterThan(100); // Ensure setup is correct
444
364
  await client.tryCompressChat('prompt-id-4', false); // This fails and sets hasFailedCompressionAttempt = true
365
+ // Mock the next call to return NOOP
366
+ vi.mocked(ChatCompressionService.prototype.compress).mockResolvedValueOnce({
367
+ newHistory: null,
368
+ info: {
369
+ originalTokenCount: 0,
370
+ newTokenCount: 0,
371
+ compressionStatus: CompressionStatus.NOOP,
372
+ },
373
+ });
445
374
  // This call should now be a NOOP
446
375
  const result = await client.tryCompressChat('prompt-id-5', false);
447
- // generateContent (for summary) should only have been called once
448
- expect(mockGenerateContentFn).toHaveBeenCalledTimes(1);
449
- expect(result).toEqual({
450
- compressionStatus: CompressionStatus.NOOP,
451
- newTokenCount: 0,
452
- originalTokenCount: 0,
453
- });
376
+ expect(result.compressionStatus).toBe(CompressionStatus.NOOP);
377
+ expect(ChatCompressionService.prototype.compress).toHaveBeenCalledTimes(2);
378
+ expect(ChatCompressionService.prototype.compress).toHaveBeenLastCalledWith(expect.anything(), 'prompt-id-5', false, expect.anything(), expect.anything(), true);
454
379
  });
455
380
  });
456
381
  it('should not trigger summarization if token count is below threshold', async () => {
457
382
  const MOCKED_TOKEN_LIMIT = 1000;
458
- vi.mocked(tokenLimit).mockReturnValue(MOCKED_TOKEN_LIMIT);
459
- mockGetHistory.mockReturnValue([
460
- { role: 'user', parts: [{ text: '...history...' }] },
461
- ]);
462
383
  const originalTokenCount = MOCKED_TOKEN_LIMIT * 0.699;
463
- vi.mocked(uiTelemetryService.getLastPromptTokenCount).mockReturnValue(originalTokenCount);
384
+ vi.mocked(ChatCompressionService.prototype.compress).mockResolvedValue({
385
+ newHistory: null,
386
+ info: {
387
+ originalTokenCount,
388
+ newTokenCount: originalTokenCount,
389
+ compressionStatus: CompressionStatus.NOOP,
390
+ },
391
+ });
464
392
  const initialChat = client.getChat();
465
393
  const result = await client.tryCompressChat('prompt-id-2', false);
466
394
  const newChat = client.getChat();
467
- expect(tokenLimit).toHaveBeenCalled();
468
395
  expect(result).toEqual({
469
396
  compressionStatus: CompressionStatus.NOOP,
470
397
  newTokenCount: originalTokenCount,
@@ -476,6 +403,8 @@ describe('Gemini Client (client.ts)', () => {
476
403
  const { client } = setup({
477
404
  chatHistory: [{ role: 'user', parts: [{ text: 'hi' }] }],
478
405
  originalTokenCount: 50,
406
+ newTokenCount: 50,
407
+ compressionStatus: CompressionStatus.NOOP,
479
408
  });
480
409
  const result = await client.tryCompressChat('prompt-id-noop', false);
481
410
  expect(result).toEqual({
@@ -483,270 +412,6 @@ describe('Gemini Client (client.ts)', () => {
483
412
  originalTokenCount: 50,
484
413
  newTokenCount: 50,
485
414
  });
486
- expect(mockGenerateContentFn).not.toHaveBeenCalled();
487
- });
488
- it('logs a telemetry event when compressing', async () => {
489
- vi.spyOn(ClearcutLogger.prototype, 'logChatCompressionEvent');
490
- const MOCKED_TOKEN_LIMIT = 1000;
491
- const MOCKED_CONTEXT_PERCENTAGE_THRESHOLD = 0.5;
492
- vi.spyOn(client['config'], 'getChatCompression').mockReturnValue({
493
- contextPercentageThreshold: MOCKED_CONTEXT_PERCENTAGE_THRESHOLD,
494
- });
495
- const history = [
496
- { role: 'user', parts: [{ text: '...history...' }] },
497
- { role: 'model', parts: [{ text: '...history...' }] },
498
- { role: 'user', parts: [{ text: '...history...' }] },
499
- { role: 'model', parts: [{ text: '...history...' }] },
500
- { role: 'user', parts: [{ text: '...history...' }] },
501
- { role: 'model', parts: [{ text: '...history...' }] },
502
- ];
503
- mockGetHistory.mockReturnValue(history);
504
- const originalTokenCount = MOCKED_TOKEN_LIMIT * MOCKED_CONTEXT_PERCENTAGE_THRESHOLD;
505
- vi.mocked(uiTelemetryService.getLastPromptTokenCount).mockReturnValue(originalTokenCount);
506
- // We need to control the estimated new token count.
507
- // We mock startChat to return a chat with a known history.
508
- const summaryText = 'This is a summary.';
509
- const splitPoint = findCompressSplitPoint(history, 0.7);
510
- const historyToKeep = history.slice(splitPoint);
511
- const newCompressedHistory = [
512
- { role: 'user', parts: [{ text: 'Mocked env context' }] },
513
- { role: 'model', parts: [{ text: 'Got it. Thanks for the context!' }] },
514
- { role: 'user', parts: [{ text: summaryText }] },
515
- {
516
- role: 'model',
517
- parts: [{ text: 'Got it. Thanks for the additional context!' }],
518
- },
519
- ...historyToKeep,
520
- ];
521
- const mockNewChat = {
522
- getHistory: vi.fn().mockReturnValue(newCompressedHistory),
523
- };
524
- client['startChat'] = vi
525
- .fn()
526
- .mockResolvedValue(mockNewChat);
527
- const totalChars = newCompressedHistory.reduce((total, content) => total + JSON.stringify(content).length, 0);
528
- const newTokenCount = Math.floor(totalChars / 4);
529
- // Mock the summary response from the chat
530
- mockGenerateContentFn.mockResolvedValue({
531
- candidates: [
532
- {
533
- content: {
534
- role: 'model',
535
- parts: [{ text: summaryText }],
536
- },
537
- },
538
- ],
539
- });
540
- await client.tryCompressChat('prompt-id-3', false);
541
- expect(ClearcutLogger.prototype.logChatCompressionEvent).toHaveBeenCalledWith(expect.objectContaining({
542
- tokens_before: originalTokenCount,
543
- tokens_after: newTokenCount,
544
- }));
545
- expect(uiTelemetryService.setLastPromptTokenCount).toHaveBeenCalledWith(newTokenCount);
546
- expect(uiTelemetryService.setLastPromptTokenCount).toHaveBeenCalledTimes(1);
547
- });
548
- it('should trigger summarization if token count is at threshold with contextPercentageThreshold setting', async () => {
549
- const MOCKED_TOKEN_LIMIT = 1000;
550
- const MOCKED_CONTEXT_PERCENTAGE_THRESHOLD = 0.5;
551
- vi.mocked(tokenLimit).mockReturnValue(MOCKED_TOKEN_LIMIT);
552
- vi.spyOn(client['config'], 'getChatCompression').mockReturnValue({
553
- contextPercentageThreshold: MOCKED_CONTEXT_PERCENTAGE_THRESHOLD,
554
- });
555
- const history = [
556
- { role: 'user', parts: [{ text: '...history...' }] },
557
- { role: 'model', parts: [{ text: '...history...' }] },
558
- { role: 'user', parts: [{ text: '...history...' }] },
559
- { role: 'model', parts: [{ text: '...history...' }] },
560
- { role: 'user', parts: [{ text: '...history...' }] },
561
- { role: 'model', parts: [{ text: '...history...' }] },
562
- ];
563
- mockGetHistory.mockReturnValue(history);
564
- const originalTokenCount = MOCKED_TOKEN_LIMIT * MOCKED_CONTEXT_PERCENTAGE_THRESHOLD;
565
- vi.mocked(uiTelemetryService.getLastPromptTokenCount).mockReturnValue(originalTokenCount);
566
- // Mock summary and new chat
567
- const summaryText = 'This is a summary.';
568
- const splitPoint = findCompressSplitPoint(history, 0.7);
569
- const historyToKeep = history.slice(splitPoint);
570
- const newCompressedHistory = [
571
- { role: 'user', parts: [{ text: 'Mocked env context' }] },
572
- { role: 'model', parts: [{ text: 'Got it. Thanks for the context!' }] },
573
- { role: 'user', parts: [{ text: summaryText }] },
574
- {
575
- role: 'model',
576
- parts: [{ text: 'Got it. Thanks for the additional context!' }],
577
- },
578
- ...historyToKeep,
579
- ];
580
- const mockNewChat = {
581
- getHistory: vi.fn().mockReturnValue(newCompressedHistory),
582
- };
583
- client['startChat'] = vi
584
- .fn()
585
- .mockResolvedValue(mockNewChat);
586
- const totalChars = newCompressedHistory.reduce((total, content) => total + JSON.stringify(content).length, 0);
587
- const newTokenCount = Math.floor(totalChars / 4);
588
- // Mock the summary response from the chat
589
- mockGenerateContentFn.mockResolvedValue({
590
- candidates: [
591
- {
592
- content: {
593
- role: 'model',
594
- parts: [{ text: summaryText }],
595
- },
596
- },
597
- ],
598
- });
599
- const initialChat = client.getChat();
600
- const result = await client.tryCompressChat('prompt-id-3', false);
601
- const newChat = client.getChat();
602
- expect(tokenLimit).toHaveBeenCalled();
603
- expect(mockGenerateContentFn).toHaveBeenCalled();
604
- // Assert that summarization happened and returned the correct stats
605
- expect(result).toEqual({
606
- compressionStatus: CompressionStatus.COMPRESSED,
607
- originalTokenCount,
608
- newTokenCount,
609
- });
610
- // Assert that the chat was reset
611
- expect(newChat).not.toBe(initialChat);
612
- });
613
- it('should not compress across a function call response', async () => {
614
- const MOCKED_TOKEN_LIMIT = 1000;
615
- vi.mocked(tokenLimit).mockReturnValue(MOCKED_TOKEN_LIMIT);
616
- const history = [
617
- { role: 'user', parts: [{ text: '...history 1...' }] },
618
- { role: 'model', parts: [{ text: '...history 2...' }] },
619
- { role: 'user', parts: [{ text: '...history 3...' }] },
620
- { role: 'model', parts: [{ text: '...history 4...' }] },
621
- { role: 'user', parts: [{ text: '...history 5...' }] },
622
- { role: 'model', parts: [{ text: '...history 6...' }] },
623
- { role: 'user', parts: [{ text: '...history 7...' }] },
624
- { role: 'model', parts: [{ text: '...history 8...' }] },
625
- // Normally we would break here, but we have a function response.
626
- {
627
- role: 'user',
628
- parts: [{ functionResponse: { name: '...history 8...' } }],
629
- },
630
- { role: 'model', parts: [{ text: '...history 10...' }] },
631
- // Instead we will break here.
632
- { role: 'user', parts: [{ text: '...history 10...' }] },
633
- ];
634
- mockGetHistory.mockReturnValue(history);
635
- const originalTokenCount = 1000 * 0.7;
636
- vi.mocked(uiTelemetryService.getLastPromptTokenCount).mockReturnValue(originalTokenCount);
637
- // Mock summary and new chat
638
- const summaryText = 'This is a summary.';
639
- const splitPoint = findCompressSplitPoint(history, 0.7); // This should be 10
640
- expect(splitPoint).toBe(10); // Verify split point logic
641
- const historyToKeep = history.slice(splitPoint); // Should keep last user message
642
- expect(historyToKeep).toEqual([
643
- { role: 'user', parts: [{ text: '...history 10...' }] },
644
- ]);
645
- const newCompressedHistory = [
646
- { role: 'user', parts: [{ text: 'Mocked env context' }] },
647
- { role: 'model', parts: [{ text: 'Got it. Thanks for the context!' }] },
648
- { role: 'user', parts: [{ text: summaryText }] },
649
- {
650
- role: 'model',
651
- parts: [{ text: 'Got it. Thanks for the additional context!' }],
652
- },
653
- ...historyToKeep,
654
- ];
655
- const mockNewChat = {
656
- getHistory: vi.fn().mockReturnValue(newCompressedHistory),
657
- };
658
- client['startChat'] = vi
659
- .fn()
660
- .mockResolvedValue(mockNewChat);
661
- const totalChars = newCompressedHistory.reduce((total, content) => total + JSON.stringify(content).length, 0);
662
- const newTokenCount = Math.floor(totalChars / 4);
663
- // Mock the summary response from the chat
664
- mockGenerateContentFn.mockResolvedValue({
665
- candidates: [
666
- {
667
- content: {
668
- role: 'model',
669
- parts: [{ text: summaryText }],
670
- },
671
- },
672
- ],
673
- });
674
- const initialChat = client.getChat();
675
- const result = await client.tryCompressChat('prompt-id-3', false);
676
- const newChat = client.getChat();
677
- expect(tokenLimit).toHaveBeenCalled();
678
- expect(mockGenerateContentFn).toHaveBeenCalled();
679
- // Assert that summarization happened and returned the correct stats
680
- expect(result).toEqual({
681
- compressionStatus: CompressionStatus.COMPRESSED,
682
- originalTokenCount,
683
- newTokenCount,
684
- });
685
- // Assert that the chat was reset
686
- expect(newChat).not.toBe(initialChat);
687
- // 1. standard start context message (env)
688
- // 2. standard canned model response
689
- // 3. compressed summary message (user)
690
- // 4. standard canned model response
691
- // 5. The last user message (historyToKeep)
692
- expect(newChat.getHistory().length).toEqual(5);
693
- });
694
- it('should always trigger summarization when force is true, regardless of token count', async () => {
695
- const history = [
696
- { role: 'user', parts: [{ text: '...history...' }] },
697
- { role: 'model', parts: [{ text: '...history...' }] },
698
- { role: 'user', parts: [{ text: '...history...' }] },
699
- { role: 'model', parts: [{ text: '...history...' }] },
700
- { role: 'user', parts: [{ text: '...history...' }] },
701
- { role: 'model', parts: [{ text: '...history...' }] },
702
- ];
703
- mockGetHistory.mockReturnValue(history);
704
- const originalTokenCount = 100; // Well below threshold, but > estimated new count
705
- vi.mocked(uiTelemetryService.getLastPromptTokenCount).mockReturnValue(originalTokenCount);
706
- // Mock summary and new chat
707
- const summaryText = 'This is a summary.';
708
- const splitPoint = findCompressSplitPoint(history, 0.7);
709
- const historyToKeep = history.slice(splitPoint);
710
- const newCompressedHistory = [
711
- { role: 'user', parts: [{ text: 'Mocked env context' }] },
712
- { role: 'model', parts: [{ text: 'Got it. Thanks for the context!' }] },
713
- { role: 'user', parts: [{ text: summaryText }] },
714
- {
715
- role: 'model',
716
- parts: [{ text: 'Got it. Thanks for the additional context!' }],
717
- },
718
- ...historyToKeep,
719
- ];
720
- const mockNewChat = {
721
- getHistory: vi.fn().mockReturnValue(newCompressedHistory),
722
- };
723
- client['startChat'] = vi
724
- .fn()
725
- .mockResolvedValue(mockNewChat);
726
- const totalChars = newCompressedHistory.reduce((total, content) => total + JSON.stringify(content).length, 0);
727
- const newTokenCount = Math.floor(totalChars / 4);
728
- // Mock the summary response from the chat
729
- mockGenerateContentFn.mockResolvedValue({
730
- candidates: [
731
- {
732
- content: {
733
- role: 'model',
734
- parts: [{ text: summaryText }],
735
- },
736
- },
737
- ],
738
- });
739
- const initialChat = client.getChat();
740
- const result = await client.tryCompressChat('prompt-id-1', true); // force = true
741
- const newChat = client.getChat();
742
- expect(mockGenerateContentFn).toHaveBeenCalled();
743
- expect(result).toEqual({
744
- compressionStatus: CompressionStatus.COMPRESSED,
745
- originalTokenCount,
746
- newTokenCount,
747
- });
748
- // Assert that the chat was reset
749
- expect(newChat).not.toBe(initialChat);
750
415
  });
751
416
  });
752
417
  describe('sendMessageStream', () => {
@@ -831,6 +496,7 @@ describe('Gemini Client (client.ts)', () => {
831
496
  const mockChat = {
832
497
  addHistory: vi.fn(),
833
498
  getHistory: vi.fn().mockReturnValue([]),
499
+ getLastPromptTokenCount: vi.fn(),
834
500
  };
835
501
  client['chat'] = mockChat;
836
502
  const initialRequest = [{ text: 'Hi' }];
@@ -878,6 +544,7 @@ ${JSON.stringify({
878
544
  const mockChat = {
879
545
  addHistory: vi.fn(),
880
546
  getHistory: vi.fn().mockReturnValue([]),
547
+ getLastPromptTokenCount: vi.fn(),
881
548
  };
882
549
  client['chat'] = mockChat;
883
550
  const initialRequest = [{ text: 'Hi' }];
@@ -922,6 +589,7 @@ ${JSON.stringify({
922
589
  const mockChat = {
923
590
  addHistory: vi.fn(),
924
591
  getHistory: vi.fn().mockReturnValue([]),
592
+ getLastPromptTokenCount: vi.fn(),
925
593
  };
926
594
  client['chat'] = mockChat;
927
595
  const initialRequest = [{ text: 'Hi' }];
@@ -982,6 +650,7 @@ ${JSON.stringify({
982
650
  const mockChat = {
983
651
  addHistory: vi.fn(),
984
652
  getHistory: vi.fn().mockReturnValue([]),
653
+ getLastPromptTokenCount: vi.fn(),
985
654
  };
986
655
  client['chat'] = mockChat;
987
656
  const initialRequest = [{ text: 'Hi' }];
@@ -1015,6 +684,7 @@ ${JSON.stringify({
1015
684
  const mockChat = {
1016
685
  addHistory: vi.fn(),
1017
686
  getHistory: vi.fn().mockReturnValue([]),
687
+ getLastPromptTokenCount: vi.fn(),
1018
688
  };
1019
689
  client['chat'] = mockChat;
1020
690
  // Act
@@ -1048,6 +718,7 @@ ${JSON.stringify({
1048
718
  const mockChat = {
1049
719
  addHistory: vi.fn(),
1050
720
  getHistory: vi.fn().mockReturnValue([]),
721
+ getLastPromptTokenCount: vi.fn(),
1051
722
  };
1052
723
  client['chat'] = mockChat;
1053
724
  // Use a signal that never gets aborted
@@ -1093,6 +764,7 @@ ${JSON.stringify({
1093
764
  const mockChat = {
1094
765
  addHistory: vi.fn(),
1095
766
  getHistory: vi.fn().mockReturnValue([]),
767
+ getLastPromptTokenCount: vi.fn(),
1096
768
  };
1097
769
  client['chat'] = mockChat;
1098
770
  // Act & Assert
@@ -1131,6 +803,7 @@ ${JSON.stringify({
1131
803
  const mockChat = {
1132
804
  addHistory: vi.fn(),
1133
805
  getHistory: vi.fn().mockReturnValue([]),
806
+ getLastPromptTokenCount: vi.fn(),
1134
807
  };
1135
808
  client['chat'] = mockChat;
1136
809
  // Use a signal that never gets aborted
@@ -1158,9 +831,8 @@ ${JSON.stringify({
1158
831
  }
1159
832
  }
1160
833
  }
1161
- catch (error) {
834
+ catch (_) {
1162
835
  // If the test framework times out, that also demonstrates the infinite loop
1163
- console.error('Test timed out or errored:', error);
1164
836
  }
1165
837
  // Assert that the fix works - the loop should stop at MAX_TURNS
1166
838
  const callCount = mockCheckNextSpeaker.mock.calls.length;
@@ -1175,7 +847,11 @@ ${JSON.stringify({
1175
847
  vi.mocked(tokenLimit).mockReturnValue(MOCKED_TOKEN_LIMIT);
1176
848
  // Set last prompt token count
1177
849
  const lastPromptTokenCount = 900;
1178
- vi.mocked(uiTelemetryService.getLastPromptTokenCount).mockReturnValue(lastPromptTokenCount);
850
+ const mockChat = {
851
+ getLastPromptTokenCount: vi.fn().mockReturnValue(lastPromptTokenCount),
852
+ getHistory: vi.fn().mockReturnValue([]),
853
+ };
854
+ client['chat'] = mockChat;
1179
855
  // Remaining = 100. Threshold (95%) = 95.
1180
856
  // We need a request > 95 tokens.
1181
857
  // A string of length 400 is roughly 100 tokens.
@@ -1218,7 +894,11 @@ ${JSON.stringify({
1218
894
  client['currentSequenceModel'] = STICKY_MODEL;
1219
895
  // Set token count
1220
896
  const lastPromptTokenCount = 900;
1221
- vi.mocked(uiTelemetryService.getLastPromptTokenCount).mockReturnValue(lastPromptTokenCount);
897
+ const mockChat = {
898
+ getLastPromptTokenCount: vi.fn().mockReturnValue(lastPromptTokenCount),
899
+ getHistory: vi.fn().mockReturnValue([]),
900
+ };
901
+ client['chat'] = mockChat;
1222
902
  // Remaining (sticky) = 100. Threshold (95%) = 95.
1223
903
  // We need a request > 95 tokens.
1224
904
  const longText = 'a'.repeat(400);
@@ -1257,6 +937,12 @@ ${JSON.stringify({
1257
937
  mockTurnRunFn.mockReturnValue((async function* () {
1258
938
  yield { type: 'content', value: 'Hello' };
1259
939
  })());
940
+ const mockChat = {
941
+ addHistory: vi.fn(),
942
+ getHistory: vi.fn().mockReturnValue([]),
943
+ getLastPromptTokenCount: vi.fn(),
944
+ };
945
+ client['chat'] = mockChat;
1260
946
  });
1261
947
  it('should use the model router service to select a model on the first turn', async () => {
1262
948
  const stream = client.sendMessageStream([{ text: 'Hi' }], new AbortController().signal, 'prompt-1');
@@ -1345,6 +1031,7 @@ ${JSON.stringify({
1345
1031
  const mockChat = {
1346
1032
  addHistory: vi.fn(),
1347
1033
  getHistory: vi.fn().mockReturnValue([]),
1034
+ getLastPromptTokenCount: vi.fn(),
1348
1035
  };
1349
1036
  client['chat'] = mockChat;
1350
1037
  const initialRequest = [{ text: 'Hi' }];
@@ -1375,6 +1062,7 @@ ${JSON.stringify({
1375
1062
  const mockChat = {
1376
1063
  addHistory: vi.fn(),
1377
1064
  getHistory: vi.fn().mockReturnValue([]),
1065
+ getLastPromptTokenCount: vi.fn(),
1378
1066
  };
1379
1067
  client['chat'] = mockChat;
1380
1068
  const initialRequest = [{ text: 'Hi' }];
@@ -1398,6 +1086,7 @@ ${JSON.stringify({
1398
1086
  const mockChat = {
1399
1087
  addHistory: vi.fn(),
1400
1088
  getHistory: vi.fn().mockReturnValue([]),
1089
+ getLastPromptTokenCount: vi.fn(),
1401
1090
  };
1402
1091
  client['chat'] = mockChat;
1403
1092
  const initialRequest = [{ text: 'Hi' }];
@@ -1435,6 +1124,7 @@ ${JSON.stringify({
1435
1124
  .mockReturnValue([
1436
1125
  { role: 'user', parts: [{ text: 'previous message' }] },
1437
1126
  ]),
1127
+ getLastPromptTokenCount: vi.fn(),
1438
1128
  };
1439
1129
  client['chat'] = mockChat;
1440
1130
  });
@@ -1555,7 +1245,11 @@ ${JSON.stringify({
1555
1245
  vi.mocked(ideContextStore.get).mockReturnValue({
1556
1246
  workspaceState: {
1557
1247
  openFiles: [
1558
- { ...currentActiveFile, isActive: true, timestamp: Date.now() },
1248
+ {
1249
+ ...currentActiveFile,
1250
+ isActive: true,
1251
+ timestamp: Date.now(),
1252
+ },
1559
1253
  ],
1560
1254
  },
1561
1255
  });
@@ -1643,6 +1337,7 @@ ${JSON.stringify({
1643
1337
  addHistory: vi.fn(),
1644
1338
  getHistory: vi.fn().mockReturnValue([]), // Default empty history
1645
1339
  setHistory: vi.fn(),
1340
+ getLastPromptTokenCount: vi.fn(),
1646
1341
  };
1647
1342
  client['chat'] = mockChat;
1648
1343
  vi.spyOn(client['config'], 'getIdeMode').mockReturnValue(true);
@@ -1906,6 +1601,7 @@ ${JSON.stringify({
1906
1601
  const mockChat = {
1907
1602
  addHistory: vi.fn(),
1908
1603
  getHistory: vi.fn().mockReturnValue([]),
1604
+ getLastPromptTokenCount: vi.fn(),
1909
1605
  };
1910
1606
  client['chat'] = mockChat;
1911
1607
  // Act
@@ -1931,6 +1627,7 @@ ${JSON.stringify({
1931
1627
  const mockChat = {
1932
1628
  addHistory: vi.fn(),
1933
1629
  getHistory: vi.fn().mockReturnValue([]),
1630
+ getLastPromptTokenCount: vi.fn(),
1934
1631
  };
1935
1632
  client['chat'] = mockChat;
1936
1633
  // Act
@@ -1958,6 +1655,7 @@ ${JSON.stringify({
1958
1655
  const mockChat = {
1959
1656
  addHistory: vi.fn(),
1960
1657
  getHistory: vi.fn().mockReturnValue([]),
1658
+ getLastPromptTokenCount: vi.fn(),
1961
1659
  };
1962
1660
  client['chat'] = mockChat;
1963
1661
  // Act