@machina.ai/cell-cli-core 1.10.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 (549) 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 +13 -5
  5. package/dist/src/agents/codebase-investigator.js +2 -5
  6. package/dist/src/agents/codebase-investigator.js.map +1 -1
  7. package/dist/src/agents/executor.d.ts +19 -0
  8. package/dist/src/agents/executor.js +234 -46
  9. package/dist/src/agents/executor.js.map +1 -1
  10. package/dist/src/agents/executor.test.js +371 -40
  11. package/dist/src/agents/executor.test.js.map +1 -1
  12. package/dist/src/agents/registry.js +4 -3
  13. package/dist/src/agents/registry.js.map +1 -1
  14. package/dist/src/agents/subagent-tool-wrapper.test.js +2 -4
  15. package/dist/src/agents/subagent-tool-wrapper.test.js.map +1 -1
  16. package/dist/src/agents/types.d.ts +2 -1
  17. package/dist/src/agents/types.js +1 -0
  18. package/dist/src/agents/types.js.map +1 -1
  19. package/dist/src/code_assist/converter.d.ts +1 -0
  20. package/dist/src/code_assist/converter.js +1 -0
  21. package/dist/src/code_assist/converter.js.map +1 -1
  22. package/dist/src/code_assist/converter.test.js +19 -0
  23. package/dist/src/code_assist/converter.test.js.map +1 -1
  24. package/dist/src/code_assist/experiments/client_metadata.d.ts +12 -0
  25. package/dist/src/code_assist/experiments/client_metadata.js +49 -0
  26. package/dist/src/code_assist/experiments/client_metadata.js.map +1 -0
  27. package/dist/src/code_assist/experiments/experiments.d.ts +17 -0
  28. package/dist/src/code_assist/experiments/experiments.js +36 -0
  29. package/dist/src/code_assist/experiments/experiments.js.map +1 -0
  30. package/dist/src/code_assist/experiments/types.d.ts +35 -0
  31. package/dist/src/code_assist/experiments/types.js +7 -0
  32. package/dist/src/code_assist/experiments/types.js.map +1 -0
  33. package/dist/src/code_assist/oauth-credential-storage.js +5 -4
  34. package/dist/src/code_assist/oauth-credential-storage.js.map +1 -1
  35. package/dist/src/code_assist/oauth-credential-storage.test.js +15 -3
  36. package/dist/src/code_assist/oauth-credential-storage.test.js.map +1 -1
  37. package/dist/src/code_assist/oauth2.d.ts +2 -2
  38. package/dist/src/code_assist/oauth2.js +64 -51
  39. package/dist/src/code_assist/oauth2.js.map +1 -1
  40. package/dist/src/code_assist/oauth2.test.js +65 -33
  41. package/dist/src/code_assist/oauth2.test.js.map +1 -1
  42. package/dist/src/code_assist/server.d.ts +6 -4
  43. package/dist/src/code_assist/server.js +11 -0
  44. package/dist/src/code_assist/server.js.map +1 -1
  45. package/dist/src/code_assist/server.test.js +17 -0
  46. package/dist/src/code_assist/server.test.js.map +1 -1
  47. package/dist/src/code_assist/setup.d.ts +2 -2
  48. package/dist/src/code_assist/setup.js.map +1 -1
  49. package/dist/src/code_assist/types.d.ts +1 -1
  50. package/dist/src/code_assist/types.js.map +1 -1
  51. package/dist/src/commands/extensions.d.ts +7 -0
  52. package/dist/src/commands/extensions.js +9 -0
  53. package/dist/src/commands/extensions.js.map +1 -0
  54. package/dist/src/commands/extensions.test.js +19 -0
  55. package/dist/src/commands/extensions.test.js.map +1 -0
  56. package/dist/src/config/config.d.ts +81 -32
  57. package/dist/src/config/config.js +193 -66
  58. package/dist/src/config/config.js.map +1 -1
  59. package/dist/src/config/config.test.js +115 -36
  60. package/dist/src/config/config.test.js.map +1 -1
  61. package/dist/src/config/models.d.ts +1 -1
  62. package/dist/src/config/models.js +2 -2
  63. package/dist/src/config/models.js.map +1 -1
  64. package/dist/src/config/storage.d.ts +3 -0
  65. package/dist/src/config/storage.js +20 -0
  66. package/dist/src/config/storage.js.map +1 -1
  67. package/dist/src/confirmation-bus/message-bus.d.ts +2 -1
  68. package/dist/src/confirmation-bus/message-bus.js +7 -1
  69. package/dist/src/confirmation-bus/message-bus.js.map +1 -1
  70. package/dist/src/confirmation-bus/types.d.ts +12 -2
  71. package/dist/src/confirmation-bus/types.js +1 -0
  72. package/dist/src/confirmation-bus/types.js.map +1 -1
  73. package/dist/src/core/apiKeyCredentialStorage.d.ts +17 -0
  74. package/dist/src/core/apiKeyCredentialStorage.js +64 -0
  75. package/dist/src/core/apiKeyCredentialStorage.js.map +1 -0
  76. package/dist/src/core/apiKeyCredentialStorage.test.d.ts +6 -0
  77. package/dist/src/core/apiKeyCredentialStorage.test.js +71 -0
  78. package/dist/src/core/apiKeyCredentialStorage.test.js.map +1 -0
  79. package/dist/src/core/client.d.ts +2 -11
  80. package/dist/src/core/client.js +31 -170
  81. package/dist/src/core/client.js.map +1 -1
  82. package/dist/src/core/client.test.js +107 -429
  83. package/dist/src/core/client.test.js.map +1 -1
  84. package/dist/src/core/contentGenerator.js +64 -59
  85. package/dist/src/core/contentGenerator.js.map +1 -1
  86. package/dist/src/core/contentGenerator.test.js +38 -4
  87. package/dist/src/core/contentGenerator.test.js.map +1 -1
  88. package/dist/src/core/coreToolScheduler.d.ts +8 -2
  89. package/dist/src/core/coreToolScheduler.js +337 -172
  90. package/dist/src/core/coreToolScheduler.js.map +1 -1
  91. package/dist/src/core/coreToolScheduler.test.js +363 -12
  92. package/dist/src/core/coreToolScheduler.test.js.map +1 -1
  93. package/dist/src/core/fakeContentGenerator.d.ts +33 -0
  94. package/dist/src/core/fakeContentGenerator.js +58 -0
  95. package/dist/src/core/fakeContentGenerator.js.map +1 -0
  96. package/dist/src/core/fakeContentGenerator.test.d.ts +6 -0
  97. package/dist/src/core/fakeContentGenerator.test.js +127 -0
  98. package/dist/src/core/fakeContentGenerator.test.js.map +1 -0
  99. package/dist/src/core/geminiChat.d.ts +2 -0
  100. package/dist/src/core/geminiChat.js +7 -2
  101. package/dist/src/core/geminiChat.js.map +1 -1
  102. package/dist/src/core/geminiChat.test.js +15 -3
  103. package/dist/src/core/geminiChat.test.js.map +1 -1
  104. package/dist/src/core/logger.js +21 -19
  105. package/dist/src/core/logger.js.map +1 -1
  106. package/dist/src/core/loggingContentGenerator.d.ts +1 -0
  107. package/dist/src/core/loggingContentGenerator.js +113 -33
  108. package/dist/src/core/loggingContentGenerator.js.map +1 -1
  109. package/dist/src/core/nonInteractiveToolExecutor.js +5 -4
  110. package/dist/src/core/nonInteractiveToolExecutor.js.map +1 -1
  111. package/dist/src/core/nonInteractiveToolExecutor.test.js +3 -0
  112. package/dist/src/core/nonInteractiveToolExecutor.test.js.map +1 -1
  113. package/dist/src/core/prompts.js +115 -72
  114. package/dist/src/core/prompts.js.map +1 -1
  115. package/dist/src/core/prompts.test.js +30 -108
  116. package/dist/src/core/prompts.test.js.map +1 -1
  117. package/dist/src/core/recordingContentGenerator.d.ts +18 -0
  118. package/dist/src/core/recordingContentGenerator.js +77 -0
  119. package/dist/src/core/recordingContentGenerator.js.map +1 -0
  120. package/dist/src/core/recordingContentGenerator.test.d.ts +6 -0
  121. package/dist/src/core/recordingContentGenerator.test.js +101 -0
  122. package/dist/src/core/recordingContentGenerator.test.js.map +1 -0
  123. package/dist/src/core/turn.d.ts +2 -0
  124. package/dist/src/core/turn.js +3 -1
  125. package/dist/src/core/turn.js.map +1 -1
  126. package/dist/src/core/turn.test.js +48 -0
  127. package/dist/src/core/turn.test.js.map +1 -1
  128. package/dist/src/fallback/handler.js +2 -0
  129. package/dist/src/fallback/handler.js.map +1 -1
  130. package/dist/src/generated/git-commit.d.ts +2 -2
  131. package/dist/src/generated/git-commit.js +2 -2
  132. package/dist/src/hooks/hookPlanner.d.ts +46 -0
  133. package/dist/src/hooks/hookPlanner.js +108 -0
  134. package/dist/src/hooks/hookPlanner.js.map +1 -0
  135. package/dist/src/hooks/hookPlanner.test.d.ts +6 -0
  136. package/dist/src/hooks/hookPlanner.test.js +255 -0
  137. package/dist/src/hooks/hookPlanner.test.js.map +1 -0
  138. package/dist/src/hooks/hookRegistry.d.ts +87 -0
  139. package/dist/src/hooks/hookRegistry.js +198 -0
  140. package/dist/src/hooks/hookRegistry.js.map +1 -0
  141. package/dist/src/hooks/hookRegistry.test.d.ts +6 -0
  142. package/dist/src/hooks/hookRegistry.test.js +341 -0
  143. package/dist/src/hooks/hookRegistry.test.js.map +1 -0
  144. package/dist/src/hooks/hookTranslator.d.ts +113 -0
  145. package/dist/src/hooks/hookTranslator.js +232 -0
  146. package/dist/src/hooks/hookTranslator.js.map +1 -0
  147. package/dist/src/hooks/hookTranslator.test.d.ts +6 -0
  148. package/dist/src/hooks/hookTranslator.test.js +192 -0
  149. package/dist/src/hooks/hookTranslator.test.js.map +1 -0
  150. package/dist/src/hooks/types.d.ts +384 -0
  151. package/dist/src/hooks/types.js +284 -0
  152. package/dist/src/hooks/types.js.map +1 -0
  153. package/dist/src/hooks/types.test.d.ts +6 -0
  154. package/dist/src/hooks/types.test.js +35 -0
  155. package/dist/src/hooks/types.test.js.map +1 -0
  156. package/dist/src/ide/ide-client.js +2 -1
  157. package/dist/src/ide/ide-client.js.map +1 -1
  158. package/dist/src/index.d.ts +15 -0
  159. package/dist/src/index.js +18 -0
  160. package/dist/src/index.js.map +1 -1
  161. package/dist/src/mcp/google-auth-provider.d.ts +2 -0
  162. package/dist/src/mcp/google-auth-provider.js +21 -3
  163. package/dist/src/mcp/google-auth-provider.js.map +1 -1
  164. package/dist/src/mcp/google-auth-provider.test.js +42 -9
  165. package/dist/src/mcp/google-auth-provider.test.js.map +1 -1
  166. package/dist/src/mcp/mcpLauncher.d.ts +26 -0
  167. package/dist/src/mcp/mcpLauncher.js +238 -0
  168. package/dist/src/mcp/mcpLauncher.js.map +1 -0
  169. package/dist/src/mcp/oauth-provider.d.ts +8 -5
  170. package/dist/src/mcp/oauth-provider.js +140 -55
  171. package/dist/src/mcp/oauth-provider.js.map +1 -1
  172. package/dist/src/mcp/oauth-provider.test.js +191 -2
  173. package/dist/src/mcp/oauth-provider.test.js.map +1 -1
  174. package/dist/src/mcp/oauth-token-storage.js +5 -4
  175. package/dist/src/mcp/oauth-token-storage.js.map +1 -1
  176. package/dist/src/mcp/oauth-token-storage.test.js +17 -11
  177. package/dist/src/mcp/oauth-token-storage.test.js.map +1 -1
  178. package/dist/src/mcp/oauth-utils.d.ts +7 -0
  179. package/dist/src/mcp/oauth-utils.js +28 -8
  180. package/dist/src/mcp/oauth-utils.js.map +1 -1
  181. package/dist/src/mcp/oauth-utils.test.js +45 -2
  182. package/dist/src/mcp/oauth-utils.test.js.map +1 -1
  183. package/dist/src/mcp/sa-impersonation-provider.d.ts +0 -6
  184. package/dist/src/mcp/sa-impersonation-provider.js +6 -23
  185. package/dist/src/mcp/sa-impersonation-provider.js.map +1 -1
  186. package/dist/src/mcp/token-storage/base-token-storage.test.js +75 -84
  187. package/dist/src/mcp/token-storage/base-token-storage.test.js.map +1 -1
  188. package/dist/src/mcp/token-storage/file-token-storage.js +1 -1
  189. package/dist/src/mcp/token-storage/file-token-storage.js.map +1 -1
  190. package/dist/src/mcp/token-storage/file-token-storage.test.js +7 -5
  191. package/dist/src/mcp/token-storage/file-token-storage.test.js.map +1 -1
  192. package/dist/src/mcp/token-storage/hybrid-token-storage.js +1 -1
  193. package/dist/src/mcp/token-storage/hybrid-token-storage.js.map +1 -1
  194. package/dist/src/mcp/token-storage/hybrid-token-storage.test.js +2 -2
  195. package/dist/src/mcp/token-storage/hybrid-token-storage.test.js.map +1 -1
  196. package/dist/src/mcp/token-storage/keychain-token-storage.d.ts +6 -2
  197. package/dist/src/mcp/token-storage/keychain-token-storage.js +63 -7
  198. package/dist/src/mcp/token-storage/keychain-token-storage.js.map +1 -1
  199. package/dist/src/mcp/token-storage/keychain-token-storage.test.js +54 -3
  200. package/dist/src/mcp/token-storage/keychain-token-storage.test.js.map +1 -1
  201. package/dist/src/mcp/token-storage/types.d.ts +6 -0
  202. package/dist/src/mcp/token-storage/types.js.map +1 -1
  203. package/dist/src/output/stream-json-formatter.d.ts +32 -0
  204. package/dist/src/output/stream-json-formatter.js +52 -0
  205. package/dist/src/output/stream-json-formatter.js.map +1 -0
  206. package/dist/src/output/stream-json-formatter.test.d.ts +6 -0
  207. package/dist/src/output/stream-json-formatter.test.js +479 -0
  208. package/dist/src/output/stream-json-formatter.test.js.map +1 -0
  209. package/dist/src/output/types.d.ts +63 -1
  210. package/dist/src/output/types.js +11 -0
  211. package/dist/src/output/types.js.map +1 -1
  212. package/dist/src/policy/config.d.ts +31 -0
  213. package/dist/src/policy/config.js +197 -0
  214. package/dist/src/policy/config.js.map +1 -0
  215. package/dist/src/policy/config.test.d.ts +6 -0
  216. package/dist/src/policy/config.test.js +404 -0
  217. package/dist/src/policy/config.test.js.map +1 -0
  218. package/dist/src/policy/index.d.ts +2 -0
  219. package/dist/src/policy/index.js +2 -0
  220. package/dist/src/policy/index.js.map +1 -1
  221. package/dist/src/policy/policies/read-only.toml +56 -0
  222. package/dist/src/policy/policies/write.toml +63 -0
  223. package/dist/src/policy/policies/yolo.toml +31 -0
  224. package/dist/src/policy/policy-engine.js +4 -0
  225. package/dist/src/policy/policy-engine.js.map +1 -1
  226. package/dist/src/policy/toml-loader.d.ts +46 -0
  227. package/dist/src/policy/toml-loader.js +314 -0
  228. package/dist/src/policy/toml-loader.js.map +1 -0
  229. package/dist/src/policy/toml-loader.test.d.ts +6 -0
  230. package/dist/src/policy/toml-loader.test.js +522 -0
  231. package/dist/src/policy/toml-loader.test.js.map +1 -0
  232. package/dist/src/policy/types.d.ts +18 -0
  233. package/dist/src/policy/types.js +6 -0
  234. package/dist/src/policy/types.js.map +1 -1
  235. package/dist/src/prompts/prompt-registry.js +2 -1
  236. package/dist/src/prompts/prompt-registry.js.map +1 -1
  237. package/dist/src/routing/strategies/classifierStrategy.js +3 -2
  238. package/dist/src/routing/strategies/classifierStrategy.js.map +1 -1
  239. package/dist/src/services/chatCompressionService.d.ts +32 -0
  240. package/dist/src/services/chatCompressionService.js +162 -0
  241. package/dist/src/services/chatCompressionService.js.map +1 -0
  242. package/dist/src/services/chatCompressionService.test.d.ts +6 -0
  243. package/dist/src/services/chatCompressionService.test.js +209 -0
  244. package/dist/src/services/chatCompressionService.test.js.map +1 -0
  245. package/dist/src/services/chatRecordingService.js +9 -8
  246. package/dist/src/services/chatRecordingService.js.map +1 -1
  247. package/dist/src/services/fileDiscoveryService.d.ts +2 -14
  248. package/dist/src/services/fileDiscoveryService.js +19 -55
  249. package/dist/src/services/fileDiscoveryService.js.map +1 -1
  250. package/dist/src/services/fileDiscoveryService.test.js +91 -11
  251. package/dist/src/services/fileDiscoveryService.test.js.map +1 -1
  252. package/dist/src/services/loopDetectionService.d.ts +1 -1
  253. package/dist/src/services/loopDetectionService.js +27 -13
  254. package/dist/src/services/loopDetectionService.js.map +1 -1
  255. package/dist/src/services/loopDetectionService.test.js +119 -11
  256. package/dist/src/services/loopDetectionService.test.js.map +1 -1
  257. package/dist/src/services/shellExecutionService.js +50 -23
  258. package/dist/src/services/shellExecutionService.js.map +1 -1
  259. package/dist/src/services/shellExecutionService.test.js +82 -15
  260. package/dist/src/services/shellExecutionService.test.js.map +1 -1
  261. package/dist/src/telemetry/activity-monitor.d.ts +116 -0
  262. package/dist/src/telemetry/activity-monitor.js +209 -0
  263. package/dist/src/telemetry/activity-monitor.js.map +1 -0
  264. package/dist/src/telemetry/activity-monitor.test.d.ts +6 -0
  265. package/dist/src/telemetry/activity-monitor.test.js +248 -0
  266. package/dist/src/telemetry/activity-monitor.test.js.map +1 -0
  267. package/dist/src/telemetry/clearcut-logger/clearcut-logger.d.ts +5 -1
  268. package/dist/src/telemetry/clearcut-logger/clearcut-logger.js +135 -57
  269. package/dist/src/telemetry/clearcut-logger/clearcut-logger.js.map +1 -1
  270. package/dist/src/telemetry/clearcut-logger/clearcut-logger.test.js +43 -75
  271. package/dist/src/telemetry/clearcut-logger/clearcut-logger.test.js.map +1 -1
  272. package/dist/src/telemetry/clearcut-logger/event-metadata-key.d.ts +7 -1
  273. package/dist/src/telemetry/clearcut-logger/event-metadata-key.js +13 -1
  274. package/dist/src/telemetry/clearcut-logger/event-metadata-key.js.map +1 -1
  275. package/dist/src/telemetry/gcp-exporters.js +0 -1
  276. package/dist/src/telemetry/gcp-exporters.js.map +1 -1
  277. package/dist/src/telemetry/gcp-exporters.test.js +1 -1
  278. package/dist/src/telemetry/gcp-exporters.test.js.map +1 -1
  279. package/dist/src/telemetry/index.d.ts +4 -2
  280. package/dist/src/telemetry/index.js +5 -3
  281. package/dist/src/telemetry/index.js.map +1 -1
  282. package/dist/src/telemetry/loggers.d.ts +2 -1
  283. package/dist/src/telemetry/loggers.js +37 -26
  284. package/dist/src/telemetry/loggers.js.map +1 -1
  285. package/dist/src/telemetry/loggers.test.js +215 -56
  286. package/dist/src/telemetry/loggers.test.js.map +1 -1
  287. package/dist/src/telemetry/metrics.d.ts +55 -6
  288. package/dist/src/telemetry/metrics.js +89 -1
  289. package/dist/src/telemetry/metrics.js.map +1 -1
  290. package/dist/src/telemetry/metrics.test.js +172 -213
  291. package/dist/src/telemetry/metrics.test.js.map +1 -1
  292. package/dist/src/telemetry/sdk.js +3 -2
  293. package/dist/src/telemetry/sdk.js.map +1 -1
  294. package/dist/src/telemetry/semantic.d.ts +82 -0
  295. package/dist/src/telemetry/semantic.js +269 -0
  296. package/dist/src/telemetry/semantic.js.map +1 -0
  297. package/dist/src/telemetry/semantic.test.d.ts +6 -0
  298. package/dist/src/telemetry/semantic.test.js +387 -0
  299. package/dist/src/telemetry/semantic.test.js.map +1 -0
  300. package/dist/src/telemetry/telemetry-utils.test.js +29 -28
  301. package/dist/src/telemetry/telemetry-utils.test.js.map +1 -1
  302. package/dist/src/telemetry/trace.d.ts +46 -0
  303. package/dist/src/telemetry/trace.js +121 -0
  304. package/dist/src/telemetry/trace.js.map +1 -0
  305. package/dist/src/telemetry/types.d.ts +79 -34
  306. package/dist/src/telemetry/types.js +191 -61
  307. package/dist/src/telemetry/types.js.map +1 -1
  308. package/dist/src/telemetry/uiTelemetry.js +6 -6
  309. package/dist/src/telemetry/uiTelemetry.js.map +1 -1
  310. package/dist/src/telemetry/uiTelemetry.test.js +88 -66
  311. package/dist/src/telemetry/uiTelemetry.test.js.map +1 -1
  312. package/dist/src/tools/edit.d.ts +3 -2
  313. package/dist/src/tools/edit.js +24 -19
  314. package/dist/src/tools/edit.js.map +1 -1
  315. package/dist/src/tools/edit.test.js +78 -2
  316. package/dist/src/tools/edit.test.js.map +1 -1
  317. package/dist/src/tools/glob.d.ts +3 -2
  318. package/dist/src/tools/glob.js +15 -19
  319. package/dist/src/tools/glob.js.map +1 -1
  320. package/dist/src/tools/glob.test.js +203 -199
  321. package/dist/src/tools/glob.test.js.map +1 -1
  322. package/dist/src/tools/grep.d.ts +3 -2
  323. package/dist/src/tools/grep.js +22 -16
  324. package/dist/src/tools/grep.js.map +1 -1
  325. package/dist/src/tools/ls.d.ts +3 -2
  326. package/dist/src/tools/ls.js +15 -20
  327. package/dist/src/tools/ls.js.map +1 -1
  328. package/dist/src/tools/ls.test.js +2 -9
  329. package/dist/src/tools/ls.test.js.map +1 -1
  330. package/dist/src/tools/mcp-client-manager.d.ts +49 -11
  331. package/dist/src/tools/mcp-client-manager.js +209 -31
  332. package/dist/src/tools/mcp-client-manager.js.map +1 -1
  333. package/dist/src/tools/mcp-client-manager.test.js +132 -25
  334. package/dist/src/tools/mcp-client-manager.test.js.map +1 -1
  335. package/dist/src/tools/mcp-client.d.ts +5 -1
  336. package/dist/src/tools/mcp-client.js +85 -104
  337. package/dist/src/tools/mcp-client.js.map +1 -1
  338. package/dist/src/tools/mcp-client.test.js +65 -6
  339. package/dist/src/tools/mcp-client.test.js.map +1 -1
  340. package/dist/src/tools/mcp-tool.d.ts +5 -2
  341. package/dist/src/tools/mcp-tool.js +16 -8
  342. package/dist/src/tools/mcp-tool.js.map +1 -1
  343. package/dist/src/tools/memoryTool.d.ts +6 -4
  344. package/dist/src/tools/memoryTool.js +13 -10
  345. package/dist/src/tools/memoryTool.js.map +1 -1
  346. package/dist/src/tools/message-bus-integration.test.js +14 -1
  347. package/dist/src/tools/message-bus-integration.test.js.map +1 -1
  348. package/dist/src/tools/modifiable-tool.js +3 -2
  349. package/dist/src/tools/modifiable-tool.js.map +1 -1
  350. package/dist/src/tools/read-file.d.ts +4 -3
  351. package/dist/src/tools/read-file.js +16 -11
  352. package/dist/src/tools/read-file.js.map +1 -1
  353. package/dist/src/tools/read-file.test.js +25 -2
  354. package/dist/src/tools/read-file.test.js.map +1 -1
  355. package/dist/src/tools/read-many-files.d.ts +4 -3
  356. package/dist/src/tools/read-many-files.js +19 -37
  357. package/dist/src/tools/read-many-files.js.map +1 -1
  358. package/dist/src/tools/read-many-files.test.js +0 -1
  359. package/dist/src/tools/read-many-files.test.js.map +1 -1
  360. package/dist/src/tools/ripGrep.d.ts +3 -2
  361. package/dist/src/tools/ripGrep.js +47 -17
  362. package/dist/src/tools/ripGrep.js.map +1 -1
  363. package/dist/src/tools/ripGrep.test.js +106 -60
  364. package/dist/src/tools/ripGrep.test.js.map +1 -1
  365. package/dist/src/tools/shell.d.ts +7 -5
  366. package/dist/src/tools/shell.js +39 -68
  367. package/dist/src/tools/shell.js.map +1 -1
  368. package/dist/src/tools/shell.test.js +69 -9
  369. package/dist/src/tools/shell.test.js.map +1 -1
  370. package/dist/src/tools/smart-edit.d.ts +3 -2
  371. package/dist/src/tools/smart-edit.js +30 -18
  372. package/dist/src/tools/smart-edit.js.map +1 -1
  373. package/dist/src/tools/smart-edit.test.js +62 -2
  374. package/dist/src/tools/smart-edit.test.js.map +1 -1
  375. package/dist/src/tools/tool-names.d.ts +8 -0
  376. package/dist/src/tools/tool-names.js +8 -5
  377. package/dist/src/tools/tool-names.js.map +1 -1
  378. package/dist/src/tools/tool-registry.d.ts +6 -19
  379. package/dist/src/tools/tool-registry.js +14 -49
  380. package/dist/src/tools/tool-registry.js.map +1 -1
  381. package/dist/src/tools/tool-registry.test.js +2 -24
  382. package/dist/src/tools/tool-registry.test.js.map +1 -1
  383. package/dist/src/tools/tools.d.ts +22 -8
  384. package/dist/src/tools/tools.js +65 -36
  385. package/dist/src/tools/tools.js.map +1 -1
  386. package/dist/src/tools/web-fetch.d.ts +4 -3
  387. package/dist/src/tools/web-fetch.js +37 -25
  388. package/dist/src/tools/web-fetch.js.map +1 -1
  389. package/dist/src/tools/web-fetch.test.js +262 -1
  390. package/dist/src/tools/web-fetch.test.js.map +1 -1
  391. package/dist/src/tools/web-search.d.ts +4 -3
  392. package/dist/src/tools/web-search.js +8 -6
  393. package/dist/src/tools/web-search.js.map +1 -1
  394. package/dist/src/tools/write-file.d.ts +3 -2
  395. package/dist/src/tools/write-file.js +8 -8
  396. package/dist/src/tools/write-file.js.map +1 -1
  397. package/dist/src/tools/write-file.test.js +1 -2
  398. package/dist/src/tools/write-file.test.js.map +1 -1
  399. package/dist/src/tools/write-todos.d.ts +4 -8
  400. package/dist/src/tools/write-todos.js +15 -6
  401. package/dist/src/tools/write-todos.js.map +1 -1
  402. package/dist/src/tools/write-todos.test.js +2 -2
  403. package/dist/src/tools/write-todos.test.js.map +1 -1
  404. package/dist/src/utils/bfsFileSearch.js +3 -2
  405. package/dist/src/utils/bfsFileSearch.js.map +1 -1
  406. package/dist/src/utils/channel.d.ts +19 -0
  407. package/dist/src/utils/channel.js +49 -0
  408. package/dist/src/utils/channel.js.map +1 -0
  409. package/dist/src/utils/channel.test.d.ts +6 -0
  410. package/dist/src/utils/channel.test.js +170 -0
  411. package/dist/src/utils/channel.test.js.map +1 -0
  412. package/dist/src/utils/debugLogger.d.ts +25 -0
  413. package/dist/src/utils/debugLogger.js +33 -0
  414. package/dist/src/utils/debugLogger.js.map +1 -0
  415. package/dist/src/utils/debugLogger.test.d.ts +6 -0
  416. package/dist/src/utils/debugLogger.test.js +67 -0
  417. package/dist/src/utils/debugLogger.test.js.map +1 -0
  418. package/dist/src/utils/delay.d.ts +16 -0
  419. package/dist/src/utils/delay.js +43 -0
  420. package/dist/src/utils/delay.js.map +1 -0
  421. package/dist/src/utils/delay.test.d.ts +6 -0
  422. package/dist/src/utils/delay.test.js +88 -0
  423. package/dist/src/utils/delay.test.js.map +1 -0
  424. package/dist/src/utils/editCorrector.js +5 -9
  425. package/dist/src/utils/editCorrector.js.map +1 -1
  426. package/dist/src/utils/editCorrector.test.js +3 -5
  427. package/dist/src/utils/editCorrector.test.js.map +1 -1
  428. package/dist/src/utils/editor.js +33 -38
  429. package/dist/src/utils/editor.js.map +1 -1
  430. package/dist/src/utils/environmentContext.d.ts +2 -1
  431. package/dist/src/utils/environmentContext.js +18 -33
  432. package/dist/src/utils/environmentContext.js.map +1 -1
  433. package/dist/src/utils/environmentContext.test.js +0 -34
  434. package/dist/src/utils/environmentContext.test.js.map +1 -1
  435. package/dist/src/utils/errorParsing.d.ts +1 -1
  436. package/dist/src/utils/errorParsing.js +5 -33
  437. package/dist/src/utils/errorParsing.js.map +1 -1
  438. package/dist/src/utils/errorParsing.test.js +0 -88
  439. package/dist/src/utils/errorParsing.test.js.map +1 -1
  440. package/dist/src/utils/errors.d.ts +3 -0
  441. package/dist/src/utils/errors.js +6 -0
  442. package/dist/src/utils/errors.js.map +1 -1
  443. package/dist/src/utils/events.d.ts +88 -0
  444. package/dist/src/utils/events.js +77 -0
  445. package/dist/src/utils/events.js.map +1 -0
  446. package/dist/src/utils/events.test.d.ts +6 -0
  447. package/dist/src/utils/events.test.js +131 -0
  448. package/dist/src/utils/events.test.js.map +1 -0
  449. package/dist/src/utils/extensionLoader.d.ts +78 -0
  450. package/dist/src/utils/extensionLoader.js +162 -0
  451. package/dist/src/utils/extensionLoader.js.map +1 -0
  452. package/dist/src/utils/extensionLoader.test.d.ts +6 -0
  453. package/dist/src/utils/extensionLoader.test.js +90 -0
  454. package/dist/src/utils/extensionLoader.test.js.map +1 -0
  455. package/dist/src/utils/fetch.d.ts +1 -0
  456. package/dist/src/utils/fetch.js +4 -0
  457. package/dist/src/utils/fetch.js.map +1 -1
  458. package/dist/src/utils/fileUtils.d.ts +4 -0
  459. package/dist/src/utils/fileUtils.js +34 -2
  460. package/dist/src/utils/fileUtils.js.map +1 -1
  461. package/dist/src/utils/fileUtils.test.js +12 -1
  462. package/dist/src/utils/fileUtils.test.js.map +1 -1
  463. package/dist/src/utils/flashFallback.test.js +26 -45
  464. package/dist/src/utils/flashFallback.test.js.map +1 -1
  465. package/dist/src/utils/getFolderStructure.js +9 -17
  466. package/dist/src/utils/getFolderStructure.js.map +1 -1
  467. package/dist/src/utils/gitIgnoreParser.d.ts +4 -1
  468. package/dist/src/utils/gitIgnoreParser.js +28 -10
  469. package/dist/src/utils/gitIgnoreParser.js.map +1 -1
  470. package/dist/src/utils/gitIgnoreParser.test.js +58 -0
  471. package/dist/src/utils/gitIgnoreParser.test.js.map +1 -1
  472. package/dist/src/utils/googleErrors.d.ts +104 -0
  473. package/dist/src/utils/googleErrors.js +152 -0
  474. package/dist/src/utils/googleErrors.js.map +1 -0
  475. package/dist/src/utils/googleErrors.test.d.ts +6 -0
  476. package/dist/src/utils/googleErrors.test.js +301 -0
  477. package/dist/src/utils/googleErrors.test.js.map +1 -0
  478. package/dist/src/utils/googleQuotaErrors.d.ts +36 -0
  479. package/dist/src/utils/googleQuotaErrors.js +149 -0
  480. package/dist/src/utils/googleQuotaErrors.js.map +1 -0
  481. package/dist/src/utils/googleQuotaErrors.test.d.ts +6 -0
  482. package/dist/src/utils/googleQuotaErrors.test.js +311 -0
  483. package/dist/src/utils/googleQuotaErrors.test.js.map +1 -0
  484. package/dist/src/utils/ignorePatterns.test.js +26 -30
  485. package/dist/src/utils/ignorePatterns.test.js.map +1 -1
  486. package/dist/src/utils/installationManager.js +2 -1
  487. package/dist/src/utils/installationManager.js.map +1 -1
  488. package/dist/src/utils/installationManager.test.js +3 -3
  489. package/dist/src/utils/installationManager.test.js.map +1 -1
  490. package/dist/src/utils/llm-edit-fixer.d.ts +1 -1
  491. package/dist/src/utils/llm-edit-fixer.js +29 -4
  492. package/dist/src/utils/llm-edit-fixer.js.map +1 -1
  493. package/dist/src/utils/llm-edit-fixer.test.js +21 -0
  494. package/dist/src/utils/llm-edit-fixer.test.js.map +1 -1
  495. package/dist/src/utils/memoryDiscovery.d.ts +11 -1
  496. package/dist/src/utils/memoryDiscovery.js +150 -11
  497. package/dist/src/utils/memoryDiscovery.js.map +1 -1
  498. package/dist/src/utils/memoryDiscovery.test.js +157 -19
  499. package/dist/src/utils/memoryDiscovery.test.js.map +1 -1
  500. package/dist/src/utils/memoryImportProcessor.js +3 -2
  501. package/dist/src/utils/memoryImportProcessor.js.map +1 -1
  502. package/dist/src/utils/nextSpeakerChecker.js +2 -1
  503. package/dist/src/utils/nextSpeakerChecker.js.map +1 -1
  504. package/dist/src/utils/package.d.ts +12 -0
  505. package/dist/src/utils/package.js +15 -0
  506. package/dist/src/utils/package.js.map +1 -0
  507. package/dist/src/utils/paths.js +126 -26
  508. package/dist/src/utils/paths.js.map +1 -1
  509. package/dist/src/utils/paths.test.js +200 -68
  510. package/dist/src/utils/paths.test.js.map +1 -1
  511. package/dist/src/utils/quotaErrorDetection.d.ts +0 -2
  512. package/dist/src/utils/quotaErrorDetection.js +0 -46
  513. package/dist/src/utils/quotaErrorDetection.js.map +1 -1
  514. package/dist/src/utils/retry.d.ts +1 -0
  515. package/dist/src/utils/retry.js +57 -158
  516. package/dist/src/utils/retry.js.map +1 -1
  517. package/dist/src/utils/retry.test.js +48 -109
  518. package/dist/src/utils/retry.test.js.map +1 -1
  519. package/dist/src/utils/safeJsonStringify.d.ts +4 -4
  520. package/dist/src/utils/safeJsonStringify.js +31 -7
  521. package/dist/src/utils/safeJsonStringify.js.map +1 -1
  522. package/dist/src/utils/shell-utils.d.ts +14 -2
  523. package/dist/src/utils/shell-utils.js +381 -136
  524. package/dist/src/utils/shell-utils.js.map +1 -1
  525. package/dist/src/utils/shell-utils.test.js +242 -60
  526. package/dist/src/utils/shell-utils.test.js.map +1 -1
  527. package/dist/src/utils/summarizer.js +2 -1
  528. package/dist/src/utils/summarizer.js.map +1 -1
  529. package/dist/src/utils/summarizer.test.js +0 -1
  530. package/dist/src/utils/summarizer.test.js.map +1 -1
  531. package/dist/src/utils/systemEncoding.js +5 -4
  532. package/dist/src/utils/systemEncoding.js.map +1 -1
  533. package/dist/src/utils/tool-utils.d.ts +2 -2
  534. package/dist/src/utils/tool-utils.js +14 -5
  535. package/dist/src/utils/tool-utils.js.map +1 -1
  536. package/dist/src/utils/userAccountManager.js +5 -4
  537. package/dist/src/utils/userAccountManager.js.map +1 -1
  538. package/dist/src/utils/workspaceContext.js +3 -2
  539. package/dist/src/utils/workspaceContext.js.map +1 -1
  540. package/dist/src/utils/workspaceContext.test.js +2 -2
  541. package/dist/src/utils/workspaceContext.test.js.map +1 -1
  542. package/dist/tsconfig.tsbuildinfo +1 -1
  543. package/package.json +13 -5
  544. package/dist/src/core/subagent.d.ts +0 -236
  545. package/dist/src/core/subagent.js +0 -482
  546. package/dist/src/core/subagent.js.map +0 -1
  547. package/dist/src/core/subagent.test.js +0 -556
  548. package/dist/src/core/subagent.test.js.map +0 -1
  549. /package/dist/src/{core/subagent.test.d.ts → commands/extensions.test.d.ts} +0 -0
@@ -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
  });
@@ -228,7 +173,6 @@ describe('Gemini Client (client.ts)', () => {
228
173
  getVertexAI: vi.fn().mockReturnValue(false),
229
174
  getUserAgent: vi.fn().mockReturnValue('test-agent'),
230
175
  getUserMemory: vi.fn().mockReturnValue(''),
231
- getFullContext: vi.fn().mockReturnValue(false),
232
176
  getSessionId: vi.fn().mockReturnValue('test-session-id'),
233
177
  getProxy: vi.fn().mockReturnValue(undefined),
234
178
  getWorkingDir: vi.fn().mockReturnValue('/test/dir'),
@@ -270,6 +214,7 @@ describe('Gemini Client (client.ts)', () => {
270
214
  client = new GeminiClient(mockConfig);
271
215
  await client.initialize();
272
216
  vi.mocked(mockConfig.getGeminiClient).mockReturnValue(client);
217
+ vi.mocked(uiTelemetryService.setLastPromptTokenCount).mockClear();
273
218
  });
274
219
  afterEach(() => {
275
220
  vi.restoreAllMocks();
@@ -320,87 +265,64 @@ describe('Gemini Client (client.ts)', () => {
320
265
  getHistory: mockGetHistory,
321
266
  addHistory: vi.fn(),
322
267
  setHistory: vi.fn(),
268
+ getLastPromptTokenCount: vi.fn(),
323
269
  };
324
270
  });
325
271
  function setup({ chatHistory = [
326
272
  { role: 'user', parts: [{ text: 'Long conversation' }] },
327
273
  { role: 'model', parts: [{ text: 'Long response' }] },
328
- ], originalTokenCount = 1000, summaryText = 'This is a summary.', } = {}) {
274
+ ], originalTokenCount = 1000, newTokenCount = 500, compressionStatus = CompressionStatus.COMPRESSED, } = {}) {
329
275
  const mockOriginalChat = {
330
276
  getHistory: vi.fn((_curated) => chatHistory),
331
277
  setHistory: vi.fn(),
278
+ getLastPromptTokenCount: vi.fn().mockReturnValue(originalTokenCount),
332
279
  };
333
280
  client['chat'] = mockOriginalChat;
334
281
  vi.mocked(uiTelemetryService.getLastPromptTokenCount).mockReturnValue(originalTokenCount);
335
- mockGenerateContentFn.mockResolvedValue({
336
- candidates: [
337
- {
338
- content: {
339
- role: 'model',
340
- parts: [{ text: summaryText }],
341
- },
342
- },
343
- ],
344
- });
345
- // Calculate what the new history will be
346
- const splitPoint = findCompressSplitPoint(chatHistory, 0.7); // 1 - 0.3
347
- const historyToKeep = chatHistory.slice(splitPoint);
348
- // This is the history that the new chat will have.
349
- // It includes the default startChat history + the extra history from tryCompressChat
350
- const newCompressedHistory = [
351
- // Mocked envParts + canned response from startChat
352
- {
353
- role: 'user',
354
- parts: [{ text: 'Mocked env context' }],
355
- },
356
- {
357
- role: 'model',
358
- parts: [{ text: 'Got it. Thanks for the context!' }],
359
- },
360
- // extraHistory from tryCompressChat
361
- {
362
- role: 'user',
363
- parts: [{ text: summaryText }],
364
- },
365
- {
366
- role: 'model',
367
- parts: [{ text: 'Got it. Thanks for the additional context!' }],
368
- },
369
- ...historyToKeep,
282
+ const newHistory = [
283
+ { role: 'user', parts: [{ text: 'Summary' }] },
284
+ { role: 'model', parts: [{ text: 'Got it' }] },
370
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
+ });
371
296
  const mockNewChat = {
372
- getHistory: vi.fn().mockReturnValue(newCompressedHistory),
297
+ getHistory: vi.fn().mockReturnValue(newHistory),
373
298
  setHistory: vi.fn(),
299
+ getLastPromptTokenCount: vi.fn().mockReturnValue(newTokenCount),
374
300
  };
375
301
  client['startChat'] = vi
376
302
  .fn()
377
303
  .mockResolvedValue(mockNewChat);
378
- const totalChars = newCompressedHistory.reduce((total, content) => total + JSON.stringify(content).length, 0);
379
- const estimatedNewTokenCount = Math.floor(totalChars / 4);
380
304
  return {
381
305
  client,
382
306
  mockOriginalChat,
383
307
  mockNewChat,
384
- estimatedNewTokenCount,
308
+ estimatedNewTokenCount: newTokenCount,
385
309
  };
386
310
  }
387
311
  describe('when compression inflates the token count', () => {
388
312
  it('allows compression to be forced/manual after a failure', async () => {
389
- // Call 1 (Fails): Setup with a long summary to inflate tokens
390
- const longSummary = 'long summary '.repeat(100);
391
- const { client, estimatedNewTokenCount: inflatedTokenCount } = setup({
313
+ // Call 1 (Fails): Setup with inflated tokens
314
+ setup({
392
315
  originalTokenCount: 100,
393
- summaryText: longSummary,
316
+ newTokenCount: 200,
317
+ compressionStatus: CompressionStatus.COMPRESSION_FAILED_INFLATED_TOKEN_COUNT,
394
318
  });
395
- expect(inflatedTokenCount).toBeGreaterThan(100); // Ensure setup is correct
396
319
  await client.tryCompressChat('prompt-id-4', false); // Fails
397
- // Call 2 (Forced): Re-setup with a short summary
398
- const shortSummary = 'short';
320
+ // Call 2 (Forced): Re-setup with compressed tokens
399
321
  const { estimatedNewTokenCount: compressedTokenCount } = setup({
400
322
  originalTokenCount: 100,
401
- summaryText: shortSummary,
323
+ newTokenCount: 50,
324
+ compressionStatus: CompressionStatus.COMPRESSED,
402
325
  });
403
- expect(compressedTokenCount).toBeLessThanOrEqual(100); // Ensure setup is correct
404
326
  const result = await client.tryCompressChat('prompt-id-4', true); // Forced
405
327
  expect(result).toEqual({
406
328
  compressionStatus: CompressionStatus.COMPRESSED,
@@ -409,12 +331,11 @@ describe('Gemini Client (client.ts)', () => {
409
331
  });
410
332
  });
411
333
  it('yields the result even if the compression inflated the tokens', async () => {
412
- const longSummary = 'long summary '.repeat(100);
413
334
  const { client, estimatedNewTokenCount } = setup({
414
335
  originalTokenCount: 100,
415
- summaryText: longSummary,
336
+ newTokenCount: 200,
337
+ compressionStatus: CompressionStatus.COMPRESSION_FAILED_INFLATED_TOKEN_COUNT,
416
338
  });
417
- expect(estimatedNewTokenCount).toBeGreaterThan(100); // Ensure setup is correct
418
339
  const result = await client.tryCompressChat('prompt-id-4', false);
419
340
  expect(result).toEqual({
420
341
  compressionStatus: CompressionStatus.COMPRESSION_FAILED_INFLATED_TOKEN_COUNT,
@@ -425,47 +346,52 @@ describe('Gemini Client (client.ts)', () => {
425
346
  expect(uiTelemetryService.setLastPromptTokenCount).not.toHaveBeenCalled();
426
347
  });
427
348
  it('does not manipulate the source chat', async () => {
428
- const longSummary = 'long summary '.repeat(100);
429
- const { client, mockOriginalChat, estimatedNewTokenCount } = setup({
349
+ const { client, mockOriginalChat } = setup({
430
350
  originalTokenCount: 100,
431
- summaryText: longSummary,
351
+ newTokenCount: 200,
352
+ compressionStatus: CompressionStatus.COMPRESSION_FAILED_INFLATED_TOKEN_COUNT,
432
353
  });
433
- expect(estimatedNewTokenCount).toBeGreaterThan(100); // Ensure setup is correct
434
354
  await client.tryCompressChat('prompt-id-4', false);
435
355
  // On failure, the chat should NOT be replaced
436
356
  expect(client['chat']).toBe(mockOriginalChat);
437
357
  });
438
- it('will not attempt to compress context after a failure', async () => {
439
- const longSummary = 'long summary '.repeat(100);
440
- const { client, estimatedNewTokenCount } = setup({
358
+ it.skip('will not attempt to compress context after a failure', async () => {
359
+ const { client } = setup({
441
360
  originalTokenCount: 100,
442
- summaryText: longSummary,
361
+ newTokenCount: 200,
362
+ compressionStatus: CompressionStatus.COMPRESSION_FAILED_INFLATED_TOKEN_COUNT,
443
363
  });
444
- expect(estimatedNewTokenCount).toBeGreaterThan(100); // Ensure setup is correct
445
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
+ });
446
374
  // This call should now be a NOOP
447
375
  const result = await client.tryCompressChat('prompt-id-5', false);
448
- // generateContent (for summary) should only have been called once
449
- expect(mockGenerateContentFn).toHaveBeenCalledTimes(1);
450
- expect(result).toEqual({
451
- compressionStatus: CompressionStatus.NOOP,
452
- newTokenCount: 0,
453
- originalTokenCount: 0,
454
- });
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);
455
379
  });
456
380
  });
457
381
  it('should not trigger summarization if token count is below threshold', async () => {
458
382
  const MOCKED_TOKEN_LIMIT = 1000;
459
- vi.mocked(tokenLimit).mockReturnValue(MOCKED_TOKEN_LIMIT);
460
- mockGetHistory.mockReturnValue([
461
- { role: 'user', parts: [{ text: '...history...' }] },
462
- ]);
463
383
  const originalTokenCount = MOCKED_TOKEN_LIMIT * 0.699;
464
- 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
+ });
465
392
  const initialChat = client.getChat();
466
393
  const result = await client.tryCompressChat('prompt-id-2', false);
467
394
  const newChat = client.getChat();
468
- expect(tokenLimit).toHaveBeenCalled();
469
395
  expect(result).toEqual({
470
396
  compressionStatus: CompressionStatus.NOOP,
471
397
  newTokenCount: originalTokenCount,
@@ -477,6 +403,8 @@ describe('Gemini Client (client.ts)', () => {
477
403
  const { client } = setup({
478
404
  chatHistory: [{ role: 'user', parts: [{ text: 'hi' }] }],
479
405
  originalTokenCount: 50,
406
+ newTokenCount: 50,
407
+ compressionStatus: CompressionStatus.NOOP,
480
408
  });
481
409
  const result = await client.tryCompressChat('prompt-id-noop', false);
482
410
  expect(result).toEqual({
@@ -484,270 +412,6 @@ describe('Gemini Client (client.ts)', () => {
484
412
  originalTokenCount: 50,
485
413
  newTokenCount: 50,
486
414
  });
487
- expect(mockGenerateContentFn).not.toHaveBeenCalled();
488
- });
489
- it('logs a telemetry event when compressing', async () => {
490
- vi.spyOn(ClearcutLogger.prototype, 'logChatCompressionEvent');
491
- const MOCKED_TOKEN_LIMIT = 1000;
492
- const MOCKED_CONTEXT_PERCENTAGE_THRESHOLD = 0.5;
493
- vi.spyOn(client['config'], 'getChatCompression').mockReturnValue({
494
- contextPercentageThreshold: MOCKED_CONTEXT_PERCENTAGE_THRESHOLD,
495
- });
496
- const history = [
497
- { role: 'user', parts: [{ text: '...history...' }] },
498
- { role: 'model', parts: [{ text: '...history...' }] },
499
- { role: 'user', parts: [{ text: '...history...' }] },
500
- { role: 'model', parts: [{ text: '...history...' }] },
501
- { role: 'user', parts: [{ text: '...history...' }] },
502
- { role: 'model', parts: [{ text: '...history...' }] },
503
- ];
504
- mockGetHistory.mockReturnValue(history);
505
- const originalTokenCount = MOCKED_TOKEN_LIMIT * MOCKED_CONTEXT_PERCENTAGE_THRESHOLD;
506
- vi.mocked(uiTelemetryService.getLastPromptTokenCount).mockReturnValue(originalTokenCount);
507
- // We need to control the estimated new token count.
508
- // We mock startChat to return a chat with a known history.
509
- const summaryText = 'This is a summary.';
510
- const splitPoint = findCompressSplitPoint(history, 0.7);
511
- const historyToKeep = history.slice(splitPoint);
512
- const newCompressedHistory = [
513
- { role: 'user', parts: [{ text: 'Mocked env context' }] },
514
- { role: 'model', parts: [{ text: 'Got it. Thanks for the context!' }] },
515
- { role: 'user', parts: [{ text: summaryText }] },
516
- {
517
- role: 'model',
518
- parts: [{ text: 'Got it. Thanks for the additional context!' }],
519
- },
520
- ...historyToKeep,
521
- ];
522
- const mockNewChat = {
523
- getHistory: vi.fn().mockReturnValue(newCompressedHistory),
524
- };
525
- client['startChat'] = vi
526
- .fn()
527
- .mockResolvedValue(mockNewChat);
528
- const totalChars = newCompressedHistory.reduce((total, content) => total + JSON.stringify(content).length, 0);
529
- const newTokenCount = Math.floor(totalChars / 4);
530
- // Mock the summary response from the chat
531
- mockGenerateContentFn.mockResolvedValue({
532
- candidates: [
533
- {
534
- content: {
535
- role: 'model',
536
- parts: [{ text: summaryText }],
537
- },
538
- },
539
- ],
540
- });
541
- await client.tryCompressChat('prompt-id-3', false);
542
- expect(ClearcutLogger.prototype.logChatCompressionEvent).toHaveBeenCalledWith(expect.objectContaining({
543
- tokens_before: originalTokenCount,
544
- tokens_after: newTokenCount,
545
- }));
546
- expect(uiTelemetryService.setLastPromptTokenCount).toHaveBeenCalledWith(newTokenCount);
547
- expect(uiTelemetryService.setLastPromptTokenCount).toHaveBeenCalledTimes(1);
548
- });
549
- it('should trigger summarization if token count is at threshold with contextPercentageThreshold setting', async () => {
550
- const MOCKED_TOKEN_LIMIT = 1000;
551
- const MOCKED_CONTEXT_PERCENTAGE_THRESHOLD = 0.5;
552
- vi.mocked(tokenLimit).mockReturnValue(MOCKED_TOKEN_LIMIT);
553
- vi.spyOn(client['config'], 'getChatCompression').mockReturnValue({
554
- contextPercentageThreshold: MOCKED_CONTEXT_PERCENTAGE_THRESHOLD,
555
- });
556
- const history = [
557
- { role: 'user', parts: [{ text: '...history...' }] },
558
- { role: 'model', parts: [{ text: '...history...' }] },
559
- { role: 'user', parts: [{ text: '...history...' }] },
560
- { role: 'model', parts: [{ text: '...history...' }] },
561
- { role: 'user', parts: [{ text: '...history...' }] },
562
- { role: 'model', parts: [{ text: '...history...' }] },
563
- ];
564
- mockGetHistory.mockReturnValue(history);
565
- const originalTokenCount = MOCKED_TOKEN_LIMIT * MOCKED_CONTEXT_PERCENTAGE_THRESHOLD;
566
- vi.mocked(uiTelemetryService.getLastPromptTokenCount).mockReturnValue(originalTokenCount);
567
- // Mock summary and new chat
568
- const summaryText = 'This is a summary.';
569
- const splitPoint = findCompressSplitPoint(history, 0.7);
570
- const historyToKeep = history.slice(splitPoint);
571
- const newCompressedHistory = [
572
- { role: 'user', parts: [{ text: 'Mocked env context' }] },
573
- { role: 'model', parts: [{ text: 'Got it. Thanks for the context!' }] },
574
- { role: 'user', parts: [{ text: summaryText }] },
575
- {
576
- role: 'model',
577
- parts: [{ text: 'Got it. Thanks for the additional context!' }],
578
- },
579
- ...historyToKeep,
580
- ];
581
- const mockNewChat = {
582
- getHistory: vi.fn().mockReturnValue(newCompressedHistory),
583
- };
584
- client['startChat'] = vi
585
- .fn()
586
- .mockResolvedValue(mockNewChat);
587
- const totalChars = newCompressedHistory.reduce((total, content) => total + JSON.stringify(content).length, 0);
588
- const newTokenCount = Math.floor(totalChars / 4);
589
- // Mock the summary response from the chat
590
- mockGenerateContentFn.mockResolvedValue({
591
- candidates: [
592
- {
593
- content: {
594
- role: 'model',
595
- parts: [{ text: summaryText }],
596
- },
597
- },
598
- ],
599
- });
600
- const initialChat = client.getChat();
601
- const result = await client.tryCompressChat('prompt-id-3', false);
602
- const newChat = client.getChat();
603
- expect(tokenLimit).toHaveBeenCalled();
604
- expect(mockGenerateContentFn).toHaveBeenCalled();
605
- // Assert that summarization happened and returned the correct stats
606
- expect(result).toEqual({
607
- compressionStatus: CompressionStatus.COMPRESSED,
608
- originalTokenCount,
609
- newTokenCount,
610
- });
611
- // Assert that the chat was reset
612
- expect(newChat).not.toBe(initialChat);
613
- });
614
- it('should not compress across a function call response', async () => {
615
- const MOCKED_TOKEN_LIMIT = 1000;
616
- vi.mocked(tokenLimit).mockReturnValue(MOCKED_TOKEN_LIMIT);
617
- const history = [
618
- { role: 'user', parts: [{ text: '...history 1...' }] },
619
- { role: 'model', parts: [{ text: '...history 2...' }] },
620
- { role: 'user', parts: [{ text: '...history 3...' }] },
621
- { role: 'model', parts: [{ text: '...history 4...' }] },
622
- { role: 'user', parts: [{ text: '...history 5...' }] },
623
- { role: 'model', parts: [{ text: '...history 6...' }] },
624
- { role: 'user', parts: [{ text: '...history 7...' }] },
625
- { role: 'model', parts: [{ text: '...history 8...' }] },
626
- // Normally we would break here, but we have a function response.
627
- {
628
- role: 'user',
629
- parts: [{ functionResponse: { name: '...history 8...' } }],
630
- },
631
- { role: 'model', parts: [{ text: '...history 10...' }] },
632
- // Instead we will break here.
633
- { role: 'user', parts: [{ text: '...history 10...' }] },
634
- ];
635
- mockGetHistory.mockReturnValue(history);
636
- const originalTokenCount = 1000 * 0.7;
637
- vi.mocked(uiTelemetryService.getLastPromptTokenCount).mockReturnValue(originalTokenCount);
638
- // Mock summary and new chat
639
- const summaryText = 'This is a summary.';
640
- const splitPoint = findCompressSplitPoint(history, 0.7); // This should be 10
641
- expect(splitPoint).toBe(10); // Verify split point logic
642
- const historyToKeep = history.slice(splitPoint); // Should keep last user message
643
- expect(historyToKeep).toEqual([
644
- { role: 'user', parts: [{ text: '...history 10...' }] },
645
- ]);
646
- const newCompressedHistory = [
647
- { role: 'user', parts: [{ text: 'Mocked env context' }] },
648
- { role: 'model', parts: [{ text: 'Got it. Thanks for the context!' }] },
649
- { role: 'user', parts: [{ text: summaryText }] },
650
- {
651
- role: 'model',
652
- parts: [{ text: 'Got it. Thanks for the additional context!' }],
653
- },
654
- ...historyToKeep,
655
- ];
656
- const mockNewChat = {
657
- getHistory: vi.fn().mockReturnValue(newCompressedHistory),
658
- };
659
- client['startChat'] = vi
660
- .fn()
661
- .mockResolvedValue(mockNewChat);
662
- const totalChars = newCompressedHistory.reduce((total, content) => total + JSON.stringify(content).length, 0);
663
- const newTokenCount = Math.floor(totalChars / 4);
664
- // Mock the summary response from the chat
665
- mockGenerateContentFn.mockResolvedValue({
666
- candidates: [
667
- {
668
- content: {
669
- role: 'model',
670
- parts: [{ text: summaryText }],
671
- },
672
- },
673
- ],
674
- });
675
- const initialChat = client.getChat();
676
- const result = await client.tryCompressChat('prompt-id-3', false);
677
- const newChat = client.getChat();
678
- expect(tokenLimit).toHaveBeenCalled();
679
- expect(mockGenerateContentFn).toHaveBeenCalled();
680
- // Assert that summarization happened and returned the correct stats
681
- expect(result).toEqual({
682
- compressionStatus: CompressionStatus.COMPRESSED,
683
- originalTokenCount,
684
- newTokenCount,
685
- });
686
- // Assert that the chat was reset
687
- expect(newChat).not.toBe(initialChat);
688
- // 1. standard start context message (env)
689
- // 2. standard canned model response
690
- // 3. compressed summary message (user)
691
- // 4. standard canned model response
692
- // 5. The last user message (historyToKeep)
693
- expect(newChat.getHistory().length).toEqual(5);
694
- });
695
- it('should always trigger summarization when force is true, regardless of token count', async () => {
696
- const history = [
697
- { role: 'user', parts: [{ text: '...history...' }] },
698
- { role: 'model', parts: [{ text: '...history...' }] },
699
- { role: 'user', parts: [{ text: '...history...' }] },
700
- { role: 'model', parts: [{ text: '...history...' }] },
701
- { role: 'user', parts: [{ text: '...history...' }] },
702
- { role: 'model', parts: [{ text: '...history...' }] },
703
- ];
704
- mockGetHistory.mockReturnValue(history);
705
- const originalTokenCount = 100; // Well below threshold, but > estimated new count
706
- vi.mocked(uiTelemetryService.getLastPromptTokenCount).mockReturnValue(originalTokenCount);
707
- // Mock summary and new chat
708
- const summaryText = 'This is a summary.';
709
- const splitPoint = findCompressSplitPoint(history, 0.7);
710
- const historyToKeep = history.slice(splitPoint);
711
- const newCompressedHistory = [
712
- { role: 'user', parts: [{ text: 'Mocked env context' }] },
713
- { role: 'model', parts: [{ text: 'Got it. Thanks for the context!' }] },
714
- { role: 'user', parts: [{ text: summaryText }] },
715
- {
716
- role: 'model',
717
- parts: [{ text: 'Got it. Thanks for the additional context!' }],
718
- },
719
- ...historyToKeep,
720
- ];
721
- const mockNewChat = {
722
- getHistory: vi.fn().mockReturnValue(newCompressedHistory),
723
- };
724
- client['startChat'] = vi
725
- .fn()
726
- .mockResolvedValue(mockNewChat);
727
- const totalChars = newCompressedHistory.reduce((total, content) => total + JSON.stringify(content).length, 0);
728
- const newTokenCount = Math.floor(totalChars / 4);
729
- // Mock the summary response from the chat
730
- mockGenerateContentFn.mockResolvedValue({
731
- candidates: [
732
- {
733
- content: {
734
- role: 'model',
735
- parts: [{ text: summaryText }],
736
- },
737
- },
738
- ],
739
- });
740
- const initialChat = client.getChat();
741
- const result = await client.tryCompressChat('prompt-id-1', true); // force = true
742
- const newChat = client.getChat();
743
- expect(mockGenerateContentFn).toHaveBeenCalled();
744
- expect(result).toEqual({
745
- compressionStatus: CompressionStatus.COMPRESSED,
746
- originalTokenCount,
747
- newTokenCount,
748
- });
749
- // Assert that the chat was reset
750
- expect(newChat).not.toBe(initialChat);
751
415
  });
752
416
  });
753
417
  describe('sendMessageStream', () => {
@@ -832,6 +496,7 @@ describe('Gemini Client (client.ts)', () => {
832
496
  const mockChat = {
833
497
  addHistory: vi.fn(),
834
498
  getHistory: vi.fn().mockReturnValue([]),
499
+ getLastPromptTokenCount: vi.fn(),
835
500
  };
836
501
  client['chat'] = mockChat;
837
502
  const initialRequest = [{ text: 'Hi' }];
@@ -879,6 +544,7 @@ ${JSON.stringify({
879
544
  const mockChat = {
880
545
  addHistory: vi.fn(),
881
546
  getHistory: vi.fn().mockReturnValue([]),
547
+ getLastPromptTokenCount: vi.fn(),
882
548
  };
883
549
  client['chat'] = mockChat;
884
550
  const initialRequest = [{ text: 'Hi' }];
@@ -923,6 +589,7 @@ ${JSON.stringify({
923
589
  const mockChat = {
924
590
  addHistory: vi.fn(),
925
591
  getHistory: vi.fn().mockReturnValue([]),
592
+ getLastPromptTokenCount: vi.fn(),
926
593
  };
927
594
  client['chat'] = mockChat;
928
595
  const initialRequest = [{ text: 'Hi' }];
@@ -983,6 +650,7 @@ ${JSON.stringify({
983
650
  const mockChat = {
984
651
  addHistory: vi.fn(),
985
652
  getHistory: vi.fn().mockReturnValue([]),
653
+ getLastPromptTokenCount: vi.fn(),
986
654
  };
987
655
  client['chat'] = mockChat;
988
656
  const initialRequest = [{ text: 'Hi' }];
@@ -1016,6 +684,7 @@ ${JSON.stringify({
1016
684
  const mockChat = {
1017
685
  addHistory: vi.fn(),
1018
686
  getHistory: vi.fn().mockReturnValue([]),
687
+ getLastPromptTokenCount: vi.fn(),
1019
688
  };
1020
689
  client['chat'] = mockChat;
1021
690
  // Act
@@ -1049,6 +718,7 @@ ${JSON.stringify({
1049
718
  const mockChat = {
1050
719
  addHistory: vi.fn(),
1051
720
  getHistory: vi.fn().mockReturnValue([]),
721
+ getLastPromptTokenCount: vi.fn(),
1052
722
  };
1053
723
  client['chat'] = mockChat;
1054
724
  // Use a signal that never gets aborted
@@ -1075,27 +745,10 @@ ${JSON.stringify({
1075
745
  }
1076
746
  // Assert
1077
747
  expect(finalResult).toBeInstanceOf(Turn);
1078
- // Debug: Check how many times checkNextSpeaker was called
1079
- const callCount = mockCheckNextSpeaker.mock.calls.length;
1080
748
  // If infinite loop protection is working, checkNextSpeaker should be called many times
1081
749
  // but stop at MAX_TURNS (100). Since each recursive call should trigger checkNextSpeaker,
1082
750
  // we expect it to be called multiple times before hitting the limit
1083
751
  expect(mockCheckNextSpeaker).toHaveBeenCalled();
1084
- // The test should demonstrate that the infinite loop protection works:
1085
- // - If checkNextSpeaker is called many times (close to MAX_TURNS), it shows the loop was happening
1086
- // - If it's only called once, the recursive behavior might not be triggered
1087
- if (callCount === 0) {
1088
- throw new Error('checkNextSpeaker was never called - the recursive condition was not met');
1089
- }
1090
- else if (callCount === 1) {
1091
- // This might be expected behavior if the turn has pending tool calls or other conditions prevent recursion
1092
- console.log('checkNextSpeaker called only once - no infinite loop occurred');
1093
- }
1094
- else {
1095
- console.log(`checkNextSpeaker called ${callCount} times - infinite loop protection worked`);
1096
- // If called multiple times, we expect it to be stopped before MAX_TURNS
1097
- expect(callCount).toBeLessThanOrEqual(100); // Should not exceed MAX_TURNS
1098
- }
1099
752
  // The stream should produce events and eventually terminate
1100
753
  expect(eventCount).toBeGreaterThanOrEqual(1);
1101
754
  expect(eventCount).toBeLessThan(200); // Should not exceed our safety limit
@@ -1111,6 +764,7 @@ ${JSON.stringify({
1111
764
  const mockChat = {
1112
765
  addHistory: vi.fn(),
1113
766
  getHistory: vi.fn().mockReturnValue([]),
767
+ getLastPromptTokenCount: vi.fn(),
1114
768
  };
1115
769
  client['chat'] = mockChat;
1116
770
  // Act & Assert
@@ -1149,6 +803,7 @@ ${JSON.stringify({
1149
803
  const mockChat = {
1150
804
  addHistory: vi.fn(),
1151
805
  getHistory: vi.fn().mockReturnValue([]),
806
+ getLastPromptTokenCount: vi.fn(),
1152
807
  };
1153
808
  client['chat'] = mockChat;
1154
809
  // Use a signal that never gets aborted
@@ -1176,9 +831,8 @@ ${JSON.stringify({
1176
831
  }
1177
832
  }
1178
833
  }
1179
- catch (error) {
834
+ catch (_) {
1180
835
  // If the test framework times out, that also demonstrates the infinite loop
1181
- console.error('Test timed out or errored:', error);
1182
836
  }
1183
837
  // Assert that the fix works - the loop should stop at MAX_TURNS
1184
838
  const callCount = mockCheckNextSpeaker.mock.calls.length;
@@ -1186,8 +840,6 @@ ${JSON.stringify({
1186
840
  // the loop should stop at MAX_TURNS (100)
1187
841
  expect(callCount).toBeLessThanOrEqual(100); // Should not exceed MAX_TURNS
1188
842
  expect(eventCount).toBeLessThanOrEqual(200); // Should have reasonable number of events
1189
- console.log(`Infinite loop protection working: checkNextSpeaker called ${callCount} times, ` +
1190
- `${eventCount} events generated (properly bounded by MAX_TURNS)`);
1191
843
  });
1192
844
  it('should yield ContextWindowWillOverflow when the context window is about to overflow', async () => {
1193
845
  // Arrange
@@ -1195,7 +847,11 @@ ${JSON.stringify({
1195
847
  vi.mocked(tokenLimit).mockReturnValue(MOCKED_TOKEN_LIMIT);
1196
848
  // Set last prompt token count
1197
849
  const lastPromptTokenCount = 900;
1198
- 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;
1199
855
  // Remaining = 100. Threshold (95%) = 95.
1200
856
  // We need a request > 95 tokens.
1201
857
  // A string of length 400 is roughly 100 tokens.
@@ -1238,7 +894,11 @@ ${JSON.stringify({
1238
894
  client['currentSequenceModel'] = STICKY_MODEL;
1239
895
  // Set token count
1240
896
  const lastPromptTokenCount = 900;
1241
- 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;
1242
902
  // Remaining (sticky) = 100. Threshold (95%) = 95.
1243
903
  // We need a request > 95 tokens.
1244
904
  const longText = 'a'.repeat(400);
@@ -1277,6 +937,12 @@ ${JSON.stringify({
1277
937
  mockTurnRunFn.mockReturnValue((async function* () {
1278
938
  yield { type: 'content', value: 'Hello' };
1279
939
  })());
940
+ const mockChat = {
941
+ addHistory: vi.fn(),
942
+ getHistory: vi.fn().mockReturnValue([]),
943
+ getLastPromptTokenCount: vi.fn(),
944
+ };
945
+ client['chat'] = mockChat;
1280
946
  });
1281
947
  it('should use the model router service to select a model on the first turn', async () => {
1282
948
  const stream = client.sendMessageStream([{ text: 'Hi' }], new AbortController().signal, 'prompt-1');
@@ -1365,6 +1031,7 @@ ${JSON.stringify({
1365
1031
  const mockChat = {
1366
1032
  addHistory: vi.fn(),
1367
1033
  getHistory: vi.fn().mockReturnValue([]),
1034
+ getLastPromptTokenCount: vi.fn(),
1368
1035
  };
1369
1036
  client['chat'] = mockChat;
1370
1037
  const initialRequest = [{ text: 'Hi' }];
@@ -1395,6 +1062,7 @@ ${JSON.stringify({
1395
1062
  const mockChat = {
1396
1063
  addHistory: vi.fn(),
1397
1064
  getHistory: vi.fn().mockReturnValue([]),
1065
+ getLastPromptTokenCount: vi.fn(),
1398
1066
  };
1399
1067
  client['chat'] = mockChat;
1400
1068
  const initialRequest = [{ text: 'Hi' }];
@@ -1418,6 +1086,7 @@ ${JSON.stringify({
1418
1086
  const mockChat = {
1419
1087
  addHistory: vi.fn(),
1420
1088
  getHistory: vi.fn().mockReturnValue([]),
1089
+ getLastPromptTokenCount: vi.fn(),
1421
1090
  };
1422
1091
  client['chat'] = mockChat;
1423
1092
  const initialRequest = [{ text: 'Hi' }];
@@ -1455,6 +1124,7 @@ ${JSON.stringify({
1455
1124
  .mockReturnValue([
1456
1125
  { role: 'user', parts: [{ text: 'previous message' }] },
1457
1126
  ]),
1127
+ getLastPromptTokenCount: vi.fn(),
1458
1128
  };
1459
1129
  client['chat'] = mockChat;
1460
1130
  });
@@ -1575,7 +1245,11 @@ ${JSON.stringify({
1575
1245
  vi.mocked(ideContextStore.get).mockReturnValue({
1576
1246
  workspaceState: {
1577
1247
  openFiles: [
1578
- { ...currentActiveFile, isActive: true, timestamp: Date.now() },
1248
+ {
1249
+ ...currentActiveFile,
1250
+ isActive: true,
1251
+ timestamp: Date.now(),
1252
+ },
1579
1253
  ],
1580
1254
  },
1581
1255
  });
@@ -1663,6 +1337,7 @@ ${JSON.stringify({
1663
1337
  addHistory: vi.fn(),
1664
1338
  getHistory: vi.fn().mockReturnValue([]), // Default empty history
1665
1339
  setHistory: vi.fn(),
1340
+ getLastPromptTokenCount: vi.fn(),
1666
1341
  };
1667
1342
  client['chat'] = mockChat;
1668
1343
  vi.spyOn(client['config'], 'getIdeMode').mockReturnValue(true);
@@ -1926,6 +1601,7 @@ ${JSON.stringify({
1926
1601
  const mockChat = {
1927
1602
  addHistory: vi.fn(),
1928
1603
  getHistory: vi.fn().mockReturnValue([]),
1604
+ getLastPromptTokenCount: vi.fn(),
1929
1605
  };
1930
1606
  client['chat'] = mockChat;
1931
1607
  // Act
@@ -1951,6 +1627,7 @@ ${JSON.stringify({
1951
1627
  const mockChat = {
1952
1628
  addHistory: vi.fn(),
1953
1629
  getHistory: vi.fn().mockReturnValue([]),
1630
+ getLastPromptTokenCount: vi.fn(),
1954
1631
  };
1955
1632
  client['chat'] = mockChat;
1956
1633
  // Act
@@ -1978,6 +1655,7 @@ ${JSON.stringify({
1978
1655
  const mockChat = {
1979
1656
  addHistory: vi.fn(),
1980
1657
  getHistory: vi.fn().mockReturnValue([]),
1658
+ getLastPromptTokenCount: vi.fn(),
1981
1659
  };
1982
1660
  client['chat'] = mockChat;
1983
1661
  // Act