@office-ai/aioncli-core 0.8.1 → 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 (777) hide show
  1. package/dist/index.d.ts +8 -2
  2. package/dist/index.js +7 -2
  3. package/dist/index.js.map +1 -1
  4. package/dist/src/agents/codebase-investigator.d.ts +36 -1
  5. package/dist/src/agents/codebase-investigator.js +93 -34
  6. package/dist/src/agents/codebase-investigator.js.map +1 -1
  7. package/dist/src/agents/codebase-investigator.test.d.ts +6 -0
  8. package/dist/src/agents/codebase-investigator.test.js +35 -0
  9. package/dist/src/agents/codebase-investigator.test.js.map +1 -0
  10. package/dist/src/agents/executor.d.ts +37 -11
  11. package/dist/src/agents/executor.js +512 -150
  12. package/dist/src/agents/executor.js.map +1 -1
  13. package/dist/src/agents/executor.test.js +1188 -245
  14. package/dist/src/agents/executor.test.js.map +1 -1
  15. package/dist/src/agents/invocation.d.ts +5 -2
  16. package/dist/src/agents/invocation.js +4 -2
  17. package/dist/src/agents/invocation.js.map +1 -1
  18. package/dist/src/agents/invocation.test.js +9 -0
  19. package/dist/src/agents/invocation.test.js.map +1 -1
  20. package/dist/src/agents/registry.d.ts +6 -1
  21. package/dist/src/agents/registry.js +51 -4
  22. package/dist/src/agents/registry.js.map +1 -1
  23. package/dist/src/agents/registry.test.js +30 -16
  24. package/dist/src/agents/registry.test.js.map +1 -1
  25. package/dist/src/agents/subagent-tool-wrapper.d.ts +3 -1
  26. package/dist/src/agents/subagent-tool-wrapper.js +4 -3
  27. package/dist/src/agents/subagent-tool-wrapper.js.map +1 -1
  28. package/dist/src/agents/subagent-tool-wrapper.test.js +9 -4
  29. package/dist/src/agents/subagent-tool-wrapper.test.js.map +1 -1
  30. package/dist/src/agents/types.d.ts +37 -7
  31. package/dist/src/agents/types.js +2 -0
  32. package/dist/src/agents/types.js.map +1 -1
  33. package/dist/src/code_assist/codeAssist.js +1 -1
  34. package/dist/src/code_assist/codeAssist.test.d.ts +6 -0
  35. package/dist/src/code_assist/codeAssist.test.js +99 -0
  36. package/dist/src/code_assist/codeAssist.test.js.map +1 -0
  37. package/dist/src/code_assist/converter.d.ts +1 -0
  38. package/dist/src/code_assist/converter.js +1 -0
  39. package/dist/src/code_assist/converter.js.map +1 -1
  40. package/dist/src/code_assist/converter.test.js +19 -0
  41. package/dist/src/code_assist/converter.test.js.map +1 -1
  42. package/dist/src/code_assist/experiments/client_metadata.d.ts +12 -0
  43. package/dist/src/code_assist/experiments/client_metadata.js +50 -0
  44. package/dist/src/code_assist/experiments/client_metadata.js.map +1 -0
  45. package/dist/src/code_assist/experiments/client_metadata.test.d.ts +6 -0
  46. package/dist/src/code_assist/experiments/client_metadata.test.js +99 -0
  47. package/dist/src/code_assist/experiments/client_metadata.test.js.map +1 -0
  48. package/dist/src/code_assist/experiments/experiments.d.ts +17 -0
  49. package/dist/src/code_assist/experiments/experiments.js +36 -0
  50. package/dist/src/code_assist/experiments/experiments.js.map +1 -0
  51. package/dist/src/code_assist/experiments/experiments.test.d.ts +6 -0
  52. package/dist/src/code_assist/experiments/experiments.test.js +92 -0
  53. package/dist/src/code_assist/experiments/experiments.test.js.map +1 -0
  54. package/dist/src/code_assist/experiments/flagNames.d.ts +13 -0
  55. package/dist/src/code_assist/experiments/flagNames.js +13 -0
  56. package/dist/src/code_assist/experiments/flagNames.js.map +1 -0
  57. package/dist/src/code_assist/experiments/types.d.ts +35 -0
  58. package/dist/src/code_assist/experiments/types.js +7 -0
  59. package/dist/src/code_assist/experiments/types.js.map +1 -0
  60. package/dist/src/code_assist/oauth-credential-storage.js +6 -5
  61. package/dist/src/code_assist/oauth-credential-storage.js.map +1 -1
  62. package/dist/src/code_assist/oauth-credential-storage.test.js +65 -3
  63. package/dist/src/code_assist/oauth-credential-storage.test.js.map +1 -1
  64. package/dist/src/code_assist/oauth2.d.ts +2 -2
  65. package/dist/src/code_assist/oauth2.js +161 -93
  66. package/dist/src/code_assist/oauth2.js.map +1 -1
  67. package/dist/src/code_assist/oauth2.test.js +103 -57
  68. package/dist/src/code_assist/oauth2.test.js.map +1 -1
  69. package/dist/src/code_assist/server.d.ts +6 -4
  70. package/dist/src/code_assist/server.js +16 -8
  71. package/dist/src/code_assist/server.js.map +1 -1
  72. package/dist/src/code_assist/server.test.js +126 -28
  73. package/dist/src/code_assist/server.test.js.map +1 -1
  74. package/dist/src/code_assist/setup.d.ts +2 -2
  75. package/dist/src/code_assist/setup.js +4 -2
  76. package/dist/src/code_assist/setup.js.map +1 -1
  77. package/dist/src/code_assist/types.d.ts +1 -1
  78. package/dist/src/code_assist/types.js.map +1 -1
  79. package/dist/src/commands/extensions.d.ts +7 -0
  80. package/dist/src/commands/extensions.js +9 -0
  81. package/dist/src/commands/extensions.js.map +1 -0
  82. package/dist/src/commands/extensions.test.d.ts +6 -0
  83. package/dist/src/commands/extensions.test.js +19 -0
  84. package/dist/src/commands/extensions.test.js.map +1 -0
  85. package/dist/src/config/config.d.ts +169 -43
  86. package/dist/src/config/config.js +418 -79
  87. package/dist/src/config/config.js.map +1 -1
  88. package/dist/src/config/config.test.js +684 -49
  89. package/dist/src/config/config.test.js.map +1 -1
  90. package/dist/src/config/defaultModelConfigs.d.ts +7 -0
  91. package/dist/src/config/defaultModelConfigs.js +185 -0
  92. package/dist/src/config/defaultModelConfigs.js.map +1 -0
  93. package/dist/src/config/models.d.ts +23 -2
  94. package/dist/src/config/models.js +50 -7
  95. package/dist/src/config/models.js.map +1 -1
  96. package/dist/src/config/models.test.js +71 -10
  97. package/dist/src/config/models.test.js.map +1 -1
  98. package/dist/src/config/storage.d.ts +3 -1
  99. package/dist/src/config/storage.js +22 -2
  100. package/dist/src/config/storage.js.map +1 -1
  101. package/dist/src/config/storage.test.js +7 -6
  102. package/dist/src/config/storage.test.js.map +1 -1
  103. package/dist/src/confirmation-bus/message-bus.d.ts +3 -2
  104. package/dist/src/confirmation-bus/message-bus.js +9 -3
  105. package/dist/src/confirmation-bus/message-bus.js.map +1 -1
  106. package/dist/src/confirmation-bus/message-bus.test.js +30 -24
  107. package/dist/src/confirmation-bus/message-bus.test.js.map +1 -1
  108. package/dist/src/confirmation-bus/types.d.ts +13 -2
  109. package/dist/src/confirmation-bus/types.js +1 -0
  110. package/dist/src/confirmation-bus/types.js.map +1 -1
  111. package/dist/src/core/apiKeyCredentialStorage.d.ts +17 -0
  112. package/dist/src/core/apiKeyCredentialStorage.js +64 -0
  113. package/dist/src/core/apiKeyCredentialStorage.js.map +1 -0
  114. package/dist/src/core/apiKeyCredentialStorage.test.d.ts +6 -0
  115. package/dist/src/core/apiKeyCredentialStorage.test.js +71 -0
  116. package/dist/src/core/apiKeyCredentialStorage.test.js.map +1 -0
  117. package/dist/src/core/baseLlmClient.d.ts +4 -8
  118. package/dist/src/core/baseLlmClient.js +6 -11
  119. package/dist/src/core/baseLlmClient.js.map +1 -1
  120. package/dist/src/core/baseLlmClient.test.js +22 -27
  121. package/dist/src/core/baseLlmClient.test.js.map +1 -1
  122. package/dist/src/core/client.d.ts +12 -19
  123. package/dist/src/core/client.js +104 -206
  124. package/dist/src/core/client.js.map +1 -1
  125. package/dist/src/core/client.test.js +329 -452
  126. package/dist/src/core/client.test.js.map +1 -1
  127. package/dist/src/core/contentGenerator.d.ts +3 -2
  128. package/dist/src/core/contentGenerator.js +56 -41
  129. package/dist/src/core/contentGenerator.js.map +1 -1
  130. package/dist/src/core/contentGenerator.test.js +49 -1
  131. package/dist/src/core/contentGenerator.test.js.map +1 -1
  132. package/dist/src/core/coreToolScheduler.d.ts +8 -4
  133. package/dist/src/core/coreToolScheduler.js +348 -179
  134. package/dist/src/core/coreToolScheduler.js.map +1 -1
  135. package/dist/src/core/coreToolScheduler.test.js +575 -219
  136. package/dist/src/core/coreToolScheduler.test.js.map +1 -1
  137. package/dist/src/core/fakeContentGenerator.d.ts +33 -0
  138. package/dist/src/core/fakeContentGenerator.js +58 -0
  139. package/dist/src/core/fakeContentGenerator.js.map +1 -0
  140. package/dist/src/core/fakeContentGenerator.test.d.ts +6 -0
  141. package/dist/src/core/fakeContentGenerator.test.js +127 -0
  142. package/dist/src/core/fakeContentGenerator.test.js.map +1 -0
  143. package/dist/src/core/geminiChat.d.ts +23 -18
  144. package/dist/src/core/geminiChat.js +186 -108
  145. package/dist/src/core/geminiChat.js.map +1 -1
  146. package/dist/src/core/geminiChat.test.js +581 -270
  147. package/dist/src/core/geminiChat.test.js.map +1 -1
  148. package/dist/src/core/logger.d.ts +7 -2
  149. package/dist/src/core/logger.js +35 -27
  150. package/dist/src/core/logger.js.map +1 -1
  151. package/dist/src/core/logger.test.js +45 -29
  152. package/dist/src/core/logger.test.js.map +1 -1
  153. package/dist/src/core/loggingContentGenerator.d.ts +1 -0
  154. package/dist/src/core/loggingContentGenerator.js +113 -33
  155. package/dist/src/core/loggingContentGenerator.js.map +1 -1
  156. package/dist/src/core/loggingContentGenerator.test.d.ts +6 -0
  157. package/dist/src/core/loggingContentGenerator.test.js +180 -0
  158. package/dist/src/core/loggingContentGenerator.test.js.map +1 -0
  159. package/dist/src/core/nonInteractiveToolExecutor.d.ts +3 -2
  160. package/dist/src/core/nonInteractiveToolExecutor.js +12 -7
  161. package/dist/src/core/nonInteractiveToolExecutor.js.map +1 -1
  162. package/dist/src/core/nonInteractiveToolExecutor.test.js +12 -8
  163. package/dist/src/core/nonInteractiveToolExecutor.test.js.map +1 -1
  164. package/dist/src/core/openaiContentGenerator.js +31 -8
  165. package/dist/src/core/openaiContentGenerator.js.map +1 -1
  166. package/dist/src/core/prompts.d.ts +2 -1
  167. package/dist/src/core/prompts.js +135 -154
  168. package/dist/src/core/prompts.js.map +1 -1
  169. package/dist/src/core/prompts.test.js +128 -189
  170. package/dist/src/core/prompts.test.js.map +1 -1
  171. package/dist/src/core/recordingContentGenerator.d.ts +18 -0
  172. package/dist/src/core/recordingContentGenerator.js +77 -0
  173. package/dist/src/core/recordingContentGenerator.js.map +1 -0
  174. package/dist/src/core/recordingContentGenerator.test.d.ts +6 -0
  175. package/dist/src/core/recordingContentGenerator.test.js +101 -0
  176. package/dist/src/core/recordingContentGenerator.test.js.map +1 -0
  177. package/dist/src/core/tokenLimits.test.d.ts +6 -0
  178. package/dist/src/core/tokenLimits.test.js +26 -0
  179. package/dist/src/core/tokenLimits.test.js.map +1 -0
  180. package/dist/src/core/turn.d.ts +23 -3
  181. package/dist/src/core/turn.js +18 -9
  182. package/dist/src/core/turn.js.map +1 -1
  183. package/dist/src/core/turn.test.js +98 -104
  184. package/dist/src/core/turn.test.js.map +1 -1
  185. package/dist/src/fallback/handler.js +60 -8
  186. package/dist/src/fallback/handler.js.map +1 -1
  187. package/dist/src/fallback/handler.test.js +132 -17
  188. package/dist/src/fallback/handler.test.js.map +1 -1
  189. package/dist/src/fallback/types.d.ts +1 -1
  190. package/dist/src/generated/git-commit.d.ts +2 -2
  191. package/dist/src/generated/git-commit.js +2 -2
  192. package/dist/src/generated/git-commit.js.map +1 -1
  193. package/dist/src/hooks/hookAggregator.d.ts +68 -0
  194. package/dist/src/hooks/hookAggregator.js +262 -0
  195. package/dist/src/hooks/hookAggregator.js.map +1 -0
  196. package/dist/src/hooks/hookAggregator.test.d.ts +6 -0
  197. package/dist/src/hooks/hookAggregator.test.js +387 -0
  198. package/dist/src/hooks/hookAggregator.test.js.map +1 -0
  199. package/dist/src/hooks/hookPlanner.d.ts +46 -0
  200. package/dist/src/hooks/hookPlanner.js +108 -0
  201. package/dist/src/hooks/hookPlanner.js.map +1 -0
  202. package/dist/src/hooks/hookPlanner.test.d.ts +6 -0
  203. package/dist/src/hooks/hookPlanner.test.js +255 -0
  204. package/dist/src/hooks/hookPlanner.test.js.map +1 -0
  205. package/dist/src/hooks/hookRegistry.d.ts +87 -0
  206. package/dist/src/hooks/hookRegistry.js +198 -0
  207. package/dist/src/hooks/hookRegistry.js.map +1 -0
  208. package/dist/src/hooks/hookRegistry.test.d.ts +6 -0
  209. package/dist/src/hooks/hookRegistry.test.js +341 -0
  210. package/dist/src/hooks/hookRegistry.test.js.map +1 -0
  211. package/dist/src/hooks/hookRunner.d.ts +42 -0
  212. package/dist/src/hooks/hookRunner.js +272 -0
  213. package/dist/src/hooks/hookRunner.js.map +1 -0
  214. package/dist/src/hooks/hookRunner.test.d.ts +6 -0
  215. package/dist/src/hooks/hookRunner.test.js +468 -0
  216. package/dist/src/hooks/hookRunner.test.js.map +1 -0
  217. package/dist/src/hooks/hookTranslator.d.ts +113 -0
  218. package/dist/src/hooks/hookTranslator.js +232 -0
  219. package/dist/src/hooks/hookTranslator.js.map +1 -0
  220. package/dist/src/hooks/hookTranslator.test.d.ts +6 -0
  221. package/dist/src/hooks/hookTranslator.test.js +192 -0
  222. package/dist/src/hooks/hookTranslator.test.js.map +1 -0
  223. package/dist/src/hooks/types.d.ts +384 -0
  224. package/dist/src/hooks/types.js +284 -0
  225. package/dist/src/hooks/types.js.map +1 -0
  226. package/dist/src/hooks/types.test.d.ts +6 -0
  227. package/dist/src/hooks/types.test.js +313 -0
  228. package/dist/src/hooks/types.test.js.map +1 -0
  229. package/dist/src/ide/detect-ide.d.ts +4 -0
  230. package/dist/src/ide/detect-ide.js +6 -1
  231. package/dist/src/ide/detect-ide.js.map +1 -1
  232. package/dist/src/ide/detect-ide.test.js +16 -0
  233. package/dist/src/ide/detect-ide.test.js.map +1 -1
  234. package/dist/src/ide/ide-client.d.ts +3 -1
  235. package/dist/src/ide/ide-client.js +12 -10
  236. package/dist/src/ide/ide-client.js.map +1 -1
  237. package/dist/src/ide/ide-client.test.js +163 -4
  238. package/dist/src/ide/ide-client.test.js.map +1 -1
  239. package/dist/src/ide/ide-installer.js +66 -21
  240. package/dist/src/ide/ide-installer.js.map +1 -1
  241. package/dist/src/ide/ide-installer.test.js +54 -1
  242. package/dist/src/ide/ide-installer.test.js.map +1 -1
  243. package/dist/src/ide/process-utils.js +85 -75
  244. package/dist/src/ide/process-utils.js.map +1 -1
  245. package/dist/src/ide/process-utils.test.js +83 -90
  246. package/dist/src/ide/process-utils.test.js.map +1 -1
  247. package/dist/src/ide/types.d.ts +1 -1
  248. package/dist/src/ide/types.js +1 -1
  249. package/dist/src/index.d.ts +21 -0
  250. package/dist/src/index.js +24 -0
  251. package/dist/src/index.js.map +1 -1
  252. package/dist/src/mcp/google-auth-provider.d.ts +2 -0
  253. package/dist/src/mcp/google-auth-provider.js +21 -3
  254. package/dist/src/mcp/google-auth-provider.js.map +1 -1
  255. package/dist/src/mcp/google-auth-provider.test.js +42 -9
  256. package/dist/src/mcp/google-auth-provider.test.js.map +1 -1
  257. package/dist/src/mcp/oauth-provider.d.ts +8 -5
  258. package/dist/src/mcp/oauth-provider.js +140 -55
  259. package/dist/src/mcp/oauth-provider.js.map +1 -1
  260. package/dist/src/mcp/oauth-provider.test.js +369 -2
  261. package/dist/src/mcp/oauth-provider.test.js.map +1 -1
  262. package/dist/src/mcp/oauth-token-storage.js +5 -4
  263. package/dist/src/mcp/oauth-token-storage.js.map +1 -1
  264. package/dist/src/mcp/oauth-token-storage.test.js +17 -11
  265. package/dist/src/mcp/oauth-token-storage.test.js.map +1 -1
  266. package/dist/src/mcp/oauth-utils.d.ts +7 -0
  267. package/dist/src/mcp/oauth-utils.js +28 -8
  268. package/dist/src/mcp/oauth-utils.js.map +1 -1
  269. package/dist/src/mcp/oauth-utils.test.js +45 -2
  270. package/dist/src/mcp/oauth-utils.test.js.map +1 -1
  271. package/dist/src/mcp/sa-impersonation-provider.d.ts +0 -6
  272. package/dist/src/mcp/sa-impersonation-provider.js +6 -23
  273. package/dist/src/mcp/sa-impersonation-provider.js.map +1 -1
  274. package/dist/src/mcp/token-storage/base-token-storage.test.js +75 -84
  275. package/dist/src/mcp/token-storage/base-token-storage.test.js.map +1 -1
  276. package/dist/src/mcp/token-storage/file-token-storage.js +3 -2
  277. package/dist/src/mcp/token-storage/file-token-storage.js.map +1 -1
  278. package/dist/src/mcp/token-storage/file-token-storage.test.js +11 -8
  279. package/dist/src/mcp/token-storage/file-token-storage.test.js.map +1 -1
  280. package/dist/src/mcp/token-storage/keychain-token-storage.d.ts +6 -2
  281. package/dist/src/mcp/token-storage/keychain-token-storage.js +63 -7
  282. package/dist/src/mcp/token-storage/keychain-token-storage.js.map +1 -1
  283. package/dist/src/mcp/token-storage/keychain-token-storage.test.js +54 -3
  284. package/dist/src/mcp/token-storage/keychain-token-storage.test.js.map +1 -1
  285. package/dist/src/mcp/token-storage/types.d.ts +6 -0
  286. package/dist/src/mcp/token-storage/types.js.map +1 -1
  287. package/dist/src/output/stream-json-formatter.d.ts +32 -0
  288. package/dist/src/output/stream-json-formatter.js +52 -0
  289. package/dist/src/output/stream-json-formatter.js.map +1 -0
  290. package/dist/src/output/stream-json-formatter.test.d.ts +6 -0
  291. package/dist/src/output/stream-json-formatter.test.js +479 -0
  292. package/dist/src/output/stream-json-formatter.test.js.map +1 -0
  293. package/dist/src/output/types.d.ts +63 -1
  294. package/dist/src/output/types.js +11 -0
  295. package/dist/src/output/types.js.map +1 -1
  296. package/dist/src/policy/config.d.ts +31 -0
  297. package/dist/src/policy/config.js +199 -0
  298. package/dist/src/policy/config.js.map +1 -0
  299. package/dist/src/policy/config.test.d.ts +6 -0
  300. package/dist/src/policy/config.test.js +538 -0
  301. package/dist/src/policy/config.test.js.map +1 -0
  302. package/dist/src/policy/index.d.ts +2 -0
  303. package/dist/src/policy/index.js +2 -0
  304. package/dist/src/policy/index.js.map +1 -1
  305. package/dist/src/policy/policies/discovered.toml +8 -0
  306. package/dist/src/policy/policies/read-only.toml +56 -0
  307. package/dist/src/policy/policies/write.toml +73 -0
  308. package/dist/src/policy/policies/yolo.toml +31 -0
  309. package/dist/src/policy/policy-engine.d.ts +12 -3
  310. package/dist/src/policy/policy-engine.js +74 -8
  311. package/dist/src/policy/policy-engine.js.map +1 -1
  312. package/dist/src/policy/policy-engine.test.js +460 -76
  313. package/dist/src/policy/policy-engine.test.js.map +1 -1
  314. package/dist/src/policy/toml-loader.d.ts +47 -0
  315. package/dist/src/policy/toml-loader.js +411 -0
  316. package/dist/src/policy/toml-loader.js.map +1 -0
  317. package/dist/src/policy/toml-loader.test.d.ts +6 -0
  318. package/dist/src/policy/toml-loader.test.js +376 -0
  319. package/dist/src/policy/toml-loader.test.js.map +1 -0
  320. package/dist/src/policy/types.d.ts +83 -0
  321. package/dist/src/policy/types.js +10 -0
  322. package/dist/src/policy/types.js.map +1 -1
  323. package/dist/src/prompts/mcp-prompts.test.d.ts +6 -0
  324. package/dist/src/prompts/mcp-prompts.test.js +39 -0
  325. package/dist/src/prompts/mcp-prompts.test.js.map +1 -0
  326. package/dist/src/prompts/prompt-registry.js +2 -1
  327. package/dist/src/prompts/prompt-registry.js.map +1 -1
  328. package/dist/src/prompts/prompt-registry.test.d.ts +6 -0
  329. package/dist/src/prompts/prompt-registry.test.js +96 -0
  330. package/dist/src/prompts/prompt-registry.test.js.map +1 -0
  331. package/dist/src/routing/modelRouterService.js +15 -0
  332. package/dist/src/routing/modelRouterService.js.map +1 -1
  333. package/dist/src/routing/modelRouterService.test.js +62 -0
  334. package/dist/src/routing/modelRouterService.test.js.map +1 -1
  335. package/dist/src/routing/strategies/classifierStrategy.d.ts +1 -1
  336. package/dist/src/routing/strategies/classifierStrategy.js +9 -16
  337. package/dist/src/routing/strategies/classifierStrategy.js.map +1 -1
  338. package/dist/src/routing/strategies/classifierStrategy.test.js +17 -13
  339. package/dist/src/routing/strategies/classifierStrategy.test.js.map +1 -1
  340. package/dist/src/routing/strategies/fallbackStrategy.js +1 -1
  341. package/dist/src/routing/strategies/fallbackStrategy.js.map +1 -1
  342. package/dist/src/routing/strategies/fallbackStrategy.test.js +4 -0
  343. package/dist/src/routing/strategies/fallbackStrategy.test.js.map +1 -1
  344. package/dist/src/routing/strategies/overrideStrategy.js +2 -2
  345. package/dist/src/routing/strategies/overrideStrategy.js.map +1 -1
  346. package/dist/src/routing/strategies/overrideStrategy.test.js +3 -0
  347. package/dist/src/routing/strategies/overrideStrategy.test.js.map +1 -1
  348. package/dist/src/safety/built-in.d.ts +21 -0
  349. package/dist/src/safety/built-in.js +106 -0
  350. package/dist/src/safety/built-in.js.map +1 -0
  351. package/dist/src/safety/built-in.test.d.ts +6 -0
  352. package/dist/src/safety/built-in.test.js +199 -0
  353. package/dist/src/safety/built-in.test.js.map +1 -0
  354. package/dist/src/safety/checker-runner.d.ts +48 -0
  355. package/dist/src/safety/checker-runner.js +208 -0
  356. package/dist/src/safety/checker-runner.js.map +1 -0
  357. package/dist/src/safety/checker-runner.test.d.ts +6 -0
  358. package/dist/src/safety/checker-runner.test.js +238 -0
  359. package/dist/src/safety/checker-runner.test.js.map +1 -0
  360. package/dist/src/safety/context-builder.d.ts +23 -0
  361. package/dist/src/safety/context-builder.js +47 -0
  362. package/dist/src/safety/context-builder.js.map +1 -0
  363. package/dist/src/safety/context-builder.test.d.ts +6 -0
  364. package/dist/src/safety/context-builder.test.js +49 -0
  365. package/dist/src/safety/context-builder.test.js.map +1 -0
  366. package/dist/src/safety/protocol.d.ts +88 -0
  367. package/dist/src/safety/protocol.js +15 -0
  368. package/dist/src/safety/protocol.js.map +1 -0
  369. package/dist/src/safety/registry.d.ts +26 -0
  370. package/dist/src/safety/registry.js +65 -0
  371. package/dist/src/safety/registry.js.map +1 -0
  372. package/dist/src/safety/registry.test.d.ts +6 -0
  373. package/dist/src/safety/registry.test.js +31 -0
  374. package/dist/src/safety/registry.test.js.map +1 -0
  375. package/dist/src/services/chatCompressionService.d.ts +32 -0
  376. package/dist/src/services/chatCompressionService.js +162 -0
  377. package/dist/src/services/chatCompressionService.js.map +1 -0
  378. package/dist/src/services/chatCompressionService.test.d.ts +6 -0
  379. package/dist/src/services/chatCompressionService.test.js +210 -0
  380. package/dist/src/services/chatCompressionService.test.js.map +1 -0
  381. package/dist/src/services/chatRecordingService.d.ts +3 -2
  382. package/dist/src/services/chatRecordingService.js +11 -9
  383. package/dist/src/services/chatRecordingService.js.map +1 -1
  384. package/dist/src/services/fileDiscoveryService.d.ts +2 -14
  385. package/dist/src/services/fileDiscoveryService.js +19 -55
  386. package/dist/src/services/fileDiscoveryService.js.map +1 -1
  387. package/dist/src/services/fileDiscoveryService.test.js +91 -11
  388. package/dist/src/services/fileDiscoveryService.test.js.map +1 -1
  389. package/dist/src/services/loopDetectionService.d.ts +4 -1
  390. package/dist/src/services/loopDetectionService.js +95 -42
  391. package/dist/src/services/loopDetectionService.js.map +1 -1
  392. package/dist/src/services/loopDetectionService.test.js +220 -12
  393. package/dist/src/services/loopDetectionService.test.js.map +1 -1
  394. package/dist/src/services/modelConfig.golden.test.d.ts +6 -0
  395. package/dist/src/services/modelConfig.golden.test.js +42 -0
  396. package/dist/src/services/modelConfig.golden.test.js.map +1 -0
  397. package/dist/src/services/modelConfig.integration.test.d.ts +6 -0
  398. package/dist/src/services/modelConfig.integration.test.js +247 -0
  399. package/dist/src/services/modelConfig.integration.test.js.map +1 -0
  400. package/dist/src/services/modelConfigService.d.ts +48 -0
  401. package/dist/src/services/modelConfigService.js +151 -0
  402. package/dist/src/services/modelConfigService.js.map +1 -0
  403. package/dist/src/services/modelConfigService.test.d.ts +6 -0
  404. package/dist/src/services/modelConfigService.test.js +531 -0
  405. package/dist/src/services/modelConfigService.test.js.map +1 -0
  406. package/dist/src/services/shellExecutionService.d.ts +1 -0
  407. package/dist/src/services/shellExecutionService.js +195 -92
  408. package/dist/src/services/shellExecutionService.js.map +1 -1
  409. package/dist/src/services/shellExecutionService.test.js +137 -14
  410. package/dist/src/services/shellExecutionService.test.js.map +1 -1
  411. package/dist/src/services/test-data/resolved-aliases.golden.json +202 -0
  412. package/dist/src/telemetry/activity-monitor.d.ts +116 -0
  413. package/dist/src/telemetry/activity-monitor.js +209 -0
  414. package/dist/src/telemetry/activity-monitor.js.map +1 -0
  415. package/dist/src/telemetry/activity-monitor.test.d.ts +6 -0
  416. package/dist/src/telemetry/activity-monitor.test.js +251 -0
  417. package/dist/src/telemetry/activity-monitor.test.js.map +1 -0
  418. package/dist/src/telemetry/clearcut-logger/clearcut-logger.d.ts +25 -7
  419. package/dist/src/telemetry/clearcut-logger/clearcut-logger.js +294 -76
  420. package/dist/src/telemetry/clearcut-logger/clearcut-logger.js.map +1 -1
  421. package/dist/src/telemetry/clearcut-logger/clearcut-logger.test.d.ts +1 -0
  422. package/dist/src/telemetry/clearcut-logger/clearcut-logger.test.js +192 -66
  423. package/dist/src/telemetry/clearcut-logger/clearcut-logger.test.js.map +1 -1
  424. package/dist/src/telemetry/clearcut-logger/event-metadata-key.d.ts +25 -3
  425. package/dist/src/telemetry/clearcut-logger/event-metadata-key.js +59 -5
  426. package/dist/src/telemetry/clearcut-logger/event-metadata-key.js.map +1 -1
  427. package/dist/src/telemetry/constants.d.ts +0 -28
  428. package/dist/src/telemetry/constants.js +0 -29
  429. package/dist/src/telemetry/constants.js.map +1 -1
  430. package/dist/src/telemetry/gcp-exporters.js +0 -1
  431. package/dist/src/telemetry/gcp-exporters.js.map +1 -1
  432. package/dist/src/telemetry/gcp-exporters.test.js +1 -1
  433. package/dist/src/telemetry/gcp-exporters.test.js.map +1 -1
  434. package/dist/src/telemetry/index.d.ts +7 -3
  435. package/dist/src/telemetry/index.js +13 -4
  436. package/dist/src/telemetry/index.js.map +1 -1
  437. package/dist/src/telemetry/loggers.d.ts +14 -7
  438. package/dist/src/telemetry/loggers.js +197 -320
  439. package/dist/src/telemetry/loggers.js.map +1 -1
  440. package/dist/src/telemetry/loggers.test.circular.js +0 -1
  441. package/dist/src/telemetry/loggers.test.circular.js.map +1 -1
  442. package/dist/src/telemetry/loggers.test.js +460 -59
  443. package/dist/src/telemetry/loggers.test.js.map +1 -1
  444. package/dist/src/telemetry/memory-monitor.d.ts +149 -0
  445. package/dist/src/telemetry/memory-monitor.js +335 -0
  446. package/dist/src/telemetry/memory-monitor.js.map +1 -0
  447. package/dist/src/telemetry/memory-monitor.test.d.ts +6 -0
  448. package/dist/src/telemetry/memory-monitor.test.js +472 -0
  449. package/dist/src/telemetry/memory-monitor.test.js.map +1 -0
  450. package/dist/src/telemetry/metrics.d.ts +180 -4
  451. package/dist/src/telemetry/metrics.js +270 -6
  452. package/dist/src/telemetry/metrics.js.map +1 -1
  453. package/dist/src/telemetry/metrics.test.js +502 -184
  454. package/dist/src/telemetry/metrics.test.js.map +1 -1
  455. package/dist/src/telemetry/sdk.js +3 -2
  456. package/dist/src/telemetry/sdk.js.map +1 -1
  457. package/dist/src/telemetry/semantic.d.ts +82 -0
  458. package/dist/src/telemetry/semantic.js +269 -0
  459. package/dist/src/telemetry/semantic.js.map +1 -0
  460. package/dist/src/telemetry/semantic.test.d.ts +6 -0
  461. package/dist/src/telemetry/semantic.test.js +387 -0
  462. package/dist/src/telemetry/semantic.test.js.map +1 -0
  463. package/dist/src/telemetry/telemetry-utils.test.js +29 -28
  464. package/dist/src/telemetry/telemetry-utils.test.js.map +1 -1
  465. package/dist/src/telemetry/telemetryAttributes.d.ts +8 -0
  466. package/dist/src/telemetry/telemetryAttributes.js +19 -0
  467. package/dist/src/telemetry/telemetryAttributes.js.map +1 -0
  468. package/dist/src/telemetry/trace.d.ts +46 -0
  469. package/dist/src/telemetry/trace.js +121 -0
  470. package/dist/src/telemetry/trace.js.map +1 -0
  471. package/dist/src/telemetry/types.d.ts +227 -29
  472. package/dist/src/telemetry/types.js +858 -72
  473. package/dist/src/telemetry/types.js.map +1 -1
  474. package/dist/src/telemetry/uiTelemetry.d.ts +1 -1
  475. package/dist/src/telemetry/uiTelemetry.js +7 -7
  476. package/dist/src/telemetry/uiTelemetry.js.map +1 -1
  477. package/dist/src/telemetry/uiTelemetry.test.js +89 -67
  478. package/dist/src/telemetry/uiTelemetry.test.js.map +1 -1
  479. package/dist/src/test-utils/config.d.ts +1 -1
  480. package/dist/src/test-utils/config.js +1 -1
  481. package/dist/src/tools/base-tool-invocation.test.d.ts +6 -0
  482. package/dist/src/tools/base-tool-invocation.test.js +85 -0
  483. package/dist/src/tools/base-tool-invocation.test.js.map +1 -0
  484. package/dist/src/tools/edit.d.ts +4 -3
  485. package/dist/src/tools/edit.js +50 -47
  486. package/dist/src/tools/edit.js.map +1 -1
  487. package/dist/src/tools/edit.test.js +306 -216
  488. package/dist/src/tools/edit.test.js.map +1 -1
  489. package/dist/src/tools/glob.d.ts +4 -3
  490. package/dist/src/tools/glob.js +24 -27
  491. package/dist/src/tools/glob.js.map +1 -1
  492. package/dist/src/tools/glob.test.js +212 -205
  493. package/dist/src/tools/glob.test.js.map +1 -1
  494. package/dist/src/tools/grep.d.ts +4 -3
  495. package/dist/src/tools/grep.js +31 -25
  496. package/dist/src/tools/grep.js.map +1 -1
  497. package/dist/src/tools/grep.test.js +15 -12
  498. package/dist/src/tools/grep.test.js.map +1 -1
  499. package/dist/src/tools/ls.d.ts +4 -3
  500. package/dist/src/tools/ls.js +29 -35
  501. package/dist/src/tools/ls.js.map +1 -1
  502. package/dist/src/tools/ls.test.js +34 -42
  503. package/dist/src/tools/ls.test.js.map +1 -1
  504. package/dist/src/tools/mcp-client-manager.d.ts +49 -11
  505. package/dist/src/tools/mcp-client-manager.js +191 -31
  506. package/dist/src/tools/mcp-client-manager.js.map +1 -1
  507. package/dist/src/tools/mcp-client-manager.test.js +132 -25
  508. package/dist/src/tools/mcp-client-manager.test.js.map +1 -1
  509. package/dist/src/tools/mcp-client.d.ts +12 -5
  510. package/dist/src/tools/mcp-client.js +287 -267
  511. package/dist/src/tools/mcp-client.js.map +1 -1
  512. package/dist/src/tools/mcp-client.test.js +352 -45
  513. package/dist/src/tools/mcp-client.test.js.map +1 -1
  514. package/dist/src/tools/mcp-tool.d.ts +6 -2
  515. package/dist/src/tools/mcp-tool.js +19 -8
  516. package/dist/src/tools/mcp-tool.js.map +1 -1
  517. package/dist/src/tools/mcp-tool.test.js +186 -273
  518. package/dist/src/tools/mcp-tool.test.js.map +1 -1
  519. package/dist/src/tools/memoryTool.d.ts +7 -5
  520. package/dist/src/tools/memoryTool.js +14 -12
  521. package/dist/src/tools/memoryTool.js.map +1 -1
  522. package/dist/src/tools/memoryTool.test.js +10 -9
  523. package/dist/src/tools/memoryTool.test.js.map +1 -1
  524. package/dist/src/tools/message-bus-integration.test.js +14 -1
  525. package/dist/src/tools/message-bus-integration.test.js.map +1 -1
  526. package/dist/src/tools/modifiable-tool.d.ts +5 -1
  527. package/dist/src/tools/modifiable-tool.js +38 -16
  528. package/dist/src/tools/modifiable-tool.js.map +1 -1
  529. package/dist/src/tools/modifiable-tool.test.js +66 -31
  530. package/dist/src/tools/modifiable-tool.test.js.map +1 -1
  531. package/dist/src/tools/read-file.d.ts +6 -5
  532. package/dist/src/tools/read-file.js +26 -32
  533. package/dist/src/tools/read-file.js.map +1 -1
  534. package/dist/src/tools/read-file.test.js +68 -33
  535. package/dist/src/tools/read-file.test.js.map +1 -1
  536. package/dist/src/tools/read-many-files.d.ts +6 -12
  537. package/dist/src/tools/read-many-files.js +28 -57
  538. package/dist/src/tools/read-many-files.js.map +1 -1
  539. package/dist/src/tools/read-many-files.test.js +37 -36
  540. package/dist/src/tools/read-many-files.test.js.map +1 -1
  541. package/dist/src/tools/ripGrep.d.ts +28 -10
  542. package/dist/src/tools/ripGrep.js +195 -193
  543. package/dist/src/tools/ripGrep.js.map +1 -1
  544. package/dist/src/tools/ripGrep.test.js +533 -204
  545. package/dist/src/tools/ripGrep.test.js.map +1 -1
  546. package/dist/src/tools/shell.d.ts +8 -6
  547. package/dist/src/tools/shell.js +64 -38
  548. package/dist/src/tools/shell.js.map +1 -1
  549. package/dist/src/tools/shell.test.js +134 -43
  550. package/dist/src/tools/shell.test.js.map +1 -1
  551. package/dist/src/tools/smart-edit.d.ts +10 -23
  552. package/dist/src/tools/smart-edit.js +100 -85
  553. package/dist/src/tools/smart-edit.js.map +1 -1
  554. package/dist/src/tools/smart-edit.test.js +229 -179
  555. package/dist/src/tools/smart-edit.test.js.map +1 -1
  556. package/dist/src/tools/tool-error.d.ts +21 -0
  557. package/dist/src/tools/tool-error.js +27 -0
  558. package/dist/src/tools/tool-error.js.map +1 -1
  559. package/dist/src/tools/tool-names.d.ts +17 -0
  560. package/dist/src/tools/tool-names.js +21 -0
  561. package/dist/src/tools/tool-names.js.map +1 -0
  562. package/dist/src/tools/tool-registry.d.ts +32 -20
  563. package/dist/src/tools/tool-registry.js +122 -78
  564. package/dist/src/tools/tool-registry.js.map +1 -1
  565. package/dist/src/tools/tool-registry.test.js +167 -90
  566. package/dist/src/tools/tool-registry.test.js.map +1 -1
  567. package/dist/src/tools/tools.d.ts +23 -8
  568. package/dist/src/tools/tools.js +69 -37
  569. package/dist/src/tools/tools.js.map +1 -1
  570. package/dist/src/tools/web-fetch.d.ts +11 -3
  571. package/dist/src/tools/web-fetch.js +80 -38
  572. package/dist/src/tools/web-fetch.js.map +1 -1
  573. package/dist/src/tools/web-fetch.test.js +338 -9
  574. package/dist/src/tools/web-fetch.test.js.map +1 -1
  575. package/dist/src/tools/web-search.d.ts +4 -3
  576. package/dist/src/tools/web-search.js +11 -9
  577. package/dist/src/tools/web-search.js.map +1 -1
  578. package/dist/src/tools/web-search.test.js +6 -0
  579. package/dist/src/tools/web-search.test.js.map +1 -1
  580. package/dist/src/tools/write-file.d.ts +3 -2
  581. package/dist/src/tools/write-file.js +41 -40
  582. package/dist/src/tools/write-file.js.map +1 -1
  583. package/dist/src/tools/write-file.test.js +130 -123
  584. package/dist/src/tools/write-file.test.js.map +1 -1
  585. package/dist/src/tools/write-todos.d.ts +34 -9
  586. package/dist/src/tools/write-todos.js +54 -11
  587. package/dist/src/tools/write-todos.js.map +1 -1
  588. package/dist/src/tools/write-todos.test.js +2 -2
  589. package/dist/src/tools/write-todos.test.js.map +1 -1
  590. package/dist/src/utils/bfsFileSearch.js +3 -2
  591. package/dist/src/utils/bfsFileSearch.js.map +1 -1
  592. package/dist/src/utils/channel.d.ts +19 -0
  593. package/dist/src/utils/channel.js +49 -0
  594. package/dist/src/utils/channel.js.map +1 -0
  595. package/dist/src/utils/channel.test.d.ts +6 -0
  596. package/dist/src/utils/channel.test.js +170 -0
  597. package/dist/src/utils/channel.test.js.map +1 -0
  598. package/dist/src/utils/debugLogger.d.ts +25 -0
  599. package/dist/src/utils/debugLogger.js +33 -0
  600. package/dist/src/utils/debugLogger.js.map +1 -0
  601. package/dist/src/utils/debugLogger.test.d.ts +6 -0
  602. package/dist/src/utils/debugLogger.test.js +69 -0
  603. package/dist/src/utils/debugLogger.test.js.map +1 -0
  604. package/dist/src/utils/delay.d.ts +16 -0
  605. package/dist/src/utils/delay.js +43 -0
  606. package/dist/src/utils/delay.js.map +1 -0
  607. package/dist/src/utils/delay.test.d.ts +6 -0
  608. package/dist/src/utils/delay.test.js +88 -0
  609. package/dist/src/utils/delay.test.js.map +1 -0
  610. package/dist/src/utils/editCorrector.js +10 -25
  611. package/dist/src/utils/editCorrector.js.map +1 -1
  612. package/dist/src/utils/editCorrector.test.js +19 -5
  613. package/dist/src/utils/editCorrector.test.js.map +1 -1
  614. package/dist/src/utils/editor.d.ts +4 -2
  615. package/dist/src/utils/editor.js +53 -39
  616. package/dist/src/utils/editor.js.map +1 -1
  617. package/dist/src/utils/editor.test.js +18 -45
  618. package/dist/src/utils/editor.test.js.map +1 -1
  619. package/dist/src/utils/environmentContext.d.ts +2 -1
  620. package/dist/src/utils/environmentContext.js +20 -33
  621. package/dist/src/utils/environmentContext.js.map +1 -1
  622. package/dist/src/utils/environmentContext.test.js +6 -34
  623. package/dist/src/utils/environmentContext.test.js.map +1 -1
  624. package/dist/src/utils/errorParsing.d.ts +1 -1
  625. package/dist/src/utils/errorParsing.js +5 -33
  626. package/dist/src/utils/errorParsing.js.map +1 -1
  627. package/dist/src/utils/errorParsing.test.js +0 -88
  628. package/dist/src/utils/errorParsing.test.js.map +1 -1
  629. package/dist/src/utils/errors.d.ts +3 -0
  630. package/dist/src/utils/errors.js +6 -0
  631. package/dist/src/utils/errors.js.map +1 -1
  632. package/dist/src/utils/events.d.ts +121 -0
  633. package/dist/src/utils/events.js +84 -0
  634. package/dist/src/utils/events.js.map +1 -0
  635. package/dist/src/utils/events.test.d.ts +6 -0
  636. package/dist/src/utils/events.test.js +212 -0
  637. package/dist/src/utils/events.test.js.map +1 -0
  638. package/dist/src/utils/extensionLoader.d.ts +86 -0
  639. package/dist/src/utils/extensionLoader.js +208 -0
  640. package/dist/src/utils/extensionLoader.js.map +1 -0
  641. package/dist/src/utils/extensionLoader.test.d.ts +6 -0
  642. package/dist/src/utils/extensionLoader.test.js +154 -0
  643. package/dist/src/utils/extensionLoader.test.js.map +1 -0
  644. package/dist/src/utils/fetch.d.ts +1 -0
  645. package/dist/src/utils/fetch.js +4 -0
  646. package/dist/src/utils/fetch.js.map +1 -1
  647. package/dist/src/utils/fileUtils.d.ts +4 -0
  648. package/dist/src/utils/fileUtils.js +34 -2
  649. package/dist/src/utils/fileUtils.js.map +1 -1
  650. package/dist/src/utils/fileUtils.test.js +87 -61
  651. package/dist/src/utils/fileUtils.test.js.map +1 -1
  652. package/dist/src/utils/filesearch/fileSearch.js +1 -1
  653. package/dist/src/utils/filesearch/fileSearch.js.map +1 -1
  654. package/dist/src/utils/flashFallback.test.js +28 -47
  655. package/dist/src/utils/flashFallback.test.js.map +1 -1
  656. package/dist/src/utils/formatters.d.ts +1 -0
  657. package/dist/src/utils/formatters.js +2 -1
  658. package/dist/src/utils/formatters.js.map +1 -1
  659. package/dist/src/utils/formatters.test.d.ts +6 -0
  660. package/dist/src/utils/formatters.test.js +26 -0
  661. package/dist/src/utils/formatters.test.js.map +1 -0
  662. package/dist/src/utils/getFolderStructure.js +9 -17
  663. package/dist/src/utils/getFolderStructure.js.map +1 -1
  664. package/dist/src/utils/getFolderStructure.test.js +7 -6
  665. package/dist/src/utils/getFolderStructure.test.js.map +1 -1
  666. package/dist/src/utils/gitIgnoreParser.d.ts +4 -1
  667. package/dist/src/utils/gitIgnoreParser.js +28 -10
  668. package/dist/src/utils/gitIgnoreParser.js.map +1 -1
  669. package/dist/src/utils/gitIgnoreParser.test.js +58 -0
  670. package/dist/src/utils/gitIgnoreParser.test.js.map +1 -1
  671. package/dist/src/utils/googleErrors.d.ts +104 -0
  672. package/dist/src/utils/googleErrors.js +152 -0
  673. package/dist/src/utils/googleErrors.js.map +1 -0
  674. package/dist/src/utils/googleErrors.test.d.ts +6 -0
  675. package/dist/src/utils/googleErrors.test.js +301 -0
  676. package/dist/src/utils/googleErrors.test.js.map +1 -0
  677. package/dist/src/utils/googleQuotaErrors.d.ts +37 -0
  678. package/dist/src/utils/googleQuotaErrors.js +157 -0
  679. package/dist/src/utils/googleQuotaErrors.js.map +1 -0
  680. package/dist/src/utils/googleQuotaErrors.test.d.ts +6 -0
  681. package/dist/src/utils/googleQuotaErrors.test.js +311 -0
  682. package/dist/src/utils/googleQuotaErrors.test.js.map +1 -0
  683. package/dist/src/utils/httpErrors.d.ts +18 -0
  684. package/dist/src/utils/httpErrors.js +36 -0
  685. package/dist/src/utils/httpErrors.js.map +1 -0
  686. package/dist/src/utils/ignorePatterns.test.js +26 -30
  687. package/dist/src/utils/ignorePatterns.test.js.map +1 -1
  688. package/dist/src/utils/installationManager.js +2 -1
  689. package/dist/src/utils/installationManager.js.map +1 -1
  690. package/dist/src/utils/installationManager.test.js +6 -4
  691. package/dist/src/utils/installationManager.test.js.map +1 -1
  692. package/dist/src/utils/llm-edit-fixer.d.ts +1 -1
  693. package/dist/src/utils/llm-edit-fixer.js +33 -9
  694. package/dist/src/utils/llm-edit-fixer.js.map +1 -1
  695. package/dist/src/utils/llm-edit-fixer.test.js +38 -1
  696. package/dist/src/utils/llm-edit-fixer.test.js.map +1 -1
  697. package/dist/src/utils/memoryDiscovery.d.ts +20 -1
  698. package/dist/src/utils/memoryDiscovery.js +176 -12
  699. package/dist/src/utils/memoryDiscovery.js.map +1 -1
  700. package/dist/src/utils/memoryDiscovery.test.js +299 -40
  701. package/dist/src/utils/memoryDiscovery.test.js.map +1 -1
  702. package/dist/src/utils/memoryImportProcessor.js +4 -3
  703. package/dist/src/utils/memoryImportProcessor.js.map +1 -1
  704. package/dist/src/utils/memoryImportProcessor.test.js +8 -14
  705. package/dist/src/utils/memoryImportProcessor.test.js.map +1 -1
  706. package/dist/src/utils/nextSpeakerChecker.js +3 -3
  707. package/dist/src/utils/nextSpeakerChecker.js.map +1 -1
  708. package/dist/src/utils/nextSpeakerChecker.test.js +13 -5
  709. package/dist/src/utils/nextSpeakerChecker.test.js.map +1 -1
  710. package/dist/src/utils/package.d.ts +12 -0
  711. package/dist/src/utils/package.js +15 -0
  712. package/dist/src/utils/package.js.map +1 -0
  713. package/dist/src/utils/pathCorrector.d.ts +25 -0
  714. package/dist/src/utils/pathCorrector.js +33 -0
  715. package/dist/src/utils/pathCorrector.js.map +1 -0
  716. package/dist/src/utils/pathCorrector.test.d.ts +6 -0
  717. package/dist/src/utils/pathCorrector.test.js +83 -0
  718. package/dist/src/utils/pathCorrector.test.js.map +1 -0
  719. package/dist/src/utils/pathReader.js +4 -4
  720. package/dist/src/utils/pathReader.js.map +1 -1
  721. package/dist/src/utils/pathReader.test.js +44 -1
  722. package/dist/src/utils/pathReader.test.js.map +1 -1
  723. package/dist/src/utils/paths.d.ts +1 -1
  724. package/dist/src/utils/paths.js +131 -29
  725. package/dist/src/utils/paths.js.map +1 -1
  726. package/dist/src/utils/paths.test.js +200 -68
  727. package/dist/src/utils/paths.test.js.map +1 -1
  728. package/dist/src/utils/quotaErrorDetection.d.ts +0 -2
  729. package/dist/src/utils/quotaErrorDetection.js +0 -46
  730. package/dist/src/utils/quotaErrorDetection.js.map +1 -1
  731. package/dist/src/utils/retry.d.ts +3 -10
  732. package/dist/src/utils/retry.js +97 -195
  733. package/dist/src/utils/retry.js.map +1 -1
  734. package/dist/src/utils/retry.test.js +179 -145
  735. package/dist/src/utils/retry.test.js.map +1 -1
  736. package/dist/src/utils/safeJsonStringify.d.ts +4 -4
  737. package/dist/src/utils/safeJsonStringify.js +31 -7
  738. package/dist/src/utils/safeJsonStringify.js.map +1 -1
  739. package/dist/src/utils/shell-utils.d.ts +15 -2
  740. package/dist/src/utils/shell-utils.js +387 -140
  741. package/dist/src/utils/shell-utils.js.map +1 -1
  742. package/dist/src/utils/shell-utils.test.js +244 -62
  743. package/dist/src/utils/shell-utils.test.js.map +1 -1
  744. package/dist/src/utils/stdio.d.ts +32 -0
  745. package/dist/src/utils/stdio.js +85 -0
  746. package/dist/src/utils/stdio.js.map +1 -0
  747. package/dist/src/utils/stdio.test.d.ts +6 -0
  748. package/dist/src/utils/stdio.test.js +47 -0
  749. package/dist/src/utils/stdio.test.js.map +1 -0
  750. package/dist/src/utils/summarizer.d.ts +4 -2
  751. package/dist/src/utils/summarizer.js +8 -9
  752. package/dist/src/utils/summarizer.js.map +1 -1
  753. package/dist/src/utils/summarizer.test.js +32 -12
  754. package/dist/src/utils/summarizer.test.js.map +1 -1
  755. package/dist/src/utils/systemEncoding.js +5 -4
  756. package/dist/src/utils/systemEncoding.js.map +1 -1
  757. package/dist/src/utils/systemEncoding.test.js +2 -1
  758. package/dist/src/utils/systemEncoding.test.js.map +1 -1
  759. package/dist/src/utils/terminal.d.ts +14 -0
  760. package/dist/src/utils/terminal.js +38 -0
  761. package/dist/src/utils/terminal.js.map +1 -0
  762. package/dist/src/utils/tool-utils.d.ts +2 -2
  763. package/dist/src/utils/tool-utils.js +15 -6
  764. package/dist/src/utils/tool-utils.js.map +1 -1
  765. package/dist/src/utils/tool-utils.test.js +8 -0
  766. package/dist/src/utils/tool-utils.test.js.map +1 -1
  767. package/dist/src/utils/userAccountManager.js +5 -4
  768. package/dist/src/utils/userAccountManager.js.map +1 -1
  769. package/dist/src/utils/userAccountManager.test.js +9 -7
  770. package/dist/src/utils/userAccountManager.test.js.map +1 -1
  771. package/dist/src/utils/workspaceContext.d.ts +4 -3
  772. package/dist/src/utils/workspaceContext.js +13 -13
  773. package/dist/src/utils/workspaceContext.js.map +1 -1
  774. package/dist/src/utils/workspaceContext.test.js +8 -7
  775. package/dist/src/utils/workspaceContext.test.js.map +1 -1
  776. package/dist/tsconfig.tsbuildinfo +1 -1
  777. package/package.json +12 -7
@@ -4,7 +4,7 @@
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
6
  import { describe, it, expect, vi, beforeEach, afterEach, } from 'vitest';
7
- import { findCompressSplitPoint, isThinkingDefault, isThinkingSupported, GeminiClient, } from './client.js';
7
+ import { GeminiClient } from './client.js';
8
8
  import { AuthType, } from './contentGenerator.js';
9
9
  import {} from './geminiChat.js';
10
10
  import { CompressionStatus, GeminiEventType, Turn, } from './turn.js';
@@ -14,8 +14,10 @@ import { FileDiscoveryService } from '../services/fileDiscoveryService.js';
14
14
  import { setSimulate429 } from '../utils/testUtils.js';
15
15
  import { tokenLimit } from './tokenLimits.js';
16
16
  import { ideContextStore } from '../ide/ideContext.js';
17
- import { ClearcutLogger } from '../telemetry/clearcut-logger/clearcut-logger.js';
18
17
  import { uiTelemetryService } from '../telemetry/uiTelemetry.js';
18
+ import { ChatCompressionService } from '../services/chatCompressionService.js';
19
+ import { ClearcutLogger } from '../telemetry/clearcut-logger/clearcut-logger.js';
20
+ vi.mock('../services/chatCompressionService.js');
19
21
  // Mock fs module to prevent actual file system operations during tests
20
22
  const mockFileSystem = new Map();
21
23
  vi.mock('node:fs', () => {
@@ -95,97 +97,6 @@ async function fromAsync(promise) {
95
97
  }
96
98
  return results;
97
99
  }
98
- describe('findCompressSplitPoint', () => {
99
- it('should throw an error for non-positive numbers', () => {
100
- expect(() => findCompressSplitPoint([], 0)).toThrow('Fraction must be between 0 and 1');
101
- });
102
- it('should throw an error for a fraction greater than or equal to 1', () => {
103
- expect(() => findCompressSplitPoint([], 1)).toThrow('Fraction must be between 0 and 1');
104
- });
105
- it('should handle an empty history', () => {
106
- expect(findCompressSplitPoint([], 0.5)).toBe(0);
107
- });
108
- it('should handle a fraction in the middle', () => {
109
- const history = [
110
- { role: 'user', parts: [{ text: 'This is the first message.' }] }, // JSON length: 66 (19%)
111
- { role: 'model', parts: [{ text: 'This is the second message.' }] }, // JSON length: 68 (40%)
112
- { role: 'user', parts: [{ text: 'This is the third message.' }] }, // JSON length: 66 (60%)
113
- { role: 'model', parts: [{ text: 'This is the fourth message.' }] }, // JSON length: 68 (80%)
114
- { role: 'user', parts: [{ text: 'This is the fifth message.' }] }, // JSON length: 65 (100%)
115
- ];
116
- expect(findCompressSplitPoint(history, 0.5)).toBe(4);
117
- });
118
- it('should handle a fraction of last index', () => {
119
- const history = [
120
- { role: 'user', parts: [{ text: 'This is the first message.' }] }, // JSON length: 66 (19%)
121
- { role: 'model', parts: [{ text: 'This is the second message.' }] }, // JSON length: 68 (40%)
122
- { role: 'user', parts: [{ text: 'This is the third message.' }] }, // JSON length: 66 (60%)
123
- { role: 'model', parts: [{ text: 'This is the fourth message.' }] }, // JSON length: 68 (80%)
124
- { role: 'user', parts: [{ text: 'This is the fifth message.' }] }, // JSON length: 65 (100%)
125
- ];
126
- expect(findCompressSplitPoint(history, 0.9)).toBe(4);
127
- });
128
- it('should handle a fraction of after last index', () => {
129
- const history = [
130
- { role: 'user', parts: [{ text: 'This is the first message.' }] }, // JSON length: 66 (24%%)
131
- { role: 'model', parts: [{ text: 'This is the second message.' }] }, // JSON length: 68 (50%)
132
- { role: 'user', parts: [{ text: 'This is the third message.' }] }, // JSON length: 66 (74%)
133
- { role: 'model', parts: [{ text: 'This is the fourth message.' }] }, // JSON length: 68 (100%)
134
- ];
135
- expect(findCompressSplitPoint(history, 0.8)).toBe(4);
136
- });
137
- it('should return earlier splitpoint if no valid ones are after threshhold', () => {
138
- const history = [
139
- { role: 'user', parts: [{ text: 'This is the first message.' }] },
140
- { role: 'model', parts: [{ text: 'This is the second message.' }] },
141
- { role: 'user', parts: [{ text: 'This is the third message.' }] },
142
- { role: 'model', parts: [{ functionCall: {} }] },
143
- ];
144
- // Can't return 4 because the previous item has a function call.
145
- expect(findCompressSplitPoint(history, 0.99)).toBe(2);
146
- });
147
- it('should handle a history with only one item', () => {
148
- const historyWithEmptyParts = [
149
- { role: 'user', parts: [{ text: 'Message 1' }] },
150
- ];
151
- expect(findCompressSplitPoint(historyWithEmptyParts, 0.5)).toBe(0);
152
- });
153
- it('should handle history with weird parts', () => {
154
- const historyWithEmptyParts = [
155
- { role: 'user', parts: [{ text: 'Message 1' }] },
156
- { role: 'model', parts: [{ fileData: { fileUri: 'derp' } }] },
157
- { role: 'user', parts: [{ text: 'Message 2' }] },
158
- ];
159
- expect(findCompressSplitPoint(historyWithEmptyParts, 0.5)).toBe(2);
160
- });
161
- });
162
- describe('isThinkingSupported', () => {
163
- it('should return true for gemini-2.5', () => {
164
- expect(isThinkingSupported('gemini-2.5')).toBe(true);
165
- });
166
- it('should return true for gemini-2.5-pro', () => {
167
- expect(isThinkingSupported('gemini-2.5-pro')).toBe(true);
168
- });
169
- it('should return false for other models', () => {
170
- expect(isThinkingSupported('gemini-1.5-flash')).toBe(false);
171
- expect(isThinkingSupported('some-other-model')).toBe(false);
172
- });
173
- });
174
- describe('isThinkingDefault', () => {
175
- it('should return false for gemini-2.5-flash-lite', () => {
176
- expect(isThinkingDefault('gemini-2.5-flash-lite')).toBe(false);
177
- });
178
- it('should return true for gemini-2.5', () => {
179
- expect(isThinkingDefault('gemini-2.5')).toBe(true);
180
- });
181
- it('should return true for gemini-2.5-pro', () => {
182
- expect(isThinkingDefault('gemini-2.5-pro')).toBe(true);
183
- });
184
- it('should return false for other models', () => {
185
- expect(isThinkingDefault('gemini-1.5-flash')).toBe(false);
186
- expect(isThinkingDefault('some-other-model')).toBe(false);
187
- });
188
- });
189
100
  describe('Gemini Client (client.ts)', () => {
190
101
  let mockContentGenerator;
191
102
  let mockConfig;
@@ -193,7 +104,16 @@ describe('Gemini Client (client.ts)', () => {
193
104
  let mockGenerateContentFn;
194
105
  beforeEach(async () => {
195
106
  vi.resetAllMocks();
107
+ ClearcutLogger.clearInstance();
196
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
+ },
116
+ });
197
117
  mockGenerateContentFn = vi.fn().mockResolvedValue({
198
118
  candidates: [{ content: { parts: [{ text: '{"key": "value"}' }] } }],
199
119
  });
@@ -228,7 +148,6 @@ describe('Gemini Client (client.ts)', () => {
228
148
  getVertexAI: vi.fn().mockReturnValue(false),
229
149
  getUserAgent: vi.fn().mockReturnValue('test-agent'),
230
150
  getUserMemory: vi.fn().mockReturnValue(''),
231
- getFullContext: vi.fn().mockReturnValue(false),
232
151
  getSessionId: vi.fn().mockReturnValue('test-session-id'),
233
152
  getProxy: vi.fn().mockReturnValue(undefined),
234
153
  getWorkingDir: vi.fn().mockReturnValue('/test/dir'),
@@ -241,6 +160,7 @@ describe('Gemini Client (client.ts)', () => {
241
160
  getIdeModeFeature: vi.fn().mockReturnValue(false),
242
161
  getIdeMode: vi.fn().mockReturnValue(true),
243
162
  getDebugMode: vi.fn().mockReturnValue(false),
163
+ getPreviewFeatures: vi.fn().mockReturnValue(false),
244
164
  getWorkspaceContext: vi.fn().mockReturnValue({
245
165
  getDirectories: vi.fn().mockReturnValue(['/test/dir']),
246
166
  }),
@@ -254,6 +174,8 @@ describe('Gemini Client (client.ts)', () => {
254
174
  getSkipNextSpeakerCheck: vi.fn().mockReturnValue(false),
255
175
  getUseSmartEdit: vi.fn().mockReturnValue(false),
256
176
  getUseModelRouter: vi.fn().mockReturnValue(false),
177
+ getShowModelInfoInChat: vi.fn().mockReturnValue(false),
178
+ getContinueOnFailedApiCall: vi.fn(),
257
179
  getProjectRoot: vi.fn().mockReturnValue('/test/project/root'),
258
180
  storage: {
259
181
  getProjectTempDir: vi.fn().mockReturnValue('/test/temp'),
@@ -265,10 +187,23 @@ describe('Gemini Client (client.ts)', () => {
265
187
  reasoning: 'test',
266
188
  }),
267
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),
268
202
  };
269
203
  client = new GeminiClient(mockConfig);
270
204
  await client.initialize();
271
205
  vi.mocked(mockConfig.getGeminiClient).mockReturnValue(client);
206
+ vi.mocked(uiTelemetryService.setLastPromptTokenCount).mockClear();
272
207
  });
273
208
  afterEach(() => {
274
209
  vi.restoreAllMocks();
@@ -309,6 +244,23 @@ describe('Gemini Client (client.ts)', () => {
309
244
  expect(JSON.stringify(newHistory)).not.toContain('some old message');
310
245
  });
311
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
+ });
312
264
  describe('tryCompressChat', () => {
313
265
  const mockGetHistory = vi.fn();
314
266
  beforeEach(() => {
@@ -319,87 +271,64 @@ describe('Gemini Client (client.ts)', () => {
319
271
  getHistory: mockGetHistory,
320
272
  addHistory: vi.fn(),
321
273
  setHistory: vi.fn(),
274
+ getLastPromptTokenCount: vi.fn(),
322
275
  };
323
276
  });
324
277
  function setup({ chatHistory = [
325
278
  { role: 'user', parts: [{ text: 'Long conversation' }] },
326
279
  { role: 'model', parts: [{ text: 'Long response' }] },
327
- ], originalTokenCount = 1000, summaryText = 'This is a summary.', } = {}) {
280
+ ], originalTokenCount = 1000, newTokenCount = 500, compressionStatus = CompressionStatus.COMPRESSED, } = {}) {
328
281
  const mockOriginalChat = {
329
282
  getHistory: vi.fn((_curated) => chatHistory),
330
283
  setHistory: vi.fn(),
284
+ getLastPromptTokenCount: vi.fn().mockReturnValue(originalTokenCount),
331
285
  };
332
286
  client['chat'] = mockOriginalChat;
333
287
  vi.mocked(uiTelemetryService.getLastPromptTokenCount).mockReturnValue(originalTokenCount);
334
- mockGenerateContentFn.mockResolvedValue({
335
- candidates: [
336
- {
337
- content: {
338
- role: 'model',
339
- parts: [{ text: summaryText }],
340
- },
341
- },
342
- ],
343
- });
344
- // Calculate what the new history will be
345
- const splitPoint = findCompressSplitPoint(chatHistory, 0.7); // 1 - 0.3
346
- const historyToKeep = chatHistory.slice(splitPoint);
347
- // This is the history that the new chat will have.
348
- // It includes the default startChat history + the extra history from tryCompressChat
349
- const newCompressedHistory = [
350
- // Mocked envParts + canned response from startChat
351
- {
352
- role: 'user',
353
- parts: [{ text: 'Mocked env context' }],
354
- },
355
- {
356
- role: 'model',
357
- parts: [{ text: 'Got it. Thanks for the context!' }],
358
- },
359
- // extraHistory from tryCompressChat
360
- {
361
- role: 'user',
362
- parts: [{ text: summaryText }],
363
- },
364
- {
365
- role: 'model',
366
- parts: [{ text: 'Got it. Thanks for the additional context!' }],
367
- },
368
- ...historyToKeep,
288
+ const newHistory = [
289
+ { role: 'user', parts: [{ text: 'Summary' }] },
290
+ { role: 'model', parts: [{ text: 'Got it' }] },
369
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
+ },
301
+ });
370
302
  const mockNewChat = {
371
- getHistory: vi.fn().mockReturnValue(newCompressedHistory),
303
+ getHistory: vi.fn().mockReturnValue(newHistory),
372
304
  setHistory: vi.fn(),
305
+ getLastPromptTokenCount: vi.fn().mockReturnValue(newTokenCount),
373
306
  };
374
307
  client['startChat'] = vi
375
308
  .fn()
376
309
  .mockResolvedValue(mockNewChat);
377
- const totalChars = newCompressedHistory.reduce((total, content) => total + JSON.stringify(content).length, 0);
378
- const estimatedNewTokenCount = Math.floor(totalChars / 4);
379
310
  return {
380
311
  client,
381
312
  mockOriginalChat,
382
313
  mockNewChat,
383
- estimatedNewTokenCount,
314
+ estimatedNewTokenCount: newTokenCount,
384
315
  };
385
316
  }
386
317
  describe('when compression inflates the token count', () => {
387
318
  it('allows compression to be forced/manual after a failure', async () => {
388
- // Call 1 (Fails): Setup with a long summary to inflate tokens
389
- const longSummary = 'long summary '.repeat(100);
390
- const { client, estimatedNewTokenCount: inflatedTokenCount } = setup({
319
+ // Call 1 (Fails): Setup with inflated tokens
320
+ setup({
391
321
  originalTokenCount: 100,
392
- summaryText: longSummary,
322
+ newTokenCount: 200,
323
+ compressionStatus: CompressionStatus.COMPRESSION_FAILED_INFLATED_TOKEN_COUNT,
393
324
  });
394
- expect(inflatedTokenCount).toBeGreaterThan(100); // Ensure setup is correct
395
325
  await client.tryCompressChat('prompt-id-4', false); // Fails
396
- // Call 2 (Forced): Re-setup with a short summary
397
- const shortSummary = 'short';
326
+ // Call 2 (Forced): Re-setup with compressed tokens
398
327
  const { estimatedNewTokenCount: compressedTokenCount } = setup({
399
328
  originalTokenCount: 100,
400
- summaryText: shortSummary,
329
+ newTokenCount: 50,
330
+ compressionStatus: CompressionStatus.COMPRESSED,
401
331
  });
402
- expect(compressedTokenCount).toBeLessThanOrEqual(100); // Ensure setup is correct
403
332
  const result = await client.tryCompressChat('prompt-id-4', true); // Forced
404
333
  expect(result).toEqual({
405
334
  compressionStatus: CompressionStatus.COMPRESSED,
@@ -408,12 +337,11 @@ describe('Gemini Client (client.ts)', () => {
408
337
  });
409
338
  });
410
339
  it('yields the result even if the compression inflated the tokens', async () => {
411
- const longSummary = 'long summary '.repeat(100);
412
340
  const { client, estimatedNewTokenCount } = setup({
413
341
  originalTokenCount: 100,
414
- summaryText: longSummary,
342
+ newTokenCount: 200,
343
+ compressionStatus: CompressionStatus.COMPRESSION_FAILED_INFLATED_TOKEN_COUNT,
415
344
  });
416
- expect(estimatedNewTokenCount).toBeGreaterThan(100); // Ensure setup is correct
417
345
  const result = await client.tryCompressChat('prompt-id-4', false);
418
346
  expect(result).toEqual({
419
347
  compressionStatus: CompressionStatus.COMPRESSION_FAILED_INFLATED_TOKEN_COUNT,
@@ -424,47 +352,52 @@ describe('Gemini Client (client.ts)', () => {
424
352
  expect(uiTelemetryService.setLastPromptTokenCount).not.toHaveBeenCalled();
425
353
  });
426
354
  it('does not manipulate the source chat', async () => {
427
- const longSummary = 'long summary '.repeat(100);
428
- const { client, mockOriginalChat, estimatedNewTokenCount } = setup({
355
+ const { client, mockOriginalChat } = setup({
429
356
  originalTokenCount: 100,
430
- summaryText: longSummary,
357
+ newTokenCount: 200,
358
+ compressionStatus: CompressionStatus.COMPRESSION_FAILED_INFLATED_TOKEN_COUNT,
431
359
  });
432
- expect(estimatedNewTokenCount).toBeGreaterThan(100); // Ensure setup is correct
433
360
  await client.tryCompressChat('prompt-id-4', false);
434
361
  // On failure, the chat should NOT be replaced
435
362
  expect(client['chat']).toBe(mockOriginalChat);
436
363
  });
437
- it('will not attempt to compress context after a failure', async () => {
438
- const longSummary = 'long summary '.repeat(100);
439
- const { client, estimatedNewTokenCount } = setup({
364
+ it.skip('will not attempt to compress context after a failure', async () => {
365
+ const { client } = setup({
440
366
  originalTokenCount: 100,
441
- summaryText: longSummary,
367
+ newTokenCount: 200,
368
+ compressionStatus: CompressionStatus.COMPRESSION_FAILED_INFLATED_TOKEN_COUNT,
442
369
  });
443
- expect(estimatedNewTokenCount).toBeGreaterThan(100); // Ensure setup is correct
444
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
+ });
445
380
  // This call should now be a NOOP
446
381
  const result = await client.tryCompressChat('prompt-id-5', false);
447
- // generateContent (for summary) should only have been called once
448
- expect(mockGenerateContentFn).toHaveBeenCalledTimes(1);
449
- expect(result).toEqual({
450
- compressionStatus: CompressionStatus.NOOP,
451
- newTokenCount: 0,
452
- originalTokenCount: 0,
453
- });
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);
454
385
  });
455
386
  });
456
387
  it('should not trigger summarization if token count is below threshold', async () => {
457
388
  const MOCKED_TOKEN_LIMIT = 1000;
458
- vi.mocked(tokenLimit).mockReturnValue(MOCKED_TOKEN_LIMIT);
459
- mockGetHistory.mockReturnValue([
460
- { role: 'user', parts: [{ text: '...history...' }] },
461
- ]);
462
389
  const originalTokenCount = MOCKED_TOKEN_LIMIT * 0.699;
463
- vi.mocked(uiTelemetryService.getLastPromptTokenCount).mockReturnValue(originalTokenCount);
390
+ vi.mocked(ChatCompressionService.prototype.compress).mockResolvedValue({
391
+ newHistory: null,
392
+ info: {
393
+ originalTokenCount,
394
+ newTokenCount: originalTokenCount,
395
+ compressionStatus: CompressionStatus.NOOP,
396
+ },
397
+ });
464
398
  const initialChat = client.getChat();
465
399
  const result = await client.tryCompressChat('prompt-id-2', false);
466
400
  const newChat = client.getChat();
467
- expect(tokenLimit).toHaveBeenCalled();
468
401
  expect(result).toEqual({
469
402
  compressionStatus: CompressionStatus.NOOP,
470
403
  newTokenCount: originalTokenCount,
@@ -472,248 +405,19 @@ describe('Gemini Client (client.ts)', () => {
472
405
  });
473
406
  expect(newChat).toBe(initialChat);
474
407
  });
475
- it('logs a telemetry event when compressing', async () => {
476
- vi.spyOn(ClearcutLogger.prototype, 'logChatCompressionEvent');
477
- const MOCKED_TOKEN_LIMIT = 1000;
478
- const MOCKED_CONTEXT_PERCENTAGE_THRESHOLD = 0.5;
479
- vi.mocked(tokenLimit).mockReturnValue(MOCKED_TOKEN_LIMIT);
480
- vi.spyOn(client['config'], 'getChatCompression').mockReturnValue({
481
- contextPercentageThreshold: MOCKED_CONTEXT_PERCENTAGE_THRESHOLD,
482
- });
483
- const history = [{ role: 'user', parts: [{ text: '...history...' }] }];
484
- mockGetHistory.mockReturnValue(history);
485
- const originalTokenCount = MOCKED_TOKEN_LIMIT * MOCKED_CONTEXT_PERCENTAGE_THRESHOLD;
486
- vi.mocked(uiTelemetryService.getLastPromptTokenCount).mockReturnValue(originalTokenCount);
487
- // We need to control the estimated new token count.
488
- // We mock startChat to return a chat with a known history.
489
- const summaryText = 'This is a summary.';
490
- const splitPoint = findCompressSplitPoint(history, 0.7);
491
- const historyToKeep = history.slice(splitPoint);
492
- const newCompressedHistory = [
493
- { role: 'user', parts: [{ text: 'Mocked env context' }] },
494
- { role: 'model', parts: [{ text: 'Got it. Thanks for the context!' }] },
495
- { role: 'user', parts: [{ text: summaryText }] },
496
- {
497
- role: 'model',
498
- parts: [{ text: 'Got it. Thanks for the additional context!' }],
499
- },
500
- ...historyToKeep,
501
- ];
502
- const mockNewChat = {
503
- getHistory: vi.fn().mockReturnValue(newCompressedHistory),
504
- };
505
- client['startChat'] = vi
506
- .fn()
507
- .mockResolvedValue(mockNewChat);
508
- const totalChars = newCompressedHistory.reduce((total, content) => total + JSON.stringify(content).length, 0);
509
- const newTokenCount = Math.floor(totalChars / 4);
510
- // Mock the summary response from the chat
511
- mockGenerateContentFn.mockResolvedValue({
512
- candidates: [
513
- {
514
- content: {
515
- role: 'model',
516
- parts: [{ text: summaryText }],
517
- },
518
- },
519
- ],
520
- });
521
- await client.tryCompressChat('prompt-id-3', false);
522
- expect(ClearcutLogger.prototype.logChatCompressionEvent).toHaveBeenCalledWith(expect.objectContaining({
523
- tokens_before: originalTokenCount,
524
- tokens_after: newTokenCount,
525
- }));
526
- expect(uiTelemetryService.setLastPromptTokenCount).toHaveBeenCalledWith(newTokenCount);
527
- expect(uiTelemetryService.setLastPromptTokenCount).toHaveBeenCalledTimes(1);
528
- });
529
- it('should trigger summarization if token count is at threshold with contextPercentageThreshold setting', async () => {
530
- const MOCKED_TOKEN_LIMIT = 1000;
531
- const MOCKED_CONTEXT_PERCENTAGE_THRESHOLD = 0.5;
532
- vi.mocked(tokenLimit).mockReturnValue(MOCKED_TOKEN_LIMIT);
533
- vi.spyOn(client['config'], 'getChatCompression').mockReturnValue({
534
- contextPercentageThreshold: MOCKED_CONTEXT_PERCENTAGE_THRESHOLD,
535
- });
536
- const history = [{ role: 'user', parts: [{ text: '...history...' }] }];
537
- mockGetHistory.mockReturnValue(history);
538
- const originalTokenCount = MOCKED_TOKEN_LIMIT * MOCKED_CONTEXT_PERCENTAGE_THRESHOLD;
539
- vi.mocked(uiTelemetryService.getLastPromptTokenCount).mockReturnValue(originalTokenCount);
540
- // Mock summary and new chat
541
- const summaryText = 'This is a summary.';
542
- const splitPoint = findCompressSplitPoint(history, 0.7);
543
- const historyToKeep = history.slice(splitPoint);
544
- const newCompressedHistory = [
545
- { role: 'user', parts: [{ text: 'Mocked env context' }] },
546
- { role: 'model', parts: [{ text: 'Got it. Thanks for the context!' }] },
547
- { role: 'user', parts: [{ text: summaryText }] },
548
- {
549
- role: 'model',
550
- parts: [{ text: 'Got it. Thanks for the additional context!' }],
551
- },
552
- ...historyToKeep,
553
- ];
554
- const mockNewChat = {
555
- getHistory: vi.fn().mockReturnValue(newCompressedHistory),
556
- };
557
- client['startChat'] = vi
558
- .fn()
559
- .mockResolvedValue(mockNewChat);
560
- const totalChars = newCompressedHistory.reduce((total, content) => total + JSON.stringify(content).length, 0);
561
- const newTokenCount = Math.floor(totalChars / 4);
562
- // Mock the summary response from the chat
563
- mockGenerateContentFn.mockResolvedValue({
564
- candidates: [
565
- {
566
- content: {
567
- role: 'model',
568
- parts: [{ text: summaryText }],
569
- },
570
- },
571
- ],
572
- });
573
- const initialChat = client.getChat();
574
- const result = await client.tryCompressChat('prompt-id-3', false);
575
- const newChat = client.getChat();
576
- expect(tokenLimit).toHaveBeenCalled();
577
- expect(mockGenerateContentFn).toHaveBeenCalled();
578
- // Assert that summarization happened and returned the correct stats
579
- expect(result).toEqual({
580
- compressionStatus: CompressionStatus.COMPRESSED,
581
- originalTokenCount,
582
- newTokenCount,
583
- });
584
- // Assert that the chat was reset
585
- expect(newChat).not.toBe(initialChat);
586
- });
587
- it('should not compress across a function call response', async () => {
588
- const MOCKED_TOKEN_LIMIT = 1000;
589
- vi.mocked(tokenLimit).mockReturnValue(MOCKED_TOKEN_LIMIT);
590
- const history = [
591
- { role: 'user', parts: [{ text: '...history 1...' }] },
592
- { role: 'model', parts: [{ text: '...history 2...' }] },
593
- { role: 'user', parts: [{ text: '...history 3...' }] },
594
- { role: 'model', parts: [{ text: '...history 4...' }] },
595
- { role: 'user', parts: [{ text: '...history 5...' }] },
596
- { role: 'model', parts: [{ text: '...history 6...' }] },
597
- { role: 'user', parts: [{ text: '...history 7...' }] },
598
- { role: 'model', parts: [{ text: '...history 8...' }] },
599
- // Normally we would break here, but we have a function response.
600
- {
601
- role: 'user',
602
- parts: [{ functionResponse: { name: '...history 8...' } }],
603
- },
604
- { role: 'model', parts: [{ text: '...history 10...' }] },
605
- // Instead we will break here.
606
- { role: 'user', parts: [{ text: '...history 10...' }] },
607
- ];
608
- mockGetHistory.mockReturnValue(history);
609
- const originalTokenCount = 1000 * 0.7;
610
- vi.mocked(uiTelemetryService.getLastPromptTokenCount).mockReturnValue(originalTokenCount);
611
- // Mock summary and new chat
612
- const summaryText = 'This is a summary.';
613
- const splitPoint = findCompressSplitPoint(history, 0.7); // This should be 10
614
- expect(splitPoint).toBe(10); // Verify split point logic
615
- const historyToKeep = history.slice(splitPoint); // Should keep last user message
616
- expect(historyToKeep).toEqual([
617
- { role: 'user', parts: [{ text: '...history 10...' }] },
618
- ]);
619
- const newCompressedHistory = [
620
- { role: 'user', parts: [{ text: 'Mocked env context' }] },
621
- { role: 'model', parts: [{ text: 'Got it. Thanks for the context!' }] },
622
- { role: 'user', parts: [{ text: summaryText }] },
623
- {
624
- role: 'model',
625
- parts: [{ text: 'Got it. Thanks for the additional context!' }],
626
- },
627
- ...historyToKeep,
628
- ];
629
- const mockNewChat = {
630
- getHistory: vi.fn().mockReturnValue(newCompressedHistory),
631
- };
632
- client['startChat'] = vi
633
- .fn()
634
- .mockResolvedValue(mockNewChat);
635
- const totalChars = newCompressedHistory.reduce((total, content) => total + JSON.stringify(content).length, 0);
636
- const newTokenCount = Math.floor(totalChars / 4);
637
- // Mock the summary response from the chat
638
- mockGenerateContentFn.mockResolvedValue({
639
- candidates: [
640
- {
641
- content: {
642
- role: 'model',
643
- parts: [{ text: summaryText }],
644
- },
645
- },
646
- ],
647
- });
648
- const initialChat = client.getChat();
649
- const result = await client.tryCompressChat('prompt-id-3', false);
650
- const newChat = client.getChat();
651
- expect(tokenLimit).toHaveBeenCalled();
652
- expect(mockGenerateContentFn).toHaveBeenCalled();
653
- // Assert that summarization happened and returned the correct stats
654
- expect(result).toEqual({
655
- compressionStatus: CompressionStatus.COMPRESSED,
656
- originalTokenCount,
657
- newTokenCount,
658
- });
659
- // Assert that the chat was reset
660
- expect(newChat).not.toBe(initialChat);
661
- // 1. standard start context message (env)
662
- // 2. standard canned model response
663
- // 3. compressed summary message (user)
664
- // 4. standard canned model response
665
- // 5. The last user message (historyToKeep)
666
- expect(newChat.getHistory().length).toEqual(5);
667
- });
668
- it('should always trigger summarization when force is true, regardless of token count', async () => {
669
- const history = [{ role: 'user', parts: [{ text: '...history...' }] }];
670
- mockGetHistory.mockReturnValue(history);
671
- const originalTokenCount = 100; // Well below threshold, but > estimated new count
672
- vi.mocked(uiTelemetryService.getLastPromptTokenCount).mockReturnValue(originalTokenCount);
673
- // Mock summary and new chat
674
- const summaryText = 'This is a summary.';
675
- const splitPoint = findCompressSplitPoint(history, 0.7);
676
- const historyToKeep = history.slice(splitPoint);
677
- const newCompressedHistory = [
678
- { role: 'user', parts: [{ text: 'Mocked env context' }] },
679
- { role: 'model', parts: [{ text: 'Got it. Thanks for the context!' }] },
680
- { role: 'user', parts: [{ text: summaryText }] },
681
- {
682
- role: 'model',
683
- parts: [{ text: 'Got it. Thanks for the additional context!' }],
684
- },
685
- ...historyToKeep,
686
- ];
687
- const mockNewChat = {
688
- getHistory: vi.fn().mockReturnValue(newCompressedHistory),
689
- };
690
- client['startChat'] = vi
691
- .fn()
692
- .mockResolvedValue(mockNewChat);
693
- const totalChars = newCompressedHistory.reduce((total, content) => total + JSON.stringify(content).length, 0);
694
- const newTokenCount = Math.floor(totalChars / 4);
695
- // Mock the summary response from the chat
696
- mockGenerateContentFn.mockResolvedValue({
697
- candidates: [
698
- {
699
- content: {
700
- role: 'model',
701
- parts: [{ text: summaryText }],
702
- },
703
- },
704
- ],
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,
705
414
  });
706
- const initialChat = client.getChat();
707
- const result = await client.tryCompressChat('prompt-id-1', true); // force = true
708
- const newChat = client.getChat();
709
- expect(mockGenerateContentFn).toHaveBeenCalled();
415
+ const result = await client.tryCompressChat('prompt-id-noop', false);
710
416
  expect(result).toEqual({
711
- compressionStatus: CompressionStatus.COMPRESSED,
712
- originalTokenCount,
713
- newTokenCount,
417
+ compressionStatus: CompressionStatus.NOOP,
418
+ originalTokenCount: 50,
419
+ newTokenCount: 50,
714
420
  });
715
- // Assert that the chat was reset
716
- expect(newChat).not.toBe(initialChat);
717
421
  });
718
422
  });
719
423
  describe('sendMessageStream', () => {
@@ -798,6 +502,7 @@ describe('Gemini Client (client.ts)', () => {
798
502
  const mockChat = {
799
503
  addHistory: vi.fn(),
800
504
  getHistory: vi.fn().mockReturnValue([]),
505
+ getLastPromptTokenCount: vi.fn(),
801
506
  };
802
507
  client['chat'] = mockChat;
803
508
  const initialRequest = [{ text: 'Hi' }];
@@ -845,6 +550,7 @@ ${JSON.stringify({
845
550
  const mockChat = {
846
551
  addHistory: vi.fn(),
847
552
  getHistory: vi.fn().mockReturnValue([]),
553
+ getLastPromptTokenCount: vi.fn(),
848
554
  };
849
555
  client['chat'] = mockChat;
850
556
  const initialRequest = [{ text: 'Hi' }];
@@ -855,11 +561,7 @@ ${JSON.stringify({
855
561
  }
856
562
  // Assert
857
563
  expect(ideContextStore.get).toHaveBeenCalled();
858
- // The `turn.run` method is now called with the model name as the first
859
- // argument. We use `expect.any(String)` because this test is
860
- // concerned with the IDE context logic, not the model routing,
861
- // which is tested in its own dedicated suite.
862
- expect(mockTurnRunFn).toHaveBeenCalledWith(expect.any(String), initialRequest, expect.any(Object));
564
+ expect(mockTurnRunFn).toHaveBeenCalledWith({ model: 'default-routed-model' }, initialRequest, expect.any(AbortSignal));
863
565
  });
864
566
  it('should add context if ideMode is enabled and there is one active file', async () => {
865
567
  // Arrange
@@ -889,6 +591,7 @@ ${JSON.stringify({
889
591
  const mockChat = {
890
592
  addHistory: vi.fn(),
891
593
  getHistory: vi.fn().mockReturnValue([]),
594
+ getLastPromptTokenCount: vi.fn(),
892
595
  };
893
596
  client['chat'] = mockChat;
894
597
  const initialRequest = [{ text: 'Hi' }];
@@ -949,6 +652,7 @@ ${JSON.stringify({
949
652
  const mockChat = {
950
653
  addHistory: vi.fn(),
951
654
  getHistory: vi.fn().mockReturnValue([]),
655
+ getLastPromptTokenCount: vi.fn(),
952
656
  };
953
657
  client['chat'] = mockChat;
954
658
  const initialRequest = [{ text: 'Hi' }];
@@ -982,6 +686,7 @@ ${JSON.stringify({
982
686
  const mockChat = {
983
687
  addHistory: vi.fn(),
984
688
  getHistory: vi.fn().mockReturnValue([]),
689
+ getLastPromptTokenCount: vi.fn(),
985
690
  };
986
691
  client['chat'] = mockChat;
987
692
  // Act
@@ -999,6 +704,7 @@ ${JSON.stringify({
999
704
  expect(finalResult).toBeInstanceOf(Turn);
1000
705
  });
1001
706
  it('should stop infinite loop after MAX_TURNS when nextSpeaker always returns model', async () => {
707
+ vi.spyOn(client['config'], 'getContinueOnFailedApiCall').mockReturnValue(true);
1002
708
  // Get the mocked checkNextSpeaker function and configure it to trigger infinite loop
1003
709
  const { checkNextSpeaker } = await import('../utils/nextSpeakerChecker.js');
1004
710
  const mockCheckNextSpeaker = vi.mocked(checkNextSpeaker);
@@ -1014,6 +720,7 @@ ${JSON.stringify({
1014
720
  const mockChat = {
1015
721
  addHistory: vi.fn(),
1016
722
  getHistory: vi.fn().mockReturnValue([]),
723
+ getLastPromptTokenCount: vi.fn(),
1017
724
  };
1018
725
  client['chat'] = mockChat;
1019
726
  // Use a signal that never gets aborted
@@ -1040,27 +747,10 @@ ${JSON.stringify({
1040
747
  }
1041
748
  // Assert
1042
749
  expect(finalResult).toBeInstanceOf(Turn);
1043
- // Debug: Check how many times checkNextSpeaker was called
1044
- const callCount = mockCheckNextSpeaker.mock.calls.length;
1045
750
  // If infinite loop protection is working, checkNextSpeaker should be called many times
1046
751
  // but stop at MAX_TURNS (100). Since each recursive call should trigger checkNextSpeaker,
1047
752
  // we expect it to be called multiple times before hitting the limit
1048
753
  expect(mockCheckNextSpeaker).toHaveBeenCalled();
1049
- // The test should demonstrate that the infinite loop protection works:
1050
- // - If checkNextSpeaker is called many times (close to MAX_TURNS), it shows the loop was happening
1051
- // - If it's only called once, the recursive behavior might not be triggered
1052
- if (callCount === 0) {
1053
- throw new Error('checkNextSpeaker was never called - the recursive condition was not met');
1054
- }
1055
- else if (callCount === 1) {
1056
- // This might be expected behavior if the turn has pending tool calls or other conditions prevent recursion
1057
- console.log('checkNextSpeaker called only once - no infinite loop occurred');
1058
- }
1059
- else {
1060
- console.log(`checkNextSpeaker called ${callCount} times - infinite loop protection worked`);
1061
- // If called multiple times, we expect it to be stopped before MAX_TURNS
1062
- expect(callCount).toBeLessThanOrEqual(100); // Should not exceed MAX_TURNS
1063
- }
1064
754
  // The stream should produce events and eventually terminate
1065
755
  expect(eventCount).toBeGreaterThanOrEqual(1);
1066
756
  expect(eventCount).toBeLessThan(200); // Should not exceed our safety limit
@@ -1076,6 +766,7 @@ ${JSON.stringify({
1076
766
  const mockChat = {
1077
767
  addHistory: vi.fn(),
1078
768
  getHistory: vi.fn().mockReturnValue([]),
769
+ getLastPromptTokenCount: vi.fn(),
1079
770
  };
1080
771
  client['chat'] = mockChat;
1081
772
  // Act & Assert
@@ -1114,6 +805,7 @@ ${JSON.stringify({
1114
805
  const mockChat = {
1115
806
  addHistory: vi.fn(),
1116
807
  getHistory: vi.fn().mockReturnValue([]),
808
+ getLastPromptTokenCount: vi.fn(),
1117
809
  };
1118
810
  client['chat'] = mockChat;
1119
811
  // Use a signal that never gets aborted
@@ -1141,9 +833,8 @@ ${JSON.stringify({
1141
833
  }
1142
834
  }
1143
835
  }
1144
- catch (error) {
836
+ catch (_) {
1145
837
  // If the test framework times out, that also demonstrates the infinite loop
1146
- console.error('Test timed out or errored:', error);
1147
838
  }
1148
839
  // Assert that the fix works - the loop should stop at MAX_TURNS
1149
840
  const callCount = mockCheckNextSpeaker.mock.calls.length;
@@ -1151,8 +842,90 @@ ${JSON.stringify({
1151
842
  // the loop should stop at MAX_TURNS (100)
1152
843
  expect(callCount).toBeLessThanOrEqual(100); // Should not exceed MAX_TURNS
1153
844
  expect(eventCount).toBeLessThanOrEqual(200); // Should have reasonable number of events
1154
- console.log(`Infinite loop protection working: checkNextSpeaker called ${callCount} times, ` +
1155
- `${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();
1156
929
  });
1157
930
  describe('Model Routing', () => {
1158
931
  let mockRouterService;
@@ -1166,35 +939,40 @@ ${JSON.stringify({
1166
939
  mockTurnRunFn.mockReturnValue((async function* () {
1167
940
  yield { type: 'content', value: 'Hello' };
1168
941
  })());
942
+ const mockChat = {
943
+ addHistory: vi.fn(),
944
+ getHistory: vi.fn().mockReturnValue([]),
945
+ getLastPromptTokenCount: vi.fn(),
946
+ };
947
+ client['chat'] = mockChat;
1169
948
  });
1170
949
  it('should use the model router service to select a model on the first turn', async () => {
1171
950
  const stream = client.sendMessageStream([{ text: 'Hi' }], new AbortController().signal, 'prompt-1');
1172
951
  await fromAsync(stream); // consume stream
1173
952
  expect(mockConfig.getModelRouterService).toHaveBeenCalled();
1174
953
  expect(mockRouterService.route).toHaveBeenCalled();
1175
- expect(mockTurnRunFn).toHaveBeenCalledWith('routed-model', // The model from the router
1176
- [{ text: 'Hi' }], expect.any(Object));
954
+ expect(mockTurnRunFn).toHaveBeenCalledWith({ model: 'routed-model' }, [{ text: 'Hi' }], expect.any(AbortSignal));
1177
955
  });
1178
956
  it('should use the same model for subsequent turns in the same prompt (stickiness)', async () => {
1179
957
  // First turn
1180
958
  let stream = client.sendMessageStream([{ text: 'Hi' }], new AbortController().signal, 'prompt-1');
1181
959
  await fromAsync(stream);
1182
960
  expect(mockRouterService.route).toHaveBeenCalledTimes(1);
1183
- expect(mockTurnRunFn).toHaveBeenCalledWith('routed-model', [{ text: 'Hi' }], expect.any(Object));
961
+ expect(mockTurnRunFn).toHaveBeenCalledWith({ model: 'routed-model' }, [{ text: 'Hi' }], expect.any(AbortSignal));
1184
962
  // Second turn
1185
963
  stream = client.sendMessageStream([{ text: 'Continue' }], new AbortController().signal, 'prompt-1');
1186
964
  await fromAsync(stream);
1187
965
  // Router should not be called again
1188
966
  expect(mockRouterService.route).toHaveBeenCalledTimes(1);
1189
967
  // Should stick to the first model
1190
- expect(mockTurnRunFn).toHaveBeenCalledWith('routed-model', [{ text: 'Continue' }], expect.any(Object));
968
+ expect(mockTurnRunFn).toHaveBeenCalledWith({ model: 'routed-model' }, [{ text: 'Continue' }], expect.any(AbortSignal));
1191
969
  });
1192
970
  it('should reset the sticky model and re-route when the prompt_id changes', async () => {
1193
971
  // First prompt
1194
972
  let stream = client.sendMessageStream([{ text: 'Hi' }], new AbortController().signal, 'prompt-1');
1195
973
  await fromAsync(stream);
1196
974
  expect(mockRouterService.route).toHaveBeenCalledTimes(1);
1197
- expect(mockTurnRunFn).toHaveBeenCalledWith('routed-model', [{ text: 'Hi' }], expect.any(Object));
975
+ expect(mockTurnRunFn).toHaveBeenCalledWith({ model: 'routed-model' }, [{ text: 'Hi' }], expect.any(AbortSignal));
1198
976
  // New prompt
1199
977
  mockRouterService.route.mockResolvedValue({
1200
978
  model: 'new-routed-model',
@@ -1205,7 +983,7 @@ ${JSON.stringify({
1205
983
  // Router should be called again for the new prompt
1206
984
  expect(mockRouterService.route).toHaveBeenCalledTimes(2);
1207
985
  // Should use the newly routed model
1208
- expect(mockTurnRunFn).toHaveBeenCalledWith('new-routed-model', [{ text: 'A new topic' }], expect.any(Object));
986
+ expect(mockTurnRunFn).toHaveBeenCalledWith({ model: 'new-routed-model' }, [{ text: 'A new topic' }], expect.any(AbortSignal));
1209
987
  });
1210
988
  it('should use the fallback model and bypass routing when in fallback mode', async () => {
1211
989
  vi.mocked(mockConfig.isInFallbackMode).mockReturnValue(true);
@@ -1215,7 +993,7 @@ ${JSON.stringify({
1215
993
  });
1216
994
  const stream = client.sendMessageStream([{ text: 'Hi' }], new AbortController().signal, 'prompt-1');
1217
995
  await fromAsync(stream);
1218
- expect(mockTurnRunFn).toHaveBeenCalledWith(DEFAULT_GEMINI_FLASH_MODEL, [{ text: 'Hi' }], expect.any(Object));
996
+ expect(mockTurnRunFn).toHaveBeenCalledWith({ model: DEFAULT_GEMINI_FLASH_MODEL }, [{ text: 'Hi' }], expect.any(AbortSignal));
1219
997
  });
1220
998
  it('should stick to the fallback model for the entire sequence even if fallback mode ends', async () => {
1221
999
  // Start the sequence in fallback mode
@@ -1227,7 +1005,7 @@ ${JSON.stringify({
1227
1005
  let stream = client.sendMessageStream([{ text: 'Hi' }], new AbortController().signal, 'prompt-fallback-stickiness');
1228
1006
  await fromAsync(stream);
1229
1007
  // First call should use fallback model
1230
- expect(mockTurnRunFn).toHaveBeenCalledWith(DEFAULT_GEMINI_FLASH_MODEL, [{ text: 'Hi' }], expect.any(Object));
1008
+ expect(mockTurnRunFn).toHaveBeenCalledWith({ model: DEFAULT_GEMINI_FLASH_MODEL }, [{ text: 'Hi' }], expect.any(AbortSignal));
1231
1009
  // End fallback mode
1232
1010
  vi.mocked(mockConfig.isInFallbackMode).mockReturnValue(false);
1233
1011
  // Second call in the same sequence
@@ -1235,10 +1013,102 @@ ${JSON.stringify({
1235
1013
  await fromAsync(stream);
1236
1014
  // Router should still not be called, and it should stick to the fallback model
1237
1015
  expect(mockTurnRunFn).toHaveBeenCalledTimes(2); // Ensure it was called again
1238
- expect(mockTurnRunFn).toHaveBeenLastCalledWith(DEFAULT_GEMINI_FLASH_MODEL, // Still the fallback model
1239
- [{ text: 'Continue' }], expect.any(Object));
1016
+ expect(mockTurnRunFn).toHaveBeenLastCalledWith({ model: DEFAULT_GEMINI_FLASH_MODEL }, // Still the fallback model
1017
+ [{ text: 'Continue' }], expect.any(AbortSignal));
1240
1018
  });
1241
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);
1111
+ });
1242
1112
  describe('Editor context delta', () => {
1243
1113
  const mockStream = (async function* () {
1244
1114
  yield { type: 'content', value: 'Hello' };
@@ -1261,6 +1131,7 @@ ${JSON.stringify({
1261
1131
  .mockReturnValue([
1262
1132
  { role: 'user', parts: [{ text: 'previous message' }] },
1263
1133
  ]),
1134
+ getLastPromptTokenCount: vi.fn(),
1264
1135
  };
1265
1136
  client['chat'] = mockChat;
1266
1137
  });
@@ -1381,7 +1252,11 @@ ${JSON.stringify({
1381
1252
  vi.mocked(ideContextStore.get).mockReturnValue({
1382
1253
  workspaceState: {
1383
1254
  openFiles: [
1384
- { ...currentActiveFile, isActive: true, timestamp: Date.now() },
1255
+ {
1256
+ ...currentActiveFile,
1257
+ isActive: true,
1258
+ timestamp: Date.now(),
1259
+ },
1385
1260
  ],
1386
1261
  },
1387
1262
  });
@@ -1469,6 +1344,7 @@ ${JSON.stringify({
1469
1344
  addHistory: vi.fn(),
1470
1345
  getHistory: vi.fn().mockReturnValue([]), // Default empty history
1471
1346
  setHistory: vi.fn(),
1347
+ getLastPromptTokenCount: vi.fn(),
1472
1348
  };
1473
1349
  client['chat'] = mockChat;
1474
1350
  vi.spyOn(client['config'], 'getIdeMode').mockReturnValue(true);
@@ -1732,6 +1608,7 @@ ${JSON.stringify({
1732
1608
  const mockChat = {
1733
1609
  addHistory: vi.fn(),
1734
1610
  getHistory: vi.fn().mockReturnValue([]),
1611
+ getLastPromptTokenCount: vi.fn(),
1735
1612
  };
1736
1613
  client['chat'] = mockChat;
1737
1614
  // Act
@@ -1757,6 +1634,7 @@ ${JSON.stringify({
1757
1634
  const mockChat = {
1758
1635
  addHistory: vi.fn(),
1759
1636
  getHistory: vi.fn().mockReturnValue([]),
1637
+ getLastPromptTokenCount: vi.fn(),
1760
1638
  };
1761
1639
  client['chat'] = mockChat;
1762
1640
  // Act
@@ -1774,7 +1652,7 @@ ${JSON.stringify({
1774
1652
  .mockReturnValueOnce(false)
1775
1653
  .mockReturnValueOnce(true);
1776
1654
  let capturedSignal;
1777
- mockTurnRunFn.mockImplementation((model, request, signal) => {
1655
+ mockTurnRunFn.mockImplementation((_modelConfigKey, _request, signal) => {
1778
1656
  capturedSignal = signal;
1779
1657
  return (async function* () {
1780
1658
  yield { type: 'content', value: 'First event' };
@@ -1784,6 +1662,7 @@ ${JSON.stringify({
1784
1662
  const mockChat = {
1785
1663
  addHistory: vi.fn(),
1786
1664
  getHistory: vi.fn().mockReturnValue([]),
1665
+ getLastPromptTokenCount: vi.fn(),
1787
1666
  };
1788
1667
  client['chat'] = mockChat;
1789
1668
  // Act
@@ -1800,15 +1679,14 @@ ${JSON.stringify({
1800
1679
  describe('generateContent', () => {
1801
1680
  it('should call generateContent with the correct parameters', async () => {
1802
1681
  const contents = [{ role: 'user', parts: [{ text: 'hello' }] }];
1803
- const generationConfig = { temperature: 0.5 };
1804
1682
  const abortSignal = new AbortController().signal;
1805
- await client.generateContent(contents, generationConfig, abortSignal, DEFAULT_GEMINI_FLASH_MODEL);
1683
+ await client.generateContent({ model: DEFAULT_GEMINI_FLASH_MODEL }, contents, abortSignal);
1806
1684
  expect(mockContentGenerator.generateContent).toHaveBeenCalledWith({
1807
1685
  model: DEFAULT_GEMINI_FLASH_MODEL,
1808
1686
  config: {
1809
1687
  abortSignal,
1810
- systemInstruction: getCoreSystemPrompt(''),
1811
- temperature: 0.5,
1688
+ systemInstruction: getCoreSystemPrompt({}, ''),
1689
+ temperature: 0,
1812
1690
  topP: 1,
1813
1691
  },
1814
1692
  contents,
@@ -1819,7 +1697,7 @@ ${JSON.stringify({
1819
1697
  const contents = [{ role: 'user', parts: [{ text: 'test' }] }];
1820
1698
  const currentModel = initialModel + '-changed';
1821
1699
  vi.spyOn(client['config'], 'getModel').mockReturnValueOnce(currentModel);
1822
- await client.generateContent(contents, {}, new AbortController().signal, DEFAULT_GEMINI_FLASH_MODEL);
1700
+ await client.generateContent({ model: DEFAULT_GEMINI_FLASH_MODEL }, contents, new AbortController().signal);
1823
1701
  expect(mockContentGenerator.generateContent).not.toHaveBeenCalledWith({
1824
1702
  model: initialModel,
1825
1703
  config: expect.any(Object),
@@ -1833,12 +1711,11 @@ ${JSON.stringify({
1833
1711
  });
1834
1712
  it('should use the Flash model when fallback mode is active', async () => {
1835
1713
  const contents = [{ role: 'user', parts: [{ text: 'hello' }] }];
1836
- const generationConfig = { temperature: 0.5 };
1837
1714
  const abortSignal = new AbortController().signal;
1838
1715
  const requestedModel = 'gemini-2.5-pro'; // A non-flash model
1839
1716
  // Mock config to be in fallback mode
1840
1717
  vi.spyOn(client['config'], 'isInFallbackMode').mockReturnValue(true);
1841
- await client.generateContent(contents, generationConfig, abortSignal, requestedModel);
1718
+ await client.generateContent({ model: requestedModel }, contents, abortSignal);
1842
1719
  expect(mockGenerateContentFn).toHaveBeenCalledWith(expect.objectContaining({
1843
1720
  model: DEFAULT_GEMINI_FLASH_MODEL,
1844
1721
  }), 'test-session-id');