@office-ai/aioncli-core 0.2.3 → 0.18.4

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 (1051) hide show
  1. package/dist/index.d.ts +16 -3
  2. package/dist/index.js +15 -3
  3. package/dist/index.js.map +1 -1
  4. package/dist/src/agents/codebase-investigator.d.ts +46 -0
  5. package/dist/src/agents/codebase-investigator.js +132 -0
  6. package/dist/src/agents/codebase-investigator.js.map +1 -0
  7. package/dist/src/agents/codebase-investigator.test.js +35 -0
  8. package/dist/src/agents/codebase-investigator.test.js.map +1 -0
  9. package/dist/src/agents/executor.d.ts +114 -0
  10. package/dist/src/agents/executor.js +779 -0
  11. package/dist/src/agents/executor.js.map +1 -0
  12. package/dist/src/agents/executor.test.js +1362 -0
  13. package/dist/src/agents/executor.test.js.map +1 -0
  14. package/dist/src/agents/invocation.d.ts +46 -0
  15. package/dist/src/agents/invocation.js +102 -0
  16. package/dist/src/agents/invocation.js.map +1 -0
  17. package/dist/src/agents/invocation.test.js +215 -0
  18. package/dist/src/agents/invocation.test.js.map +1 -0
  19. package/dist/src/agents/registry.d.ts +40 -0
  20. package/dist/src/agents/registry.js +105 -0
  21. package/dist/src/agents/registry.js.map +1 -0
  22. package/dist/src/agents/registry.test.d.ts +6 -0
  23. package/dist/src/agents/registry.test.js +160 -0
  24. package/dist/src/agents/registry.test.js.map +1 -0
  25. package/dist/src/agents/schema-utils.d.ts +39 -0
  26. package/dist/src/agents/schema-utils.js +57 -0
  27. package/dist/src/agents/schema-utils.js.map +1 -0
  28. package/dist/src/agents/schema-utils.test.d.ts +6 -0
  29. package/dist/src/agents/schema-utils.test.js +144 -0
  30. package/dist/src/agents/schema-utils.test.js.map +1 -0
  31. package/dist/src/agents/subagent-tool-wrapper.d.ts +38 -0
  32. package/dist/src/agents/subagent-tool-wrapper.js +48 -0
  33. package/dist/src/agents/subagent-tool-wrapper.js.map +1 -0
  34. package/dist/src/agents/subagent-tool-wrapper.test.d.ts +6 -0
  35. package/dist/src/agents/subagent-tool-wrapper.test.js +110 -0
  36. package/dist/src/agents/subagent-tool-wrapper.test.js.map +1 -0
  37. package/dist/src/agents/types.d.ts +146 -0
  38. package/dist/src/agents/types.js +19 -0
  39. package/dist/src/agents/types.js.map +1 -0
  40. package/dist/src/agents/utils.d.ts +15 -0
  41. package/dist/src/agents/utils.js +29 -0
  42. package/dist/src/agents/utils.js.map +1 -0
  43. package/dist/src/agents/utils.test.d.ts +6 -0
  44. package/dist/src/agents/utils.test.js +87 -0
  45. package/dist/src/agents/utils.test.js.map +1 -0
  46. package/dist/src/code_assist/codeAssist.d.ts +6 -3
  47. package/dist/src/code_assist/codeAssist.js +13 -1
  48. package/dist/src/code_assist/codeAssist.js.map +1 -1
  49. package/dist/src/code_assist/codeAssist.test.d.ts +6 -0
  50. package/dist/src/code_assist/codeAssist.test.js +99 -0
  51. package/dist/src/code_assist/codeAssist.test.js.map +1 -0
  52. package/dist/src/code_assist/converter.d.ts +5 -1
  53. package/dist/src/code_assist/converter.js +39 -5
  54. package/dist/src/code_assist/converter.js.map +1 -1
  55. package/dist/src/code_assist/converter.test.js +112 -0
  56. package/dist/src/code_assist/converter.test.js.map +1 -1
  57. package/dist/src/code_assist/experiments/client_metadata.d.ts +12 -0
  58. package/dist/src/code_assist/experiments/client_metadata.js +50 -0
  59. package/dist/src/code_assist/experiments/client_metadata.js.map +1 -0
  60. package/dist/src/code_assist/experiments/client_metadata.test.d.ts +6 -0
  61. package/dist/src/code_assist/experiments/client_metadata.test.js +99 -0
  62. package/dist/src/code_assist/experiments/client_metadata.test.js.map +1 -0
  63. package/dist/src/code_assist/experiments/experiments.d.ts +17 -0
  64. package/dist/src/code_assist/experiments/experiments.js +36 -0
  65. package/dist/src/code_assist/experiments/experiments.js.map +1 -0
  66. package/dist/src/code_assist/experiments/experiments.test.d.ts +6 -0
  67. package/dist/src/code_assist/experiments/experiments.test.js +92 -0
  68. package/dist/src/code_assist/experiments/experiments.test.js.map +1 -0
  69. package/dist/src/code_assist/experiments/flagNames.d.ts +13 -0
  70. package/dist/src/code_assist/experiments/flagNames.js +13 -0
  71. package/dist/src/code_assist/experiments/flagNames.js.map +1 -0
  72. package/dist/src/code_assist/experiments/types.d.ts +35 -0
  73. package/dist/src/code_assist/experiments/types.js +7 -0
  74. package/dist/src/code_assist/experiments/types.js.map +1 -0
  75. package/dist/src/code_assist/oauth-credential-storage.d.ts +25 -0
  76. package/dist/src/code_assist/oauth-credential-storage.js +110 -0
  77. package/dist/src/code_assist/oauth-credential-storage.js.map +1 -0
  78. package/dist/src/code_assist/oauth-credential-storage.test.d.ts +6 -0
  79. package/dist/src/code_assist/oauth-credential-storage.test.js +198 -0
  80. package/dist/src/code_assist/oauth-credential-storage.test.js.map +1 -0
  81. package/dist/src/code_assist/oauth2.d.ts +3 -3
  82. package/dist/src/code_assist/oauth2.js +252 -125
  83. package/dist/src/code_assist/oauth2.js.map +1 -1
  84. package/dist/src/code_assist/oauth2.test.js +788 -350
  85. package/dist/src/code_assist/oauth2.test.js.map +1 -1
  86. package/dist/src/code_assist/server.d.ts +8 -6
  87. package/dist/src/code_assist/server.js +41 -10
  88. package/dist/src/code_assist/server.js.map +1 -1
  89. package/dist/src/code_assist/server.test.js +151 -28
  90. package/dist/src/code_assist/server.test.js.map +1 -1
  91. package/dist/src/code_assist/setup.d.ts +2 -2
  92. package/dist/src/code_assist/setup.js +5 -3
  93. package/dist/src/code_assist/setup.js.map +1 -1
  94. package/dist/src/code_assist/setup.test.js.map +1 -1
  95. package/dist/src/code_assist/types.d.ts +18 -3
  96. package/dist/src/code_assist/types.js.map +1 -1
  97. package/dist/src/commands/extensions.d.ts +7 -0
  98. package/dist/src/commands/extensions.js +9 -0
  99. package/dist/src/commands/extensions.js.map +1 -0
  100. package/dist/src/commands/extensions.test.d.ts +6 -0
  101. package/dist/src/commands/extensions.test.js +19 -0
  102. package/dist/src/commands/extensions.test.js.map +1 -0
  103. package/dist/src/config/config.d.ts +282 -60
  104. package/dist/src/config/config.js +677 -129
  105. package/dist/src/config/config.js.map +1 -1
  106. package/dist/src/config/config.test.js +1020 -146
  107. package/dist/src/config/config.test.js.map +1 -1
  108. package/dist/src/config/constants.d.ts +11 -0
  109. package/dist/src/config/constants.js +16 -0
  110. package/dist/src/config/constants.js.map +1 -0
  111. package/dist/src/config/defaultModelConfigs.d.ts +7 -0
  112. package/dist/src/config/defaultModelConfigs.js +185 -0
  113. package/dist/src/config/defaultModelConfigs.js.map +1 -0
  114. package/dist/src/config/models.d.ts +37 -0
  115. package/dist/src/config/models.js +72 -0
  116. package/dist/src/config/models.js.map +1 -1
  117. package/dist/src/config/models.test.d.ts +6 -0
  118. package/dist/src/config/models.test.js +116 -0
  119. package/dist/src/config/models.test.js.map +1 -0
  120. package/dist/src/config/storage.d.ts +36 -0
  121. package/dist/src/config/storage.js +115 -0
  122. package/dist/src/config/storage.js.map +1 -0
  123. package/dist/src/config/storage.test.d.ts +6 -0
  124. package/dist/src/config/storage.test.js +48 -0
  125. package/dist/src/config/storage.test.js.map +1 -0
  126. package/dist/src/confirmation-bus/index.d.ts +7 -0
  127. package/dist/src/confirmation-bus/index.js +8 -0
  128. package/dist/src/confirmation-bus/index.js.map +1 -0
  129. package/dist/src/confirmation-bus/message-bus.d.ts +18 -0
  130. package/dist/src/confirmation-bus/message-bus.js +87 -0
  131. package/dist/src/confirmation-bus/message-bus.js.map +1 -0
  132. package/dist/src/confirmation-bus/message-bus.test.d.ts +6 -0
  133. package/dist/src/confirmation-bus/message-bus.test.js +170 -0
  134. package/dist/src/confirmation-bus/message-bus.test.js.map +1 -0
  135. package/dist/src/confirmation-bus/types.d.ts +49 -0
  136. package/dist/src/confirmation-bus/types.js +16 -0
  137. package/dist/src/confirmation-bus/types.js.map +1 -0
  138. package/dist/src/core/apiKeyCredentialStorage.d.ts +17 -0
  139. package/dist/src/core/apiKeyCredentialStorage.js +64 -0
  140. package/dist/src/core/apiKeyCredentialStorage.js.map +1 -0
  141. package/dist/src/core/apiKeyCredentialStorage.test.d.ts +6 -0
  142. package/dist/src/core/apiKeyCredentialStorage.test.js +71 -0
  143. package/dist/src/core/apiKeyCredentialStorage.test.js.map +1 -0
  144. package/dist/src/core/baseLlmClient.d.ts +50 -0
  145. package/dist/src/core/baseLlmClient.js +185 -0
  146. package/dist/src/core/baseLlmClient.js.map +1 -0
  147. package/dist/src/core/baseLlmClient.test.d.ts +6 -0
  148. package/dist/src/core/baseLlmClient.test.js +311 -0
  149. package/dist/src/core/baseLlmClient.test.js.map +1 -0
  150. package/dist/src/core/client.d.ts +30 -42
  151. package/dist/src/core/client.js +178 -477
  152. package/dist/src/core/client.js.map +1 -1
  153. package/dist/src/core/client.test.js +739 -617
  154. package/dist/src/core/client.test.js.map +1 -1
  155. package/dist/src/core/contentGenerator.d.ts +7 -6
  156. package/dist/src/core/contentGenerator.js +59 -45
  157. package/dist/src/core/contentGenerator.js.map +1 -1
  158. package/dist/src/core/contentGenerator.test.js +50 -4
  159. package/dist/src/core/contentGenerator.test.js.map +1 -1
  160. package/dist/src/core/coreToolScheduler.d.ts +28 -11
  161. package/dist/src/core/coreToolScheduler.js +493 -161
  162. package/dist/src/core/coreToolScheduler.js.map +1 -1
  163. package/dist/src/core/coreToolScheduler.test.js +995 -163
  164. package/dist/src/core/coreToolScheduler.test.js.map +1 -1
  165. package/dist/src/core/fakeContentGenerator.d.ts +33 -0
  166. package/dist/src/core/fakeContentGenerator.js +58 -0
  167. package/dist/src/core/fakeContentGenerator.js.map +1 -0
  168. package/dist/src/core/fakeContentGenerator.test.d.ts +6 -0
  169. package/dist/src/core/fakeContentGenerator.test.js +127 -0
  170. package/dist/src/core/fakeContentGenerator.test.js.map +1 -0
  171. package/dist/src/core/geminiChat.d.ts +61 -55
  172. package/dist/src/core/geminiChat.js +388 -366
  173. package/dist/src/core/geminiChat.js.map +1 -1
  174. package/dist/src/core/geminiChat.test.js +1600 -355
  175. package/dist/src/core/geminiChat.test.js.map +1 -1
  176. package/dist/src/core/geminiRequest.js +1 -0
  177. package/dist/src/core/geminiRequest.js.map +1 -1
  178. package/dist/src/core/logger.d.ts +11 -4
  179. package/dist/src/core/logger.js +39 -30
  180. package/dist/src/core/logger.js.map +1 -1
  181. package/dist/src/core/logger.test.js +62 -45
  182. package/dist/src/core/logger.test.js.map +1 -1
  183. package/dist/src/core/loggingContentGenerator.d.ts +4 -3
  184. package/dist/src/core/loggingContentGenerator.js +116 -37
  185. package/dist/src/core/loggingContentGenerator.js.map +1 -1
  186. package/dist/src/core/loggingContentGenerator.test.d.ts +6 -0
  187. package/dist/src/core/loggingContentGenerator.test.js +180 -0
  188. package/dist/src/core/loggingContentGenerator.test.js.map +1 -0
  189. package/dist/src/core/nonInteractiveToolExecutor.d.ts +4 -5
  190. package/dist/src/core/nonInteractiveToolExecutor.js +17 -120
  191. package/dist/src/core/nonInteractiveToolExecutor.js.map +1 -1
  192. package/dist/src/core/nonInteractiveToolExecutor.test.js +168 -84
  193. package/dist/src/core/nonInteractiveToolExecutor.test.js.map +1 -1
  194. package/dist/src/core/openaiContentGenerator.d.ts +4 -3
  195. package/dist/src/core/openaiContentGenerator.js +49 -19
  196. package/dist/src/core/openaiContentGenerator.js.map +1 -1
  197. package/dist/src/core/openaiContentGenerator.test.js +1 -0
  198. package/dist/src/core/openaiContentGenerator.test.js.map +1 -1
  199. package/dist/src/core/prompts.d.ts +7 -1
  200. package/dist/src/core/prompts.js +198 -195
  201. package/dist/src/core/prompts.js.map +1 -1
  202. package/dist/src/core/prompts.test.js +172 -104
  203. package/dist/src/core/prompts.test.js.map +1 -1
  204. package/dist/src/core/recordingContentGenerator.d.ts +18 -0
  205. package/dist/src/core/recordingContentGenerator.js +77 -0
  206. package/dist/src/core/recordingContentGenerator.js.map +1 -0
  207. package/dist/src/core/recordingContentGenerator.test.d.ts +6 -0
  208. package/dist/src/core/recordingContentGenerator.test.js +101 -0
  209. package/dist/src/core/recordingContentGenerator.test.js.map +1 -0
  210. package/dist/src/core/subagent.d.ts +24 -18
  211. package/dist/src/core/subagent.js +125 -90
  212. package/dist/src/core/subagent.js.map +1 -1
  213. package/dist/src/core/subagent.test.js +59 -44
  214. package/dist/src/core/subagent.test.js.map +1 -1
  215. package/dist/src/core/tokenLimits.test.d.ts +6 -0
  216. package/dist/src/core/tokenLimits.test.js +26 -0
  217. package/dist/src/core/tokenLimits.test.js.map +1 -0
  218. package/dist/src/core/turn.d.ts +57 -13
  219. package/dist/src/core/turn.js +79 -35
  220. package/dist/src/core/turn.js.map +1 -1
  221. package/dist/src/core/turn.test.js +373 -120
  222. package/dist/src/core/turn.test.js.map +1 -1
  223. package/dist/src/fallback/handler.d.ts +7 -0
  224. package/dist/src/fallback/handler.js +181 -0
  225. package/dist/src/fallback/handler.js.map +1 -0
  226. package/dist/src/fallback/handler.test.d.ts +6 -0
  227. package/dist/src/fallback/handler.test.js +245 -0
  228. package/dist/src/fallback/handler.test.js.map +1 -0
  229. package/dist/src/fallback/types.d.ts +14 -0
  230. package/dist/src/fallback/types.js +7 -0
  231. package/dist/src/fallback/types.js.map +1 -0
  232. package/dist/src/generated/git-commit.d.ts +2 -2
  233. package/dist/src/generated/git-commit.js +2 -2
  234. package/dist/src/generated/git-commit.js.map +1 -1
  235. package/dist/src/hooks/hookAggregator.d.ts +68 -0
  236. package/dist/src/hooks/hookAggregator.js +262 -0
  237. package/dist/src/hooks/hookAggregator.js.map +1 -0
  238. package/dist/src/hooks/hookAggregator.test.d.ts +6 -0
  239. package/dist/src/hooks/hookAggregator.test.js +387 -0
  240. package/dist/src/hooks/hookAggregator.test.js.map +1 -0
  241. package/dist/src/hooks/hookPlanner.d.ts +46 -0
  242. package/dist/src/hooks/hookPlanner.js +108 -0
  243. package/dist/src/hooks/hookPlanner.js.map +1 -0
  244. package/dist/src/hooks/hookPlanner.test.d.ts +6 -0
  245. package/dist/src/hooks/hookPlanner.test.js +255 -0
  246. package/dist/src/hooks/hookPlanner.test.js.map +1 -0
  247. package/dist/src/hooks/hookRegistry.d.ts +87 -0
  248. package/dist/src/hooks/hookRegistry.js +198 -0
  249. package/dist/src/hooks/hookRegistry.js.map +1 -0
  250. package/dist/src/hooks/hookRegistry.test.d.ts +6 -0
  251. package/dist/src/hooks/hookRegistry.test.js +341 -0
  252. package/dist/src/hooks/hookRegistry.test.js.map +1 -0
  253. package/dist/src/hooks/hookRunner.d.ts +42 -0
  254. package/dist/src/hooks/hookRunner.js +272 -0
  255. package/dist/src/hooks/hookRunner.js.map +1 -0
  256. package/dist/src/hooks/hookRunner.test.d.ts +6 -0
  257. package/dist/src/hooks/hookRunner.test.js +468 -0
  258. package/dist/src/hooks/hookRunner.test.js.map +1 -0
  259. package/dist/src/hooks/hookTranslator.d.ts +113 -0
  260. package/dist/src/hooks/hookTranslator.js +232 -0
  261. package/dist/src/hooks/hookTranslator.js.map +1 -0
  262. package/dist/src/hooks/hookTranslator.test.d.ts +6 -0
  263. package/dist/src/hooks/hookTranslator.test.js +192 -0
  264. package/dist/src/hooks/hookTranslator.test.js.map +1 -0
  265. package/dist/src/hooks/types.d.ts +384 -0
  266. package/dist/src/hooks/types.js +284 -0
  267. package/dist/src/hooks/types.js.map +1 -0
  268. package/dist/src/hooks/types.test.d.ts +6 -0
  269. package/dist/src/hooks/types.test.js +313 -0
  270. package/dist/src/hooks/types.test.js.map +1 -0
  271. package/dist/src/ide/constants.d.ts +3 -0
  272. package/dist/src/ide/constants.js +3 -0
  273. package/dist/src/ide/constants.js.map +1 -1
  274. package/dist/src/ide/detect-ide.d.ts +52 -12
  275. package/dist/src/ide/detect-ide.js +51 -65
  276. package/dist/src/ide/detect-ide.js.map +1 -1
  277. package/dist/src/ide/detect-ide.test.js +95 -52
  278. package/dist/src/ide/detect-ide.test.js.map +1 -1
  279. package/dist/src/ide/ide-client.d.ts +72 -24
  280. package/dist/src/ide/ide-client.js +380 -84
  281. package/dist/src/ide/ide-client.js.map +1 -1
  282. package/dist/src/ide/ide-client.test.js +539 -35
  283. package/dist/src/ide/ide-client.test.js.map +1 -1
  284. package/dist/src/ide/ide-installer.d.ts +2 -2
  285. package/dist/src/ide/ide-installer.js +93 -35
  286. package/dist/src/ide/ide-installer.js.map +1 -1
  287. package/dist/src/ide/ide-installer.test.js +157 -26
  288. package/dist/src/ide/ide-installer.test.js.map +1 -1
  289. package/dist/src/ide/ideContext.d.ts +35 -365
  290. package/dist/src/ide/ideContext.js +60 -106
  291. package/dist/src/ide/ideContext.js.map +1 -1
  292. package/dist/src/ide/ideContext.test.js +152 -24
  293. package/dist/src/ide/ideContext.test.js.map +1 -1
  294. package/dist/src/ide/process-utils.d.ts +7 -5
  295. package/dist/src/ide/process-utils.js +108 -67
  296. package/dist/src/ide/process-utils.js.map +1 -1
  297. package/dist/src/ide/process-utils.test.d.ts +6 -0
  298. package/dist/src/ide/process-utils.test.js +151 -0
  299. package/dist/src/ide/process-utils.test.js.map +1 -0
  300. package/dist/src/ide/types.d.ts +486 -0
  301. package/dist/src/ide/types.js +138 -0
  302. package/dist/src/ide/types.js.map +1 -0
  303. package/dist/src/index.d.ts +41 -2
  304. package/dist/src/index.js +44 -2
  305. package/dist/src/index.js.map +1 -1
  306. package/dist/src/mcp/google-auth-provider.d.ts +5 -3
  307. package/dist/src/mcp/google-auth-provider.js +21 -3
  308. package/dist/src/mcp/google-auth-provider.js.map +1 -1
  309. package/dist/src/mcp/google-auth-provider.test.js +42 -9
  310. package/dist/src/mcp/google-auth-provider.test.js.map +1 -1
  311. package/dist/src/mcp/oauth-provider.d.ts +25 -18
  312. package/dist/src/mcp/oauth-provider.js +194 -97
  313. package/dist/src/mcp/oauth-provider.js.map +1 -1
  314. package/dist/src/mcp/oauth-provider.test.js +581 -39
  315. package/dist/src/mcp/oauth-provider.test.js.map +1 -1
  316. package/dist/src/mcp/oauth-token-storage.d.ts +14 -32
  317. package/dist/src/mcp/oauth-token-storage.js +58 -28
  318. package/dist/src/mcp/oauth-token-storage.js.map +1 -1
  319. package/dist/src/mcp/oauth-token-storage.test.js +262 -162
  320. package/dist/src/mcp/oauth-token-storage.test.js.map +1 -1
  321. package/dist/src/mcp/oauth-utils.d.ts +16 -1
  322. package/dist/src/mcp/oauth-utils.js +68 -33
  323. package/dist/src/mcp/oauth-utils.js.map +1 -1
  324. package/dist/src/mcp/oauth-utils.test.js +86 -3
  325. package/dist/src/mcp/oauth-utils.test.js.map +1 -1
  326. package/dist/src/mcp/sa-impersonation-provider.d.ts +27 -0
  327. package/dist/src/mcp/sa-impersonation-provider.js +113 -0
  328. package/dist/src/mcp/sa-impersonation-provider.js.map +1 -0
  329. package/dist/src/mcp/sa-impersonation-provider.test.d.ts +6 -0
  330. package/dist/src/mcp/sa-impersonation-provider.test.js +117 -0
  331. package/dist/src/mcp/sa-impersonation-provider.test.js.map +1 -0
  332. package/dist/src/mcp/token-storage/base-token-storage.d.ts +19 -0
  333. package/dist/src/mcp/token-storage/base-token-storage.js +36 -0
  334. package/dist/src/mcp/token-storage/base-token-storage.js.map +1 -0
  335. package/dist/src/mcp/token-storage/base-token-storage.test.d.ts +6 -0
  336. package/dist/src/mcp/token-storage/base-token-storage.test.js +151 -0
  337. package/dist/src/mcp/token-storage/base-token-storage.test.js.map +1 -0
  338. package/dist/src/mcp/token-storage/file-token-storage.d.ts +24 -0
  339. package/dist/src/mcp/token-storage/file-token-storage.js +145 -0
  340. package/dist/src/mcp/token-storage/file-token-storage.js.map +1 -0
  341. package/dist/src/mcp/token-storage/file-token-storage.test.d.ts +6 -0
  342. package/dist/src/mcp/token-storage/file-token-storage.test.js +238 -0
  343. package/dist/src/mcp/token-storage/file-token-storage.test.js.map +1 -0
  344. package/dist/src/mcp/token-storage/hybrid-token-storage.d.ts +23 -0
  345. package/dist/src/mcp/token-storage/hybrid-token-storage.js +78 -0
  346. package/dist/src/mcp/token-storage/hybrid-token-storage.js.map +1 -0
  347. package/dist/src/mcp/token-storage/hybrid-token-storage.test.d.ts +6 -0
  348. package/dist/src/mcp/token-storage/hybrid-token-storage.test.js +193 -0
  349. package/dist/src/mcp/token-storage/hybrid-token-storage.test.js.map +1 -0
  350. package/dist/src/mcp/token-storage/index.d.ts +11 -0
  351. package/dist/src/mcp/token-storage/index.js +12 -0
  352. package/dist/src/mcp/token-storage/index.js.map +1 -0
  353. package/dist/src/mcp/token-storage/keychain-token-storage.d.ts +35 -0
  354. package/dist/src/mcp/token-storage/keychain-token-storage.js +246 -0
  355. package/dist/src/mcp/token-storage/keychain-token-storage.js.map +1 -0
  356. package/dist/src/mcp/token-storage/keychain-token-storage.test.d.ts +6 -0
  357. package/dist/src/mcp/token-storage/keychain-token-storage.test.js +305 -0
  358. package/dist/src/mcp/token-storage/keychain-token-storage.test.js.map +1 -0
  359. package/dist/src/mcp/token-storage/types.d.ts +44 -0
  360. package/dist/src/mcp/token-storage/types.js +11 -0
  361. package/dist/src/mcp/token-storage/types.js.map +1 -0
  362. package/dist/src/output/json-formatter.d.ts +11 -0
  363. package/dist/src/output/json-formatter.js +30 -0
  364. package/dist/src/output/json-formatter.js.map +1 -0
  365. package/dist/src/output/json-formatter.test.d.ts +6 -0
  366. package/dist/src/output/json-formatter.test.js +266 -0
  367. package/dist/src/output/json-formatter.test.js.map +1 -0
  368. package/dist/src/output/stream-json-formatter.d.ts +32 -0
  369. package/dist/src/output/stream-json-formatter.js +52 -0
  370. package/dist/src/output/stream-json-formatter.js.map +1 -0
  371. package/dist/src/output/stream-json-formatter.test.d.ts +6 -0
  372. package/dist/src/output/stream-json-formatter.test.js +479 -0
  373. package/dist/src/output/stream-json-formatter.test.js.map +1 -0
  374. package/dist/src/output/types.d.ts +82 -0
  375. package/dist/src/output/types.js +22 -0
  376. package/dist/src/output/types.js.map +1 -0
  377. package/dist/src/policy/config.d.ts +31 -0
  378. package/dist/src/policy/config.js +199 -0
  379. package/dist/src/policy/config.js.map +1 -0
  380. package/dist/src/policy/config.test.d.ts +6 -0
  381. package/dist/src/policy/config.test.js +538 -0
  382. package/dist/src/policy/config.test.js.map +1 -0
  383. package/dist/src/policy/index.d.ts +9 -0
  384. package/dist/src/policy/index.js +10 -0
  385. package/dist/src/policy/index.js.map +1 -0
  386. package/dist/src/policy/policies/discovered.toml +8 -0
  387. package/dist/src/policy/policies/read-only.toml +56 -0
  388. package/dist/src/policy/policies/write.toml +73 -0
  389. package/dist/src/policy/policies/yolo.toml +31 -0
  390. package/dist/src/policy/policy-engine.d.ts +39 -0
  391. package/dist/src/policy/policy-engine.js +158 -0
  392. package/dist/src/policy/policy-engine.js.map +1 -0
  393. package/dist/src/policy/policy-engine.test.d.ts +6 -0
  394. package/dist/src/policy/policy-engine.test.js +899 -0
  395. package/dist/src/policy/policy-engine.test.js.map +1 -0
  396. package/dist/src/policy/stable-stringify.d.ts +58 -0
  397. package/dist/src/policy/stable-stringify.js +122 -0
  398. package/dist/src/policy/stable-stringify.js.map +1 -0
  399. package/dist/src/policy/toml-loader.d.ts +47 -0
  400. package/dist/src/policy/toml-loader.js +411 -0
  401. package/dist/src/policy/toml-loader.js.map +1 -0
  402. package/dist/src/policy/toml-loader.test.d.ts +6 -0
  403. package/dist/src/policy/toml-loader.test.js +376 -0
  404. package/dist/src/policy/toml-loader.test.js.map +1 -0
  405. package/dist/src/policy/types.d.ts +130 -0
  406. package/dist/src/policy/types.js +22 -0
  407. package/dist/src/policy/types.js.map +1 -0
  408. package/dist/src/prompts/mcp-prompts.d.ts +2 -2
  409. package/dist/src/prompts/mcp-prompts.test.d.ts +6 -0
  410. package/dist/src/prompts/mcp-prompts.test.js +39 -0
  411. package/dist/src/prompts/mcp-prompts.test.js.map +1 -0
  412. package/dist/src/prompts/prompt-registry.d.ts +1 -1
  413. package/dist/src/prompts/prompt-registry.js +2 -1
  414. package/dist/src/prompts/prompt-registry.js.map +1 -1
  415. package/dist/src/prompts/prompt-registry.test.d.ts +6 -0
  416. package/dist/src/prompts/prompt-registry.test.js +96 -0
  417. package/dist/src/prompts/prompt-registry.test.js.map +1 -0
  418. package/dist/src/routing/modelRouterService.d.ts +23 -0
  419. package/dist/src/routing/modelRouterService.js +85 -0
  420. package/dist/src/routing/modelRouterService.js.map +1 -0
  421. package/dist/src/routing/modelRouterService.test.d.ts +6 -0
  422. package/dist/src/routing/modelRouterService.test.js +160 -0
  423. package/dist/src/routing/modelRouterService.test.js.map +1 -0
  424. package/dist/src/routing/routingStrategy.d.ts +62 -0
  425. package/dist/src/routing/routingStrategy.js +7 -0
  426. package/dist/src/routing/routingStrategy.js.map +1 -0
  427. package/dist/src/routing/strategies/classifierStrategy.d.ts +12 -0
  428. package/dist/src/routing/strategies/classifierStrategy.js +166 -0
  429. package/dist/src/routing/strategies/classifierStrategy.js.map +1 -0
  430. package/dist/src/routing/strategies/classifierStrategy.test.d.ts +6 -0
  431. package/dist/src/routing/strategies/classifierStrategy.test.js +196 -0
  432. package/dist/src/routing/strategies/classifierStrategy.test.js.map +1 -0
  433. package/dist/src/routing/strategies/compositeStrategy.d.ts +26 -0
  434. package/dist/src/routing/strategies/compositeStrategy.js +68 -0
  435. package/dist/src/routing/strategies/compositeStrategy.js.map +1 -0
  436. package/dist/src/routing/strategies/compositeStrategy.test.d.ts +6 -0
  437. package/dist/src/routing/strategies/compositeStrategy.test.js +123 -0
  438. package/dist/src/routing/strategies/compositeStrategy.test.js.map +1 -0
  439. package/dist/src/routing/strategies/defaultStrategy.d.ts +12 -0
  440. package/dist/src/routing/strategies/defaultStrategy.js +20 -0
  441. package/dist/src/routing/strategies/defaultStrategy.js.map +1 -0
  442. package/dist/src/routing/strategies/defaultStrategy.test.d.ts +6 -0
  443. package/dist/src/routing/strategies/defaultStrategy.test.js +26 -0
  444. package/dist/src/routing/strategies/defaultStrategy.test.js.map +1 -0
  445. package/dist/src/routing/strategies/fallbackStrategy.d.ts +12 -0
  446. package/dist/src/routing/strategies/fallbackStrategy.js +25 -0
  447. package/dist/src/routing/strategies/fallbackStrategy.js.map +1 -0
  448. package/dist/src/routing/strategies/fallbackStrategy.test.d.ts +6 -0
  449. package/dist/src/routing/strategies/fallbackStrategy.test.js +59 -0
  450. package/dist/src/routing/strategies/fallbackStrategy.test.js.map +1 -0
  451. package/dist/src/routing/strategies/overrideStrategy.d.ts +15 -0
  452. package/dist/src/routing/strategies/overrideStrategy.js +28 -0
  453. package/dist/src/routing/strategies/overrideStrategy.js.map +1 -0
  454. package/dist/src/routing/strategies/overrideStrategy.test.d.ts +6 -0
  455. package/dist/src/routing/strategies/overrideStrategy.test.js +45 -0
  456. package/dist/src/routing/strategies/overrideStrategy.test.js.map +1 -0
  457. package/dist/src/safety/built-in.d.ts +21 -0
  458. package/dist/src/safety/built-in.js +106 -0
  459. package/dist/src/safety/built-in.js.map +1 -0
  460. package/dist/src/safety/built-in.test.d.ts +6 -0
  461. package/dist/src/safety/built-in.test.js +199 -0
  462. package/dist/src/safety/built-in.test.js.map +1 -0
  463. package/dist/src/safety/checker-runner.d.ts +48 -0
  464. package/dist/src/safety/checker-runner.js +208 -0
  465. package/dist/src/safety/checker-runner.js.map +1 -0
  466. package/dist/src/safety/checker-runner.test.d.ts +6 -0
  467. package/dist/src/safety/checker-runner.test.js +238 -0
  468. package/dist/src/safety/checker-runner.test.js.map +1 -0
  469. package/dist/src/safety/context-builder.d.ts +23 -0
  470. package/dist/src/safety/context-builder.js +47 -0
  471. package/dist/src/safety/context-builder.js.map +1 -0
  472. package/dist/src/safety/context-builder.test.d.ts +6 -0
  473. package/dist/src/safety/context-builder.test.js +49 -0
  474. package/dist/src/safety/context-builder.test.js.map +1 -0
  475. package/dist/src/safety/protocol.d.ts +88 -0
  476. package/dist/src/safety/protocol.js +15 -0
  477. package/dist/src/safety/protocol.js.map +1 -0
  478. package/dist/src/safety/registry.d.ts +26 -0
  479. package/dist/src/safety/registry.js +65 -0
  480. package/dist/src/safety/registry.js.map +1 -0
  481. package/dist/src/safety/registry.test.d.ts +6 -0
  482. package/dist/src/safety/registry.test.js +31 -0
  483. package/dist/src/safety/registry.test.js.map +1 -0
  484. package/dist/src/services/chatCompressionService.d.ts +32 -0
  485. package/dist/src/services/chatCompressionService.js +162 -0
  486. package/dist/src/services/chatCompressionService.js.map +1 -0
  487. package/dist/src/services/chatCompressionService.test.d.ts +6 -0
  488. package/dist/src/services/chatCompressionService.test.js +210 -0
  489. package/dist/src/services/chatCompressionService.test.js.map +1 -0
  490. package/dist/src/services/chatRecordingService.d.ts +10 -15
  491. package/dist/src/services/chatRecordingService.js +43 -29
  492. package/dist/src/services/chatRecordingService.js.map +1 -1
  493. package/dist/src/services/chatRecordingService.test.js +69 -25
  494. package/dist/src/services/chatRecordingService.test.js.map +1 -1
  495. package/dist/src/services/fileDiscoveryService.d.ts +8 -10
  496. package/dist/src/services/fileDiscoveryService.js +31 -53
  497. package/dist/src/services/fileDiscoveryService.js.map +1 -1
  498. package/dist/src/services/fileDiscoveryService.test.js +94 -14
  499. package/dist/src/services/fileDiscoveryService.test.js.map +1 -1
  500. package/dist/src/services/fileSystemService.d.ts +9 -0
  501. package/dist/src/services/fileSystemService.js +12 -1
  502. package/dist/src/services/fileSystemService.js.map +1 -1
  503. package/dist/src/services/fileSystemService.test.js +1 -1
  504. package/dist/src/services/fileSystemService.test.js.map +1 -1
  505. package/dist/src/services/gitService.d.ts +3 -1
  506. package/dist/src/services/gitService.js +30 -24
  507. package/dist/src/services/gitService.js.map +1 -1
  508. package/dist/src/services/gitService.test.js +30 -37
  509. package/dist/src/services/gitService.test.js.map +1 -1
  510. package/dist/src/services/loopDetectionService.d.ts +12 -3
  511. package/dist/src/services/loopDetectionService.js +148 -55
  512. package/dist/src/services/loopDetectionService.js.map +1 -1
  513. package/dist/src/services/loopDetectionService.test.js +280 -21
  514. package/dist/src/services/loopDetectionService.test.js.map +1 -1
  515. package/dist/src/services/modelConfig.golden.test.d.ts +6 -0
  516. package/dist/src/services/modelConfig.golden.test.js +42 -0
  517. package/dist/src/services/modelConfig.golden.test.js.map +1 -0
  518. package/dist/src/services/modelConfig.integration.test.d.ts +6 -0
  519. package/dist/src/services/modelConfig.integration.test.js +247 -0
  520. package/dist/src/services/modelConfig.integration.test.js.map +1 -0
  521. package/dist/src/services/modelConfigService.d.ts +48 -0
  522. package/dist/src/services/modelConfigService.js +151 -0
  523. package/dist/src/services/modelConfigService.js.map +1 -0
  524. package/dist/src/services/modelConfigService.test.d.ts +6 -0
  525. package/dist/src/services/modelConfigService.test.js +531 -0
  526. package/dist/src/services/modelConfigService.test.js.map +1 -0
  527. package/dist/src/services/shellExecutionService.d.ts +37 -2
  528. package/dist/src/services/shellExecutionService.js +361 -67
  529. package/dist/src/services/shellExecutionService.js.map +1 -1
  530. package/dist/src/services/shellExecutionService.test.js +333 -71
  531. package/dist/src/services/shellExecutionService.test.js.map +1 -1
  532. package/dist/src/services/test-data/resolved-aliases.golden.json +202 -0
  533. package/dist/src/telemetry/activity-detector.d.ts +41 -0
  534. package/dist/src/telemetry/activity-detector.js +61 -0
  535. package/dist/src/telemetry/activity-detector.js.map +1 -0
  536. package/dist/src/telemetry/activity-detector.test.d.ts +6 -0
  537. package/dist/src/telemetry/activity-detector.test.js +136 -0
  538. package/dist/src/telemetry/activity-detector.test.js.map +1 -0
  539. package/dist/src/telemetry/activity-monitor.d.ts +116 -0
  540. package/dist/src/telemetry/activity-monitor.js +209 -0
  541. package/dist/src/telemetry/activity-monitor.js.map +1 -0
  542. package/dist/src/telemetry/activity-monitor.test.d.ts +6 -0
  543. package/dist/src/telemetry/activity-monitor.test.js +251 -0
  544. package/dist/src/telemetry/activity-monitor.test.js.map +1 -0
  545. package/dist/src/telemetry/activity-types.d.ts +19 -0
  546. package/dist/src/telemetry/activity-types.js +21 -0
  547. package/dist/src/telemetry/activity-types.js.map +1 -0
  548. package/dist/src/telemetry/clearcut-logger/clearcut-logger.d.ts +53 -5
  549. package/dist/src/telemetry/clearcut-logger/clearcut-logger.js +581 -56
  550. package/dist/src/telemetry/clearcut-logger/clearcut-logger.js.map +1 -1
  551. package/dist/src/telemetry/clearcut-logger/clearcut-logger.test.d.ts +2 -0
  552. package/dist/src/telemetry/clearcut-logger/clearcut-logger.test.js +467 -31
  553. package/dist/src/telemetry/clearcut-logger/clearcut-logger.test.js.map +1 -1
  554. package/dist/src/telemetry/clearcut-logger/event-metadata-key.d.ts +75 -4
  555. package/dist/src/telemetry/clearcut-logger/event-metadata-key.js +182 -6
  556. package/dist/src/telemetry/clearcut-logger/event-metadata-key.js.map +1 -1
  557. package/dist/src/telemetry/config.d.ts +31 -0
  558. package/dist/src/telemetry/config.js +76 -0
  559. package/dist/src/telemetry/config.js.map +1 -0
  560. package/dist/src/telemetry/config.test.d.ts +6 -0
  561. package/dist/src/telemetry/config.test.js +124 -0
  562. package/dist/src/telemetry/config.test.js.map +1 -0
  563. package/dist/src/telemetry/constants.d.ts +0 -18
  564. package/dist/src/telemetry/constants.js +0 -18
  565. package/dist/src/telemetry/constants.js.map +1 -1
  566. package/dist/src/telemetry/file-exporters.d.ts +5 -4
  567. package/dist/src/telemetry/file-exporters.js +1 -1
  568. package/dist/src/telemetry/file-exporters.js.map +1 -1
  569. package/dist/src/telemetry/gcp-exporters.d.ts +34 -0
  570. package/dist/src/telemetry/gcp-exporters.js +116 -0
  571. package/dist/src/telemetry/gcp-exporters.js.map +1 -0
  572. package/dist/src/telemetry/gcp-exporters.test.d.ts +6 -0
  573. package/dist/src/telemetry/gcp-exporters.test.js +318 -0
  574. package/dist/src/telemetry/gcp-exporters.test.js.map +1 -0
  575. package/dist/src/telemetry/high-water-mark-tracker.d.ts +43 -0
  576. package/dist/src/telemetry/high-water-mark-tracker.js +88 -0
  577. package/dist/src/telemetry/high-water-mark-tracker.js.map +1 -0
  578. package/dist/src/telemetry/high-water-mark-tracker.test.d.ts +6 -0
  579. package/dist/src/telemetry/high-water-mark-tracker.test.js +152 -0
  580. package/dist/src/telemetry/high-water-mark-tracker.test.js.map +1 -0
  581. package/dist/src/telemetry/index.d.ts +16 -2
  582. package/dist/src/telemetry/index.js +25 -2
  583. package/dist/src/telemetry/index.js.map +1 -1
  584. package/dist/src/telemetry/loggers.d.ts +25 -3
  585. package/dist/src/telemetry/loggers.js +333 -154
  586. package/dist/src/telemetry/loggers.js.map +1 -1
  587. package/dist/src/telemetry/loggers.test.circular.js +3 -4
  588. package/dist/src/telemetry/loggers.test.circular.js.map +1 -1
  589. package/dist/src/telemetry/loggers.test.js +868 -63
  590. package/dist/src/telemetry/loggers.test.js.map +1 -1
  591. package/dist/src/telemetry/memory-monitor.d.ts +149 -0
  592. package/dist/src/telemetry/memory-monitor.js +335 -0
  593. package/dist/src/telemetry/memory-monitor.js.map +1 -0
  594. package/dist/src/telemetry/memory-monitor.test.d.ts +6 -0
  595. package/dist/src/telemetry/memory-monitor.test.js +472 -0
  596. package/dist/src/telemetry/memory-monitor.test.js.map +1 -0
  597. package/dist/src/telemetry/metrics.d.ts +498 -11
  598. package/dist/src/telemetry/metrics.js +729 -84
  599. package/dist/src/telemetry/metrics.js.map +1 -1
  600. package/dist/src/telemetry/metrics.test.js +964 -101
  601. package/dist/src/telemetry/metrics.test.js.map +1 -1
  602. package/dist/src/telemetry/rate-limiter.d.ts +48 -0
  603. package/dist/src/telemetry/rate-limiter.js +100 -0
  604. package/dist/src/telemetry/rate-limiter.js.map +1 -0
  605. package/dist/src/telemetry/rate-limiter.test.d.ts +6 -0
  606. package/dist/src/telemetry/rate-limiter.test.js +207 -0
  607. package/dist/src/telemetry/rate-limiter.test.js.map +1 -0
  608. package/dist/src/telemetry/sdk.d.ts +1 -1
  609. package/dist/src/telemetry/sdk.js +23 -4
  610. package/dist/src/telemetry/sdk.js.map +1 -1
  611. package/dist/src/telemetry/sdk.test.js +108 -0
  612. package/dist/src/telemetry/sdk.test.js.map +1 -1
  613. package/dist/src/telemetry/semantic.d.ts +82 -0
  614. package/dist/src/telemetry/semantic.js +269 -0
  615. package/dist/src/telemetry/semantic.js.map +1 -0
  616. package/dist/src/telemetry/semantic.test.d.ts +6 -0
  617. package/dist/src/telemetry/semantic.test.js +387 -0
  618. package/dist/src/telemetry/semantic.test.js.map +1 -0
  619. package/dist/src/telemetry/telemetry-utils.d.ts +6 -0
  620. package/dist/src/telemetry/telemetry-utils.js +14 -0
  621. package/dist/src/telemetry/telemetry-utils.js.map +1 -0
  622. package/dist/src/telemetry/telemetry-utils.test.d.ts +6 -0
  623. package/dist/src/telemetry/telemetry-utils.test.js +41 -0
  624. package/dist/src/telemetry/telemetry-utils.test.js.map +1 -0
  625. package/dist/src/telemetry/telemetryAttributes.d.ts +8 -0
  626. package/dist/src/telemetry/telemetryAttributes.js +19 -0
  627. package/dist/src/telemetry/telemetryAttributes.js.map +1 -0
  628. package/dist/src/telemetry/trace.d.ts +46 -0
  629. package/dist/src/telemetry/trace.js +121 -0
  630. package/dist/src/telemetry/trace.js.map +1 -0
  631. package/dist/src/telemetry/types.d.ts +351 -25
  632. package/dist/src/telemetry/types.js +1071 -63
  633. package/dist/src/telemetry/types.js.map +1 -1
  634. package/dist/src/telemetry/uiTelemetry.d.ts +4 -4
  635. package/dist/src/telemetry/uiTelemetry.js +14 -15
  636. package/dist/src/telemetry/uiTelemetry.js.map +1 -1
  637. package/dist/src/telemetry/uiTelemetry.test.js +122 -96
  638. package/dist/src/telemetry/uiTelemetry.test.js.map +1 -1
  639. package/dist/src/test-utils/config.d.ts +3 -2
  640. package/dist/src/test-utils/config.js +1 -1
  641. package/dist/src/test-utils/config.js.map +1 -1
  642. package/dist/src/test-utils/index.d.ts +6 -0
  643. package/dist/src/test-utils/index.js +7 -0
  644. package/dist/src/test-utils/index.js.map +1 -0
  645. package/dist/src/test-utils/mock-tool.d.ts +66 -0
  646. package/dist/src/test-utils/{tools.js → mock-tool.js} +45 -29
  647. package/dist/src/test-utils/mock-tool.js.map +1 -0
  648. package/dist/src/test-utils/mockWorkspaceContext.d.ts +1 -1
  649. package/dist/src/tools/base-tool-invocation.test.d.ts +6 -0
  650. package/dist/src/tools/base-tool-invocation.test.js +85 -0
  651. package/dist/src/tools/base-tool-invocation.test.js.map +1 -0
  652. package/dist/src/tools/diffOptions.d.ts +1 -1
  653. package/dist/src/tools/diffOptions.js +21 -13
  654. package/dist/src/tools/diffOptions.js.map +1 -1
  655. package/dist/src/tools/diffOptions.test.js +58 -22
  656. package/dist/src/tools/diffOptions.test.js.map +1 -1
  657. package/dist/src/tools/edit.d.ts +10 -8
  658. package/dist/src/tools/edit.js +100 -79
  659. package/dist/src/tools/edit.js.map +1 -1
  660. package/dist/src/tools/edit.test.js +429 -163
  661. package/dist/src/tools/edit.test.js.map +1 -1
  662. package/dist/src/tools/glob.d.ts +11 -5
  663. package/dist/src/tools/glob.js +58 -42
  664. package/dist/src/tools/glob.js.map +1 -1
  665. package/dist/src/tools/glob.test.js +249 -166
  666. package/dist/src/tools/glob.test.js.map +1 -1
  667. package/dist/src/tools/grep.d.ts +7 -5
  668. package/dist/src/tools/grep.js +66 -40
  669. package/dist/src/tools/grep.js.map +1 -1
  670. package/dist/src/tools/grep.test.js +41 -15
  671. package/dist/src/tools/grep.test.js.map +1 -1
  672. package/dist/src/tools/ls.d.ts +7 -5
  673. package/dist/src/tools/ls.js +54 -68
  674. package/dist/src/tools/ls.js.map +1 -1
  675. package/dist/src/tools/ls.test.js +150 -293
  676. package/dist/src/tools/ls.test.js.map +1 -1
  677. package/dist/src/tools/mcp-client-manager.d.ts +52 -12
  678. package/dist/src/tools/mcp-client-manager.js +196 -27
  679. package/dist/src/tools/mcp-client-manager.js.map +1 -1
  680. package/dist/src/tools/mcp-client-manager.test.js +139 -13
  681. package/dist/src/tools/mcp-client-manager.test.js.map +1 -1
  682. package/dist/src/tools/mcp-client.d.ts +28 -24
  683. package/dist/src/tools/mcp-client.js +338 -351
  684. package/dist/src/tools/mcp-client.js.map +1 -1
  685. package/dist/src/tools/mcp-client.test.js +380 -193
  686. package/dist/src/tools/mcp-client.test.js.map +1 -1
  687. package/dist/src/tools/mcp-tool.d.ts +11 -5
  688. package/dist/src/tools/mcp-tool.js +66 -17
  689. package/dist/src/tools/mcp-tool.js.map +1 -1
  690. package/dist/src/tools/mcp-tool.test.js +278 -211
  691. package/dist/src/tools/mcp-tool.test.js.map +1 -1
  692. package/dist/src/tools/memoryTool.d.ts +10 -7
  693. package/dist/src/tools/memoryTool.js +28 -49
  694. package/dist/src/tools/memoryTool.js.map +1 -1
  695. package/dist/src/tools/memoryTool.test.js +26 -13
  696. package/dist/src/tools/memoryTool.test.js.map +1 -1
  697. package/dist/src/tools/message-bus-integration.test.d.ts +6 -0
  698. package/dist/src/tools/message-bus-integration.test.js +196 -0
  699. package/dist/src/tools/message-bus-integration.test.js.map +1 -0
  700. package/dist/src/tools/modifiable-tool.d.ts +7 -3
  701. package/dist/src/tools/modifiable-tool.js +41 -19
  702. package/dist/src/tools/modifiable-tool.js.map +1 -1
  703. package/dist/src/tools/modifiable-tool.test.js +70 -35
  704. package/dist/src/tools/modifiable-tool.test.js.map +1 -1
  705. package/dist/src/tools/read-file.d.ts +9 -7
  706. package/dist/src/tools/read-file.js +39 -60
  707. package/dist/src/tools/read-file.js.map +1 -1
  708. package/dist/src/tools/read-file.test.js +103 -36
  709. package/dist/src/tools/read-file.test.js.map +1 -1
  710. package/dist/src/tools/read-many-files.d.ts +9 -14
  711. package/dist/src/tools/read-many-files.js +71 -155
  712. package/dist/src/tools/read-many-files.js.map +1 -1
  713. package/dist/src/tools/read-many-files.test.js +98 -44
  714. package/dist/src/tools/read-many-files.test.js.map +1 -1
  715. package/dist/src/tools/ripGrep.d.ts +73 -0
  716. package/dist/src/tools/ripGrep.js +395 -0
  717. package/dist/src/tools/ripGrep.js.map +1 -0
  718. package/dist/src/tools/ripGrep.test.d.ts +6 -0
  719. package/dist/src/tools/ripGrep.test.js +1305 -0
  720. package/dist/src/tools/ripGrep.test.js.map +1 -0
  721. package/dist/src/tools/shell.d.ts +19 -6
  722. package/dist/src/tools/shell.js +92 -56
  723. package/dist/src/tools/shell.js.map +1 -1
  724. package/dist/src/tools/shell.test.js +176 -103
  725. package/dist/src/tools/shell.test.js.map +1 -1
  726. package/dist/src/tools/smart-edit.d.ts +78 -0
  727. package/dist/src/tools/smart-edit.js +717 -0
  728. package/dist/src/tools/smart-edit.js.map +1 -0
  729. package/dist/src/tools/smart-edit.test.d.ts +6 -0
  730. package/dist/src/tools/smart-edit.test.js +592 -0
  731. package/dist/src/tools/smart-edit.test.js.map +1 -0
  732. package/dist/src/tools/tool-error.d.ts +39 -1
  733. package/dist/src/tools/tool-error.js +54 -0
  734. package/dist/src/tools/tool-error.js.map +1 -1
  735. package/dist/src/tools/tool-names.d.ts +17 -0
  736. package/dist/src/tools/tool-names.js +21 -0
  737. package/dist/src/tools/tool-names.js.map +1 -0
  738. package/dist/src/tools/tool-registry.d.ts +38 -20
  739. package/dist/src/tools/tool-registry.js +134 -77
  740. package/dist/src/tools/tool-registry.js.map +1 -1
  741. package/dist/src/tools/tool-registry.test.js +218 -58
  742. package/dist/src/tools/tool-registry.test.js.map +1 -1
  743. package/dist/src/tools/tools.d.ts +49 -17
  744. package/dist/src/tools/tools.js +150 -8
  745. package/dist/src/tools/tools.js.map +1 -1
  746. package/dist/src/tools/tools.test.js +1 -2
  747. package/dist/src/tools/tools.test.js.map +1 -1
  748. package/dist/src/tools/web-fetch.d.ts +14 -5
  749. package/dist/src/tools/web-fetch.js +90 -44
  750. package/dist/src/tools/web-fetch.js.map +1 -1
  751. package/dist/src/tools/web-fetch.test.js +388 -20
  752. package/dist/src/tools/web-fetch.test.js.map +1 -1
  753. package/dist/src/tools/web-search.d.ts +8 -6
  754. package/dist/src/tools/web-search.js +40 -15
  755. package/dist/src/tools/web-search.js.map +1 -1
  756. package/dist/src/tools/web-search.test.js +75 -1
  757. package/dist/src/tools/web-search.test.js.map +1 -1
  758. package/dist/src/tools/write-file.d.ts +7 -5
  759. package/dist/src/tools/write-file.js +54 -54
  760. package/dist/src/tools/write-file.js.map +1 -1
  761. package/dist/src/tools/write-file.test.js +237 -146
  762. package/dist/src/tools/write-file.test.js.map +1 -1
  763. package/dist/src/tools/write-todos.d.ts +50 -0
  764. package/dist/src/tools/write-todos.js +193 -0
  765. package/dist/src/tools/write-todos.js.map +1 -0
  766. package/dist/src/tools/write-todos.test.d.ts +6 -0
  767. package/dist/src/tools/write-todos.test.js +89 -0
  768. package/dist/src/tools/write-todos.test.js.map +1 -0
  769. package/dist/src/utils/bfsFileSearch.d.ts +2 -2
  770. package/dist/src/utils/bfsFileSearch.js +16 -9
  771. package/dist/src/utils/bfsFileSearch.js.map +1 -1
  772. package/dist/src/utils/bfsFileSearch.test.js +3 -3
  773. package/dist/src/utils/bfsFileSearch.test.js.map +1 -1
  774. package/dist/src/utils/channel.d.ts +19 -0
  775. package/dist/src/utils/channel.js +49 -0
  776. package/dist/src/utils/channel.js.map +1 -0
  777. package/dist/src/utils/channel.test.d.ts +6 -0
  778. package/dist/src/utils/channel.test.js +170 -0
  779. package/dist/src/utils/channel.test.js.map +1 -0
  780. package/dist/src/utils/debugLogger.d.ts +25 -0
  781. package/dist/src/utils/debugLogger.js +33 -0
  782. package/dist/src/utils/debugLogger.js.map +1 -0
  783. package/dist/src/utils/debugLogger.test.d.ts +6 -0
  784. package/dist/src/utils/debugLogger.test.js +69 -0
  785. package/dist/src/utils/debugLogger.test.js.map +1 -0
  786. package/dist/src/utils/delay.d.ts +16 -0
  787. package/dist/src/utils/delay.js +43 -0
  788. package/dist/src/utils/delay.js.map +1 -0
  789. package/dist/src/utils/delay.test.d.ts +6 -0
  790. package/dist/src/utils/delay.test.js +88 -0
  791. package/dist/src/utils/delay.test.js.map +1 -0
  792. package/dist/src/utils/editCorrector.d.ts +9 -8
  793. package/dist/src/utils/editCorrector.js +62 -34
  794. package/dist/src/utils/editCorrector.js.map +1 -1
  795. package/dist/src/utils/editCorrector.test.js +52 -87
  796. package/dist/src/utils/editCorrector.test.js.map +1 -1
  797. package/dist/src/utils/editor.d.ts +4 -2
  798. package/dist/src/utils/editor.js +56 -55
  799. package/dist/src/utils/editor.js.map +1 -1
  800. package/dist/src/utils/editor.test.js +47 -88
  801. package/dist/src/utils/editor.test.js.map +1 -1
  802. package/dist/src/utils/environmentContext.d.ts +3 -2
  803. package/dist/src/utils/environmentContext.js +20 -33
  804. package/dist/src/utils/environmentContext.js.map +1 -1
  805. package/dist/src/utils/environmentContext.test.js +6 -34
  806. package/dist/src/utils/environmentContext.test.js.map +1 -1
  807. package/dist/src/utils/errorParsing.d.ts +1 -1
  808. package/dist/src/utils/errorParsing.js +5 -33
  809. package/dist/src/utils/errorParsing.js.map +1 -1
  810. package/dist/src/utils/errorParsing.test.js +0 -88
  811. package/dist/src/utils/errorParsing.test.js.map +1 -1
  812. package/dist/src/utils/errorReporting.d.ts +1 -1
  813. package/dist/src/utils/errors.d.ts +28 -0
  814. package/dist/src/utils/errors.js +48 -0
  815. package/dist/src/utils/errors.js.map +1 -1
  816. package/dist/src/utils/events.d.ts +121 -0
  817. package/dist/src/utils/events.js +84 -0
  818. package/dist/src/utils/events.js.map +1 -0
  819. package/dist/src/utils/events.test.d.ts +6 -0
  820. package/dist/src/utils/events.test.js +212 -0
  821. package/dist/src/utils/events.test.js.map +1 -0
  822. package/dist/src/utils/extensionLoader.d.ts +86 -0
  823. package/dist/src/utils/extensionLoader.js +208 -0
  824. package/dist/src/utils/extensionLoader.js.map +1 -0
  825. package/dist/src/utils/extensionLoader.test.d.ts +6 -0
  826. package/dist/src/utils/extensionLoader.test.js +154 -0
  827. package/dist/src/utils/extensionLoader.test.js.map +1 -0
  828. package/dist/src/utils/fetch.d.ts +1 -0
  829. package/dist/src/utils/fetch.js +5 -1
  830. package/dist/src/utils/fetch.js.map +1 -1
  831. package/dist/src/utils/fileUtils.d.ts +28 -12
  832. package/dist/src/utils/fileUtils.js +204 -81
  833. package/dist/src/utils/fileUtils.js.map +1 -1
  834. package/dist/src/utils/fileUtils.test.js +426 -82
  835. package/dist/src/utils/fileUtils.test.js.map +1 -1
  836. package/dist/src/utils/filesearch/crawler.d.ts +1 -1
  837. package/dist/src/utils/filesearch/crawler.test.js +2 -2
  838. package/dist/src/utils/filesearch/crawler.test.js.map +1 -1
  839. package/dist/src/utils/filesearch/fileSearch.d.ts +1 -0
  840. package/dist/src/utils/filesearch/fileSearch.js +14 -9
  841. package/dist/src/utils/filesearch/fileSearch.js.map +1 -1
  842. package/dist/src/utils/filesearch/fileSearch.test.js +90 -0
  843. package/dist/src/utils/filesearch/fileSearch.test.js.map +1 -1
  844. package/dist/src/utils/flashFallback.test.d.ts +6 -0
  845. package/dist/src/utils/flashFallback.test.js +103 -0
  846. package/dist/src/utils/flashFallback.test.js.map +1 -0
  847. package/dist/src/utils/formatters.d.ts +1 -0
  848. package/dist/src/utils/formatters.js +2 -1
  849. package/dist/src/utils/formatters.js.map +1 -1
  850. package/dist/src/utils/formatters.test.d.ts +6 -0
  851. package/dist/src/utils/formatters.test.js +26 -0
  852. package/dist/src/utils/formatters.test.js.map +1 -0
  853. package/dist/src/utils/geminiIgnoreParser.d.ts +18 -0
  854. package/dist/src/utils/geminiIgnoreParser.js +61 -0
  855. package/dist/src/utils/geminiIgnoreParser.js.map +1 -0
  856. package/dist/src/utils/geminiIgnoreParser.test.d.ts +6 -0
  857. package/dist/src/utils/geminiIgnoreParser.test.js +50 -0
  858. package/dist/src/utils/geminiIgnoreParser.test.js.map +1 -0
  859. package/dist/src/utils/generateContentResponseUtilities.d.ts +1 -2
  860. package/dist/src/utils/generateContentResponseUtilities.js +1 -13
  861. package/dist/src/utils/generateContentResponseUtilities.js.map +1 -1
  862. package/dist/src/utils/generateContentResponseUtilities.test.js +2 -40
  863. package/dist/src/utils/generateContentResponseUtilities.test.js.map +1 -1
  864. package/dist/src/utils/getFolderStructure.d.ts +2 -2
  865. package/dist/src/utils/getFolderStructure.js +12 -20
  866. package/dist/src/utils/getFolderStructure.js.map +1 -1
  867. package/dist/src/utils/getFolderStructure.test.js +11 -10
  868. package/dist/src/utils/getFolderStructure.test.js.map +1 -1
  869. package/dist/src/utils/gitIgnoreParser.d.ts +7 -8
  870. package/dist/src/utils/gitIgnoreParser.js +145 -36
  871. package/dist/src/utils/gitIgnoreParser.js.map +1 -1
  872. package/dist/src/utils/gitIgnoreParser.test.js +127 -38
  873. package/dist/src/utils/gitIgnoreParser.test.js.map +1 -1
  874. package/dist/src/utils/gitUtils.js +2 -2
  875. package/dist/src/utils/gitUtils.js.map +1 -1
  876. package/dist/src/utils/googleErrors.d.ts +104 -0
  877. package/dist/src/utils/googleErrors.js +152 -0
  878. package/dist/src/utils/googleErrors.js.map +1 -0
  879. package/dist/src/utils/googleErrors.test.d.ts +6 -0
  880. package/dist/src/utils/googleErrors.test.js +301 -0
  881. package/dist/src/utils/googleErrors.test.js.map +1 -0
  882. package/dist/src/utils/googleQuotaErrors.d.ts +37 -0
  883. package/dist/src/utils/googleQuotaErrors.js +157 -0
  884. package/dist/src/utils/googleQuotaErrors.js.map +1 -0
  885. package/dist/src/utils/googleQuotaErrors.test.d.ts +6 -0
  886. package/dist/src/utils/googleQuotaErrors.test.js +311 -0
  887. package/dist/src/utils/googleQuotaErrors.test.js.map +1 -0
  888. package/dist/src/utils/httpErrors.d.ts +18 -0
  889. package/dist/src/utils/httpErrors.js +36 -0
  890. package/dist/src/utils/httpErrors.js.map +1 -0
  891. package/dist/src/utils/ignorePatterns.d.ts +103 -0
  892. package/dist/src/utils/ignorePatterns.js +220 -0
  893. package/dist/src/utils/ignorePatterns.js.map +1 -0
  894. package/dist/src/utils/ignorePatterns.test.d.ts +6 -0
  895. package/dist/src/utils/ignorePatterns.test.js +246 -0
  896. package/dist/src/utils/ignorePatterns.test.js.map +1 -0
  897. package/dist/src/utils/installationManager.d.ts +16 -0
  898. package/dist/src/utils/installationManager.js +51 -0
  899. package/dist/src/utils/installationManager.js.map +1 -0
  900. package/dist/src/utils/installationManager.test.d.ts +6 -0
  901. package/dist/src/utils/installationManager.test.js +85 -0
  902. package/dist/src/utils/installationManager.test.js.map +1 -0
  903. package/dist/src/utils/language-detection.d.ts +6 -0
  904. package/dist/src/utils/language-detection.js +101 -0
  905. package/dist/src/utils/language-detection.js.map +1 -0
  906. package/dist/src/utils/llm-edit-fixer.d.ts +26 -0
  907. package/dist/src/utils/llm-edit-fixer.js +155 -0
  908. package/dist/src/utils/llm-edit-fixer.js.map +1 -0
  909. package/dist/src/utils/llm-edit-fixer.test.d.ts +6 -0
  910. package/dist/src/utils/llm-edit-fixer.test.js +223 -0
  911. package/dist/src/utils/llm-edit-fixer.test.js.map +1 -0
  912. package/dist/src/utils/memoryDiscovery.d.ts +26 -6
  913. package/dist/src/utils/memoryDiscovery.js +239 -40
  914. package/dist/src/utils/memoryDiscovery.js.map +1 -1
  915. package/dist/src/utils/memoryDiscovery.test.js +365 -44
  916. package/dist/src/utils/memoryDiscovery.test.js.map +1 -1
  917. package/dist/src/utils/memoryImportProcessor.js +19 -25
  918. package/dist/src/utils/memoryImportProcessor.js.map +1 -1
  919. package/dist/src/utils/memoryImportProcessor.test.js +24 -155
  920. package/dist/src/utils/memoryImportProcessor.test.js.map +1 -1
  921. package/dist/src/utils/messageInspectors.d.ts +1 -1
  922. package/dist/src/utils/nextSpeakerChecker.d.ts +3 -3
  923. package/dist/src/utils/nextSpeakerChecker.js +10 -4
  924. package/dist/src/utils/nextSpeakerChecker.js.map +1 -1
  925. package/dist/src/utils/nextSpeakerChecker.test.js +85 -66
  926. package/dist/src/utils/nextSpeakerChecker.test.js.map +1 -1
  927. package/dist/src/utils/package.d.ts +12 -0
  928. package/dist/src/utils/package.js +15 -0
  929. package/dist/src/utils/package.js.map +1 -0
  930. package/dist/src/utils/partUtils.d.ts +22 -1
  931. package/dist/src/utils/partUtils.js +68 -0
  932. package/dist/src/utils/partUtils.js.map +1 -1
  933. package/dist/src/utils/partUtils.test.js +112 -1
  934. package/dist/src/utils/partUtils.test.js.map +1 -1
  935. package/dist/src/utils/pathCorrector.d.ts +25 -0
  936. package/dist/src/utils/pathCorrector.js +33 -0
  937. package/dist/src/utils/pathCorrector.js.map +1 -0
  938. package/dist/src/utils/pathCorrector.test.d.ts +6 -0
  939. package/dist/src/utils/pathCorrector.test.js +83 -0
  940. package/dist/src/utils/pathCorrector.test.js.map +1 -0
  941. package/dist/src/utils/pathReader.d.ts +17 -0
  942. package/dist/src/utils/pathReader.js +92 -0
  943. package/dist/src/utils/pathReader.js.map +1 -0
  944. package/dist/src/utils/pathReader.test.d.ts +6 -0
  945. package/dist/src/utils/pathReader.test.js +406 -0
  946. package/dist/src/utils/pathReader.test.js.map +1 -0
  947. package/dist/src/utils/paths.d.ts +1 -18
  948. package/dist/src/utils/paths.js +133 -57
  949. package/dist/src/utils/paths.js.map +1 -1
  950. package/dist/src/utils/paths.test.js +200 -68
  951. package/dist/src/utils/paths.test.js.map +1 -1
  952. package/dist/src/utils/promptIdContext.d.ts +7 -0
  953. package/dist/src/utils/promptIdContext.js +8 -0
  954. package/dist/src/utils/promptIdContext.js.map +1 -0
  955. package/dist/src/utils/quotaErrorDetection.d.ts +1 -3
  956. package/dist/src/utils/quotaErrorDetection.js +0 -46
  957. package/dist/src/utils/quotaErrorDetection.js.map +1 -1
  958. package/dist/src/utils/retry.d.ts +5 -10
  959. package/dist/src/utils/retry.js +114 -197
  960. package/dist/src/utils/retry.js.map +1 -1
  961. package/dist/src/utils/retry.test.js +196 -130
  962. package/dist/src/utils/retry.test.js.map +1 -1
  963. package/dist/src/utils/safeJsonStringify.d.ts +4 -4
  964. package/dist/src/utils/safeJsonStringify.js +31 -7
  965. package/dist/src/utils/safeJsonStringify.js.map +1 -1
  966. package/dist/src/utils/schemaValidator.js +15 -1
  967. package/dist/src/utils/schemaValidator.js.map +1 -1
  968. package/dist/src/utils/schemaValidator.test.d.ts +6 -0
  969. package/dist/src/utils/schemaValidator.test.js +113 -0
  970. package/dist/src/utils/schemaValidator.test.js.map +1 -0
  971. package/dist/src/utils/session.js +1 -1
  972. package/dist/src/utils/session.js.map +1 -1
  973. package/dist/src/utils/shell-utils.d.ts +21 -3
  974. package/dist/src/utils/shell-utils.js +427 -159
  975. package/dist/src/utils/shell-utils.js.map +1 -1
  976. package/dist/src/utils/shell-utils.test.js +250 -59
  977. package/dist/src/utils/shell-utils.test.js.map +1 -1
  978. package/dist/src/utils/stdio.d.ts +32 -0
  979. package/dist/src/utils/stdio.js +85 -0
  980. package/dist/src/utils/stdio.js.map +1 -0
  981. package/dist/src/utils/stdio.test.d.ts +6 -0
  982. package/dist/src/utils/stdio.test.js +47 -0
  983. package/dist/src/utils/stdio.test.js.map +1 -0
  984. package/dist/src/utils/summarizer.d.ts +6 -4
  985. package/dist/src/utils/summarizer.js +8 -9
  986. package/dist/src/utils/summarizer.js.map +1 -1
  987. package/dist/src/utils/summarizer.test.js +32 -12
  988. package/dist/src/utils/summarizer.test.js.map +1 -1
  989. package/dist/src/utils/systemEncoding.js +7 -6
  990. package/dist/src/utils/systemEncoding.js.map +1 -1
  991. package/dist/src/utils/systemEncoding.test.js +4 -3
  992. package/dist/src/utils/systemEncoding.test.js.map +1 -1
  993. package/dist/src/utils/terminal.d.ts +14 -0
  994. package/dist/src/utils/terminal.js +38 -0
  995. package/dist/src/utils/terminal.js.map +1 -0
  996. package/dist/src/utils/terminalSerializer.d.ts +25 -0
  997. package/dist/src/utils/terminalSerializer.js +432 -0
  998. package/dist/src/utils/terminalSerializer.js.map +1 -0
  999. package/dist/src/utils/terminalSerializer.test.d.ts +6 -0
  1000. package/dist/src/utils/terminalSerializer.test.js +176 -0
  1001. package/dist/src/utils/terminalSerializer.test.js.map +1 -0
  1002. package/dist/src/utils/textUtils.d.ts +5 -0
  1003. package/dist/src/utils/textUtils.js +14 -0
  1004. package/dist/src/utils/textUtils.js.map +1 -1
  1005. package/dist/src/utils/textUtils.test.d.ts +6 -0
  1006. package/dist/src/utils/textUtils.test.js +59 -0
  1007. package/dist/src/utils/textUtils.test.js.map +1 -0
  1008. package/dist/src/utils/thoughtUtils.d.ts +21 -0
  1009. package/dist/src/utils/thoughtUtils.js +39 -0
  1010. package/dist/src/utils/thoughtUtils.js.map +1 -0
  1011. package/dist/src/utils/thoughtUtils.test.d.ts +6 -0
  1012. package/dist/src/utils/thoughtUtils.test.js +78 -0
  1013. package/dist/src/utils/thoughtUtils.test.js.map +1 -0
  1014. package/dist/src/utils/tool-utils.d.ts +19 -0
  1015. package/dist/src/utils/tool-utils.js +67 -0
  1016. package/dist/src/utils/tool-utils.js.map +1 -0
  1017. package/dist/src/utils/tool-utils.test.d.ts +6 -0
  1018. package/dist/src/utils/tool-utils.test.js +69 -0
  1019. package/dist/src/utils/tool-utils.test.js.map +1 -0
  1020. package/dist/src/utils/userAccountManager.d.ts +20 -0
  1021. package/dist/src/utils/userAccountManager.js +115 -0
  1022. package/dist/src/utils/userAccountManager.js.map +1 -0
  1023. package/dist/src/utils/userAccountManager.test.d.ts +6 -0
  1024. package/dist/src/utils/{user_account.test.js → userAccountManager.test.js} +41 -36
  1025. package/dist/src/utils/userAccountManager.test.js.map +1 -0
  1026. package/dist/src/utils/workspaceContext.d.ts +4 -3
  1027. package/dist/src/utils/workspaceContext.js +23 -17
  1028. package/dist/src/utils/workspaceContext.js.map +1 -1
  1029. package/dist/src/utils/workspaceContext.test.js +45 -19
  1030. package/dist/src/utils/workspaceContext.test.js.map +1 -1
  1031. package/dist/tsconfig.tsbuildinfo +1 -1
  1032. package/package.json +24 -10
  1033. package/dist/src/core/modelCheck.d.ts +0 -14
  1034. package/dist/src/core/modelCheck.js +0 -62
  1035. package/dist/src/core/modelCheck.js.map +0 -1
  1036. package/dist/src/test-utils/tools.d.ts +0 -44
  1037. package/dist/src/test-utils/tools.js.map +0 -1
  1038. package/dist/src/utils/flashFallback.integration.test.js +0 -118
  1039. package/dist/src/utils/flashFallback.integration.test.js.map +0 -1
  1040. package/dist/src/utils/user_account.d.ts +0 -9
  1041. package/dist/src/utils/user_account.js +0 -109
  1042. package/dist/src/utils/user_account.js.map +0 -1
  1043. package/dist/src/utils/user_account.test.js.map +0 -1
  1044. package/dist/src/utils/user_id.d.ts +0 -11
  1045. package/dist/src/utils/user_id.js +0 -49
  1046. package/dist/src/utils/user_id.js.map +0 -1
  1047. package/dist/src/utils/user_id.test.js +0 -21
  1048. package/dist/src/utils/user_id.test.js.map +0 -1
  1049. /package/dist/src/{utils/flashFallback.integration.test.d.ts → agents/codebase-investigator.test.d.ts} +0 -0
  1050. /package/dist/src/{utils/user_account.test.d.ts → agents/executor.test.d.ts} +0 -0
  1051. /package/dist/src/{utils/user_id.test.d.ts → agents/invocation.test.d.ts} +0 -0
@@ -3,26 +3,48 @@
3
3
  * Copyright 2025 Google LLC
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
- import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
7
- import { GoogleGenAI, } from '@google/genai';
8
- import { findIndexAfterFraction, GeminiClient } from './client.js';
9
- import { AuthType } from './contentGenerator.js';
10
- import { Config } from '../config/config.js';
11
- import { GeminiEventType, Turn } from './turn.js';
6
+ import { describe, it, expect, vi, beforeEach, afterEach, } from 'vitest';
7
+ import { GeminiClient } from './client.js';
8
+ import { AuthType, } from './contentGenerator.js';
9
+ import {} from './geminiChat.js';
10
+ import { CompressionStatus, GeminiEventType, Turn, } from './turn.js';
12
11
  import { getCoreSystemPrompt } from './prompts.js';
13
12
  import { DEFAULT_GEMINI_FLASH_MODEL } from '../config/models.js';
14
13
  import { FileDiscoveryService } from '../services/fileDiscoveryService.js';
15
14
  import { setSimulate429 } from '../utils/testUtils.js';
16
15
  import { tokenLimit } from './tokenLimits.js';
17
- import { ideContext } from '../ide/ideContext.js';
16
+ import { ideContextStore } from '../ide/ideContext.js';
17
+ import { uiTelemetryService } from '../telemetry/uiTelemetry.js';
18
+ import { ChatCompressionService } from '../services/chatCompressionService.js';
18
19
  import { ClearcutLogger } from '../telemetry/clearcut-logger/clearcut-logger.js';
20
+ vi.mock('../services/chatCompressionService.js');
21
+ // Mock fs module to prevent actual file system operations during tests
22
+ const mockFileSystem = new Map();
23
+ vi.mock('node:fs', () => {
24
+ const fsModule = {
25
+ mkdirSync: vi.fn(),
26
+ writeFileSync: vi.fn((path, data) => {
27
+ mockFileSystem.set(path, data);
28
+ }),
29
+ readFileSync: vi.fn((path) => {
30
+ if (mockFileSystem.has(path)) {
31
+ return mockFileSystem.get(path);
32
+ }
33
+ throw Object.assign(new Error('ENOENT: no such file or directory'), {
34
+ code: 'ENOENT',
35
+ });
36
+ }),
37
+ existsSync: vi.fn((path) => mockFileSystem.has(path)),
38
+ };
39
+ return {
40
+ default: fsModule,
41
+ ...fsModule,
42
+ };
43
+ });
19
44
  // --- Mocks ---
20
- const mockChatCreateFn = vi.fn();
21
- const mockGenerateContentFn = vi.fn();
22
- const mockEmbedContentFn = vi.fn();
23
45
  const mockTurnRunFn = vi.fn();
24
- vi.mock('@google/genai');
25
- vi.mock('./turn', () => {
46
+ vi.mock('./turn', async (importOriginal) => {
47
+ const actual = await importOriginal();
26
48
  // Define a mock class that has the same shape as the real Turn
27
49
  class MockTurn {
28
50
  pendingToolCalls = [];
@@ -34,11 +56,8 @@ vi.mock('./turn', () => {
34
56
  }
35
57
  // Export the mock class as 'Turn'
36
58
  return {
59
+ ...actual,
37
60
  Turn: MockTurn,
38
- GeminiEventType: {
39
- MaxSessionTurns: 'MaxSessionTurns',
40
- ChatCompressed: 'ChatCompressed',
41
- },
42
61
  };
43
62
  });
44
63
  vi.mock('../config/config.js');
@@ -60,80 +79,51 @@ vi.mock('../telemetry/index.js', () => ({
60
79
  logApiError: vi.fn(),
61
80
  }));
62
81
  vi.mock('../ide/ideContext.js');
63
- describe('findIndexAfterFraction', () => {
64
- const history = [
65
- { role: 'user', parts: [{ text: 'This is the first message.' }] }, // JSON length: 66
66
- { role: 'model', parts: [{ text: 'This is the second message.' }] }, // JSON length: 68
67
- { role: 'user', parts: [{ text: 'This is the third message.' }] }, // JSON length: 66
68
- { role: 'model', parts: [{ text: 'This is the fourth message.' }] }, // JSON length: 68
69
- { role: 'user', parts: [{ text: 'This is the fifth message.' }] }, // JSON length: 65
70
- ];
71
- // Total length: 333
72
- it('should throw an error for non-positive numbers', () => {
73
- expect(() => findIndexAfterFraction(history, 0)).toThrow('Fraction must be between 0 and 1');
74
- });
75
- it('should throw an error for a fraction greater than or equal to 1', () => {
76
- expect(() => findIndexAfterFraction(history, 1)).toThrow('Fraction must be between 0 and 1');
77
- });
78
- it('should handle a fraction in the middle', () => {
79
- // 333 * 0.5 = 166.5
80
- // 0: 66
81
- // 1: 66 + 68 = 134
82
- // 2: 134 + 66 = 200
83
- // 200 >= 166.5, so index is 2
84
- expect(findIndexAfterFraction(history, 0.5)).toBe(2);
85
- });
86
- it('should handle a fraction that results in the last index', () => {
87
- // 333 * 0.9 = 299.7
88
- // ...
89
- // 3: 200 + 68 = 268
90
- // 4: 268 + 65 = 333
91
- // 333 >= 299.7, so index is 4
92
- expect(findIndexAfterFraction(history, 0.9)).toBe(4);
93
- });
94
- it('should handle an empty history', () => {
95
- expect(findIndexAfterFraction([], 0.5)).toBe(0);
96
- });
97
- it('should handle a history with only one item', () => {
98
- expect(findIndexAfterFraction(history.slice(0, 1), 0.5)).toBe(0);
99
- });
100
- it('should handle history with weird parts', () => {
101
- const historyWithEmptyParts = [
102
- { role: 'user', parts: [{ text: 'Message 1' }] },
103
- { role: 'model', parts: [{ fileData: { fileUri: 'derp' } }] },
104
- { role: 'user', parts: [{ text: 'Message 2' }] },
105
- ];
106
- expect(findIndexAfterFraction(historyWithEmptyParts, 0.5)).toBe(1);
107
- });
108
- });
82
+ vi.mock('../telemetry/uiTelemetry.js', () => ({
83
+ uiTelemetryService: {
84
+ setLastPromptTokenCount: vi.fn(),
85
+ getLastPromptTokenCount: vi.fn(),
86
+ },
87
+ }));
88
+ /**
89
+ * Array.fromAsync ponyfill, which will be available in es 2024.
90
+ *
91
+ * Buffers an async generator into an array and returns the result.
92
+ */
93
+ async function fromAsync(promise) {
94
+ const results = [];
95
+ for await (const result of promise) {
96
+ results.push(result);
97
+ }
98
+ return results;
99
+ }
109
100
  describe('Gemini Client (client.ts)', () => {
101
+ let mockContentGenerator;
102
+ let mockConfig;
110
103
  let client;
104
+ let mockGenerateContentFn;
111
105
  beforeEach(async () => {
112
106
  vi.resetAllMocks();
113
- // Disable 429 simulation for tests
114
- setSimulate429(false);
115
- // Set up the mock for GoogleGenAI constructor and its methods
116
- const MockedGoogleGenAI = vi.mocked(GoogleGenAI);
117
- MockedGoogleGenAI.mockImplementation(() => {
118
- const mock = {
119
- chats: { create: mockChatCreateFn },
120
- models: {
121
- generateContent: mockGenerateContentFn,
122
- embedContent: mockEmbedContentFn,
123
- },
124
- };
125
- return mock;
107
+ ClearcutLogger.clearInstance();
108
+ vi.mocked(uiTelemetryService.setLastPromptTokenCount).mockClear();
109
+ vi.mocked(ChatCompressionService.prototype.compress).mockResolvedValue({
110
+ newHistory: null,
111
+ info: {
112
+ originalTokenCount: 0,
113
+ newTokenCount: 0,
114
+ compressionStatus: CompressionStatus.NOOP,
115
+ },
126
116
  });
127
- mockChatCreateFn.mockResolvedValue({});
128
- mockGenerateContentFn.mockResolvedValue({
129
- candidates: [
130
- {
131
- content: {
132
- parts: [{ text: '{"key": "value"}' }],
133
- },
134
- },
135
- ],
117
+ mockGenerateContentFn = vi.fn().mockResolvedValue({
118
+ candidates: [{ content: { parts: [{ text: '{"key": "value"}' }] } }],
136
119
  });
120
+ // Disable 429 simulation for tests
121
+ setSimulate429(false);
122
+ mockContentGenerator = {
123
+ generateContent: mockGenerateContentFn,
124
+ generateContentStream: vi.fn(),
125
+ batchEmbedContents: vi.fn(),
126
+ };
137
127
  // Because the GeminiClient constructor kicks off an async process (startChat)
138
128
  // that depends on a fully-formed Config object, we need to mock the
139
129
  // entire implementation of Config for these tests.
@@ -143,12 +133,11 @@ describe('Gemini Client (client.ts)', () => {
143
133
  };
144
134
  const fileService = new FileDiscoveryService('/test/dir');
145
135
  const contentGeneratorConfig = {
146
- model: 'test-model',
147
136
  apiKey: 'test-key',
148
137
  vertexai: false,
149
138
  authType: AuthType.USE_GEMINI,
150
139
  };
151
- const mockConfigObject = {
140
+ mockConfig = {
152
141
  getContentGeneratorConfig: vi
153
142
  .fn()
154
143
  .mockReturnValue(contentGeneratorConfig),
@@ -159,7 +148,6 @@ describe('Gemini Client (client.ts)', () => {
159
148
  getVertexAI: vi.fn().mockReturnValue(false),
160
149
  getUserAgent: vi.fn().mockReturnValue('test-agent'),
161
150
  getUserMemory: vi.fn().mockReturnValue(''),
162
- getFullContext: vi.fn().mockReturnValue(false),
163
151
  getSessionId: vi.fn().mockReturnValue('test-session-id'),
164
152
  getProxy: vi.fn().mockReturnValue(undefined),
165
153
  getWorkingDir: vi.fn().mockReturnValue('/test/dir'),
@@ -172,185 +160,54 @@ describe('Gemini Client (client.ts)', () => {
172
160
  getIdeModeFeature: vi.fn().mockReturnValue(false),
173
161
  getIdeMode: vi.fn().mockReturnValue(true),
174
162
  getDebugMode: vi.fn().mockReturnValue(false),
163
+ getPreviewFeatures: vi.fn().mockReturnValue(false),
175
164
  getWorkspaceContext: vi.fn().mockReturnValue({
176
165
  getDirectories: vi.fn().mockReturnValue(['/test/dir']),
177
166
  }),
178
167
  getGeminiClient: vi.fn(),
168
+ getModelRouterService: vi.fn().mockReturnValue({
169
+ route: vi.fn().mockResolvedValue({ model: 'default-routed-model' }),
170
+ }),
171
+ isInFallbackMode: vi.fn().mockReturnValue(false),
179
172
  setFallbackMode: vi.fn(),
180
173
  getChatCompression: vi.fn().mockReturnValue(undefined),
181
174
  getSkipNextSpeakerCheck: vi.fn().mockReturnValue(false),
175
+ getUseSmartEdit: vi.fn().mockReturnValue(false),
176
+ getUseModelRouter: vi.fn().mockReturnValue(false),
177
+ getShowModelInfoInChat: vi.fn().mockReturnValue(false),
178
+ getContinueOnFailedApiCall: vi.fn(),
179
+ getProjectRoot: vi.fn().mockReturnValue('/test/project/root'),
180
+ storage: {
181
+ getProjectTempDir: vi.fn().mockReturnValue('/test/temp'),
182
+ },
183
+ getContentGenerator: vi.fn().mockReturnValue(mockContentGenerator),
184
+ getBaseLlmClient: vi.fn().mockReturnValue({
185
+ generateJson: vi.fn().mockResolvedValue({
186
+ next_speaker: 'user',
187
+ reasoning: 'test',
188
+ }),
189
+ }),
190
+ modelConfigService: {
191
+ getResolvedConfig(modelConfigKey) {
192
+ return {
193
+ model: modelConfigKey.model,
194
+ generateContentConfig: {
195
+ temperature: 0,
196
+ topP: 1,
197
+ },
198
+ };
199
+ },
200
+ },
201
+ isInteractive: vi.fn().mockReturnValue(false),
182
202
  };
183
- const MockedConfig = vi.mocked(Config, true);
184
- MockedConfig.mockImplementation(() => mockConfigObject);
185
- // We can instantiate the client here since Config is mocked
186
- // and the constructor will use the mocked GoogleGenAI
187
- client = new GeminiClient(new Config({ sessionId: 'test-session-id' }));
188
- mockConfigObject.getGeminiClient.mockReturnValue(client);
189
- await client.initialize(contentGeneratorConfig);
203
+ client = new GeminiClient(mockConfig);
204
+ await client.initialize();
205
+ vi.mocked(mockConfig.getGeminiClient).mockReturnValue(client);
206
+ vi.mocked(uiTelemetryService.setLastPromptTokenCount).mockClear();
190
207
  });
191
208
  afterEach(() => {
192
209
  vi.restoreAllMocks();
193
210
  });
194
- // NOTE: The following tests for startChat were removed due to persistent issues with
195
- // the @google/genai mock. Specifically, the mockChatCreateFn (representing instance.chats.create)
196
- // was not being detected as called by the GeminiClient instance.
197
- // This likely points to a subtle issue in how the GoogleGenerativeAI class constructor
198
- // and its instance methods are mocked and then used by the class under test.
199
- // For future debugging, ensure that the `this.client` in `GeminiClient` (which is an
200
- // instance of the mocked GoogleGenerativeAI) correctly has its `chats.create` method
201
- // pointing to `mockChatCreateFn`.
202
- // it('startChat should call getCoreSystemPrompt with userMemory and pass to chats.create', async () => { ... });
203
- // it('startChat should call getCoreSystemPrompt with empty string if userMemory is empty', async () => { ... });
204
- // NOTE: The following tests for generateJson were removed due to persistent issues with
205
- // the @google/genai mock, similar to the startChat tests. The mockGenerateContentFn
206
- // (representing instance.models.generateContent) was not being detected as called, or the mock
207
- // was not preventing an actual API call (leading to API key errors).
208
- // For future debugging, ensure `this.client.models.generateContent` in `GeminiClient` correctly
209
- // uses the `mockGenerateContentFn`.
210
- // it('generateJson should call getCoreSystemPrompt with userMemory and pass to generateContent', async () => { ... });
211
- // it('generateJson should call getCoreSystemPrompt with empty string if userMemory is empty', async () => { ... });
212
- describe('generateEmbedding', () => {
213
- const texts = ['hello world', 'goodbye world'];
214
- const testEmbeddingModel = 'test-embedding-model';
215
- it('should call embedContent with correct parameters and return embeddings', async () => {
216
- const mockEmbeddings = [
217
- [0.1, 0.2, 0.3],
218
- [0.4, 0.5, 0.6],
219
- ];
220
- const mockResponse = {
221
- embeddings: [
222
- { values: mockEmbeddings[0] },
223
- { values: mockEmbeddings[1] },
224
- ],
225
- };
226
- mockEmbedContentFn.mockResolvedValue(mockResponse);
227
- const result = await client.generateEmbedding(texts);
228
- expect(mockEmbedContentFn).toHaveBeenCalledTimes(1);
229
- expect(mockEmbedContentFn).toHaveBeenCalledWith({
230
- model: testEmbeddingModel,
231
- contents: texts,
232
- });
233
- expect(result).toEqual(mockEmbeddings);
234
- });
235
- it('should return an empty array if an empty array is passed', async () => {
236
- const result = await client.generateEmbedding([]);
237
- expect(result).toEqual([]);
238
- expect(mockEmbedContentFn).not.toHaveBeenCalled();
239
- });
240
- it('should throw an error if API response has no embeddings array', async () => {
241
- mockEmbedContentFn.mockResolvedValue({}); // No `embeddings` key
242
- await expect(client.generateEmbedding(texts)).rejects.toThrow('No embeddings found in API response.');
243
- });
244
- it('should throw an error if API response has an empty embeddings array', async () => {
245
- const mockResponse = {
246
- embeddings: [],
247
- };
248
- mockEmbedContentFn.mockResolvedValue(mockResponse);
249
- await expect(client.generateEmbedding(texts)).rejects.toThrow('No embeddings found in API response.');
250
- });
251
- it('should throw an error if API returns a mismatched number of embeddings', async () => {
252
- const mockResponse = {
253
- embeddings: [{ values: [1, 2, 3] }], // Only one for two texts
254
- };
255
- mockEmbedContentFn.mockResolvedValue(mockResponse);
256
- await expect(client.generateEmbedding(texts)).rejects.toThrow('API returned a mismatched number of embeddings. Expected 2, got 1.');
257
- });
258
- it('should throw an error if any embedding has nullish values', async () => {
259
- const mockResponse = {
260
- embeddings: [{ values: [1, 2, 3] }, { values: undefined }], // Second one is bad
261
- };
262
- mockEmbedContentFn.mockResolvedValue(mockResponse);
263
- await expect(client.generateEmbedding(texts)).rejects.toThrow('API returned an empty embedding for input text at index 1: "goodbye world"');
264
- });
265
- it('should throw an error if any embedding has an empty values array', async () => {
266
- const mockResponse = {
267
- embeddings: [{ values: [] }, { values: [1, 2, 3] }], // First one is bad
268
- };
269
- mockEmbedContentFn.mockResolvedValue(mockResponse);
270
- await expect(client.generateEmbedding(texts)).rejects.toThrow('API returned an empty embedding for input text at index 0: "hello world"');
271
- });
272
- it('should propagate errors from the API call', async () => {
273
- const apiError = new Error('API Failure');
274
- mockEmbedContentFn.mockRejectedValue(apiError);
275
- await expect(client.generateEmbedding(texts)).rejects.toThrow('API Failure');
276
- });
277
- });
278
- describe('generateContent', () => {
279
- it('should call generateContent with the correct parameters', async () => {
280
- const contents = [{ role: 'user', parts: [{ text: 'hello' }] }];
281
- const generationConfig = { temperature: 0.5 };
282
- const abortSignal = new AbortController().signal;
283
- // Mock countTokens
284
- const mockGenerator = {
285
- countTokens: vi.fn().mockResolvedValue({ totalTokens: 1 }),
286
- generateContent: mockGenerateContentFn,
287
- };
288
- client['contentGenerator'] = mockGenerator;
289
- await client.generateContent(contents, generationConfig, abortSignal);
290
- expect(mockGenerateContentFn).toHaveBeenCalledWith({
291
- model: 'test-model',
292
- config: {
293
- abortSignal,
294
- systemInstruction: getCoreSystemPrompt(''),
295
- temperature: 0.5,
296
- topP: 1,
297
- },
298
- contents,
299
- }, 'test-session-id');
300
- });
301
- });
302
- describe('generateJson', () => {
303
- it('should call generateContent with the correct parameters', async () => {
304
- const contents = [{ role: 'user', parts: [{ text: 'hello' }] }];
305
- const schema = { type: 'string' };
306
- const abortSignal = new AbortController().signal;
307
- // Mock countTokens
308
- const mockGenerator = {
309
- countTokens: vi.fn().mockResolvedValue({ totalTokens: 1 }),
310
- generateContent: mockGenerateContentFn,
311
- };
312
- client['contentGenerator'] = mockGenerator;
313
- await client.generateJson(contents, schema, abortSignal);
314
- expect(mockGenerateContentFn).toHaveBeenCalledWith({
315
- model: 'test-model', // Should use current model from config
316
- config: {
317
- abortSignal,
318
- systemInstruction: getCoreSystemPrompt(''),
319
- temperature: 0,
320
- topP: 1,
321
- responseJsonSchema: schema,
322
- responseMimeType: 'application/json',
323
- },
324
- contents,
325
- }, 'test-session-id');
326
- });
327
- it('should allow overriding model and config', async () => {
328
- const contents = [{ role: 'user', parts: [{ text: 'hello' }] }];
329
- const schema = { type: 'string' };
330
- const abortSignal = new AbortController().signal;
331
- const customModel = 'custom-json-model';
332
- const customConfig = { temperature: 0.9, topK: 20 };
333
- const mockGenerator = {
334
- countTokens: vi.fn().mockResolvedValue({ totalTokens: 1 }),
335
- generateContent: mockGenerateContentFn,
336
- };
337
- client['contentGenerator'] = mockGenerator;
338
- await client.generateJson(contents, schema, abortSignal, customModel, customConfig);
339
- expect(mockGenerateContentFn).toHaveBeenCalledWith({
340
- model: customModel,
341
- config: {
342
- abortSignal,
343
- systemInstruction: getCoreSystemPrompt(''),
344
- temperature: 0.9,
345
- topP: 1, // from default
346
- topK: 20,
347
- responseJsonSchema: schema,
348
- responseMimeType: 'application/json',
349
- },
350
- contents,
351
- }, 'test-session-id');
352
- });
353
- });
354
211
  describe('addHistory', () => {
355
212
  it('should call chat.addHistory with the provided content', async () => {
356
213
  const mockChat = {
@@ -387,180 +244,232 @@ describe('Gemini Client (client.ts)', () => {
387
244
  expect(JSON.stringify(newHistory)).not.toContain('some old message');
388
245
  });
389
246
  });
247
+ describe('startChat', () => {
248
+ it('should include environment context when resuming a session', async () => {
249
+ const extraHistory = [
250
+ { role: 'user', parts: [{ text: 'Old message' }] },
251
+ { role: 'model', parts: [{ text: 'Old response' }] },
252
+ ];
253
+ const chat = await client.startChat(extraHistory);
254
+ const history = chat.getHistory();
255
+ // The first message should be the environment context
256
+ expect(history[0].role).toBe('user');
257
+ expect(history[0].parts?.[0]?.text).toContain('This is the Gemini CLI');
258
+ expect(history[0].parts?.[0]?.text).toContain("The project's temporary directory is:");
259
+ // The subsequent messages should be the extra history
260
+ expect(history[1]).toEqual(extraHistory[0]);
261
+ expect(history[2]).toEqual(extraHistory[1]);
262
+ });
263
+ });
390
264
  describe('tryCompressChat', () => {
391
- const mockCountTokens = vi.fn();
392
- const mockSendMessage = vi.fn();
393
265
  const mockGetHistory = vi.fn();
394
266
  beforeEach(() => {
395
267
  vi.mock('./tokenLimits', () => ({
396
268
  tokenLimit: vi.fn(),
397
269
  }));
398
- client['contentGenerator'] = {
399
- countTokens: mockCountTokens,
400
- };
401
270
  client['chat'] = {
402
271
  getHistory: mockGetHistory,
403
272
  addHistory: vi.fn(),
404
273
  setHistory: vi.fn(),
405
- sendMessage: mockSendMessage,
274
+ getLastPromptTokenCount: vi.fn(),
406
275
  };
407
276
  });
408
- it('should not trigger summarization if token count is below threshold', async () => {
409
- const MOCKED_TOKEN_LIMIT = 1000;
410
- vi.mocked(tokenLimit).mockReturnValue(MOCKED_TOKEN_LIMIT);
411
- mockGetHistory.mockReturnValue([
412
- { role: 'user', parts: [{ text: '...history...' }] },
413
- ]);
414
- mockCountTokens.mockResolvedValue({
415
- totalTokens: MOCKED_TOKEN_LIMIT * 0.699, // TOKEN_THRESHOLD_FOR_SUMMARIZATION = 0.7
416
- });
417
- const initialChat = client.getChat();
418
- const result = await client.tryCompressChat('prompt-id-2');
419
- const newChat = client.getChat();
420
- expect(tokenLimit).toHaveBeenCalled();
421
- expect(result).toBeNull();
422
- expect(newChat).toBe(initialChat);
423
- });
424
- it('logs a telemetry event when compressing', async () => {
425
- vi.spyOn(ClearcutLogger.prototype, 'logChatCompressionEvent');
426
- const MOCKED_TOKEN_LIMIT = 1000;
427
- const MOCKED_CONTEXT_PERCENTAGE_THRESHOLD = 0.5;
428
- vi.mocked(tokenLimit).mockReturnValue(MOCKED_TOKEN_LIMIT);
429
- vi.spyOn(client['config'], 'getChatCompression').mockReturnValue({
430
- contextPercentageThreshold: MOCKED_CONTEXT_PERCENTAGE_THRESHOLD,
277
+ function setup({ chatHistory = [
278
+ { role: 'user', parts: [{ text: 'Long conversation' }] },
279
+ { role: 'model', parts: [{ text: 'Long response' }] },
280
+ ], originalTokenCount = 1000, newTokenCount = 500, compressionStatus = CompressionStatus.COMPRESSED, } = {}) {
281
+ const mockOriginalChat = {
282
+ getHistory: vi.fn((_curated) => chatHistory),
283
+ setHistory: vi.fn(),
284
+ getLastPromptTokenCount: vi.fn().mockReturnValue(originalTokenCount),
285
+ };
286
+ client['chat'] = mockOriginalChat;
287
+ vi.mocked(uiTelemetryService.getLastPromptTokenCount).mockReturnValue(originalTokenCount);
288
+ const newHistory = [
289
+ { role: 'user', parts: [{ text: 'Summary' }] },
290
+ { role: 'model', parts: [{ text: 'Got it' }] },
291
+ ];
292
+ vi.mocked(ChatCompressionService.prototype.compress).mockResolvedValue({
293
+ newHistory: compressionStatus === CompressionStatus.COMPRESSED
294
+ ? newHistory
295
+ : null,
296
+ info: {
297
+ originalTokenCount,
298
+ newTokenCount,
299
+ compressionStatus,
300
+ },
431
301
  });
432
- mockGetHistory.mockReturnValue([
433
- { role: 'user', parts: [{ text: '...history...' }] },
434
- ]);
435
- const originalTokenCount = MOCKED_TOKEN_LIMIT * MOCKED_CONTEXT_PERCENTAGE_THRESHOLD;
436
- const newTokenCount = 100;
437
- mockCountTokens
438
- .mockResolvedValueOnce({ totalTokens: originalTokenCount }) // First call for the check
439
- .mockResolvedValueOnce({ totalTokens: newTokenCount }); // Second call for the new history
440
- // Mock the summary response from the chat
441
- mockSendMessage.mockResolvedValue({
442
- role: 'model',
443
- parts: [{ text: 'This is a summary.' }],
302
+ const mockNewChat = {
303
+ getHistory: vi.fn().mockReturnValue(newHistory),
304
+ setHistory: vi.fn(),
305
+ getLastPromptTokenCount: vi.fn().mockReturnValue(newTokenCount),
306
+ };
307
+ client['startChat'] = vi
308
+ .fn()
309
+ .mockResolvedValue(mockNewChat);
310
+ return {
311
+ client,
312
+ mockOriginalChat,
313
+ mockNewChat,
314
+ estimatedNewTokenCount: newTokenCount,
315
+ };
316
+ }
317
+ describe('when compression inflates the token count', () => {
318
+ it('allows compression to be forced/manual after a failure', async () => {
319
+ // Call 1 (Fails): Setup with inflated tokens
320
+ setup({
321
+ originalTokenCount: 100,
322
+ newTokenCount: 200,
323
+ compressionStatus: CompressionStatus.COMPRESSION_FAILED_INFLATED_TOKEN_COUNT,
324
+ });
325
+ await client.tryCompressChat('prompt-id-4', false); // Fails
326
+ // Call 2 (Forced): Re-setup with compressed tokens
327
+ const { estimatedNewTokenCount: compressedTokenCount } = setup({
328
+ originalTokenCount: 100,
329
+ newTokenCount: 50,
330
+ compressionStatus: CompressionStatus.COMPRESSED,
331
+ });
332
+ const result = await client.tryCompressChat('prompt-id-4', true); // Forced
333
+ expect(result).toEqual({
334
+ compressionStatus: CompressionStatus.COMPRESSED,
335
+ newTokenCount: compressedTokenCount,
336
+ originalTokenCount: 100,
337
+ });
444
338
  });
445
- await client.tryCompressChat('prompt-id-3');
446
- expect(ClearcutLogger.prototype.logChatCompressionEvent).toHaveBeenCalledWith(expect.objectContaining({
447
- tokens_before: originalTokenCount,
448
- tokens_after: newTokenCount,
449
- }));
450
- });
451
- it('should trigger summarization if token count is at threshold with contextPercentageThreshold setting', async () => {
452
- const MOCKED_TOKEN_LIMIT = 1000;
453
- const MOCKED_CONTEXT_PERCENTAGE_THRESHOLD = 0.5;
454
- vi.mocked(tokenLimit).mockReturnValue(MOCKED_TOKEN_LIMIT);
455
- vi.spyOn(client['config'], 'getChatCompression').mockReturnValue({
456
- contextPercentageThreshold: MOCKED_CONTEXT_PERCENTAGE_THRESHOLD,
339
+ it('yields the result even if the compression inflated the tokens', async () => {
340
+ const { client, estimatedNewTokenCount } = setup({
341
+ originalTokenCount: 100,
342
+ newTokenCount: 200,
343
+ compressionStatus: CompressionStatus.COMPRESSION_FAILED_INFLATED_TOKEN_COUNT,
344
+ });
345
+ const result = await client.tryCompressChat('prompt-id-4', false);
346
+ expect(result).toEqual({
347
+ compressionStatus: CompressionStatus.COMPRESSION_FAILED_INFLATED_TOKEN_COUNT,
348
+ newTokenCount: estimatedNewTokenCount,
349
+ originalTokenCount: 100,
350
+ });
351
+ // IMPORTANT: The change in client.ts means setLastPromptTokenCount is NOT called on failure
352
+ expect(uiTelemetryService.setLastPromptTokenCount).not.toHaveBeenCalled();
457
353
  });
458
- mockGetHistory.mockReturnValue([
459
- { role: 'user', parts: [{ text: '...history...' }] },
460
- ]);
461
- const originalTokenCount = MOCKED_TOKEN_LIMIT * MOCKED_CONTEXT_PERCENTAGE_THRESHOLD;
462
- const newTokenCount = 100;
463
- mockCountTokens
464
- .mockResolvedValueOnce({ totalTokens: originalTokenCount }) // First call for the check
465
- .mockResolvedValueOnce({ totalTokens: newTokenCount }); // Second call for the new history
466
- // Mock the summary response from the chat
467
- mockSendMessage.mockResolvedValue({
468
- role: 'model',
469
- parts: [{ text: 'This is a summary.' }],
354
+ it('does not manipulate the source chat', async () => {
355
+ const { client, mockOriginalChat } = setup({
356
+ originalTokenCount: 100,
357
+ newTokenCount: 200,
358
+ compressionStatus: CompressionStatus.COMPRESSION_FAILED_INFLATED_TOKEN_COUNT,
359
+ });
360
+ await client.tryCompressChat('prompt-id-4', false);
361
+ // On failure, the chat should NOT be replaced
362
+ expect(client['chat']).toBe(mockOriginalChat);
470
363
  });
471
- const initialChat = client.getChat();
472
- const result = await client.tryCompressChat('prompt-id-3');
473
- const newChat = client.getChat();
474
- expect(tokenLimit).toHaveBeenCalled();
475
- expect(mockSendMessage).toHaveBeenCalled();
476
- // Assert that summarization happened and returned the correct stats
477
- expect(result).toEqual({
478
- originalTokenCount,
479
- newTokenCount,
364
+ it.skip('will not attempt to compress context after a failure', async () => {
365
+ const { client } = setup({
366
+ originalTokenCount: 100,
367
+ newTokenCount: 200,
368
+ compressionStatus: CompressionStatus.COMPRESSION_FAILED_INFLATED_TOKEN_COUNT,
369
+ });
370
+ await client.tryCompressChat('prompt-id-4', false); // This fails and sets hasFailedCompressionAttempt = true
371
+ // Mock the next call to return NOOP
372
+ vi.mocked(ChatCompressionService.prototype.compress).mockResolvedValueOnce({
373
+ newHistory: null,
374
+ info: {
375
+ originalTokenCount: 0,
376
+ newTokenCount: 0,
377
+ compressionStatus: CompressionStatus.NOOP,
378
+ },
379
+ });
380
+ // This call should now be a NOOP
381
+ const result = await client.tryCompressChat('prompt-id-5', false);
382
+ expect(result.compressionStatus).toBe(CompressionStatus.NOOP);
383
+ expect(ChatCompressionService.prototype.compress).toHaveBeenCalledTimes(2);
384
+ expect(ChatCompressionService.prototype.compress).toHaveBeenLastCalledWith(expect.anything(), 'prompt-id-5', false, expect.anything(), expect.anything(), true);
480
385
  });
481
- // Assert that the chat was reset
482
- expect(newChat).not.toBe(initialChat);
483
386
  });
484
- it('should not compress across a function call response', async () => {
387
+ it('should not trigger summarization if token count is below threshold', async () => {
485
388
  const MOCKED_TOKEN_LIMIT = 1000;
486
- vi.mocked(tokenLimit).mockReturnValue(MOCKED_TOKEN_LIMIT);
487
- mockGetHistory.mockReturnValue([
488
- { role: 'user', parts: [{ text: '...history 1...' }] },
489
- { role: 'model', parts: [{ text: '...history 2...' }] },
490
- { role: 'user', parts: [{ text: '...history 3...' }] },
491
- { role: 'model', parts: [{ text: '...history 4...' }] },
492
- { role: 'user', parts: [{ text: '...history 5...' }] },
493
- { role: 'model', parts: [{ text: '...history 6...' }] },
494
- { role: 'user', parts: [{ text: '...history 7...' }] },
495
- { role: 'model', parts: [{ text: '...history 8...' }] },
496
- // Normally we would break here, but we have a function response.
497
- {
498
- role: 'user',
499
- parts: [{ functionResponse: { name: '...history 8...' } }],
389
+ const originalTokenCount = MOCKED_TOKEN_LIMIT * 0.699;
390
+ vi.mocked(ChatCompressionService.prototype.compress).mockResolvedValue({
391
+ newHistory: null,
392
+ info: {
393
+ originalTokenCount,
394
+ newTokenCount: originalTokenCount,
395
+ compressionStatus: CompressionStatus.NOOP,
500
396
  },
501
- { role: 'model', parts: [{ text: '...history 10...' }] },
502
- // Instead we will break here.
503
- { role: 'user', parts: [{ text: '...history 10...' }] },
504
- ]);
505
- const originalTokenCount = 1000 * 0.7;
506
- const newTokenCount = 100;
507
- mockCountTokens
508
- .mockResolvedValueOnce({ totalTokens: originalTokenCount }) // First call for the check
509
- .mockResolvedValueOnce({ totalTokens: newTokenCount }); // Second call for the new history
510
- // Mock the summary response from the chat
511
- mockSendMessage.mockResolvedValue({
512
- role: 'model',
513
- parts: [{ text: 'This is a summary.' }],
514
397
  });
515
398
  const initialChat = client.getChat();
516
- const result = await client.tryCompressChat('prompt-id-3');
399
+ const result = await client.tryCompressChat('prompt-id-2', false);
517
400
  const newChat = client.getChat();
518
- expect(tokenLimit).toHaveBeenCalled();
519
- expect(mockSendMessage).toHaveBeenCalled();
520
- // Assert that summarization happened and returned the correct stats
521
401
  expect(result).toEqual({
402
+ compressionStatus: CompressionStatus.NOOP,
403
+ newTokenCount: originalTokenCount,
522
404
  originalTokenCount,
523
- newTokenCount,
524
405
  });
525
- // Assert that the chat was reset
526
- expect(newChat).not.toBe(initialChat);
527
- // 1. standard start context message
528
- // 2. standard canned user start message
529
- // 3. compressed summary message
530
- // 4. standard canned user summary message
531
- // 5. The last user message (not the last 3 because that would start with a function response)
532
- expect(newChat.getHistory().length).toEqual(5);
406
+ expect(newChat).toBe(initialChat);
533
407
  });
534
- it('should always trigger summarization when force is true, regardless of token count', async () => {
535
- mockGetHistory.mockReturnValue([
536
- { role: 'user', parts: [{ text: '...history...' }] },
537
- ]);
538
- const originalTokenCount = 10; // Well below threshold
539
- const newTokenCount = 5;
540
- mockCountTokens
541
- .mockResolvedValueOnce({ totalTokens: originalTokenCount })
542
- .mockResolvedValueOnce({ totalTokens: newTokenCount });
543
- // Mock the summary response from the chat
544
- mockSendMessage.mockResolvedValue({
545
- role: 'model',
546
- parts: [{ text: 'This is a summary.' }],
408
+ it('should return NOOP if history is too short to compress', async () => {
409
+ const { client } = setup({
410
+ chatHistory: [{ role: 'user', parts: [{ text: 'hi' }] }],
411
+ originalTokenCount: 50,
412
+ newTokenCount: 50,
413
+ compressionStatus: CompressionStatus.NOOP,
547
414
  });
548
- const initialChat = client.getChat();
549
- const result = await client.tryCompressChat('prompt-id-1', true); // force = true
550
- const newChat = client.getChat();
551
- expect(mockSendMessage).toHaveBeenCalled();
415
+ const result = await client.tryCompressChat('prompt-id-noop', false);
552
416
  expect(result).toEqual({
553
- originalTokenCount,
554
- newTokenCount,
417
+ compressionStatus: CompressionStatus.NOOP,
418
+ originalTokenCount: 50,
419
+ newTokenCount: 50,
555
420
  });
556
- // Assert that the chat was reset
557
- expect(newChat).not.toBe(initialChat);
558
421
  });
559
422
  });
560
423
  describe('sendMessageStream', () => {
424
+ it('emits a compression event when the context was automatically compressed', async () => {
425
+ // Arrange
426
+ mockTurnRunFn.mockReturnValue((async function* () {
427
+ yield { type: 'content', value: 'Hello' };
428
+ })());
429
+ const compressionInfo = {
430
+ compressionStatus: CompressionStatus.COMPRESSED,
431
+ originalTokenCount: 1000,
432
+ newTokenCount: 500,
433
+ };
434
+ vi.spyOn(client, 'tryCompressChat').mockResolvedValueOnce(compressionInfo);
435
+ // Act
436
+ const stream = client.sendMessageStream([{ text: 'Hi' }], new AbortController().signal, 'prompt-id-1');
437
+ const events = await fromAsync(stream);
438
+ // Assert
439
+ expect(events).toContainEqual({
440
+ type: GeminiEventType.ChatCompressed,
441
+ value: compressionInfo,
442
+ });
443
+ });
444
+ it.each([
445
+ {
446
+ compressionStatus: CompressionStatus.COMPRESSION_FAILED_INFLATED_TOKEN_COUNT,
447
+ },
448
+ { compressionStatus: CompressionStatus.NOOP },
449
+ ])('does not emit a compression event when the status is $compressionStatus', async ({ compressionStatus }) => {
450
+ // Arrange
451
+ const mockStream = (async function* () {
452
+ yield { type: 'content', value: 'Hello' };
453
+ })();
454
+ mockTurnRunFn.mockReturnValue(mockStream);
455
+ const compressionInfo = {
456
+ compressionStatus,
457
+ originalTokenCount: 1000,
458
+ newTokenCount: 500,
459
+ };
460
+ vi.spyOn(client, 'tryCompressChat').mockResolvedValueOnce(compressionInfo);
461
+ // Act
462
+ const stream = client.sendMessageStream([{ text: 'Hi' }], new AbortController().signal, 'prompt-id-1');
463
+ const events = await fromAsync(stream);
464
+ // Assert
465
+ expect(events).not.toContainEqual({
466
+ type: GeminiEventType.ChatCompressed,
467
+ value: expect.anything(),
468
+ });
469
+ });
561
470
  it('should include editor context when ideMode is enabled', async () => {
562
471
  // Arrange
563
- vi.mocked(ideContext.getIdeContext).mockReturnValue({
472
+ vi.mocked(ideContextStore.get).mockReturnValue({
564
473
  workspaceState: {
565
474
  openFiles: [
566
475
  {
@@ -581,21 +490,21 @@ describe('Gemini Client (client.ts)', () => {
581
490
  ],
582
491
  },
583
492
  });
584
- vi.spyOn(client['config'], 'getIdeMode').mockReturnValue(true);
585
- const mockStream = (async function* () {
493
+ vi.mocked(mockConfig.getIdeMode).mockReturnValue(true);
494
+ vi.spyOn(client, 'tryCompressChat').mockResolvedValue({
495
+ originalTokenCount: 0,
496
+ newTokenCount: 0,
497
+ compressionStatus: CompressionStatus.COMPRESSED,
498
+ });
499
+ mockTurnRunFn.mockReturnValue((async function* () {
586
500
  yield { type: 'content', value: 'Hello' };
587
- })();
588
- mockTurnRunFn.mockReturnValue(mockStream);
501
+ })());
589
502
  const mockChat = {
590
503
  addHistory: vi.fn(),
591
504
  getHistory: vi.fn().mockReturnValue([]),
505
+ getLastPromptTokenCount: vi.fn(),
592
506
  };
593
507
  client['chat'] = mockChat;
594
- const mockGenerator = {
595
- countTokens: vi.fn().mockResolvedValue({ totalTokens: 0 }),
596
- generateContent: mockGenerateContentFn,
597
- };
598
- client['contentGenerator'] = mockGenerator;
599
508
  const initialRequest = [{ text: 'Hi' }];
600
509
  // Act
601
510
  const stream = client.sendMessageStream(initialRequest, new AbortController().signal, 'prompt-id-ide');
@@ -603,7 +512,7 @@ describe('Gemini Client (client.ts)', () => {
603
512
  // consume stream
604
513
  }
605
514
  // Assert
606
- expect(ideContext.getIdeContext).toHaveBeenCalled();
515
+ expect(ideContextStore.get).toHaveBeenCalled();
607
516
  const expectedContext = `
608
517
  Here is the user's editor context as a JSON object. This is for your information only.
609
518
  \`\`\`json
@@ -628,7 +537,7 @@ ${JSON.stringify({
628
537
  });
629
538
  it('should not add context if ideMode is enabled but no open files', async () => {
630
539
  // Arrange
631
- vi.mocked(ideContext.getIdeContext).mockReturnValue({
540
+ vi.mocked(ideContextStore.get).mockReturnValue({
632
541
  workspaceState: {
633
542
  openFiles: [],
634
543
  },
@@ -641,13 +550,9 @@ ${JSON.stringify({
641
550
  const mockChat = {
642
551
  addHistory: vi.fn(),
643
552
  getHistory: vi.fn().mockReturnValue([]),
553
+ getLastPromptTokenCount: vi.fn(),
644
554
  };
645
555
  client['chat'] = mockChat;
646
- const mockGenerator = {
647
- countTokens: vi.fn().mockResolvedValue({ totalTokens: 0 }),
648
- generateContent: mockGenerateContentFn,
649
- };
650
- client['contentGenerator'] = mockGenerator;
651
556
  const initialRequest = [{ text: 'Hi' }];
652
557
  // Act
653
558
  const stream = client.sendMessageStream(initialRequest, new AbortController().signal, 'prompt-id-ide');
@@ -655,12 +560,12 @@ ${JSON.stringify({
655
560
  // consume stream
656
561
  }
657
562
  // Assert
658
- expect(ideContext.getIdeContext).toHaveBeenCalled();
659
- expect(mockTurnRunFn).toHaveBeenCalledWith(initialRequest, expect.any(Object));
563
+ expect(ideContextStore.get).toHaveBeenCalled();
564
+ expect(mockTurnRunFn).toHaveBeenCalledWith({ model: 'default-routed-model' }, initialRequest, expect.any(AbortSignal));
660
565
  });
661
566
  it('should add context if ideMode is enabled and there is one active file', async () => {
662
567
  // Arrange
663
- vi.mocked(ideContext.getIdeContext).mockReturnValue({
568
+ vi.mocked(ideContextStore.get).mockReturnValue({
664
569
  workspaceState: {
665
570
  openFiles: [
666
571
  {
@@ -674,6 +579,11 @@ ${JSON.stringify({
674
579
  },
675
580
  });
676
581
  vi.spyOn(client['config'], 'getIdeMode').mockReturnValue(true);
582
+ vi.spyOn(client, 'tryCompressChat').mockResolvedValue({
583
+ originalTokenCount: 0,
584
+ newTokenCount: 0,
585
+ compressionStatus: CompressionStatus.COMPRESSED,
586
+ });
677
587
  const mockStream = (async function* () {
678
588
  yield { type: 'content', value: 'Hello' };
679
589
  })();
@@ -681,13 +591,9 @@ ${JSON.stringify({
681
591
  const mockChat = {
682
592
  addHistory: vi.fn(),
683
593
  getHistory: vi.fn().mockReturnValue([]),
594
+ getLastPromptTokenCount: vi.fn(),
684
595
  };
685
596
  client['chat'] = mockChat;
686
- const mockGenerator = {
687
- countTokens: vi.fn().mockResolvedValue({ totalTokens: 0 }),
688
- generateContent: mockGenerateContentFn,
689
- };
690
- client['contentGenerator'] = mockGenerator;
691
597
  const initialRequest = [{ text: 'Hi' }];
692
598
  // Act
693
599
  const stream = client.sendMessageStream(initialRequest, new AbortController().signal, 'prompt-id-ide');
@@ -695,7 +601,7 @@ ${JSON.stringify({
695
601
  // consume stream
696
602
  }
697
603
  // Assert
698
- expect(ideContext.getIdeContext).toHaveBeenCalled();
604
+ expect(ideContextStore.get).toHaveBeenCalled();
699
605
  const expectedContext = `
700
606
  Here is the user's editor context as a JSON object. This is for your information only.
701
607
  \`\`\`json
@@ -719,7 +625,7 @@ ${JSON.stringify({
719
625
  });
720
626
  it('should add context if ideMode is enabled and there are open files but no active file', async () => {
721
627
  // Arrange
722
- vi.mocked(ideContext.getIdeContext).mockReturnValue({
628
+ vi.mocked(ideContextStore.get).mockReturnValue({
723
629
  workspaceState: {
724
630
  openFiles: [
725
631
  {
@@ -734,6 +640,11 @@ ${JSON.stringify({
734
640
  },
735
641
  });
736
642
  vi.spyOn(client['config'], 'getIdeMode').mockReturnValue(true);
643
+ vi.spyOn(client, 'tryCompressChat').mockResolvedValue({
644
+ originalTokenCount: 0,
645
+ newTokenCount: 0,
646
+ compressionStatus: CompressionStatus.COMPRESSED,
647
+ });
737
648
  const mockStream = (async function* () {
738
649
  yield { type: 'content', value: 'Hello' };
739
650
  })();
@@ -741,13 +652,9 @@ ${JSON.stringify({
741
652
  const mockChat = {
742
653
  addHistory: vi.fn(),
743
654
  getHistory: vi.fn().mockReturnValue([]),
655
+ getLastPromptTokenCount: vi.fn(),
744
656
  };
745
657
  client['chat'] = mockChat;
746
- const mockGenerator = {
747
- countTokens: vi.fn().mockResolvedValue({ totalTokens: 0 }),
748
- generateContent: mockGenerateContentFn,
749
- };
750
- client['contentGenerator'] = mockGenerator;
751
658
  const initialRequest = [{ text: 'Hi' }];
752
659
  // Act
753
660
  const stream = client.sendMessageStream(initialRequest, new AbortController().signal, 'prompt-id-ide');
@@ -755,7 +662,7 @@ ${JSON.stringify({
755
662
  // consume stream
756
663
  }
757
664
  // Assert
758
- expect(ideContext.getIdeContext).toHaveBeenCalled();
665
+ expect(ideContextStore.get).toHaveBeenCalled();
759
666
  const expectedContext = `
760
667
  Here is the user's editor context as a JSON object. This is for your information only.
761
668
  \`\`\`json
@@ -779,13 +686,9 @@ ${JSON.stringify({
779
686
  const mockChat = {
780
687
  addHistory: vi.fn(),
781
688
  getHistory: vi.fn().mockReturnValue([]),
689
+ getLastPromptTokenCount: vi.fn(),
782
690
  };
783
691
  client['chat'] = mockChat;
784
- const mockGenerator = {
785
- countTokens: vi.fn().mockResolvedValue({ totalTokens: 0 }),
786
- generateContent: mockGenerateContentFn,
787
- };
788
- client['contentGenerator'] = mockGenerator;
789
692
  // Act
790
693
  const stream = client.sendMessageStream([{ text: 'Hi' }], new AbortController().signal, 'prompt-id-1');
791
694
  // Consume the stream manually to get the final return value.
@@ -801,6 +704,7 @@ ${JSON.stringify({
801
704
  expect(finalResult).toBeInstanceOf(Turn);
802
705
  });
803
706
  it('should stop infinite loop after MAX_TURNS when nextSpeaker always returns model', async () => {
707
+ vi.spyOn(client['config'], 'getContinueOnFailedApiCall').mockReturnValue(true);
804
708
  // Get the mocked checkNextSpeaker function and configure it to trigger infinite loop
805
709
  const { checkNextSpeaker } = await import('../utils/nextSpeakerChecker.js');
806
710
  const mockCheckNextSpeaker = vi.mocked(checkNextSpeaker);
@@ -816,13 +720,9 @@ ${JSON.stringify({
816
720
  const mockChat = {
817
721
  addHistory: vi.fn(),
818
722
  getHistory: vi.fn().mockReturnValue([]),
723
+ getLastPromptTokenCount: vi.fn(),
819
724
  };
820
725
  client['chat'] = mockChat;
821
- const mockGenerator = {
822
- countTokens: vi.fn().mockResolvedValue({ totalTokens: 0 }),
823
- generateContent: mockGenerateContentFn,
824
- };
825
- client['contentGenerator'] = mockGenerator;
826
726
  // Use a signal that never gets aborted
827
727
  const abortController = new AbortController();
828
728
  const signal = abortController.signal;
@@ -847,27 +747,10 @@ ${JSON.stringify({
847
747
  }
848
748
  // Assert
849
749
  expect(finalResult).toBeInstanceOf(Turn);
850
- // Debug: Check how many times checkNextSpeaker was called
851
- const callCount = mockCheckNextSpeaker.mock.calls.length;
852
750
  // If infinite loop protection is working, checkNextSpeaker should be called many times
853
751
  // but stop at MAX_TURNS (100). Since each recursive call should trigger checkNextSpeaker,
854
752
  // we expect it to be called multiple times before hitting the limit
855
753
  expect(mockCheckNextSpeaker).toHaveBeenCalled();
856
- // The test should demonstrate that the infinite loop protection works:
857
- // - If checkNextSpeaker is called many times (close to MAX_TURNS), it shows the loop was happening
858
- // - If it's only called once, the recursive behavior might not be triggered
859
- if (callCount === 0) {
860
- throw new Error('checkNextSpeaker was never called - the recursive condition was not met');
861
- }
862
- else if (callCount === 1) {
863
- // This might be expected behavior if the turn has pending tool calls or other conditions prevent recursion
864
- console.log('checkNextSpeaker called only once - no infinite loop occurred');
865
- }
866
- else {
867
- console.log(`checkNextSpeaker called ${callCount} times - infinite loop protection worked`);
868
- // If called multiple times, we expect it to be stopped before MAX_TURNS
869
- expect(callCount).toBeLessThanOrEqual(100); // Should not exceed MAX_TURNS
870
- }
871
754
  // The stream should produce events and eventually terminate
872
755
  expect(eventCount).toBeGreaterThanOrEqual(1);
873
756
  expect(eventCount).toBeLessThan(200); // Should not exceed our safety limit
@@ -883,13 +766,9 @@ ${JSON.stringify({
883
766
  const mockChat = {
884
767
  addHistory: vi.fn(),
885
768
  getHistory: vi.fn().mockReturnValue([]),
769
+ getLastPromptTokenCount: vi.fn(),
886
770
  };
887
771
  client['chat'] = mockChat;
888
- const mockGenerator = {
889
- countTokens: vi.fn().mockResolvedValue({ totalTokens: 0 }),
890
- generateContent: mockGenerateContentFn,
891
- };
892
- client['contentGenerator'] = mockGenerator;
893
772
  // Act & Assert
894
773
  // Run up to the limit
895
774
  for (let i = 0; i < MAX_SESSION_TURNS; i++) {
@@ -926,13 +805,9 @@ ${JSON.stringify({
926
805
  const mockChat = {
927
806
  addHistory: vi.fn(),
928
807
  getHistory: vi.fn().mockReturnValue([]),
808
+ getLastPromptTokenCount: vi.fn(),
929
809
  };
930
810
  client['chat'] = mockChat;
931
- const mockGenerator = {
932
- countTokens: vi.fn().mockResolvedValue({ totalTokens: 0 }),
933
- generateContent: mockGenerateContentFn,
934
- };
935
- client['contentGenerator'] = mockGenerator;
936
811
  // Use a signal that never gets aborted
937
812
  const abortController = new AbortController();
938
813
  const signal = abortController.signal;
@@ -958,9 +833,8 @@ ${JSON.stringify({
958
833
  }
959
834
  }
960
835
  }
961
- catch (error) {
836
+ catch (_) {
962
837
  // If the test framework times out, that also demonstrates the infinite loop
963
- console.error('Test timed out or errored:', error);
964
838
  }
965
839
  // Assert that the fix works - the loop should stop at MAX_TURNS
966
840
  const callCount = mockCheckNextSpeaker.mock.calls.length;
@@ -968,8 +842,272 @@ ${JSON.stringify({
968
842
  // the loop should stop at MAX_TURNS (100)
969
843
  expect(callCount).toBeLessThanOrEqual(100); // Should not exceed MAX_TURNS
970
844
  expect(eventCount).toBeLessThanOrEqual(200); // Should have reasonable number of events
971
- console.log(`Infinite loop protection working: checkNextSpeaker called ${callCount} times, ` +
972
- `${eventCount} events generated (properly bounded by MAX_TURNS)`);
845
+ });
846
+ it('should yield ContextWindowWillOverflow when the context window is about to overflow', async () => {
847
+ // Arrange
848
+ const MOCKED_TOKEN_LIMIT = 1000;
849
+ vi.mocked(tokenLimit).mockReturnValue(MOCKED_TOKEN_LIMIT);
850
+ // Set last prompt token count
851
+ const lastPromptTokenCount = 900;
852
+ const mockChat = {
853
+ getLastPromptTokenCount: vi.fn().mockReturnValue(lastPromptTokenCount),
854
+ getHistory: vi.fn().mockReturnValue([]),
855
+ };
856
+ client['chat'] = mockChat;
857
+ // Remaining = 100. Threshold (95%) = 95.
858
+ // We need a request > 95 tokens.
859
+ // A string of length 400 is roughly 100 tokens.
860
+ const longText = 'a'.repeat(400);
861
+ const request = [{ text: longText }];
862
+ const estimatedRequestTokenCount = Math.floor(JSON.stringify(request).length / 4);
863
+ const remainingTokenCount = MOCKED_TOKEN_LIMIT - lastPromptTokenCount;
864
+ // Mock tryCompressChat to not compress
865
+ vi.spyOn(client, 'tryCompressChat').mockResolvedValue({
866
+ originalTokenCount: lastPromptTokenCount,
867
+ newTokenCount: lastPromptTokenCount,
868
+ compressionStatus: CompressionStatus.NOOP,
869
+ });
870
+ // Act
871
+ const stream = client.sendMessageStream(request, new AbortController().signal, 'prompt-id-overflow');
872
+ const events = await fromAsync(stream);
873
+ // Assert
874
+ expect(events).toContainEqual({
875
+ type: GeminiEventType.ContextWindowWillOverflow,
876
+ value: {
877
+ estimatedRequestTokenCount,
878
+ remainingTokenCount,
879
+ },
880
+ });
881
+ // Ensure turn.run is not called
882
+ expect(mockTurnRunFn).not.toHaveBeenCalled();
883
+ });
884
+ it("should use the sticky model's token limit for the overflow check", async () => {
885
+ // Arrange
886
+ const STICKY_MODEL = 'gemini-1.5-flash';
887
+ const STICKY_MODEL_LIMIT = 1000;
888
+ const CONFIG_MODEL_LIMIT = 2000;
889
+ // Set up token limits
890
+ vi.mocked(tokenLimit).mockImplementation((model) => {
891
+ if (model === STICKY_MODEL)
892
+ return STICKY_MODEL_LIMIT;
893
+ return CONFIG_MODEL_LIMIT;
894
+ });
895
+ // Set the sticky model
896
+ client['currentSequenceModel'] = STICKY_MODEL;
897
+ // Set token count
898
+ const lastPromptTokenCount = 900;
899
+ const mockChat = {
900
+ getLastPromptTokenCount: vi.fn().mockReturnValue(lastPromptTokenCount),
901
+ getHistory: vi.fn().mockReturnValue([]),
902
+ };
903
+ client['chat'] = mockChat;
904
+ // Remaining (sticky) = 100. Threshold (95%) = 95.
905
+ // We need a request > 95 tokens.
906
+ const longText = 'a'.repeat(400);
907
+ const request = [{ text: longText }];
908
+ const estimatedRequestTokenCount = Math.floor(JSON.stringify(request).length / 4);
909
+ const remainingTokenCount = STICKY_MODEL_LIMIT - lastPromptTokenCount;
910
+ vi.spyOn(client, 'tryCompressChat').mockResolvedValue({
911
+ originalTokenCount: lastPromptTokenCount,
912
+ newTokenCount: lastPromptTokenCount,
913
+ compressionStatus: CompressionStatus.NOOP,
914
+ });
915
+ // Act
916
+ const stream = client.sendMessageStream(request, new AbortController().signal, 'test-session-id');
917
+ const events = await fromAsync(stream);
918
+ // Assert
919
+ // Should overflow based on the sticky model's limit
920
+ expect(events).toContainEqual({
921
+ type: GeminiEventType.ContextWindowWillOverflow,
922
+ value: {
923
+ estimatedRequestTokenCount,
924
+ remainingTokenCount,
925
+ },
926
+ });
927
+ expect(tokenLimit).toHaveBeenCalledWith(STICKY_MODEL);
928
+ expect(mockTurnRunFn).not.toHaveBeenCalled();
929
+ });
930
+ describe('Model Routing', () => {
931
+ let mockRouterService;
932
+ beforeEach(() => {
933
+ mockRouterService = {
934
+ route: vi
935
+ .fn()
936
+ .mockResolvedValue({ model: 'routed-model', reason: 'test' }),
937
+ };
938
+ vi.mocked(mockConfig.getModelRouterService).mockReturnValue(mockRouterService);
939
+ mockTurnRunFn.mockReturnValue((async function* () {
940
+ yield { type: 'content', value: 'Hello' };
941
+ })());
942
+ const mockChat = {
943
+ addHistory: vi.fn(),
944
+ getHistory: vi.fn().mockReturnValue([]),
945
+ getLastPromptTokenCount: vi.fn(),
946
+ };
947
+ client['chat'] = mockChat;
948
+ });
949
+ it('should use the model router service to select a model on the first turn', async () => {
950
+ const stream = client.sendMessageStream([{ text: 'Hi' }], new AbortController().signal, 'prompt-1');
951
+ await fromAsync(stream); // consume stream
952
+ expect(mockConfig.getModelRouterService).toHaveBeenCalled();
953
+ expect(mockRouterService.route).toHaveBeenCalled();
954
+ expect(mockTurnRunFn).toHaveBeenCalledWith({ model: 'routed-model' }, [{ text: 'Hi' }], expect.any(AbortSignal));
955
+ });
956
+ it('should use the same model for subsequent turns in the same prompt (stickiness)', async () => {
957
+ // First turn
958
+ let stream = client.sendMessageStream([{ text: 'Hi' }], new AbortController().signal, 'prompt-1');
959
+ await fromAsync(stream);
960
+ expect(mockRouterService.route).toHaveBeenCalledTimes(1);
961
+ expect(mockTurnRunFn).toHaveBeenCalledWith({ model: 'routed-model' }, [{ text: 'Hi' }], expect.any(AbortSignal));
962
+ // Second turn
963
+ stream = client.sendMessageStream([{ text: 'Continue' }], new AbortController().signal, 'prompt-1');
964
+ await fromAsync(stream);
965
+ // Router should not be called again
966
+ expect(mockRouterService.route).toHaveBeenCalledTimes(1);
967
+ // Should stick to the first model
968
+ expect(mockTurnRunFn).toHaveBeenCalledWith({ model: 'routed-model' }, [{ text: 'Continue' }], expect.any(AbortSignal));
969
+ });
970
+ it('should reset the sticky model and re-route when the prompt_id changes', async () => {
971
+ // First prompt
972
+ let stream = client.sendMessageStream([{ text: 'Hi' }], new AbortController().signal, 'prompt-1');
973
+ await fromAsync(stream);
974
+ expect(mockRouterService.route).toHaveBeenCalledTimes(1);
975
+ expect(mockTurnRunFn).toHaveBeenCalledWith({ model: 'routed-model' }, [{ text: 'Hi' }], expect.any(AbortSignal));
976
+ // New prompt
977
+ mockRouterService.route.mockResolvedValue({
978
+ model: 'new-routed-model',
979
+ reason: 'test',
980
+ });
981
+ stream = client.sendMessageStream([{ text: 'A new topic' }], new AbortController().signal, 'prompt-2');
982
+ await fromAsync(stream);
983
+ // Router should be called again for the new prompt
984
+ expect(mockRouterService.route).toHaveBeenCalledTimes(2);
985
+ // Should use the newly routed model
986
+ expect(mockTurnRunFn).toHaveBeenCalledWith({ model: 'new-routed-model' }, [{ text: 'A new topic' }], expect.any(AbortSignal));
987
+ });
988
+ it('should use the fallback model and bypass routing when in fallback mode', async () => {
989
+ vi.mocked(mockConfig.isInFallbackMode).mockReturnValue(true);
990
+ mockRouterService.route.mockResolvedValue({
991
+ model: DEFAULT_GEMINI_FLASH_MODEL,
992
+ reason: 'fallback',
993
+ });
994
+ const stream = client.sendMessageStream([{ text: 'Hi' }], new AbortController().signal, 'prompt-1');
995
+ await fromAsync(stream);
996
+ expect(mockTurnRunFn).toHaveBeenCalledWith({ model: DEFAULT_GEMINI_FLASH_MODEL }, [{ text: 'Hi' }], expect.any(AbortSignal));
997
+ });
998
+ it('should stick to the fallback model for the entire sequence even if fallback mode ends', async () => {
999
+ // Start the sequence in fallback mode
1000
+ vi.mocked(mockConfig.isInFallbackMode).mockReturnValue(true);
1001
+ mockRouterService.route.mockResolvedValue({
1002
+ model: DEFAULT_GEMINI_FLASH_MODEL,
1003
+ reason: 'fallback',
1004
+ });
1005
+ let stream = client.sendMessageStream([{ text: 'Hi' }], new AbortController().signal, 'prompt-fallback-stickiness');
1006
+ await fromAsync(stream);
1007
+ // First call should use fallback model
1008
+ expect(mockTurnRunFn).toHaveBeenCalledWith({ model: DEFAULT_GEMINI_FLASH_MODEL }, [{ text: 'Hi' }], expect.any(AbortSignal));
1009
+ // End fallback mode
1010
+ vi.mocked(mockConfig.isInFallbackMode).mockReturnValue(false);
1011
+ // Second call in the same sequence
1012
+ stream = client.sendMessageStream([{ text: 'Continue' }], new AbortController().signal, 'prompt-fallback-stickiness');
1013
+ await fromAsync(stream);
1014
+ // Router should still not be called, and it should stick to the fallback model
1015
+ expect(mockTurnRunFn).toHaveBeenCalledTimes(2); // Ensure it was called again
1016
+ expect(mockTurnRunFn).toHaveBeenLastCalledWith({ model: DEFAULT_GEMINI_FLASH_MODEL }, // Still the fallback model
1017
+ [{ text: 'Continue' }], expect.any(AbortSignal));
1018
+ });
1019
+ });
1020
+ it('should recursively call sendMessageStream with "Please continue." when InvalidStream event is received', async () => {
1021
+ vi.spyOn(client['config'], 'getContinueOnFailedApiCall').mockReturnValue(true);
1022
+ // Arrange
1023
+ const mockStream1 = (async function* () {
1024
+ yield { type: GeminiEventType.InvalidStream };
1025
+ })();
1026
+ const mockStream2 = (async function* () {
1027
+ yield { type: GeminiEventType.Content, value: 'Continued content' };
1028
+ })();
1029
+ mockTurnRunFn
1030
+ .mockReturnValueOnce(mockStream1)
1031
+ .mockReturnValueOnce(mockStream2);
1032
+ const mockChat = {
1033
+ addHistory: vi.fn(),
1034
+ getHistory: vi.fn().mockReturnValue([]),
1035
+ getLastPromptTokenCount: vi.fn(),
1036
+ };
1037
+ client['chat'] = mockChat;
1038
+ const initialRequest = [{ text: 'Hi' }];
1039
+ const promptId = 'prompt-id-invalid-stream';
1040
+ const signal = new AbortController().signal;
1041
+ // Act
1042
+ const stream = client.sendMessageStream(initialRequest, signal, promptId);
1043
+ const events = await fromAsync(stream);
1044
+ // Assert
1045
+ expect(events).toEqual([
1046
+ { type: GeminiEventType.ModelInfo, value: 'default-routed-model' },
1047
+ { type: GeminiEventType.InvalidStream },
1048
+ { type: GeminiEventType.Content, value: 'Continued content' },
1049
+ ]);
1050
+ // Verify that turn.run was called twice
1051
+ expect(mockTurnRunFn).toHaveBeenCalledTimes(2);
1052
+ // First call with original request
1053
+ expect(mockTurnRunFn).toHaveBeenNthCalledWith(1, { model: 'default-routed-model' }, initialRequest, expect.any(AbortSignal));
1054
+ // Second call with "Please continue."
1055
+ expect(mockTurnRunFn).toHaveBeenNthCalledWith(2, { model: 'default-routed-model' }, [{ text: 'System: Please continue.' }], expect.any(AbortSignal));
1056
+ });
1057
+ it('should not recursively call sendMessageStream with "Please continue." when InvalidStream event is received and flag is false', async () => {
1058
+ vi.spyOn(client['config'], 'getContinueOnFailedApiCall').mockReturnValue(false);
1059
+ // Arrange
1060
+ const mockStream1 = (async function* () {
1061
+ yield { type: GeminiEventType.InvalidStream };
1062
+ })();
1063
+ mockTurnRunFn.mockReturnValueOnce(mockStream1);
1064
+ const mockChat = {
1065
+ addHistory: vi.fn(),
1066
+ getHistory: vi.fn().mockReturnValue([]),
1067
+ getLastPromptTokenCount: vi.fn(),
1068
+ };
1069
+ client['chat'] = mockChat;
1070
+ const initialRequest = [{ text: 'Hi' }];
1071
+ const promptId = 'prompt-id-invalid-stream';
1072
+ const signal = new AbortController().signal;
1073
+ // Act
1074
+ const stream = client.sendMessageStream(initialRequest, signal, promptId);
1075
+ const events = await fromAsync(stream);
1076
+ // Assert
1077
+ expect(events).toEqual([
1078
+ { type: GeminiEventType.ModelInfo, value: 'default-routed-model' },
1079
+ { type: GeminiEventType.InvalidStream },
1080
+ ]);
1081
+ // Verify that turn.run was called only once
1082
+ expect(mockTurnRunFn).toHaveBeenCalledTimes(1);
1083
+ });
1084
+ it('should stop recursing after one retry when InvalidStream events are repeatedly received', async () => {
1085
+ vi.spyOn(client['config'], 'getContinueOnFailedApiCall').mockReturnValue(true);
1086
+ // Arrange
1087
+ // Always return a new invalid stream
1088
+ mockTurnRunFn.mockImplementation(() => (async function* () {
1089
+ yield { type: GeminiEventType.InvalidStream };
1090
+ })());
1091
+ const mockChat = {
1092
+ addHistory: vi.fn(),
1093
+ getHistory: vi.fn().mockReturnValue([]),
1094
+ getLastPromptTokenCount: vi.fn(),
1095
+ };
1096
+ client['chat'] = mockChat;
1097
+ const initialRequest = [{ text: 'Hi' }];
1098
+ const promptId = 'prompt-id-infinite-invalid-stream';
1099
+ const signal = new AbortController().signal;
1100
+ // Act
1101
+ const stream = client.sendMessageStream(initialRequest, signal, promptId);
1102
+ const events = await fromAsync(stream);
1103
+ // Assert
1104
+ // We expect 3 events (model_info + original + 1 retry)
1105
+ expect(events.length).toBe(3);
1106
+ expect(events
1107
+ .filter((e) => e.type !== GeminiEventType.ModelInfo)
1108
+ .every((e) => e.type === GeminiEventType.InvalidStream)).toBe(true);
1109
+ // Verify that turn.run was called twice
1110
+ expect(mockTurnRunFn).toHaveBeenCalledTimes(2);
973
1111
  });
974
1112
  describe('Editor context delta', () => {
975
1113
  const mockStream = (async function* () {
@@ -977,26 +1115,25 @@ ${JSON.stringify({
977
1115
  })();
978
1116
  beforeEach(() => {
979
1117
  client['forceFullIdeContext'] = false; // Reset before each delta test
980
- vi.spyOn(client, 'tryCompressChat').mockResolvedValue(null);
1118
+ vi.spyOn(client, 'tryCompressChat').mockResolvedValue({
1119
+ originalTokenCount: 0,
1120
+ newTokenCount: 0,
1121
+ compressionStatus: CompressionStatus.COMPRESSED,
1122
+ });
981
1123
  vi.spyOn(client['config'], 'getIdeMode').mockReturnValue(true);
982
1124
  mockTurnRunFn.mockReturnValue(mockStream);
983
1125
  const mockChat = {
984
1126
  addHistory: vi.fn(),
985
1127
  setHistory: vi.fn(),
986
- sendMessage: vi.fn().mockResolvedValue({ text: 'summary' }),
987
1128
  // Assume history is not empty for delta checks
988
1129
  getHistory: vi
989
1130
  .fn()
990
1131
  .mockReturnValue([
991
1132
  { role: 'user', parts: [{ text: 'previous message' }] },
992
1133
  ]),
1134
+ getLastPromptTokenCount: vi.fn(),
993
1135
  };
994
1136
  client['chat'] = mockChat;
995
- const mockGenerator = {
996
- countTokens: vi.fn().mockResolvedValue({ totalTokens: 0 }),
997
- generateContent: mockGenerateContentFn,
998
- };
999
- client['contentGenerator'] = mockGenerator;
1000
1137
  });
1001
1138
  const testCases = [
1002
1139
  {
@@ -1112,10 +1249,14 @@ ${JSON.stringify({
1112
1249
  },
1113
1250
  };
1114
1251
  // Setup current context
1115
- vi.mocked(ideContext.getIdeContext).mockReturnValue({
1252
+ vi.mocked(ideContextStore.get).mockReturnValue({
1116
1253
  workspaceState: {
1117
1254
  openFiles: [
1118
- { ...currentActiveFile, isActive: true, timestamp: Date.now() },
1255
+ {
1256
+ ...currentActiveFile,
1257
+ isActive: true,
1258
+ timestamp: Date.now(),
1259
+ },
1119
1260
  ],
1120
1261
  },
1121
1262
  });
@@ -1158,7 +1299,7 @@ ${JSON.stringify({
1158
1299
  },
1159
1300
  };
1160
1301
  // Setup current context (same as previous)
1161
- vi.mocked(ideContext.getIdeContext).mockReturnValue({
1302
+ vi.mocked(ideContextStore.get).mockReturnValue({
1162
1303
  workspaceState: {
1163
1304
  openFiles: [
1164
1305
  { ...activeFile, isActive: true, timestamp: Date.now() },
@@ -1190,7 +1331,11 @@ ${JSON.stringify({
1190
1331
  describe('IDE context with pending tool calls', () => {
1191
1332
  let mockChat;
1192
1333
  beforeEach(() => {
1193
- vi.spyOn(client, 'tryCompressChat').mockResolvedValue(null);
1334
+ vi.spyOn(client, 'tryCompressChat').mockResolvedValue({
1335
+ originalTokenCount: 0,
1336
+ newTokenCount: 0,
1337
+ compressionStatus: CompressionStatus.COMPRESSED,
1338
+ });
1194
1339
  const mockStream = (async function* () {
1195
1340
  yield { type: 'content', value: 'response' };
1196
1341
  })();
@@ -1199,15 +1344,11 @@ ${JSON.stringify({
1199
1344
  addHistory: vi.fn(),
1200
1345
  getHistory: vi.fn().mockReturnValue([]), // Default empty history
1201
1346
  setHistory: vi.fn(),
1202
- sendMessage: vi.fn().mockResolvedValue({ text: 'summary' }),
1347
+ getLastPromptTokenCount: vi.fn(),
1203
1348
  };
1204
1349
  client['chat'] = mockChat;
1205
- const mockGenerator = {
1206
- countTokens: vi.fn().mockResolvedValue({ totalTokens: 0 }),
1207
- };
1208
- client['contentGenerator'] = mockGenerator;
1209
1350
  vi.spyOn(client['config'], 'getIdeMode').mockReturnValue(true);
1210
- vi.mocked(ideContext.getIdeContext).mockReturnValue({
1351
+ vi.mocked(ideContextStore.get).mockReturnValue({
1211
1352
  workspaceState: {
1212
1353
  openFiles: [{ path: '/path/to/file.ts', timestamp: Date.now() }],
1213
1354
  },
@@ -1283,7 +1424,7 @@ ${JSON.stringify({
1283
1424
  openFiles: [{ path: '/path/to/fileA.ts', timestamp: Date.now() }],
1284
1425
  },
1285
1426
  };
1286
- vi.mocked(ideContext.getIdeContext).mockReturnValue(initialIdeContext);
1427
+ vi.mocked(ideContextStore.get).mockReturnValue(initialIdeContext);
1287
1428
  // Act: Send the tool response
1288
1429
  let stream = client.sendMessageStream([
1289
1430
  {
@@ -1329,7 +1470,7 @@ ${JSON.stringify({
1329
1470
  openFiles: [{ path: '/path/to/fileB.ts', timestamp: Date.now() }],
1330
1471
  },
1331
1472
  };
1332
- vi.mocked(ideContext.getIdeContext).mockReturnValue(newIdeContext);
1473
+ vi.mocked(ideContextStore.get).mockReturnValue(newIdeContext);
1333
1474
  // Act: Send a new, regular user message
1334
1475
  stream = client.sendMessageStream([{ text: 'Thanks!' }], new AbortController().signal, 'prompt-id-final');
1335
1476
  for await (const _ of stream) {
@@ -1359,7 +1500,7 @@ ${JSON.stringify({
1359
1500
  ],
1360
1501
  },
1361
1502
  };
1362
- vi.mocked(ideContext.getIdeContext).mockReturnValue(contextA);
1503
+ vi.mocked(ideContextStore.get).mockReturnValue(contextA);
1363
1504
  // Act: Send a regular message to establish the initial context
1364
1505
  let stream = client.sendMessageStream([{ text: 'Initial message' }], new AbortController().signal, 'prompt-id-initial');
1365
1506
  for await (const _ of stream) {
@@ -1392,7 +1533,7 @@ ${JSON.stringify({
1392
1533
  ],
1393
1534
  },
1394
1535
  };
1395
- vi.mocked(ideContext.getIdeContext).mockReturnValue(contextB);
1536
+ vi.mocked(ideContextStore.get).mockReturnValue(contextB);
1396
1537
  // Act: Send the tool response
1397
1538
  stream = client.sendMessageStream([
1398
1539
  {
@@ -1437,7 +1578,7 @@ ${JSON.stringify({
1437
1578
  ],
1438
1579
  },
1439
1580
  };
1440
- vi.mocked(ideContext.getIdeContext).mockReturnValue(contextC);
1581
+ vi.mocked(ideContextStore.get).mockReturnValue(contextC);
1441
1582
  // Act: Send a new, regular user message
1442
1583
  stream = client.sendMessageStream([{ text: 'Thanks!' }], new AbortController().signal, 'prompt-id-final');
1443
1584
  for await (const _ of stream) {
@@ -1453,150 +1594,131 @@ ${JSON.stringify({
1453
1594
  expect(JSON.stringify(finalCall)).toContain('fileC.ts');
1454
1595
  });
1455
1596
  });
1597
+ it('should not call checkNextSpeaker when turn.run() yields an error', async () => {
1598
+ // Arrange
1599
+ const { checkNextSpeaker } = await import('../utils/nextSpeakerChecker.js');
1600
+ const mockCheckNextSpeaker = vi.mocked(checkNextSpeaker);
1601
+ const mockStream = (async function* () {
1602
+ yield {
1603
+ type: GeminiEventType.Error,
1604
+ value: { error: { message: 'test error' } },
1605
+ };
1606
+ })();
1607
+ mockTurnRunFn.mockReturnValue(mockStream);
1608
+ const mockChat = {
1609
+ addHistory: vi.fn(),
1610
+ getHistory: vi.fn().mockReturnValue([]),
1611
+ getLastPromptTokenCount: vi.fn(),
1612
+ };
1613
+ client['chat'] = mockChat;
1614
+ // Act
1615
+ const stream = client.sendMessageStream([{ text: 'Hi' }], new AbortController().signal, 'prompt-id-error');
1616
+ for await (const _ of stream) {
1617
+ // consume stream
1618
+ }
1619
+ // Assert
1620
+ expect(mockCheckNextSpeaker).not.toHaveBeenCalled();
1621
+ });
1622
+ it('should not call checkNextSpeaker when turn.run() yields a value then an error', async () => {
1623
+ // Arrange
1624
+ const { checkNextSpeaker } = await import('../utils/nextSpeakerChecker.js');
1625
+ const mockCheckNextSpeaker = vi.mocked(checkNextSpeaker);
1626
+ const mockStream = (async function* () {
1627
+ yield { type: GeminiEventType.Content, value: 'some content' };
1628
+ yield {
1629
+ type: GeminiEventType.Error,
1630
+ value: { error: { message: 'test error' } },
1631
+ };
1632
+ })();
1633
+ mockTurnRunFn.mockReturnValue(mockStream);
1634
+ const mockChat = {
1635
+ addHistory: vi.fn(),
1636
+ getHistory: vi.fn().mockReturnValue([]),
1637
+ getLastPromptTokenCount: vi.fn(),
1638
+ };
1639
+ client['chat'] = mockChat;
1640
+ // Act
1641
+ const stream = client.sendMessageStream([{ text: 'Hi' }], new AbortController().signal, 'prompt-id-error');
1642
+ for await (const _ of stream) {
1643
+ // consume stream
1644
+ }
1645
+ // Assert
1646
+ expect(mockCheckNextSpeaker).not.toHaveBeenCalled();
1647
+ });
1648
+ it('should abort linked signal when loop is detected', async () => {
1649
+ // Arrange
1650
+ vi.spyOn(client['loopDetector'], 'turnStarted').mockResolvedValue(false);
1651
+ vi.spyOn(client['loopDetector'], 'addAndCheck')
1652
+ .mockReturnValueOnce(false)
1653
+ .mockReturnValueOnce(true);
1654
+ let capturedSignal;
1655
+ mockTurnRunFn.mockImplementation((_modelConfigKey, _request, signal) => {
1656
+ capturedSignal = signal;
1657
+ return (async function* () {
1658
+ yield { type: 'content', value: 'First event' };
1659
+ yield { type: 'content', value: 'Second event' };
1660
+ })();
1661
+ });
1662
+ const mockChat = {
1663
+ addHistory: vi.fn(),
1664
+ getHistory: vi.fn().mockReturnValue([]),
1665
+ getLastPromptTokenCount: vi.fn(),
1666
+ };
1667
+ client['chat'] = mockChat;
1668
+ // Act
1669
+ const stream = client.sendMessageStream([{ text: 'Hi' }], new AbortController().signal, 'prompt-id-loop');
1670
+ const events = [];
1671
+ for await (const event of stream) {
1672
+ events.push(event);
1673
+ }
1674
+ // Assert
1675
+ expect(events).toContainEqual({ type: GeminiEventType.LoopDetected });
1676
+ expect(capturedSignal.aborted).toBe(true);
1677
+ });
1456
1678
  });
1457
1679
  describe('generateContent', () => {
1680
+ it('should call generateContent with the correct parameters', async () => {
1681
+ const contents = [{ role: 'user', parts: [{ text: 'hello' }] }];
1682
+ const abortSignal = new AbortController().signal;
1683
+ await client.generateContent({ model: DEFAULT_GEMINI_FLASH_MODEL }, contents, abortSignal);
1684
+ expect(mockContentGenerator.generateContent).toHaveBeenCalledWith({
1685
+ model: DEFAULT_GEMINI_FLASH_MODEL,
1686
+ config: {
1687
+ abortSignal,
1688
+ systemInstruction: getCoreSystemPrompt({}, ''),
1689
+ temperature: 0,
1690
+ topP: 1,
1691
+ },
1692
+ contents,
1693
+ }, 'test-session-id');
1694
+ });
1458
1695
  it('should use current model from config for content generation', async () => {
1459
1696
  const initialModel = client['config'].getModel();
1460
1697
  const contents = [{ role: 'user', parts: [{ text: 'test' }] }];
1461
1698
  const currentModel = initialModel + '-changed';
1462
1699
  vi.spyOn(client['config'], 'getModel').mockReturnValueOnce(currentModel);
1463
- const mockGenerator = {
1464
- countTokens: vi.fn().mockResolvedValue({ totalTokens: 1 }),
1465
- generateContent: mockGenerateContentFn,
1466
- };
1467
- client['contentGenerator'] = mockGenerator;
1468
- await client.generateContent(contents, {}, new AbortController().signal);
1469
- expect(mockGenerateContentFn).not.toHaveBeenCalledWith({
1700
+ await client.generateContent({ model: DEFAULT_GEMINI_FLASH_MODEL }, contents, new AbortController().signal);
1701
+ expect(mockContentGenerator.generateContent).not.toHaveBeenCalledWith({
1470
1702
  model: initialModel,
1471
1703
  config: expect.any(Object),
1472
1704
  contents,
1473
1705
  });
1474
- expect(mockGenerateContentFn).toHaveBeenCalledWith({
1475
- model: currentModel,
1706
+ expect(mockContentGenerator.generateContent).toHaveBeenCalledWith({
1707
+ model: DEFAULT_GEMINI_FLASH_MODEL,
1476
1708
  config: expect.any(Object),
1477
1709
  contents,
1478
1710
  }, 'test-session-id');
1479
1711
  });
1480
- });
1481
- describe('tryCompressChat', () => {
1482
- it('should use current model from config for token counting after sendMessage', async () => {
1483
- const initialModel = client['config'].getModel();
1484
- const mockCountTokens = vi
1485
- .fn()
1486
- .mockResolvedValueOnce({ totalTokens: 100000 })
1487
- .mockResolvedValueOnce({ totalTokens: 5000 });
1488
- const mockSendMessage = vi.fn().mockResolvedValue({ text: 'Summary' });
1489
- const mockChatHistory = [
1490
- { role: 'user', parts: [{ text: 'Long conversation' }] },
1491
- { role: 'model', parts: [{ text: 'Long response' }] },
1492
- ];
1493
- const mockChat = {
1494
- getHistory: vi.fn().mockReturnValue(mockChatHistory),
1495
- setHistory: vi.fn(),
1496
- sendMessage: mockSendMessage,
1497
- };
1498
- const mockGenerator = {
1499
- countTokens: mockCountTokens,
1500
- };
1501
- // mock the model has been changed between calls of `countTokens`
1502
- const firstCurrentModel = initialModel + '-changed-1';
1503
- const secondCurrentModel = initialModel + '-changed-2';
1504
- vi.spyOn(client['config'], 'getModel')
1505
- .mockReturnValueOnce(firstCurrentModel)
1506
- .mockReturnValueOnce(secondCurrentModel);
1507
- client['chat'] = mockChat;
1508
- client['contentGenerator'] = mockGenerator;
1509
- client['startChat'] = vi.fn().mockResolvedValue(mockChat);
1510
- const result = await client.tryCompressChat('prompt-id-4', true);
1511
- expect(mockCountTokens).toHaveBeenCalledTimes(2);
1512
- expect(mockCountTokens).toHaveBeenNthCalledWith(1, {
1513
- model: firstCurrentModel,
1514
- contents: mockChatHistory,
1515
- });
1516
- expect(mockCountTokens).toHaveBeenNthCalledWith(2, {
1517
- model: secondCurrentModel,
1518
- contents: expect.any(Array),
1519
- });
1520
- expect(result).toEqual({
1521
- originalTokenCount: 100000,
1522
- newTokenCount: 5000,
1523
- });
1524
- });
1525
- });
1526
- describe('handleFlashFallback', () => {
1527
- it('should use current model from config when checking for fallback', async () => {
1528
- const initialModel = client['config'].getModel();
1529
- const fallbackModel = DEFAULT_GEMINI_FLASH_MODEL;
1530
- // mock config been changed
1531
- const currentModel = initialModel + '-changed';
1532
- const getModelSpy = vi.spyOn(client['config'], 'getModel');
1533
- getModelSpy.mockReturnValue(currentModel);
1534
- const mockFallbackHandler = vi.fn().mockResolvedValue(true);
1535
- client['config'].flashFallbackHandler = mockFallbackHandler;
1536
- client['config'].setModel = vi.fn();
1537
- const result = await client['handleFlashFallback'](AuthType.LOGIN_WITH_GOOGLE);
1538
- expect(result).toBe(fallbackModel);
1539
- expect(mockFallbackHandler).toHaveBeenCalledWith(currentModel, fallbackModel, undefined);
1540
- });
1541
- });
1542
- describe('setHistory', () => {
1543
- it('should strip thought signatures when stripThoughts is true', () => {
1544
- const mockChat = {
1545
- setHistory: vi.fn(),
1546
- };
1547
- client['chat'] = mockChat;
1548
- const historyWithThoughts = [
1549
- {
1550
- role: 'user',
1551
- parts: [{ text: 'hello' }],
1552
- },
1553
- {
1554
- role: 'model',
1555
- parts: [
1556
- { text: 'thinking...', thoughtSignature: 'thought-123' },
1557
- {
1558
- functionCall: { name: 'test', args: {} },
1559
- thoughtSignature: 'thought-456',
1560
- },
1561
- ],
1562
- },
1563
- ];
1564
- client.setHistory(historyWithThoughts, { stripThoughts: true });
1565
- const expectedHistory = [
1566
- {
1567
- role: 'user',
1568
- parts: [{ text: 'hello' }],
1569
- },
1570
- {
1571
- role: 'model',
1572
- parts: [
1573
- { text: 'thinking...' },
1574
- { functionCall: { name: 'test', args: {} } },
1575
- ],
1576
- },
1577
- ];
1578
- expect(mockChat.setHistory).toHaveBeenCalledWith(expectedHistory);
1579
- });
1580
- it('should not strip thought signatures when stripThoughts is false', () => {
1581
- const mockChat = {
1582
- setHistory: vi.fn(),
1583
- };
1584
- client['chat'] = mockChat;
1585
- const historyWithThoughts = [
1586
- {
1587
- role: 'user',
1588
- parts: [{ text: 'hello' }],
1589
- },
1590
- {
1591
- role: 'model',
1592
- parts: [
1593
- { text: 'thinking...', thoughtSignature: 'thought-123' },
1594
- { text: 'ok', thoughtSignature: 'thought-456' },
1595
- ],
1596
- },
1597
- ];
1598
- client.setHistory(historyWithThoughts, { stripThoughts: false });
1599
- expect(mockChat.setHistory).toHaveBeenCalledWith(historyWithThoughts);
1712
+ it('should use the Flash model when fallback mode is active', async () => {
1713
+ const contents = [{ role: 'user', parts: [{ text: 'hello' }] }];
1714
+ const abortSignal = new AbortController().signal;
1715
+ const requestedModel = 'gemini-2.5-pro'; // A non-flash model
1716
+ // Mock config to be in fallback mode
1717
+ vi.spyOn(client['config'], 'isInFallbackMode').mockReturnValue(true);
1718
+ await client.generateContent({ model: requestedModel }, contents, abortSignal);
1719
+ expect(mockGenerateContentFn).toHaveBeenCalledWith(expect.objectContaining({
1720
+ model: DEFAULT_GEMINI_FLASH_MODEL,
1721
+ }), 'test-session-id');
1600
1722
  });
1601
1723
  });
1602
1724
  });