@office-ai/aioncli-core 0.8.1 → 0.18.5

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 (778) 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.d.ts +15 -1
  165. package/dist/src/core/openaiContentGenerator.js +139 -22
  166. package/dist/src/core/openaiContentGenerator.js.map +1 -1
  167. package/dist/src/core/prompts.d.ts +2 -1
  168. package/dist/src/core/prompts.js +135 -154
  169. package/dist/src/core/prompts.js.map +1 -1
  170. package/dist/src/core/prompts.test.js +128 -189
  171. package/dist/src/core/prompts.test.js.map +1 -1
  172. package/dist/src/core/recordingContentGenerator.d.ts +18 -0
  173. package/dist/src/core/recordingContentGenerator.js +77 -0
  174. package/dist/src/core/recordingContentGenerator.js.map +1 -0
  175. package/dist/src/core/recordingContentGenerator.test.d.ts +6 -0
  176. package/dist/src/core/recordingContentGenerator.test.js +101 -0
  177. package/dist/src/core/recordingContentGenerator.test.js.map +1 -0
  178. package/dist/src/core/tokenLimits.test.d.ts +6 -0
  179. package/dist/src/core/tokenLimits.test.js +26 -0
  180. package/dist/src/core/tokenLimits.test.js.map +1 -0
  181. package/dist/src/core/turn.d.ts +23 -3
  182. package/dist/src/core/turn.js +18 -9
  183. package/dist/src/core/turn.js.map +1 -1
  184. package/dist/src/core/turn.test.js +98 -104
  185. package/dist/src/core/turn.test.js.map +1 -1
  186. package/dist/src/fallback/handler.js +60 -8
  187. package/dist/src/fallback/handler.js.map +1 -1
  188. package/dist/src/fallback/handler.test.js +132 -17
  189. package/dist/src/fallback/handler.test.js.map +1 -1
  190. package/dist/src/fallback/types.d.ts +1 -1
  191. package/dist/src/generated/git-commit.d.ts +2 -2
  192. package/dist/src/generated/git-commit.js +2 -2
  193. package/dist/src/generated/git-commit.js.map +1 -1
  194. package/dist/src/hooks/hookAggregator.d.ts +68 -0
  195. package/dist/src/hooks/hookAggregator.js +262 -0
  196. package/dist/src/hooks/hookAggregator.js.map +1 -0
  197. package/dist/src/hooks/hookAggregator.test.d.ts +6 -0
  198. package/dist/src/hooks/hookAggregator.test.js +387 -0
  199. package/dist/src/hooks/hookAggregator.test.js.map +1 -0
  200. package/dist/src/hooks/hookPlanner.d.ts +46 -0
  201. package/dist/src/hooks/hookPlanner.js +108 -0
  202. package/dist/src/hooks/hookPlanner.js.map +1 -0
  203. package/dist/src/hooks/hookPlanner.test.d.ts +6 -0
  204. package/dist/src/hooks/hookPlanner.test.js +255 -0
  205. package/dist/src/hooks/hookPlanner.test.js.map +1 -0
  206. package/dist/src/hooks/hookRegistry.d.ts +87 -0
  207. package/dist/src/hooks/hookRegistry.js +198 -0
  208. package/dist/src/hooks/hookRegistry.js.map +1 -0
  209. package/dist/src/hooks/hookRegistry.test.d.ts +6 -0
  210. package/dist/src/hooks/hookRegistry.test.js +341 -0
  211. package/dist/src/hooks/hookRegistry.test.js.map +1 -0
  212. package/dist/src/hooks/hookRunner.d.ts +42 -0
  213. package/dist/src/hooks/hookRunner.js +272 -0
  214. package/dist/src/hooks/hookRunner.js.map +1 -0
  215. package/dist/src/hooks/hookRunner.test.d.ts +6 -0
  216. package/dist/src/hooks/hookRunner.test.js +468 -0
  217. package/dist/src/hooks/hookRunner.test.js.map +1 -0
  218. package/dist/src/hooks/hookTranslator.d.ts +113 -0
  219. package/dist/src/hooks/hookTranslator.js +232 -0
  220. package/dist/src/hooks/hookTranslator.js.map +1 -0
  221. package/dist/src/hooks/hookTranslator.test.d.ts +6 -0
  222. package/dist/src/hooks/hookTranslator.test.js +192 -0
  223. package/dist/src/hooks/hookTranslator.test.js.map +1 -0
  224. package/dist/src/hooks/types.d.ts +384 -0
  225. package/dist/src/hooks/types.js +284 -0
  226. package/dist/src/hooks/types.js.map +1 -0
  227. package/dist/src/hooks/types.test.d.ts +6 -0
  228. package/dist/src/hooks/types.test.js +313 -0
  229. package/dist/src/hooks/types.test.js.map +1 -0
  230. package/dist/src/ide/detect-ide.d.ts +4 -0
  231. package/dist/src/ide/detect-ide.js +6 -1
  232. package/dist/src/ide/detect-ide.js.map +1 -1
  233. package/dist/src/ide/detect-ide.test.js +16 -0
  234. package/dist/src/ide/detect-ide.test.js.map +1 -1
  235. package/dist/src/ide/ide-client.d.ts +3 -1
  236. package/dist/src/ide/ide-client.js +12 -10
  237. package/dist/src/ide/ide-client.js.map +1 -1
  238. package/dist/src/ide/ide-client.test.js +163 -4
  239. package/dist/src/ide/ide-client.test.js.map +1 -1
  240. package/dist/src/ide/ide-installer.js +66 -21
  241. package/dist/src/ide/ide-installer.js.map +1 -1
  242. package/dist/src/ide/ide-installer.test.js +54 -1
  243. package/dist/src/ide/ide-installer.test.js.map +1 -1
  244. package/dist/src/ide/process-utils.js +85 -75
  245. package/dist/src/ide/process-utils.js.map +1 -1
  246. package/dist/src/ide/process-utils.test.js +83 -90
  247. package/dist/src/ide/process-utils.test.js.map +1 -1
  248. package/dist/src/ide/types.d.ts +5 -5
  249. package/dist/src/ide/types.js +1 -1
  250. package/dist/src/index.d.ts +21 -0
  251. package/dist/src/index.js +24 -0
  252. package/dist/src/index.js.map +1 -1
  253. package/dist/src/mcp/google-auth-provider.d.ts +2 -0
  254. package/dist/src/mcp/google-auth-provider.js +21 -3
  255. package/dist/src/mcp/google-auth-provider.js.map +1 -1
  256. package/dist/src/mcp/google-auth-provider.test.js +42 -9
  257. package/dist/src/mcp/google-auth-provider.test.js.map +1 -1
  258. package/dist/src/mcp/oauth-provider.d.ts +8 -5
  259. package/dist/src/mcp/oauth-provider.js +140 -55
  260. package/dist/src/mcp/oauth-provider.js.map +1 -1
  261. package/dist/src/mcp/oauth-provider.test.js +369 -2
  262. package/dist/src/mcp/oauth-provider.test.js.map +1 -1
  263. package/dist/src/mcp/oauth-token-storage.js +5 -4
  264. package/dist/src/mcp/oauth-token-storage.js.map +1 -1
  265. package/dist/src/mcp/oauth-token-storage.test.js +17 -11
  266. package/dist/src/mcp/oauth-token-storage.test.js.map +1 -1
  267. package/dist/src/mcp/oauth-utils.d.ts +7 -0
  268. package/dist/src/mcp/oauth-utils.js +28 -8
  269. package/dist/src/mcp/oauth-utils.js.map +1 -1
  270. package/dist/src/mcp/oauth-utils.test.js +45 -2
  271. package/dist/src/mcp/oauth-utils.test.js.map +1 -1
  272. package/dist/src/mcp/sa-impersonation-provider.d.ts +0 -6
  273. package/dist/src/mcp/sa-impersonation-provider.js +6 -23
  274. package/dist/src/mcp/sa-impersonation-provider.js.map +1 -1
  275. package/dist/src/mcp/token-storage/base-token-storage.test.js +75 -84
  276. package/dist/src/mcp/token-storage/base-token-storage.test.js.map +1 -1
  277. package/dist/src/mcp/token-storage/file-token-storage.js +3 -2
  278. package/dist/src/mcp/token-storage/file-token-storage.js.map +1 -1
  279. package/dist/src/mcp/token-storage/file-token-storage.test.js +11 -8
  280. package/dist/src/mcp/token-storage/file-token-storage.test.js.map +1 -1
  281. package/dist/src/mcp/token-storage/keychain-token-storage.d.ts +6 -2
  282. package/dist/src/mcp/token-storage/keychain-token-storage.js +63 -7
  283. package/dist/src/mcp/token-storage/keychain-token-storage.js.map +1 -1
  284. package/dist/src/mcp/token-storage/keychain-token-storage.test.js +54 -3
  285. package/dist/src/mcp/token-storage/keychain-token-storage.test.js.map +1 -1
  286. package/dist/src/mcp/token-storage/types.d.ts +6 -0
  287. package/dist/src/mcp/token-storage/types.js.map +1 -1
  288. package/dist/src/output/stream-json-formatter.d.ts +32 -0
  289. package/dist/src/output/stream-json-formatter.js +52 -0
  290. package/dist/src/output/stream-json-formatter.js.map +1 -0
  291. package/dist/src/output/stream-json-formatter.test.d.ts +6 -0
  292. package/dist/src/output/stream-json-formatter.test.js +479 -0
  293. package/dist/src/output/stream-json-formatter.test.js.map +1 -0
  294. package/dist/src/output/types.d.ts +63 -1
  295. package/dist/src/output/types.js +11 -0
  296. package/dist/src/output/types.js.map +1 -1
  297. package/dist/src/policy/config.d.ts +31 -0
  298. package/dist/src/policy/config.js +199 -0
  299. package/dist/src/policy/config.js.map +1 -0
  300. package/dist/src/policy/config.test.d.ts +6 -0
  301. package/dist/src/policy/config.test.js +538 -0
  302. package/dist/src/policy/config.test.js.map +1 -0
  303. package/dist/src/policy/index.d.ts +2 -0
  304. package/dist/src/policy/index.js +2 -0
  305. package/dist/src/policy/index.js.map +1 -1
  306. package/dist/src/policy/policies/discovered.toml +8 -0
  307. package/dist/src/policy/policies/read-only.toml +56 -0
  308. package/dist/src/policy/policies/write.toml +73 -0
  309. package/dist/src/policy/policies/yolo.toml +31 -0
  310. package/dist/src/policy/policy-engine.d.ts +12 -3
  311. package/dist/src/policy/policy-engine.js +74 -8
  312. package/dist/src/policy/policy-engine.js.map +1 -1
  313. package/dist/src/policy/policy-engine.test.js +460 -76
  314. package/dist/src/policy/policy-engine.test.js.map +1 -1
  315. package/dist/src/policy/toml-loader.d.ts +47 -0
  316. package/dist/src/policy/toml-loader.js +411 -0
  317. package/dist/src/policy/toml-loader.js.map +1 -0
  318. package/dist/src/policy/toml-loader.test.d.ts +6 -0
  319. package/dist/src/policy/toml-loader.test.js +376 -0
  320. package/dist/src/policy/toml-loader.test.js.map +1 -0
  321. package/dist/src/policy/types.d.ts +83 -0
  322. package/dist/src/policy/types.js +10 -0
  323. package/dist/src/policy/types.js.map +1 -1
  324. package/dist/src/prompts/mcp-prompts.test.d.ts +6 -0
  325. package/dist/src/prompts/mcp-prompts.test.js +39 -0
  326. package/dist/src/prompts/mcp-prompts.test.js.map +1 -0
  327. package/dist/src/prompts/prompt-registry.js +2 -1
  328. package/dist/src/prompts/prompt-registry.js.map +1 -1
  329. package/dist/src/prompts/prompt-registry.test.d.ts +6 -0
  330. package/dist/src/prompts/prompt-registry.test.js +96 -0
  331. package/dist/src/prompts/prompt-registry.test.js.map +1 -0
  332. package/dist/src/routing/modelRouterService.js +15 -0
  333. package/dist/src/routing/modelRouterService.js.map +1 -1
  334. package/dist/src/routing/modelRouterService.test.js +62 -0
  335. package/dist/src/routing/modelRouterService.test.js.map +1 -1
  336. package/dist/src/routing/strategies/classifierStrategy.d.ts +1 -1
  337. package/dist/src/routing/strategies/classifierStrategy.js +9 -16
  338. package/dist/src/routing/strategies/classifierStrategy.js.map +1 -1
  339. package/dist/src/routing/strategies/classifierStrategy.test.js +17 -13
  340. package/dist/src/routing/strategies/classifierStrategy.test.js.map +1 -1
  341. package/dist/src/routing/strategies/fallbackStrategy.js +1 -1
  342. package/dist/src/routing/strategies/fallbackStrategy.js.map +1 -1
  343. package/dist/src/routing/strategies/fallbackStrategy.test.js +4 -0
  344. package/dist/src/routing/strategies/fallbackStrategy.test.js.map +1 -1
  345. package/dist/src/routing/strategies/overrideStrategy.js +2 -2
  346. package/dist/src/routing/strategies/overrideStrategy.js.map +1 -1
  347. package/dist/src/routing/strategies/overrideStrategy.test.js +3 -0
  348. package/dist/src/routing/strategies/overrideStrategy.test.js.map +1 -1
  349. package/dist/src/safety/built-in.d.ts +21 -0
  350. package/dist/src/safety/built-in.js +106 -0
  351. package/dist/src/safety/built-in.js.map +1 -0
  352. package/dist/src/safety/built-in.test.d.ts +6 -0
  353. package/dist/src/safety/built-in.test.js +199 -0
  354. package/dist/src/safety/built-in.test.js.map +1 -0
  355. package/dist/src/safety/checker-runner.d.ts +48 -0
  356. package/dist/src/safety/checker-runner.js +208 -0
  357. package/dist/src/safety/checker-runner.js.map +1 -0
  358. package/dist/src/safety/checker-runner.test.d.ts +6 -0
  359. package/dist/src/safety/checker-runner.test.js +238 -0
  360. package/dist/src/safety/checker-runner.test.js.map +1 -0
  361. package/dist/src/safety/context-builder.d.ts +23 -0
  362. package/dist/src/safety/context-builder.js +47 -0
  363. package/dist/src/safety/context-builder.js.map +1 -0
  364. package/dist/src/safety/context-builder.test.d.ts +6 -0
  365. package/dist/src/safety/context-builder.test.js +49 -0
  366. package/dist/src/safety/context-builder.test.js.map +1 -0
  367. package/dist/src/safety/protocol.d.ts +88 -0
  368. package/dist/src/safety/protocol.js +15 -0
  369. package/dist/src/safety/protocol.js.map +1 -0
  370. package/dist/src/safety/registry.d.ts +26 -0
  371. package/dist/src/safety/registry.js +65 -0
  372. package/dist/src/safety/registry.js.map +1 -0
  373. package/dist/src/safety/registry.test.d.ts +6 -0
  374. package/dist/src/safety/registry.test.js +31 -0
  375. package/dist/src/safety/registry.test.js.map +1 -0
  376. package/dist/src/services/chatCompressionService.d.ts +32 -0
  377. package/dist/src/services/chatCompressionService.js +162 -0
  378. package/dist/src/services/chatCompressionService.js.map +1 -0
  379. package/dist/src/services/chatCompressionService.test.d.ts +6 -0
  380. package/dist/src/services/chatCompressionService.test.js +210 -0
  381. package/dist/src/services/chatCompressionService.test.js.map +1 -0
  382. package/dist/src/services/chatRecordingService.d.ts +3 -2
  383. package/dist/src/services/chatRecordingService.js +11 -9
  384. package/dist/src/services/chatRecordingService.js.map +1 -1
  385. package/dist/src/services/fileDiscoveryService.d.ts +2 -14
  386. package/dist/src/services/fileDiscoveryService.js +19 -55
  387. package/dist/src/services/fileDiscoveryService.js.map +1 -1
  388. package/dist/src/services/fileDiscoveryService.test.js +91 -11
  389. package/dist/src/services/fileDiscoveryService.test.js.map +1 -1
  390. package/dist/src/services/loopDetectionService.d.ts +4 -1
  391. package/dist/src/services/loopDetectionService.js +95 -42
  392. package/dist/src/services/loopDetectionService.js.map +1 -1
  393. package/dist/src/services/loopDetectionService.test.js +220 -12
  394. package/dist/src/services/loopDetectionService.test.js.map +1 -1
  395. package/dist/src/services/modelConfig.golden.test.d.ts +6 -0
  396. package/dist/src/services/modelConfig.golden.test.js +42 -0
  397. package/dist/src/services/modelConfig.golden.test.js.map +1 -0
  398. package/dist/src/services/modelConfig.integration.test.d.ts +6 -0
  399. package/dist/src/services/modelConfig.integration.test.js +247 -0
  400. package/dist/src/services/modelConfig.integration.test.js.map +1 -0
  401. package/dist/src/services/modelConfigService.d.ts +48 -0
  402. package/dist/src/services/modelConfigService.js +151 -0
  403. package/dist/src/services/modelConfigService.js.map +1 -0
  404. package/dist/src/services/modelConfigService.test.d.ts +6 -0
  405. package/dist/src/services/modelConfigService.test.js +531 -0
  406. package/dist/src/services/modelConfigService.test.js.map +1 -0
  407. package/dist/src/services/shellExecutionService.d.ts +1 -0
  408. package/dist/src/services/shellExecutionService.js +195 -92
  409. package/dist/src/services/shellExecutionService.js.map +1 -1
  410. package/dist/src/services/shellExecutionService.test.js +137 -14
  411. package/dist/src/services/shellExecutionService.test.js.map +1 -1
  412. package/dist/src/services/test-data/resolved-aliases.golden.json +202 -0
  413. package/dist/src/telemetry/activity-monitor.d.ts +116 -0
  414. package/dist/src/telemetry/activity-monitor.js +209 -0
  415. package/dist/src/telemetry/activity-monitor.js.map +1 -0
  416. package/dist/src/telemetry/activity-monitor.test.d.ts +6 -0
  417. package/dist/src/telemetry/activity-monitor.test.js +251 -0
  418. package/dist/src/telemetry/activity-monitor.test.js.map +1 -0
  419. package/dist/src/telemetry/clearcut-logger/clearcut-logger.d.ts +25 -7
  420. package/dist/src/telemetry/clearcut-logger/clearcut-logger.js +294 -76
  421. package/dist/src/telemetry/clearcut-logger/clearcut-logger.js.map +1 -1
  422. package/dist/src/telemetry/clearcut-logger/clearcut-logger.test.d.ts +1 -0
  423. package/dist/src/telemetry/clearcut-logger/clearcut-logger.test.js +192 -66
  424. package/dist/src/telemetry/clearcut-logger/clearcut-logger.test.js.map +1 -1
  425. package/dist/src/telemetry/clearcut-logger/event-metadata-key.d.ts +25 -3
  426. package/dist/src/telemetry/clearcut-logger/event-metadata-key.js +59 -5
  427. package/dist/src/telemetry/clearcut-logger/event-metadata-key.js.map +1 -1
  428. package/dist/src/telemetry/constants.d.ts +0 -28
  429. package/dist/src/telemetry/constants.js +0 -29
  430. package/dist/src/telemetry/constants.js.map +1 -1
  431. package/dist/src/telemetry/gcp-exporters.js +0 -1
  432. package/dist/src/telemetry/gcp-exporters.js.map +1 -1
  433. package/dist/src/telemetry/gcp-exporters.test.js +1 -1
  434. package/dist/src/telemetry/gcp-exporters.test.js.map +1 -1
  435. package/dist/src/telemetry/index.d.ts +7 -3
  436. package/dist/src/telemetry/index.js +13 -4
  437. package/dist/src/telemetry/index.js.map +1 -1
  438. package/dist/src/telemetry/loggers.d.ts +14 -7
  439. package/dist/src/telemetry/loggers.js +197 -320
  440. package/dist/src/telemetry/loggers.js.map +1 -1
  441. package/dist/src/telemetry/loggers.test.circular.js +0 -1
  442. package/dist/src/telemetry/loggers.test.circular.js.map +1 -1
  443. package/dist/src/telemetry/loggers.test.js +460 -59
  444. package/dist/src/telemetry/loggers.test.js.map +1 -1
  445. package/dist/src/telemetry/memory-monitor.d.ts +149 -0
  446. package/dist/src/telemetry/memory-monitor.js +335 -0
  447. package/dist/src/telemetry/memory-monitor.js.map +1 -0
  448. package/dist/src/telemetry/memory-monitor.test.d.ts +6 -0
  449. package/dist/src/telemetry/memory-monitor.test.js +472 -0
  450. package/dist/src/telemetry/memory-monitor.test.js.map +1 -0
  451. package/dist/src/telemetry/metrics.d.ts +180 -4
  452. package/dist/src/telemetry/metrics.js +270 -6
  453. package/dist/src/telemetry/metrics.js.map +1 -1
  454. package/dist/src/telemetry/metrics.test.js +502 -184
  455. package/dist/src/telemetry/metrics.test.js.map +1 -1
  456. package/dist/src/telemetry/sdk.js +3 -2
  457. package/dist/src/telemetry/sdk.js.map +1 -1
  458. package/dist/src/telemetry/semantic.d.ts +82 -0
  459. package/dist/src/telemetry/semantic.js +269 -0
  460. package/dist/src/telemetry/semantic.js.map +1 -0
  461. package/dist/src/telemetry/semantic.test.d.ts +6 -0
  462. package/dist/src/telemetry/semantic.test.js +387 -0
  463. package/dist/src/telemetry/semantic.test.js.map +1 -0
  464. package/dist/src/telemetry/telemetry-utils.test.js +29 -28
  465. package/dist/src/telemetry/telemetry-utils.test.js.map +1 -1
  466. package/dist/src/telemetry/telemetryAttributes.d.ts +8 -0
  467. package/dist/src/telemetry/telemetryAttributes.js +19 -0
  468. package/dist/src/telemetry/telemetryAttributes.js.map +1 -0
  469. package/dist/src/telemetry/trace.d.ts +46 -0
  470. package/dist/src/telemetry/trace.js +121 -0
  471. package/dist/src/telemetry/trace.js.map +1 -0
  472. package/dist/src/telemetry/types.d.ts +227 -29
  473. package/dist/src/telemetry/types.js +858 -72
  474. package/dist/src/telemetry/types.js.map +1 -1
  475. package/dist/src/telemetry/uiTelemetry.d.ts +1 -1
  476. package/dist/src/telemetry/uiTelemetry.js +7 -7
  477. package/dist/src/telemetry/uiTelemetry.js.map +1 -1
  478. package/dist/src/telemetry/uiTelemetry.test.js +89 -67
  479. package/dist/src/telemetry/uiTelemetry.test.js.map +1 -1
  480. package/dist/src/test-utils/config.d.ts +1 -1
  481. package/dist/src/test-utils/config.js +1 -1
  482. package/dist/src/tools/base-tool-invocation.test.d.ts +6 -0
  483. package/dist/src/tools/base-tool-invocation.test.js +85 -0
  484. package/dist/src/tools/base-tool-invocation.test.js.map +1 -0
  485. package/dist/src/tools/edit.d.ts +4 -3
  486. package/dist/src/tools/edit.js +50 -47
  487. package/dist/src/tools/edit.js.map +1 -1
  488. package/dist/src/tools/edit.test.js +306 -216
  489. package/dist/src/tools/edit.test.js.map +1 -1
  490. package/dist/src/tools/glob.d.ts +4 -3
  491. package/dist/src/tools/glob.js +24 -27
  492. package/dist/src/tools/glob.js.map +1 -1
  493. package/dist/src/tools/glob.test.js +212 -205
  494. package/dist/src/tools/glob.test.js.map +1 -1
  495. package/dist/src/tools/grep.d.ts +4 -3
  496. package/dist/src/tools/grep.js +31 -25
  497. package/dist/src/tools/grep.js.map +1 -1
  498. package/dist/src/tools/grep.test.js +15 -12
  499. package/dist/src/tools/grep.test.js.map +1 -1
  500. package/dist/src/tools/ls.d.ts +4 -3
  501. package/dist/src/tools/ls.js +29 -35
  502. package/dist/src/tools/ls.js.map +1 -1
  503. package/dist/src/tools/ls.test.js +34 -42
  504. package/dist/src/tools/ls.test.js.map +1 -1
  505. package/dist/src/tools/mcp-client-manager.d.ts +49 -11
  506. package/dist/src/tools/mcp-client-manager.js +191 -31
  507. package/dist/src/tools/mcp-client-manager.js.map +1 -1
  508. package/dist/src/tools/mcp-client-manager.test.js +132 -25
  509. package/dist/src/tools/mcp-client-manager.test.js.map +1 -1
  510. package/dist/src/tools/mcp-client.d.ts +12 -5
  511. package/dist/src/tools/mcp-client.js +287 -267
  512. package/dist/src/tools/mcp-client.js.map +1 -1
  513. package/dist/src/tools/mcp-client.test.js +352 -45
  514. package/dist/src/tools/mcp-client.test.js.map +1 -1
  515. package/dist/src/tools/mcp-tool.d.ts +6 -2
  516. package/dist/src/tools/mcp-tool.js +19 -8
  517. package/dist/src/tools/mcp-tool.js.map +1 -1
  518. package/dist/src/tools/mcp-tool.test.js +186 -273
  519. package/dist/src/tools/mcp-tool.test.js.map +1 -1
  520. package/dist/src/tools/memoryTool.d.ts +7 -5
  521. package/dist/src/tools/memoryTool.js +14 -12
  522. package/dist/src/tools/memoryTool.js.map +1 -1
  523. package/dist/src/tools/memoryTool.test.js +10 -9
  524. package/dist/src/tools/memoryTool.test.js.map +1 -1
  525. package/dist/src/tools/message-bus-integration.test.js +14 -1
  526. package/dist/src/tools/message-bus-integration.test.js.map +1 -1
  527. package/dist/src/tools/modifiable-tool.d.ts +5 -1
  528. package/dist/src/tools/modifiable-tool.js +38 -16
  529. package/dist/src/tools/modifiable-tool.js.map +1 -1
  530. package/dist/src/tools/modifiable-tool.test.js +66 -31
  531. package/dist/src/tools/modifiable-tool.test.js.map +1 -1
  532. package/dist/src/tools/read-file.d.ts +6 -5
  533. package/dist/src/tools/read-file.js +26 -32
  534. package/dist/src/tools/read-file.js.map +1 -1
  535. package/dist/src/tools/read-file.test.js +68 -33
  536. package/dist/src/tools/read-file.test.js.map +1 -1
  537. package/dist/src/tools/read-many-files.d.ts +6 -12
  538. package/dist/src/tools/read-many-files.js +28 -57
  539. package/dist/src/tools/read-many-files.js.map +1 -1
  540. package/dist/src/tools/read-many-files.test.js +37 -36
  541. package/dist/src/tools/read-many-files.test.js.map +1 -1
  542. package/dist/src/tools/ripGrep.d.ts +28 -10
  543. package/dist/src/tools/ripGrep.js +195 -193
  544. package/dist/src/tools/ripGrep.js.map +1 -1
  545. package/dist/src/tools/ripGrep.test.js +533 -204
  546. package/dist/src/tools/ripGrep.test.js.map +1 -1
  547. package/dist/src/tools/shell.d.ts +8 -6
  548. package/dist/src/tools/shell.js +64 -38
  549. package/dist/src/tools/shell.js.map +1 -1
  550. package/dist/src/tools/shell.test.js +134 -43
  551. package/dist/src/tools/shell.test.js.map +1 -1
  552. package/dist/src/tools/smart-edit.d.ts +10 -23
  553. package/dist/src/tools/smart-edit.js +100 -85
  554. package/dist/src/tools/smart-edit.js.map +1 -1
  555. package/dist/src/tools/smart-edit.test.js +229 -179
  556. package/dist/src/tools/smart-edit.test.js.map +1 -1
  557. package/dist/src/tools/tool-error.d.ts +21 -0
  558. package/dist/src/tools/tool-error.js +27 -0
  559. package/dist/src/tools/tool-error.js.map +1 -1
  560. package/dist/src/tools/tool-names.d.ts +17 -0
  561. package/dist/src/tools/tool-names.js +21 -0
  562. package/dist/src/tools/tool-names.js.map +1 -0
  563. package/dist/src/tools/tool-registry.d.ts +32 -20
  564. package/dist/src/tools/tool-registry.js +122 -78
  565. package/dist/src/tools/tool-registry.js.map +1 -1
  566. package/dist/src/tools/tool-registry.test.js +167 -90
  567. package/dist/src/tools/tool-registry.test.js.map +1 -1
  568. package/dist/src/tools/tools.d.ts +23 -8
  569. package/dist/src/tools/tools.js +69 -37
  570. package/dist/src/tools/tools.js.map +1 -1
  571. package/dist/src/tools/web-fetch.d.ts +11 -3
  572. package/dist/src/tools/web-fetch.js +80 -38
  573. package/dist/src/tools/web-fetch.js.map +1 -1
  574. package/dist/src/tools/web-fetch.test.js +338 -9
  575. package/dist/src/tools/web-fetch.test.js.map +1 -1
  576. package/dist/src/tools/web-search.d.ts +4 -3
  577. package/dist/src/tools/web-search.js +11 -9
  578. package/dist/src/tools/web-search.js.map +1 -1
  579. package/dist/src/tools/web-search.test.js +6 -0
  580. package/dist/src/tools/web-search.test.js.map +1 -1
  581. package/dist/src/tools/write-file.d.ts +3 -2
  582. package/dist/src/tools/write-file.js +41 -40
  583. package/dist/src/tools/write-file.js.map +1 -1
  584. package/dist/src/tools/write-file.test.js +130 -123
  585. package/dist/src/tools/write-file.test.js.map +1 -1
  586. package/dist/src/tools/write-todos.d.ts +34 -9
  587. package/dist/src/tools/write-todos.js +54 -11
  588. package/dist/src/tools/write-todos.js.map +1 -1
  589. package/dist/src/tools/write-todos.test.js +2 -2
  590. package/dist/src/tools/write-todos.test.js.map +1 -1
  591. package/dist/src/utils/bfsFileSearch.js +3 -2
  592. package/dist/src/utils/bfsFileSearch.js.map +1 -1
  593. package/dist/src/utils/channel.d.ts +19 -0
  594. package/dist/src/utils/channel.js +49 -0
  595. package/dist/src/utils/channel.js.map +1 -0
  596. package/dist/src/utils/channel.test.d.ts +6 -0
  597. package/dist/src/utils/channel.test.js +170 -0
  598. package/dist/src/utils/channel.test.js.map +1 -0
  599. package/dist/src/utils/debugLogger.d.ts +25 -0
  600. package/dist/src/utils/debugLogger.js +33 -0
  601. package/dist/src/utils/debugLogger.js.map +1 -0
  602. package/dist/src/utils/debugLogger.test.d.ts +6 -0
  603. package/dist/src/utils/debugLogger.test.js +69 -0
  604. package/dist/src/utils/debugLogger.test.js.map +1 -0
  605. package/dist/src/utils/delay.d.ts +16 -0
  606. package/dist/src/utils/delay.js +43 -0
  607. package/dist/src/utils/delay.js.map +1 -0
  608. package/dist/src/utils/delay.test.d.ts +6 -0
  609. package/dist/src/utils/delay.test.js +88 -0
  610. package/dist/src/utils/delay.test.js.map +1 -0
  611. package/dist/src/utils/editCorrector.js +10 -25
  612. package/dist/src/utils/editCorrector.js.map +1 -1
  613. package/dist/src/utils/editCorrector.test.js +19 -5
  614. package/dist/src/utils/editCorrector.test.js.map +1 -1
  615. package/dist/src/utils/editor.d.ts +4 -2
  616. package/dist/src/utils/editor.js +53 -39
  617. package/dist/src/utils/editor.js.map +1 -1
  618. package/dist/src/utils/editor.test.js +18 -45
  619. package/dist/src/utils/editor.test.js.map +1 -1
  620. package/dist/src/utils/environmentContext.d.ts +2 -1
  621. package/dist/src/utils/environmentContext.js +20 -33
  622. package/dist/src/utils/environmentContext.js.map +1 -1
  623. package/dist/src/utils/environmentContext.test.js +6 -34
  624. package/dist/src/utils/environmentContext.test.js.map +1 -1
  625. package/dist/src/utils/errorParsing.d.ts +1 -1
  626. package/dist/src/utils/errorParsing.js +5 -33
  627. package/dist/src/utils/errorParsing.js.map +1 -1
  628. package/dist/src/utils/errorParsing.test.js +0 -88
  629. package/dist/src/utils/errorParsing.test.js.map +1 -1
  630. package/dist/src/utils/errors.d.ts +3 -0
  631. package/dist/src/utils/errors.js +6 -0
  632. package/dist/src/utils/errors.js.map +1 -1
  633. package/dist/src/utils/events.d.ts +121 -0
  634. package/dist/src/utils/events.js +84 -0
  635. package/dist/src/utils/events.js.map +1 -0
  636. package/dist/src/utils/events.test.d.ts +6 -0
  637. package/dist/src/utils/events.test.js +212 -0
  638. package/dist/src/utils/events.test.js.map +1 -0
  639. package/dist/src/utils/extensionLoader.d.ts +86 -0
  640. package/dist/src/utils/extensionLoader.js +208 -0
  641. package/dist/src/utils/extensionLoader.js.map +1 -0
  642. package/dist/src/utils/extensionLoader.test.d.ts +6 -0
  643. package/dist/src/utils/extensionLoader.test.js +154 -0
  644. package/dist/src/utils/extensionLoader.test.js.map +1 -0
  645. package/dist/src/utils/fetch.d.ts +1 -0
  646. package/dist/src/utils/fetch.js +4 -0
  647. package/dist/src/utils/fetch.js.map +1 -1
  648. package/dist/src/utils/fileUtils.d.ts +4 -0
  649. package/dist/src/utils/fileUtils.js +34 -2
  650. package/dist/src/utils/fileUtils.js.map +1 -1
  651. package/dist/src/utils/fileUtils.test.js +87 -61
  652. package/dist/src/utils/fileUtils.test.js.map +1 -1
  653. package/dist/src/utils/filesearch/fileSearch.js +1 -1
  654. package/dist/src/utils/filesearch/fileSearch.js.map +1 -1
  655. package/dist/src/utils/flashFallback.test.js +28 -47
  656. package/dist/src/utils/flashFallback.test.js.map +1 -1
  657. package/dist/src/utils/formatters.d.ts +1 -0
  658. package/dist/src/utils/formatters.js +2 -1
  659. package/dist/src/utils/formatters.js.map +1 -1
  660. package/dist/src/utils/formatters.test.d.ts +6 -0
  661. package/dist/src/utils/formatters.test.js +26 -0
  662. package/dist/src/utils/formatters.test.js.map +1 -0
  663. package/dist/src/utils/getFolderStructure.js +9 -17
  664. package/dist/src/utils/getFolderStructure.js.map +1 -1
  665. package/dist/src/utils/getFolderStructure.test.js +7 -6
  666. package/dist/src/utils/getFolderStructure.test.js.map +1 -1
  667. package/dist/src/utils/gitIgnoreParser.d.ts +4 -1
  668. package/dist/src/utils/gitIgnoreParser.js +28 -10
  669. package/dist/src/utils/gitIgnoreParser.js.map +1 -1
  670. package/dist/src/utils/gitIgnoreParser.test.js +58 -0
  671. package/dist/src/utils/gitIgnoreParser.test.js.map +1 -1
  672. package/dist/src/utils/googleErrors.d.ts +104 -0
  673. package/dist/src/utils/googleErrors.js +152 -0
  674. package/dist/src/utils/googleErrors.js.map +1 -0
  675. package/dist/src/utils/googleErrors.test.d.ts +6 -0
  676. package/dist/src/utils/googleErrors.test.js +301 -0
  677. package/dist/src/utils/googleErrors.test.js.map +1 -0
  678. package/dist/src/utils/googleQuotaErrors.d.ts +37 -0
  679. package/dist/src/utils/googleQuotaErrors.js +157 -0
  680. package/dist/src/utils/googleQuotaErrors.js.map +1 -0
  681. package/dist/src/utils/googleQuotaErrors.test.d.ts +6 -0
  682. package/dist/src/utils/googleQuotaErrors.test.js +311 -0
  683. package/dist/src/utils/googleQuotaErrors.test.js.map +1 -0
  684. package/dist/src/utils/httpErrors.d.ts +18 -0
  685. package/dist/src/utils/httpErrors.js +36 -0
  686. package/dist/src/utils/httpErrors.js.map +1 -0
  687. package/dist/src/utils/ignorePatterns.test.js +26 -30
  688. package/dist/src/utils/ignorePatterns.test.js.map +1 -1
  689. package/dist/src/utils/installationManager.js +2 -1
  690. package/dist/src/utils/installationManager.js.map +1 -1
  691. package/dist/src/utils/installationManager.test.js +6 -4
  692. package/dist/src/utils/installationManager.test.js.map +1 -1
  693. package/dist/src/utils/llm-edit-fixer.d.ts +1 -1
  694. package/dist/src/utils/llm-edit-fixer.js +33 -9
  695. package/dist/src/utils/llm-edit-fixer.js.map +1 -1
  696. package/dist/src/utils/llm-edit-fixer.test.js +38 -1
  697. package/dist/src/utils/llm-edit-fixer.test.js.map +1 -1
  698. package/dist/src/utils/memoryDiscovery.d.ts +20 -1
  699. package/dist/src/utils/memoryDiscovery.js +176 -12
  700. package/dist/src/utils/memoryDiscovery.js.map +1 -1
  701. package/dist/src/utils/memoryDiscovery.test.js +299 -40
  702. package/dist/src/utils/memoryDiscovery.test.js.map +1 -1
  703. package/dist/src/utils/memoryImportProcessor.js +4 -3
  704. package/dist/src/utils/memoryImportProcessor.js.map +1 -1
  705. package/dist/src/utils/memoryImportProcessor.test.js +8 -14
  706. package/dist/src/utils/memoryImportProcessor.test.js.map +1 -1
  707. package/dist/src/utils/nextSpeakerChecker.js +3 -3
  708. package/dist/src/utils/nextSpeakerChecker.js.map +1 -1
  709. package/dist/src/utils/nextSpeakerChecker.test.js +13 -5
  710. package/dist/src/utils/nextSpeakerChecker.test.js.map +1 -1
  711. package/dist/src/utils/package.d.ts +12 -0
  712. package/dist/src/utils/package.js +15 -0
  713. package/dist/src/utils/package.js.map +1 -0
  714. package/dist/src/utils/pathCorrector.d.ts +25 -0
  715. package/dist/src/utils/pathCorrector.js +33 -0
  716. package/dist/src/utils/pathCorrector.js.map +1 -0
  717. package/dist/src/utils/pathCorrector.test.d.ts +6 -0
  718. package/dist/src/utils/pathCorrector.test.js +83 -0
  719. package/dist/src/utils/pathCorrector.test.js.map +1 -0
  720. package/dist/src/utils/pathReader.js +4 -4
  721. package/dist/src/utils/pathReader.js.map +1 -1
  722. package/dist/src/utils/pathReader.test.js +44 -1
  723. package/dist/src/utils/pathReader.test.js.map +1 -1
  724. package/dist/src/utils/paths.d.ts +1 -1
  725. package/dist/src/utils/paths.js +131 -29
  726. package/dist/src/utils/paths.js.map +1 -1
  727. package/dist/src/utils/paths.test.js +200 -68
  728. package/dist/src/utils/paths.test.js.map +1 -1
  729. package/dist/src/utils/quotaErrorDetection.d.ts +0 -2
  730. package/dist/src/utils/quotaErrorDetection.js +0 -46
  731. package/dist/src/utils/quotaErrorDetection.js.map +1 -1
  732. package/dist/src/utils/retry.d.ts +3 -10
  733. package/dist/src/utils/retry.js +97 -195
  734. package/dist/src/utils/retry.js.map +1 -1
  735. package/dist/src/utils/retry.test.js +179 -145
  736. package/dist/src/utils/retry.test.js.map +1 -1
  737. package/dist/src/utils/safeJsonStringify.d.ts +4 -4
  738. package/dist/src/utils/safeJsonStringify.js +31 -7
  739. package/dist/src/utils/safeJsonStringify.js.map +1 -1
  740. package/dist/src/utils/shell-utils.d.ts +15 -2
  741. package/dist/src/utils/shell-utils.js +387 -140
  742. package/dist/src/utils/shell-utils.js.map +1 -1
  743. package/dist/src/utils/shell-utils.test.js +244 -62
  744. package/dist/src/utils/shell-utils.test.js.map +1 -1
  745. package/dist/src/utils/stdio.d.ts +32 -0
  746. package/dist/src/utils/stdio.js +85 -0
  747. package/dist/src/utils/stdio.js.map +1 -0
  748. package/dist/src/utils/stdio.test.d.ts +6 -0
  749. package/dist/src/utils/stdio.test.js +47 -0
  750. package/dist/src/utils/stdio.test.js.map +1 -0
  751. package/dist/src/utils/summarizer.d.ts +4 -2
  752. package/dist/src/utils/summarizer.js +8 -9
  753. package/dist/src/utils/summarizer.js.map +1 -1
  754. package/dist/src/utils/summarizer.test.js +32 -12
  755. package/dist/src/utils/summarizer.test.js.map +1 -1
  756. package/dist/src/utils/systemEncoding.js +5 -4
  757. package/dist/src/utils/systemEncoding.js.map +1 -1
  758. package/dist/src/utils/systemEncoding.test.js +2 -1
  759. package/dist/src/utils/systemEncoding.test.js.map +1 -1
  760. package/dist/src/utils/terminal.d.ts +14 -0
  761. package/dist/src/utils/terminal.js +38 -0
  762. package/dist/src/utils/terminal.js.map +1 -0
  763. package/dist/src/utils/tool-utils.d.ts +2 -2
  764. package/dist/src/utils/tool-utils.js +15 -6
  765. package/dist/src/utils/tool-utils.js.map +1 -1
  766. package/dist/src/utils/tool-utils.test.js +8 -0
  767. package/dist/src/utils/tool-utils.test.js.map +1 -1
  768. package/dist/src/utils/userAccountManager.js +5 -4
  769. package/dist/src/utils/userAccountManager.js.map +1 -1
  770. package/dist/src/utils/userAccountManager.test.js +9 -7
  771. package/dist/src/utils/userAccountManager.test.js.map +1 -1
  772. package/dist/src/utils/workspaceContext.d.ts +4 -3
  773. package/dist/src/utils/workspaceContext.js +13 -13
  774. package/dist/src/utils/workspaceContext.js.map +1 -1
  775. package/dist/src/utils/workspaceContext.test.js +8 -7
  776. package/dist/src/utils/workspaceContext.test.js.map +1 -1
  777. package/dist/tsconfig.tsbuildinfo +1 -1
  778. package/package.json +12 -7
@@ -4,13 +4,13 @@
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
6
  import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
7
- import { ApiError } from '@google/genai';
8
- import { GeminiChat, InvalidStreamError, StreamEventType, } from './geminiChat.js';
7
+ import { ApiError, ThinkingLevel } from '@google/genai';
8
+ import { GeminiChat, InvalidStreamError, StreamEventType, SYNTHETIC_THOUGHT_SIGNATURE, } from './geminiChat.js';
9
9
  import { setSimulate429 } from '../utils/testUtils.js';
10
- import { DEFAULT_GEMINI_FLASH_MODEL } from '../config/models.js';
10
+ import { DEFAULT_GEMINI_FLASH_MODEL, DEFAULT_GEMINI_MODEL, DEFAULT_THINKING_MODE, PREVIEW_GEMINI_MODEL, } from '../config/models.js';
11
11
  import { AuthType } from './contentGenerator.js';
12
- import {} from '../utils/retry.js';
13
- import { Kind } from '../tools/tools.js';
12
+ import { TerminalQuotaError } from '../utils/googleQuotaErrors.js';
13
+ import { retryWithBackoff } from '../utils/retry.js';
14
14
  import { uiTelemetryService } from '../telemetry/uiTelemetry.js';
15
15
  // Mock fs module to prevent actual file system operations during tests
16
16
  const mockFileSystem = new Map();
@@ -65,7 +65,6 @@ describe('GeminiChat', () => {
65
65
  let mockContentGenerator;
66
66
  let chat;
67
67
  let mockConfig;
68
- const config = {};
69
68
  beforeEach(() => {
70
69
  vi.clearAllMocks();
71
70
  vi.mocked(uiTelemetryService.setLastPromptTokenCount).mockClear();
@@ -84,6 +83,7 @@ describe('GeminiChat', () => {
84
83
  getTelemetryLogPromptsEnabled: () => true,
85
84
  getUsageStatisticsEnabled: () => true,
86
85
  getDebugMode: () => false,
86
+ getPreviewFeatures: () => false,
87
87
  getContentGeneratorConfig: vi.fn().mockReturnValue({
88
88
  authType: 'oauth-personal', // Ensure this is set for fallback tests
89
89
  model: 'test-model',
@@ -102,16 +102,55 @@ describe('GeminiChat', () => {
102
102
  getTool: vi.fn(),
103
103
  }),
104
104
  getContentGenerator: vi.fn().mockReturnValue(mockContentGenerator),
105
+ getRetryFetchErrors: vi.fn().mockReturnValue(false),
106
+ modelConfigService: {
107
+ getResolvedConfig: vi.fn().mockImplementation((modelConfigKey) => {
108
+ const thinkingConfig = modelConfigKey.model.startsWith('gemini-3')
109
+ ? {
110
+ thinkingLevel: ThinkingLevel.HIGH,
111
+ }
112
+ : {
113
+ thinkingBudget: DEFAULT_THINKING_MODE,
114
+ };
115
+ return {
116
+ model: modelConfigKey.model,
117
+ generateContentConfig: {
118
+ temperature: 0,
119
+ thinkingConfig,
120
+ },
121
+ };
122
+ }),
123
+ },
124
+ isPreviewModelBypassMode: vi.fn().mockReturnValue(false),
125
+ setPreviewModelBypassMode: vi.fn(),
126
+ isPreviewModelFallbackMode: vi.fn().mockReturnValue(false),
127
+ setPreviewModelFallbackMode: vi.fn(),
128
+ isInteractive: vi.fn().mockReturnValue(false),
105
129
  };
106
130
  // Disable 429 simulation for tests
107
131
  setSimulate429(false);
108
132
  // Reset history for each test by creating a new instance
109
- chat = new GeminiChat(mockConfig, config, []);
133
+ chat = new GeminiChat(mockConfig);
110
134
  });
111
135
  afterEach(() => {
112
136
  vi.restoreAllMocks();
113
137
  vi.resetAllMocks();
114
138
  });
139
+ describe('constructor', () => {
140
+ it('should initialize lastPromptTokenCount based on history size', () => {
141
+ const history = [
142
+ { role: 'user', parts: [{ text: 'Hello' }] },
143
+ { role: 'model', parts: [{ text: 'Hi there' }] },
144
+ ];
145
+ const chatWithHistory = new GeminiChat(mockConfig, '', [], history);
146
+ const estimatedTokens = Math.ceil(JSON.stringify(history).length / 4);
147
+ expect(chatWithHistory.getLastPromptTokenCount()).toBe(estimatedTokens);
148
+ });
149
+ it('should initialize lastPromptTokenCount for empty history', () => {
150
+ const chatEmpty = new GeminiChat(mockConfig);
151
+ expect(chatEmpty.getLastPromptTokenCount()).toBe(Math.ceil(JSON.stringify([]).length / 4));
152
+ });
153
+ });
115
154
  describe('sendMessageStream', () => {
116
155
  it('should succeed if a tool call is followed by an empty part', async () => {
117
156
  // 1. Mock a stream that contains a tool call, then an invalid (empty) part.
@@ -141,7 +180,7 @@ describe('GeminiChat', () => {
141
180
  vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue(streamWithToolCall);
142
181
  // 2. Action & Assert: The stream processing should complete without throwing an error
143
182
  // because the presence of a tool call makes the empty final chunk acceptable.
144
- const stream = await chat.sendMessageStream('test-model', { message: 'test message' }, 'prompt-id-tool-call-empty-end');
183
+ const stream = await chat.sendMessageStream({ model: 'test-model' }, 'test message', 'prompt-id-tool-call-empty-end', new AbortController().signal);
145
184
  await expect((async () => {
146
185
  for await (const _ of stream) {
147
186
  /* consume stream */
@@ -181,7 +220,7 @@ describe('GeminiChat', () => {
181
220
  })();
182
221
  vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue(streamWithNoFinish);
183
222
  // 2. Action & Assert: The stream should fail because there's no finish reason.
184
- const stream = await chat.sendMessageStream('test-model', { message: 'test message' }, 'prompt-id-no-finish-empty-end');
223
+ const stream = await chat.sendMessageStream({ model: 'gemini-2.0-flash' }, 'test message', 'prompt-id-no-finish-empty-end', new AbortController().signal);
185
224
  await expect((async () => {
186
225
  for await (const _ of stream) {
187
226
  /* consume stream */
@@ -216,7 +255,7 @@ describe('GeminiChat', () => {
216
255
  })();
217
256
  vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue(streamWithInvalidEnd);
218
257
  // 2. Action & Assert: The stream should complete without throwing an error.
219
- const stream = await chat.sendMessageStream('test-model', { message: 'test message' }, 'prompt-id-valid-then-invalid-end');
258
+ const stream = await chat.sendMessageStream({ model: 'test-model' }, 'test message', 'prompt-id-valid-then-invalid-end', new AbortController().signal);
220
259
  await expect((async () => {
221
260
  for await (const _ of stream) {
222
261
  /* consume stream */
@@ -251,7 +290,7 @@ describe('GeminiChat', () => {
251
290
  })();
252
291
  vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue(multiChunkStream);
253
292
  // 2. Action: Send a message and consume the stream.
254
- const stream = await chat.sendMessageStream('test-model', { message: 'test message' }, 'prompt-id-empty-chunk-consolidation');
293
+ const stream = await chat.sendMessageStream({ model: 'test-model' }, 'test message', 'prompt-id-empty-chunk-consolidation', new AbortController().signal);
255
294
  for await (const _ of stream) {
256
295
  // Consume the stream
257
296
  }
@@ -299,7 +338,7 @@ describe('GeminiChat', () => {
299
338
  })();
300
339
  vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue(multiChunkStream);
301
340
  // 2. Action: Send a message and consume the stream.
302
- const stream = await chat.sendMessageStream('test-model', { message: 'test message' }, 'prompt-id-multi-chunk');
341
+ const stream = await chat.sendMessageStream({ model: 'test-model' }, 'test message', 'prompt-id-multi-chunk', new AbortController().signal);
303
342
  for await (const _ of stream) {
304
343
  // Consume the stream to trigger history recording.
305
344
  }
@@ -335,7 +374,7 @@ describe('GeminiChat', () => {
335
374
  })();
336
375
  vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue(mixedContentStream);
337
376
  // 2. Action: Send a message and fully consume the stream to trigger history recording.
338
- const stream = await chat.sendMessageStream('test-model', { message: 'test message' }, 'prompt-id-mixed-chunk');
377
+ const stream = await chat.sendMessageStream({ model: 'test-model' }, 'test message', 'prompt-id-mixed-chunk', new AbortController().signal);
339
378
  for await (const _ of stream) {
340
379
  // This loop consumes the stream.
341
380
  }
@@ -351,6 +390,84 @@ describe('GeminiChat', () => {
351
390
  expect(modelTurn?.parts?.length).toBe(1);
352
391
  expect(modelTurn?.parts[0].text).toBe('This is the visible text that should not be lost.');
353
392
  });
393
+ it('should use maxAttempts=1 for retryWithBackoff when in Preview Model Fallback Mode', async () => {
394
+ vi.mocked(mockConfig.isPreviewModelFallbackMode).mockReturnValue(true);
395
+ vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue((async function* () {
396
+ yield {
397
+ candidates: [
398
+ {
399
+ content: { parts: [{ text: 'Success' }] },
400
+ finishReason: 'STOP',
401
+ },
402
+ ],
403
+ };
404
+ })());
405
+ const stream = await chat.sendMessageStream({ model: PREVIEW_GEMINI_MODEL }, 'test', 'prompt-id-fast-retry', new AbortController().signal);
406
+ for await (const _ of stream) {
407
+ // consume stream
408
+ }
409
+ expect(mockRetryWithBackoff).toHaveBeenCalledWith(expect.any(Function), expect.objectContaining({
410
+ maxAttempts: 1,
411
+ }));
412
+ });
413
+ it('should NOT use maxAttempts=1 for other models even in Preview Model Fallback Mode', async () => {
414
+ vi.mocked(mockConfig.isPreviewModelFallbackMode).mockReturnValue(true);
415
+ vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue((async function* () {
416
+ yield {
417
+ candidates: [
418
+ {
419
+ content: { parts: [{ text: 'Success' }] },
420
+ finishReason: 'STOP',
421
+ },
422
+ ],
423
+ };
424
+ })());
425
+ const stream = await chat.sendMessageStream({ model: DEFAULT_GEMINI_FLASH_MODEL }, 'test', 'prompt-id-normal-retry', new AbortController().signal);
426
+ for await (const _ of stream) {
427
+ // consume stream
428
+ }
429
+ expect(mockRetryWithBackoff).toHaveBeenCalledWith(expect.any(Function), expect.objectContaining({
430
+ maxAttempts: undefined, // Should use default
431
+ }));
432
+ });
433
+ it('should pass DEFAULT_GEMINI_MODEL to handleFallback when Preview Model is bypassed (downgraded)', async () => {
434
+ // ARRANGE
435
+ vi.mocked(mockConfig.isPreviewModelBypassMode).mockReturnValue(true);
436
+ // Mock retryWithBackoff to simulate catching the error and calling onPersistent429
437
+ vi.mocked(retryWithBackoff).mockImplementation(async (apiCall, options) => {
438
+ const onPersistent429 = options?.onPersistent429;
439
+ try {
440
+ await apiCall();
441
+ }
442
+ catch (error) {
443
+ if (onPersistent429) {
444
+ await onPersistent429(AuthType.LOGIN_WITH_GOOGLE, error);
445
+ }
446
+ throw error;
447
+ }
448
+ });
449
+ // We need the API call to fail so retryWithBackoff calls the callback.
450
+ vi.mocked(mockContentGenerator.generateContentStream).mockRejectedValue(new TerminalQuotaError('Simulated Quota Error', {
451
+ code: 429,
452
+ message: 'Simulated Quota Error',
453
+ details: [],
454
+ }));
455
+ // ACT
456
+ const consumeStream = async () => {
457
+ const stream = await chat.sendMessageStream({ model: PREVIEW_GEMINI_MODEL }, 'test', 'prompt-id-bypass', new AbortController().signal);
458
+ // Consume the stream to trigger execution
459
+ for await (const _ of stream) {
460
+ // do nothing
461
+ }
462
+ };
463
+ await expect(consumeStream()).rejects.toThrow('Simulated Quota Error');
464
+ expect(retryWithBackoff).toHaveBeenCalled();
465
+ // ASSERT
466
+ // handleFallback is called via onPersistent429Callback
467
+ // We verify it was called with DEFAULT_GEMINI_MODEL
468
+ expect(mockHandleFallback).toHaveBeenCalledWith(expect.anything(), DEFAULT_GEMINI_MODEL, // This is the key assertion
469
+ expect.anything(), expect.anything());
470
+ });
354
471
  it('should throw an error when a tool call is followed by an empty stream response', async () => {
355
472
  // 1. Setup: A history where the model has just made a function call.
356
473
  const initialHistory = [
@@ -384,14 +501,12 @@ describe('GeminiChat', () => {
384
501
  })();
385
502
  vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue(emptyStreamResponse);
386
503
  // 3. Action: Send the function response back to the model and consume the stream.
387
- const stream = await chat.sendMessageStream('test-model', {
388
- message: {
389
- functionResponse: {
390
- name: 'find_restaurant',
391
- response: { name: 'Vesuvio' },
392
- },
504
+ const stream = await chat.sendMessageStream({ model: 'gemini-2.0-flash' }, {
505
+ functionResponse: {
506
+ name: 'find_restaurant',
507
+ response: { name: 'Vesuvio' },
393
508
  },
394
- }, 'prompt-id-stream-1');
509
+ }, 'prompt-id-stream-1', new AbortController().signal);
395
510
  // 4. Assert: The stream processing should throw an InvalidStreamError.
396
511
  await expect((async () => {
397
512
  for await (const _ of stream) {
@@ -422,7 +537,7 @@ describe('GeminiChat', () => {
422
537
  };
423
538
  })();
424
539
  vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue(streamWithToolCall);
425
- const stream = await chat.sendMessageStream('test-model', { message: 'test' }, 'prompt-id-1');
540
+ const stream = await chat.sendMessageStream({ model: 'test-model' }, 'test message', 'prompt-id-1', new AbortController().signal);
426
541
  // Should not throw an error
427
542
  await expect((async () => {
428
543
  for await (const _ of stream) {
@@ -446,7 +561,7 @@ describe('GeminiChat', () => {
446
561
  };
447
562
  })();
448
563
  vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue(streamWithoutFinishReason);
449
- const stream = await chat.sendMessageStream('test-model', { message: 'test' }, 'prompt-id-1');
564
+ const stream = await chat.sendMessageStream({ model: 'gemini-2.0-flash' }, 'test message', 'prompt-id-1', new AbortController().signal);
450
565
  await expect((async () => {
451
566
  for await (const _ of stream) {
452
567
  // consume stream
@@ -469,7 +584,7 @@ describe('GeminiChat', () => {
469
584
  };
470
585
  })();
471
586
  vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue(streamWithEmptyResponse);
472
- const stream = await chat.sendMessageStream('test-model', { message: 'test' }, 'prompt-id-1');
587
+ const stream = await chat.sendMessageStream({ model: 'gemini-2.0-flash' }, 'test message', 'prompt-id-1', new AbortController().signal);
473
588
  await expect((async () => {
474
589
  for await (const _ of stream) {
475
590
  // consume stream
@@ -492,7 +607,7 @@ describe('GeminiChat', () => {
492
607
  };
493
608
  })();
494
609
  vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue(validStream);
495
- const stream = await chat.sendMessageStream('test-model', { message: 'test' }, 'prompt-id-1');
610
+ const stream = await chat.sendMessageStream({ model: 'test-model' }, 'test message', 'prompt-id-1', new AbortController().signal);
496
611
  // Should not throw an error
497
612
  await expect((async () => {
498
613
  for await (const _ of stream) {
@@ -500,6 +615,71 @@ describe('GeminiChat', () => {
500
615
  }
501
616
  })()).resolves.not.toThrow();
502
617
  });
618
+ it('should throw InvalidStreamError when finishReason is MALFORMED_FUNCTION_CALL', async () => {
619
+ // Setup: Stream with MALFORMED_FUNCTION_CALL finish reason and empty response
620
+ const streamWithMalformedFunctionCall = (async function* () {
621
+ yield {
622
+ candidates: [
623
+ {
624
+ content: {
625
+ role: 'model',
626
+ parts: [], // Empty parts
627
+ },
628
+ finishReason: 'MALFORMED_FUNCTION_CALL',
629
+ },
630
+ ],
631
+ };
632
+ })();
633
+ vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue(streamWithMalformedFunctionCall);
634
+ const stream = await chat.sendMessageStream({ model: 'gemini-2.5-pro' }, 'test', 'prompt-id-malformed', new AbortController().signal);
635
+ // Should throw an error
636
+ await expect((async () => {
637
+ for await (const _ of stream) {
638
+ // consume stream
639
+ }
640
+ })()).rejects.toThrow(InvalidStreamError);
641
+ });
642
+ it('should retry when finishReason is MALFORMED_FUNCTION_CALL', async () => {
643
+ // 1. Mock the API to fail once with MALFORMED_FUNCTION_CALL, then succeed.
644
+ vi.mocked(mockContentGenerator.generateContentStream)
645
+ .mockImplementationOnce(async () => (async function* () {
646
+ yield {
647
+ candidates: [
648
+ {
649
+ content: { parts: [], role: 'model' },
650
+ finishReason: 'MALFORMED_FUNCTION_CALL',
651
+ },
652
+ ],
653
+ };
654
+ })())
655
+ .mockImplementationOnce(async () =>
656
+ // Second attempt succeeds
657
+ (async function* () {
658
+ yield {
659
+ candidates: [
660
+ {
661
+ content: { parts: [{ text: 'Success after retry' }] },
662
+ finishReason: 'STOP',
663
+ },
664
+ ],
665
+ };
666
+ })());
667
+ // 2. Send a message
668
+ const stream = await chat.sendMessageStream({ model: 'gemini-2.5-pro' }, 'test retry', 'prompt-id-retry-malformed', new AbortController().signal);
669
+ const events = [];
670
+ for await (const event of stream) {
671
+ events.push(event);
672
+ }
673
+ // 3. Assertions
674
+ // Should be called twice (initial + retry)
675
+ expect(mockContentGenerator.generateContentStream).toHaveBeenCalledTimes(2);
676
+ // Check for a retry event
677
+ expect(events.some((e) => e.type === StreamEventType.RETRY)).toBe(true);
678
+ // Check for the successful content chunk
679
+ expect(events.some((e) => e.type === StreamEventType.CHUNK &&
680
+ e.value.candidates?.[0]?.content?.parts?.[0]?.text ===
681
+ 'Success after retry')).toBe(true);
682
+ });
503
683
  it('should call generateContentStream with the correct parameters', async () => {
504
684
  const response = (async function* () {
505
685
  yield {
@@ -523,7 +703,7 @@ describe('GeminiChat', () => {
523
703
  };
524
704
  })();
525
705
  vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue(response);
526
- const stream = await chat.sendMessageStream('test-model', { message: 'hello' }, 'prompt-id-1');
706
+ const stream = await chat.sendMessageStream({ model: 'test-model' }, 'hello', 'prompt-id-1', new AbortController().signal);
527
707
  for await (const _ of stream) {
528
708
  // consume stream
529
709
  }
@@ -535,11 +715,68 @@ describe('GeminiChat', () => {
535
715
  parts: [{ text: 'hello' }],
536
716
  },
537
717
  ],
538
- config: {},
718
+ config: {
719
+ systemInstruction: '',
720
+ tools: [],
721
+ temperature: 0,
722
+ thinkingConfig: {
723
+ thinkingBudget: DEFAULT_THINKING_MODE,
724
+ },
725
+ abortSignal: expect.any(AbortSignal),
726
+ },
539
727
  }, 'prompt-id-1');
540
- // Verify that token counting is called when usageMetadata is present
541
- expect(uiTelemetryService.setLastPromptTokenCount).toHaveBeenCalledWith(42);
542
- expect(uiTelemetryService.setLastPromptTokenCount).toHaveBeenCalledTimes(1);
728
+ });
729
+ it('should use thinkingLevel and remove thinkingBudget for gemini-3 models', async () => {
730
+ const response = (async function* () {
731
+ yield {
732
+ candidates: [
733
+ {
734
+ content: { parts: [{ text: 'response' }], role: 'model' },
735
+ finishReason: 'STOP',
736
+ },
737
+ ],
738
+ };
739
+ })();
740
+ vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue(response);
741
+ const stream = await chat.sendMessageStream({ model: 'gemini-3-test-only-model-string-for-testing' }, 'hello', 'prompt-id-thinking-level', new AbortController().signal);
742
+ for await (const _ of stream) {
743
+ // consume stream
744
+ }
745
+ expect(mockContentGenerator.generateContentStream).toHaveBeenCalledWith(expect.objectContaining({
746
+ model: 'gemini-3-test-only-model-string-for-testing',
747
+ config: expect.objectContaining({
748
+ thinkingConfig: {
749
+ thinkingBudget: undefined,
750
+ thinkingLevel: ThinkingLevel.HIGH,
751
+ },
752
+ }),
753
+ }), 'prompt-id-thinking-level');
754
+ });
755
+ it('should use thinkingBudget and remove thinkingLevel for non-gemini-3 models', async () => {
756
+ const response = (async function* () {
757
+ yield {
758
+ candidates: [
759
+ {
760
+ content: { parts: [{ text: 'response' }], role: 'model' },
761
+ finishReason: 'STOP',
762
+ },
763
+ ],
764
+ };
765
+ })();
766
+ vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue(response);
767
+ const stream = await chat.sendMessageStream({ model: 'gemini-2.0-flash' }, 'hello', 'prompt-id-thinking-budget', new AbortController().signal);
768
+ for await (const _ of stream) {
769
+ // consume stream
770
+ }
771
+ expect(mockContentGenerator.generateContentStream).toHaveBeenCalledWith(expect.objectContaining({
772
+ model: 'gemini-2.0-flash',
773
+ config: expect.objectContaining({
774
+ thinkingConfig: {
775
+ thinkingBudget: DEFAULT_THINKING_MODE,
776
+ thinkingLevel: undefined,
777
+ },
778
+ }),
779
+ }), 'prompt-id-thinking-budget');
543
780
  });
544
781
  });
545
782
  describe('addHistory', () => {
@@ -571,6 +808,23 @@ describe('GeminiChat', () => {
571
808
  });
572
809
  });
573
810
  describe('sendMessageStream with retries', () => {
811
+ it('should not retry on invalid content if model does not start with gemini-2', async () => {
812
+ // Mock the stream to fail.
813
+ vi.mocked(mockContentGenerator.generateContentStream).mockImplementation(async () => (async function* () {
814
+ yield {
815
+ candidates: [{ content: { parts: [{ text: '' }] } }],
816
+ };
817
+ })());
818
+ const stream = await chat.sendMessageStream({ model: 'gemini-1.5-pro' }, 'test', 'prompt-id-no-retry', new AbortController().signal);
819
+ await expect((async () => {
820
+ for await (const _ of stream) {
821
+ // Must loop to trigger the internal logic that throws.
822
+ }
823
+ })()).rejects.toThrow(InvalidStreamError);
824
+ // Should be called only 1 time (no retry)
825
+ expect(mockContentGenerator.generateContentStream).toHaveBeenCalledTimes(1);
826
+ expect(mockLogContentRetry).not.toHaveBeenCalled();
827
+ });
574
828
  it('should yield a RETRY event when an invalid stream is encountered', async () => {
575
829
  // ARRANGE: Mock the stream to fail once, then succeed.
576
830
  vi.mocked(mockContentGenerator.generateContentStream)
@@ -594,7 +848,7 @@ describe('GeminiChat', () => {
594
848
  };
595
849
  })());
596
850
  // ACT: Send a message and collect all events from the stream.
597
- const stream = await chat.sendMessageStream('test-model', { message: 'test' }, 'prompt-id-yield-retry');
851
+ const stream = await chat.sendMessageStream({ model: 'gemini-2.0-flash' }, 'test message', 'prompt-id-yield-retry', new AbortController().signal);
598
852
  const events = [];
599
853
  for await (const event of stream) {
600
854
  events.push(event);
@@ -626,7 +880,7 @@ describe('GeminiChat', () => {
626
880
  ],
627
881
  };
628
882
  })());
629
- const stream = await chat.sendMessageStream('test-model', { message: 'test' }, 'prompt-id-retry-success');
883
+ const stream = await chat.sendMessageStream({ model: 'gemini-2.0-flash' }, 'test', 'prompt-id-retry-success', new AbortController().signal);
630
884
  const chunks = [];
631
885
  for await (const chunk of stream) {
632
886
  chunks.push(chunk);
@@ -655,6 +909,46 @@ describe('GeminiChat', () => {
655
909
  // Verify that token counting is not called when usageMetadata is missing
656
910
  expect(uiTelemetryService.setLastPromptTokenCount).not.toHaveBeenCalled();
657
911
  });
912
+ it('should set temperature to 1 on retry', async () => {
913
+ // Use mockImplementationOnce to provide a fresh, promise-wrapped generator for each attempt.
914
+ vi.mocked(mockContentGenerator.generateContentStream)
915
+ .mockImplementationOnce(async () =>
916
+ // First call returns an invalid stream
917
+ (async function* () {
918
+ yield {
919
+ candidates: [{ content: { parts: [{ text: '' }] } }], // Invalid empty text part
920
+ };
921
+ })())
922
+ .mockImplementationOnce(async () =>
923
+ // Second call returns a valid stream
924
+ (async function* () {
925
+ yield {
926
+ candidates: [
927
+ {
928
+ content: { parts: [{ text: 'Successful response' }] },
929
+ finishReason: 'STOP',
930
+ },
931
+ ],
932
+ };
933
+ })());
934
+ const stream = await chat.sendMessageStream({ model: 'gemini-2.0-flash' }, 'test message', 'prompt-id-retry-temperature', new AbortController().signal);
935
+ for await (const _ of stream) {
936
+ // consume stream
937
+ }
938
+ expect(mockContentGenerator.generateContentStream).toHaveBeenCalledTimes(2);
939
+ // First call should have original temperature
940
+ expect(mockContentGenerator.generateContentStream).toHaveBeenNthCalledWith(1, expect.objectContaining({
941
+ config: expect.objectContaining({
942
+ temperature: 0,
943
+ }),
944
+ }), 'prompt-id-retry-temperature');
945
+ // Second call (retry) should have temperature 1
946
+ expect(mockContentGenerator.generateContentStream).toHaveBeenNthCalledWith(2, expect.objectContaining({
947
+ config: expect.objectContaining({
948
+ temperature: 1,
949
+ }),
950
+ }), 'prompt-id-retry-temperature');
951
+ });
658
952
  it('should fail after all retries on persistent invalid content and report metrics', async () => {
659
953
  vi.mocked(mockContentGenerator.generateContentStream).mockImplementation(async () => (async function* () {
660
954
  yield {
@@ -668,7 +962,7 @@ describe('GeminiChat', () => {
668
962
  ],
669
963
  };
670
964
  })());
671
- const stream = await chat.sendMessageStream('test-model', { message: 'test' }, 'prompt-id-retry-fail');
965
+ const stream = await chat.sendMessageStream({ model: 'gemini-2.0-flash' }, 'test', 'prompt-id-retry-fail', new AbortController().signal);
672
966
  await expect(async () => {
673
967
  for await (const _ of stream) {
674
968
  // Must loop to trigger the internal logic that throws.
@@ -689,13 +983,24 @@ describe('GeminiChat', () => {
689
983
  describe('API error retry behavior', () => {
690
984
  beforeEach(() => {
691
985
  // Use a more direct mock for retry testing
692
- mockRetryWithBackoff.mockImplementation(async (apiCall, options) => {
986
+ mockRetryWithBackoff.mockImplementation(async (apiCall) => {
693
987
  try {
694
988
  return await apiCall();
695
989
  }
696
990
  catch (error) {
697
- if (options?.shouldRetryOnError &&
698
- options.shouldRetryOnError(error)) {
991
+ // Simulate the logic of defaultShouldRetry for ApiError
992
+ let shouldRetry = false;
993
+ if (error instanceof ApiError && error.message) {
994
+ if (error.status === 429 ||
995
+ (error.status >= 500 && error.status < 600)) {
996
+ shouldRetry = true;
997
+ }
998
+ // Explicitly don't retry on these
999
+ if (error.status === 400) {
1000
+ shouldRetry = false;
1001
+ }
1002
+ }
1003
+ if (shouldRetry) {
699
1004
  // Try again
700
1005
  return await apiCall();
701
1006
  }
@@ -706,7 +1011,7 @@ describe('GeminiChat', () => {
706
1011
  it('should not retry on 400 Bad Request errors', async () => {
707
1012
  const error400 = new ApiError({ message: 'Bad Request', status: 400 });
708
1013
  vi.mocked(mockContentGenerator.generateContentStream).mockRejectedValue(error400);
709
- const stream = await chat.sendMessageStream('test-model', { message: 'test' }, 'prompt-id-400');
1014
+ const stream = await chat.sendMessageStream({ model: 'gemini-2.0-flash' }, 'test message', 'prompt-id-400', new AbortController().signal);
710
1015
  await expect((async () => {
711
1016
  for await (const _ of stream) {
712
1017
  /* consume stream */
@@ -729,7 +1034,7 @@ describe('GeminiChat', () => {
729
1034
  ],
730
1035
  };
731
1036
  })());
732
- const stream = await chat.sendMessageStream('test-model', { message: 'test' }, 'prompt-id-429-retry');
1037
+ const stream = await chat.sendMessageStream({ model: 'test-model' }, 'test message', 'prompt-id-429-retry', new AbortController().signal);
733
1038
  const events = [];
734
1039
  for await (const event of stream) {
735
1040
  events.push(event);
@@ -741,21 +1046,6 @@ describe('GeminiChat', () => {
741
1046
  e.value.candidates?.[0]?.content?.parts?.[0]?.text ===
742
1047
  'Success after retry')).toBe(true);
743
1048
  });
744
- it('should not retry on schema depth errors', async () => {
745
- const schemaError = new ApiError({
746
- message: 'Request failed: maximum schema depth exceeded',
747
- status: 500,
748
- });
749
- vi.mocked(mockContentGenerator.generateContentStream).mockRejectedValue(schemaError);
750
- const stream = await chat.sendMessageStream('test-model', { message: 'test' }, 'prompt-id-schema');
751
- await expect((async () => {
752
- for await (const _ of stream) {
753
- /* consume stream */
754
- }
755
- })()).rejects.toThrow(schemaError);
756
- // Should only be called once (no retry)
757
- expect(mockContentGenerator.generateContentStream).toHaveBeenCalledTimes(1);
758
- });
759
1049
  it('should retry on 5xx server errors', async () => {
760
1050
  const error500 = new ApiError({
761
1051
  message: 'Internal Server Error 500',
@@ -773,7 +1063,7 @@ describe('GeminiChat', () => {
773
1063
  ],
774
1064
  };
775
1065
  })());
776
- const stream = await chat.sendMessageStream('test-model', { message: 'test' }, 'prompt-id-500-retry');
1066
+ const stream = await chat.sendMessageStream({ model: 'test-model' }, 'test message', 'prompt-id-500-retry', new AbortController().signal);
777
1067
  const events = [];
778
1068
  for await (const event of stream) {
779
1069
  events.push(event);
@@ -781,6 +1071,44 @@ describe('GeminiChat', () => {
781
1071
  // Should be called twice (initial + retry)
782
1072
  expect(mockContentGenerator.generateContentStream).toHaveBeenCalledTimes(2);
783
1073
  });
1074
+ it('should retry on specific fetch errors when configured', async () => {
1075
+ vi.mocked(mockConfig.getRetryFetchErrors).mockReturnValue(true);
1076
+ const fetchError = new Error('exception TypeError: fetch failed sending request');
1077
+ vi.mocked(mockContentGenerator.generateContentStream)
1078
+ .mockRejectedValueOnce(fetchError)
1079
+ .mockResolvedValueOnce((async function* () {
1080
+ yield {
1081
+ candidates: [
1082
+ {
1083
+ content: { parts: [{ text: 'Success after fetch error' }] },
1084
+ finishReason: 'STOP',
1085
+ },
1086
+ ],
1087
+ };
1088
+ })());
1089
+ mockRetryWithBackoff.mockImplementation(async (apiCall, options) => {
1090
+ try {
1091
+ return await apiCall();
1092
+ }
1093
+ catch (error) {
1094
+ if (options?.retryFetchErrors &&
1095
+ error instanceof Error &&
1096
+ error.message.includes('exception TypeError: fetch failed sending request')) {
1097
+ return await apiCall();
1098
+ }
1099
+ throw error;
1100
+ }
1101
+ });
1102
+ const stream = await chat.sendMessageStream({ model: 'test-model' }, 'test message', 'prompt-id-fetch-error-retry', new AbortController().signal);
1103
+ const events = [];
1104
+ for await (const event of stream) {
1105
+ events.push(event);
1106
+ }
1107
+ expect(mockContentGenerator.generateContentStream).toHaveBeenCalledTimes(2);
1108
+ expect(events.some((e) => e.type === StreamEventType.CHUNK &&
1109
+ e.value.candidates?.[0]?.content?.parts?.[0]?.text ===
1110
+ 'Success after fetch error')).toBe(true);
1111
+ });
784
1112
  afterEach(() => {
785
1113
  // Reset to default behavior
786
1114
  mockRetryWithBackoff.mockImplementation(async (apiCall) => apiCall());
@@ -814,7 +1142,7 @@ describe('GeminiChat', () => {
814
1142
  };
815
1143
  })());
816
1144
  // 3. Send a new message
817
- const stream = await chat.sendMessageStream('test-model', { message: 'Second question' }, 'prompt-id-retry-existing');
1145
+ const stream = await chat.sendMessageStream({ model: 'gemini-2.0-flash' }, 'Second question', 'prompt-id-retry-existing', new AbortController().signal);
818
1146
  for await (const _ of stream) {
819
1147
  // consume stream
820
1148
  }
@@ -866,7 +1194,7 @@ describe('GeminiChat', () => {
866
1194
  };
867
1195
  })());
868
1196
  // 2. Call the method and consume the stream.
869
- const stream = await chat.sendMessageStream('test-model', { message: 'test empty stream' }, 'prompt-id-empty-stream');
1197
+ const stream = await chat.sendMessageStream({ model: 'gemini-2.0-flash' }, 'test empty stream', 'prompt-id-empty-stream', new AbortController().signal);
870
1198
  const chunks = [];
871
1199
  for await (const chunk of stream) {
872
1200
  chunks.push(chunk);
@@ -927,11 +1255,11 @@ describe('GeminiChat', () => {
927
1255
  .mockResolvedValueOnce(firstStreamGenerator)
928
1256
  .mockResolvedValueOnce(secondStreamGenerator);
929
1257
  // 3. Start the first stream and consume only the first chunk to pause it
930
- const firstStream = await chat.sendMessageStream('test-model', { message: 'first' }, 'prompt-1');
1258
+ const firstStream = await chat.sendMessageStream({ model: 'test-model' }, 'first', 'prompt-1', new AbortController().signal);
931
1259
  const firstStreamIterator = firstStream[Symbol.asyncIterator]();
932
1260
  await firstStreamIterator.next();
933
1261
  // 4. While the first stream is paused, start the second call. It will block.
934
- const secondStreamPromise = chat.sendMessageStream('test-model', { message: 'second' }, 'prompt-2');
1262
+ const secondStreamPromise = chat.sendMessageStream({ model: 'test-model' }, 'second', 'prompt-2', new AbortController().signal);
935
1263
  // 5. Assert that only one API call has been made so far.
936
1264
  expect(mockContentGenerator.generateContentStream).toHaveBeenCalledTimes(1);
937
1265
  // 6. Unblock and fully consume the first stream to completion.
@@ -956,215 +1284,6 @@ describe('GeminiChat', () => {
956
1284
  }
957
1285
  expect(turn4.parts[0].text).toBe('second response');
958
1286
  });
959
- describe('stopBeforeSecondMutator', () => {
960
- beforeEach(() => {
961
- // Common setup for these tests: mock the tool registry.
962
- const mockToolRegistry = {
963
- getTool: vi.fn((toolName) => {
964
- if (toolName === 'edit') {
965
- return { kind: Kind.Edit };
966
- }
967
- return { kind: Kind.Other };
968
- }),
969
- };
970
- vi.mocked(mockConfig.getToolRegistry).mockReturnValue(mockToolRegistry);
971
- });
972
- it('should stop streaming before a second mutator tool call', async () => {
973
- const responses = [
974
- {
975
- candidates: [
976
- { content: { role: 'model', parts: [{ text: 'First part. ' }] } },
977
- ],
978
- },
979
- {
980
- candidates: [
981
- {
982
- content: {
983
- role: 'model',
984
- parts: [{ functionCall: { name: 'edit', args: {} } }],
985
- },
986
- },
987
- ],
988
- },
989
- {
990
- candidates: [
991
- {
992
- content: {
993
- role: 'model',
994
- parts: [{ functionCall: { name: 'fetch', args: {} } }],
995
- },
996
- },
997
- ],
998
- },
999
- // This chunk contains the second mutator and should be clipped.
1000
- {
1001
- candidates: [
1002
- {
1003
- content: {
1004
- role: 'model',
1005
- parts: [
1006
- { functionCall: { name: 'edit', args: {} } },
1007
- { text: 'some trailing text' },
1008
- ],
1009
- },
1010
- },
1011
- ],
1012
- },
1013
- // This chunk should never be reached.
1014
- {
1015
- candidates: [
1016
- {
1017
- content: {
1018
- role: 'model',
1019
- parts: [{ text: 'This should not appear.' }],
1020
- },
1021
- },
1022
- ],
1023
- },
1024
- ];
1025
- vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue((async function* () {
1026
- for (const response of responses) {
1027
- yield response;
1028
- }
1029
- })());
1030
- const stream = await chat.sendMessageStream('test-model', { message: 'test message' }, 'prompt-id-mutator-test');
1031
- for await (const _ of stream) {
1032
- // Consume the stream to trigger history recording.
1033
- }
1034
- const history = chat.getHistory();
1035
- expect(history.length).toBe(2);
1036
- const modelTurn = history[1];
1037
- expect(modelTurn.role).toBe('model');
1038
- expect(modelTurn?.parts?.length).toBe(3);
1039
- expect(modelTurn?.parts[0].text).toBe('First part. ');
1040
- expect(modelTurn.parts[1].functionCall?.name).toBe('edit');
1041
- expect(modelTurn.parts[2].functionCall?.name).toBe('fetch');
1042
- });
1043
- it('should not stop streaming if only one mutator is present', async () => {
1044
- const responses = [
1045
- {
1046
- candidates: [
1047
- { content: { role: 'model', parts: [{ text: 'Part 1. ' }] } },
1048
- ],
1049
- },
1050
- {
1051
- candidates: [
1052
- {
1053
- content: {
1054
- role: 'model',
1055
- parts: [{ functionCall: { name: 'edit', args: {} } }],
1056
- },
1057
- },
1058
- ],
1059
- },
1060
- {
1061
- candidates: [
1062
- {
1063
- content: {
1064
- role: 'model',
1065
- parts: [{ text: 'Part 2.' }],
1066
- },
1067
- finishReason: 'STOP',
1068
- },
1069
- ],
1070
- },
1071
- ];
1072
- vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue((async function* () {
1073
- for (const response of responses) {
1074
- yield response;
1075
- }
1076
- })());
1077
- const stream = await chat.sendMessageStream('test-model', { message: 'test message' }, 'prompt-id-one-mutator');
1078
- for await (const _ of stream) {
1079
- /* consume */
1080
- }
1081
- const history = chat.getHistory();
1082
- const modelTurn = history[1];
1083
- expect(modelTurn?.parts?.length).toBe(3);
1084
- expect(modelTurn.parts[1].functionCall?.name).toBe('edit');
1085
- expect(modelTurn.parts[2].text).toBe('Part 2.');
1086
- });
1087
- it('should clip the chunk containing the second mutator, preserving prior parts', async () => {
1088
- const responses = [
1089
- {
1090
- candidates: [
1091
- {
1092
- content: {
1093
- role: 'model',
1094
- parts: [{ functionCall: { name: 'edit', args: {} } }],
1095
- },
1096
- },
1097
- ],
1098
- },
1099
- // This chunk has a valid part before the second mutator.
1100
- // The valid part should be kept, the rest of the chunk discarded.
1101
- {
1102
- candidates: [
1103
- {
1104
- content: {
1105
- role: 'model',
1106
- parts: [
1107
- { text: 'Keep this text. ' },
1108
- { functionCall: { name: 'edit', args: {} } },
1109
- { text: 'Discard this text.' },
1110
- ],
1111
- },
1112
- finishReason: 'STOP',
1113
- },
1114
- ],
1115
- },
1116
- ];
1117
- const stream = (async function* () {
1118
- for (const response of responses) {
1119
- yield response;
1120
- }
1121
- })();
1122
- vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue(stream);
1123
- const resultStream = await chat.sendMessageStream('test-model', { message: 'test' }, 'prompt-id-clip-chunk');
1124
- for await (const _ of resultStream) {
1125
- /* consume */
1126
- }
1127
- const history = chat.getHistory();
1128
- const modelTurn = history[1];
1129
- expect(modelTurn?.parts?.length).toBe(2);
1130
- expect(modelTurn.parts[0].functionCall?.name).toBe('edit');
1131
- expect(modelTurn.parts[1].text).toBe('Keep this text. ');
1132
- });
1133
- it('should handle two mutators in the same chunk (parallel call scenario)', async () => {
1134
- const responses = [
1135
- {
1136
- candidates: [
1137
- {
1138
- content: {
1139
- role: 'model',
1140
- parts: [
1141
- { text: 'Some text. ' },
1142
- { functionCall: { name: 'edit', args: {} } },
1143
- { functionCall: { name: 'edit', args: {} } },
1144
- ],
1145
- },
1146
- finishReason: 'STOP',
1147
- },
1148
- ],
1149
- },
1150
- ];
1151
- const stream = (async function* () {
1152
- for (const response of responses) {
1153
- yield response;
1154
- }
1155
- })();
1156
- vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue(stream);
1157
- const resultStream = await chat.sendMessageStream('test-model', { message: 'test' }, 'prompt-id-parallel-mutators');
1158
- for await (const _ of resultStream) {
1159
- /* consume */
1160
- }
1161
- const history = chat.getHistory();
1162
- const modelTurn = history[1];
1163
- expect(modelTurn?.parts?.length).toBe(2);
1164
- expect(modelTurn.parts[0].text).toBe('Some text. ');
1165
- expect(modelTurn.parts[1].functionCall?.name).toBe('edit');
1166
- });
1167
- });
1168
1287
  describe('Model Resolution', () => {
1169
1288
  const mockResponse = {
1170
1289
  candidates: [
@@ -1180,7 +1299,7 @@ describe('GeminiChat', () => {
1180
1299
  vi.mocked(mockContentGenerator.generateContentStream).mockImplementation(async () => (async function* () {
1181
1300
  yield mockResponse;
1182
1301
  })());
1183
- const stream = await chat.sendMessageStream('test-model', { message: 'test' }, 'prompt-id-res3');
1302
+ const stream = await chat.sendMessageStream({ model: 'test-model' }, 'test message', 'prompt-id-res3', new AbortController().signal);
1184
1303
  for await (const _ of stream) {
1185
1304
  // consume stream
1186
1305
  }
@@ -1243,7 +1362,7 @@ describe('GeminiChat', () => {
1243
1362
  isInFallbackModeSpy.mockReturnValue(true);
1244
1363
  return true; // Signal retry
1245
1364
  });
1246
- const stream = await chat.sendMessageStream('test-model', { message: 'trigger 429' }, 'prompt-id-fb1');
1365
+ const stream = await chat.sendMessageStream({ model: 'test-model' }, 'trigger 429', 'prompt-id-fb1', new AbortController().signal);
1247
1366
  // Consume stream to trigger logic
1248
1367
  for await (const _ of stream) {
1249
1368
  // no-op
@@ -1255,11 +1374,69 @@ describe('GeminiChat', () => {
1255
1374
  const modelTurn = history[1];
1256
1375
  expect(modelTurn.parts[0].text).toBe('Success on retry');
1257
1376
  });
1377
+ it('should switch to DEFAULT_GEMINI_FLASH_MODEL and use thinkingBudget when falling back from a gemini-3 model', async () => {
1378
+ // ARRANGE
1379
+ const authType = AuthType.LOGIN_WITH_GOOGLE;
1380
+ vi.mocked(mockConfig.getContentGeneratorConfig).mockReturnValue({
1381
+ authType,
1382
+ });
1383
+ // Initial state: Not in fallback mode
1384
+ const isInFallbackModeSpy = vi.spyOn(mockConfig, 'isInFallbackMode');
1385
+ isInFallbackModeSpy.mockReturnValue(false);
1386
+ // Mock API calls:
1387
+ // 1. Fails with 429 (simulating gemini-3 failure)
1388
+ // 2. Succeeds (simulating fallback success)
1389
+ vi.mocked(mockContentGenerator.generateContentStream)
1390
+ .mockRejectedValueOnce(error429)
1391
+ .mockResolvedValueOnce((async function* () {
1392
+ yield {
1393
+ candidates: [
1394
+ {
1395
+ content: { parts: [{ text: 'Fallback success' }] },
1396
+ finishReason: 'STOP',
1397
+ },
1398
+ ],
1399
+ };
1400
+ })());
1401
+ // Mock handleFallback to enable fallback mode and signal retry
1402
+ mockHandleFallback.mockImplementation(async () => {
1403
+ isInFallbackModeSpy.mockReturnValue(true); // Next call will see fallback mode = true
1404
+ return true;
1405
+ });
1406
+ // ACT
1407
+ const stream = await chat.sendMessageStream({ model: 'gemini-3-test-model' }, // Start with a gemini-3 model
1408
+ 'test fallback thinking', 'prompt-id-fb3', new AbortController().signal);
1409
+ for await (const _ of stream) {
1410
+ // consume stream
1411
+ }
1412
+ // ASSERT
1413
+ expect(mockContentGenerator.generateContentStream).toHaveBeenCalledTimes(2);
1414
+ // First call: gemini-3 model, thinkingLevel set
1415
+ expect(mockContentGenerator.generateContentStream).toHaveBeenNthCalledWith(1, expect.objectContaining({
1416
+ model: 'gemini-3-test-model',
1417
+ config: expect.objectContaining({
1418
+ thinkingConfig: {
1419
+ thinkingBudget: undefined,
1420
+ thinkingLevel: ThinkingLevel.HIGH,
1421
+ },
1422
+ }),
1423
+ }), 'prompt-id-fb3');
1424
+ // Second call: DEFAULT_GEMINI_FLASH_MODEL (due to fallback), thinkingBudget set (due to fix)
1425
+ expect(mockContentGenerator.generateContentStream).toHaveBeenNthCalledWith(2, expect.objectContaining({
1426
+ model: DEFAULT_GEMINI_FLASH_MODEL,
1427
+ config: expect.objectContaining({
1428
+ thinkingConfig: {
1429
+ thinkingBudget: DEFAULT_THINKING_MODE,
1430
+ thinkingLevel: undefined,
1431
+ },
1432
+ }),
1433
+ }), 'prompt-id-fb3');
1434
+ });
1258
1435
  it('should stop retrying if handleFallback returns false (e.g., auth intent)', async () => {
1259
1436
  vi.mocked(mockConfig.getModel).mockReturnValue('gemini-pro');
1260
1437
  vi.mocked(mockContentGenerator.generateContentStream).mockRejectedValue(error429);
1261
1438
  mockHandleFallback.mockResolvedValue(false);
1262
- const stream = await chat.sendMessageStream('test-model', { message: 'test stop' }, 'prompt-id-fb2');
1439
+ const stream = await chat.sendMessageStream({ model: 'gemini-2.0-flash' }, 'test stop', 'prompt-id-fb2', new AbortController().signal);
1263
1440
  await expect((async () => {
1264
1441
  for await (const _ of stream) {
1265
1442
  /* consume stream */
@@ -1303,7 +1480,7 @@ describe('GeminiChat', () => {
1303
1480
  };
1304
1481
  })());
1305
1482
  // Send a message and consume the stream
1306
- const stream = await chat.sendMessageStream('test-model', { message: 'test' }, 'prompt-id-discard-test');
1483
+ const stream = await chat.sendMessageStream({ model: 'gemini-2.0-flash' }, 'test message', 'prompt-id-discard-test', new AbortController().signal);
1307
1484
  const events = [];
1308
1485
  for await (const event of stream) {
1309
1486
  events.push(event);
@@ -1354,5 +1531,139 @@ describe('GeminiChat', () => {
1354
1531
  ]);
1355
1532
  });
1356
1533
  });
1534
+ describe('Preview Model Fallback Logic', () => {
1535
+ it('should reset previewModelBypassMode to false at the start of sendMessageStream', async () => {
1536
+ const stream = (async function* () {
1537
+ yield {
1538
+ candidates: [
1539
+ {
1540
+ content: { role: 'model', parts: [{ text: 'Success' }] },
1541
+ finishReason: 'STOP',
1542
+ },
1543
+ ],
1544
+ };
1545
+ })();
1546
+ vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue(stream);
1547
+ await chat.sendMessageStream({ model: 'test-model' }, 'test', 'prompt-id-preview-model-reset', new AbortController().signal);
1548
+ expect(mockConfig.setPreviewModelBypassMode).toHaveBeenCalledWith(false);
1549
+ });
1550
+ it('should reset previewModelFallbackMode to false upon successful Preview Model usage', async () => {
1551
+ const stream = (async function* () {
1552
+ yield {
1553
+ candidates: [
1554
+ {
1555
+ content: { role: 'model', parts: [{ text: 'Success' }] },
1556
+ finishReason: 'STOP',
1557
+ },
1558
+ ],
1559
+ };
1560
+ })();
1561
+ vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue(stream);
1562
+ const resultStream = await chat.sendMessageStream({ model: PREVIEW_GEMINI_MODEL }, 'test', 'prompt-id-preview-model-healing', new AbortController().signal);
1563
+ for await (const _ of resultStream) {
1564
+ // consume stream
1565
+ }
1566
+ expect(mockConfig.setPreviewModelFallbackMode).toHaveBeenCalledWith(false);
1567
+ });
1568
+ it('should NOT reset previewModelFallbackMode if Preview Model was bypassed (downgraded)', async () => {
1569
+ const stream = (async function* () {
1570
+ yield {
1571
+ candidates: [
1572
+ {
1573
+ content: { role: 'model', parts: [{ text: 'Success' }] },
1574
+ finishReason: 'STOP',
1575
+ },
1576
+ ],
1577
+ };
1578
+ })();
1579
+ vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue(stream);
1580
+ // Simulate bypass mode being active (downgrade happened)
1581
+ vi.mocked(mockConfig.isPreviewModelBypassMode).mockReturnValue(true);
1582
+ const resultStream = await chat.sendMessageStream({ model: PREVIEW_GEMINI_MODEL }, 'test', 'prompt-id-bypass-no-healing', new AbortController().signal);
1583
+ for await (const _ of resultStream) {
1584
+ // consume stream
1585
+ }
1586
+ expect(mockConfig.setPreviewModelFallbackMode).not.toHaveBeenCalled();
1587
+ });
1588
+ });
1589
+ describe('ensureActiveLoopHasThoughtSignatures', () => {
1590
+ it('should add thoughtSignature to the first functionCall in each model turn of the active loop', () => {
1591
+ const chat = new GeminiChat(mockConfig, '', [], []);
1592
+ const history = [
1593
+ { role: 'user', parts: [{ text: 'Old message' }] },
1594
+ {
1595
+ role: 'model',
1596
+ parts: [{ functionCall: { name: 'old_tool', args: {} } }],
1597
+ },
1598
+ { role: 'user', parts: [{ text: 'Find a restaurant' }] }, // active loop starts here
1599
+ {
1600
+ role: 'model',
1601
+ parts: [
1602
+ { functionCall: { name: 'find_restaurant', args: {} } }, // This one gets a signature
1603
+ { functionCall: { name: 'find_restaurant_2', args: {} } }, // This one does NOT
1604
+ ],
1605
+ },
1606
+ {
1607
+ role: 'user',
1608
+ parts: [
1609
+ { functionResponse: { name: 'find_restaurant', response: {} } },
1610
+ ],
1611
+ },
1612
+ {
1613
+ role: 'model',
1614
+ parts: [
1615
+ {
1616
+ functionCall: { name: 'tool_with_sig', args: {} },
1617
+ thoughtSignature: 'existing-sig',
1618
+ },
1619
+ { functionCall: { name: 'another_tool', args: {} } }, // This one does NOT get a signature
1620
+ ],
1621
+ },
1622
+ ];
1623
+ const newContents = chat.ensureActiveLoopHasThoughtSignatures(history);
1624
+ // Outside active loop - unchanged
1625
+ expect(newContents[1]?.parts?.[0]).not.toHaveProperty('thoughtSignature');
1626
+ // Inside active loop, first model turn
1627
+ // First function call gets a signature
1628
+ expect(newContents[3]?.parts?.[0]?.thoughtSignature).toBe(SYNTHETIC_THOUGHT_SIGNATURE);
1629
+ // Second function call does NOT
1630
+ expect(newContents[3]?.parts?.[1]).not.toHaveProperty('thoughtSignature');
1631
+ // User functionResponse part - unchanged (this is not a model turn)
1632
+ expect(newContents[4]?.parts?.[0]).not.toHaveProperty('thoughtSignature');
1633
+ // Inside active loop, second model turn
1634
+ // First function call already has a signature, so nothing changes
1635
+ expect(newContents[5]?.parts?.[0]?.thoughtSignature).toBe('existing-sig');
1636
+ // Second function call does NOT get a signature
1637
+ expect(newContents[5]?.parts?.[1]).not.toHaveProperty('thoughtSignature');
1638
+ });
1639
+ it('should not modify contents if there is no user text message', () => {
1640
+ const chat = new GeminiChat(mockConfig, '', [], []);
1641
+ const history = [
1642
+ {
1643
+ role: 'user',
1644
+ parts: [{ functionResponse: { name: 'tool1', response: {} } }],
1645
+ },
1646
+ {
1647
+ role: 'model',
1648
+ parts: [{ functionCall: { name: 'tool2', args: {} } }],
1649
+ },
1650
+ ];
1651
+ const newContents = chat.ensureActiveLoopHasThoughtSignatures(history);
1652
+ expect(newContents).toEqual(history);
1653
+ expect(newContents[1]?.parts?.[0]).not.toHaveProperty('thoughtSignature');
1654
+ });
1655
+ it('should handle an empty history', () => {
1656
+ const chat = new GeminiChat(mockConfig, '', []);
1657
+ const history = [];
1658
+ const newContents = chat.ensureActiveLoopHasThoughtSignatures(history);
1659
+ expect(newContents).toEqual([]);
1660
+ });
1661
+ it('should handle history with only a user message', () => {
1662
+ const chat = new GeminiChat(mockConfig, '', []);
1663
+ const history = [{ role: 'user', parts: [{ text: 'Hello' }] }];
1664
+ const newContents = chat.ensureActiveLoopHasThoughtSignatures(history);
1665
+ expect(newContents).toEqual(history);
1666
+ });
1667
+ });
1357
1668
  });
1358
1669
  //# sourceMappingURL=geminiChat.test.js.map