@machina.ai/cell-cli-core 1.20.2-rc1 → 1.22.5-rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (436) hide show
  1. package/dist/index.d.ts +1 -1
  2. package/dist/index.js +1 -1
  3. package/dist/index.js.map +1 -1
  4. package/dist/package.json +1 -1
  5. package/dist/src/agents/delegate-to-agent-tool.d.ts +19 -0
  6. package/dist/src/agents/delegate-to-agent-tool.js +111 -0
  7. package/dist/src/agents/delegate-to-agent-tool.js.map +1 -0
  8. package/dist/src/agents/delegate-to-agent-tool.test.d.ts +6 -0
  9. package/dist/src/agents/delegate-to-agent-tool.test.js +133 -0
  10. package/dist/src/agents/delegate-to-agent-tool.test.js.map +1 -0
  11. package/dist/src/agents/executor.js +1 -1
  12. package/dist/src/agents/executor.js.map +1 -1
  13. package/dist/src/agents/executor.test.js.map +1 -1
  14. package/dist/src/agents/registry.d.ts +15 -0
  15. package/dist/src/agents/registry.js +58 -2
  16. package/dist/src/agents/registry.js.map +1 -1
  17. package/dist/src/agents/registry.test.js +61 -0
  18. package/dist/src/agents/registry.test.js.map +1 -1
  19. package/dist/src/availability/errorClassification.d.ts +7 -0
  20. package/dist/src/availability/errorClassification.js +20 -0
  21. package/dist/src/availability/errorClassification.js.map +1 -0
  22. package/dist/src/availability/modelAvailabilityService.d.ts +1 -0
  23. package/dist/src/availability/modelAvailabilityService.js +3 -0
  24. package/dist/src/availability/modelAvailabilityService.js.map +1 -1
  25. package/dist/src/availability/modelPolicy.d.ts +8 -1
  26. package/dist/src/availability/policyCatalog.d.ts +1 -0
  27. package/dist/src/availability/policyCatalog.js +6 -7
  28. package/dist/src/availability/policyCatalog.js.map +1 -1
  29. package/dist/src/availability/policyCatalog.test.js +2 -2
  30. package/dist/src/availability/policyCatalog.test.js.map +1 -1
  31. package/dist/src/availability/policyHelpers.d.ts +33 -3
  32. package/dist/src/availability/policyHelpers.js +113 -13
  33. package/dist/src/availability/policyHelpers.js.map +1 -1
  34. package/dist/src/availability/policyHelpers.test.js +133 -13
  35. package/dist/src/availability/policyHelpers.test.js.map +1 -1
  36. package/dist/src/availability/testUtils.d.ts +10 -0
  37. package/dist/src/availability/testUtils.js +22 -0
  38. package/dist/src/availability/testUtils.js.map +1 -0
  39. package/dist/src/code_assist/experiments/client_metadata.js +2 -1
  40. package/dist/src/code_assist/experiments/client_metadata.js.map +1 -1
  41. package/dist/src/code_assist/experiments/client_metadata.test.js +7 -10
  42. package/dist/src/code_assist/experiments/client_metadata.test.js.map +1 -1
  43. package/dist/src/code_assist/oauth2.d.ts +2 -0
  44. package/dist/src/code_assist/oauth2.js +38 -12
  45. package/dist/src/code_assist/oauth2.js.map +1 -1
  46. package/dist/src/code_assist/oauth2.test.js +113 -6
  47. package/dist/src/code_assist/oauth2.test.js.map +1 -1
  48. package/dist/src/commands/init.d.ts +7 -0
  49. package/dist/src/commands/init.js +53 -0
  50. package/dist/src/commands/init.js.map +1 -0
  51. package/dist/src/commands/init.test.d.ts +6 -0
  52. package/dist/src/commands/init.test.js +25 -0
  53. package/dist/src/commands/init.test.js.map +1 -0
  54. package/dist/src/commands/restore.d.ts +9 -0
  55. package/dist/src/commands/restore.js +46 -0
  56. package/dist/src/commands/restore.js.map +1 -0
  57. package/dist/src/commands/restore.test.d.ts +6 -0
  58. package/dist/src/commands/restore.test.js +137 -0
  59. package/dist/src/commands/restore.test.js.map +1 -0
  60. package/dist/src/commands/types.d.ts +41 -0
  61. package/dist/src/commands/types.js +7 -0
  62. package/dist/src/commands/types.js.map +1 -0
  63. package/dist/src/config/config.d.ts +29 -3
  64. package/dist/src/config/config.js +146 -29
  65. package/dist/src/config/config.js.map +1 -1
  66. package/dist/src/config/config.test.js +203 -9
  67. package/dist/src/config/config.test.js.map +1 -1
  68. package/dist/src/config/defaultModelConfigs.js +21 -0
  69. package/dist/src/config/defaultModelConfigs.js.map +1 -1
  70. package/dist/src/config/models.d.ts +33 -11
  71. package/dist/src/config/models.js +82 -24
  72. package/dist/src/config/models.js.map +1 -1
  73. package/dist/src/config/models.test.js +70 -76
  74. package/dist/src/config/models.test.js.map +1 -1
  75. package/dist/src/confirmation-bus/message-bus.js +1 -0
  76. package/dist/src/confirmation-bus/message-bus.js.map +1 -1
  77. package/dist/src/confirmation-bus/types.d.ts +4 -0
  78. package/dist/src/core/baseLlmClient.d.ts +3 -1
  79. package/dist/src/core/baseLlmClient.js +40 -3
  80. package/dist/src/core/baseLlmClient.js.map +1 -1
  81. package/dist/src/core/baseLlmClient.test.js +184 -7
  82. package/dist/src/core/baseLlmClient.test.js.map +1 -1
  83. package/dist/src/core/client.js +47 -13
  84. package/dist/src/core/client.js.map +1 -1
  85. package/dist/src/core/client.test.js +133 -6
  86. package/dist/src/core/client.test.js.map +1 -1
  87. package/dist/src/core/contentGenerator.js +5 -3
  88. package/dist/src/core/contentGenerator.js.map +1 -1
  89. package/dist/src/core/contentGenerator.test.js +29 -22
  90. package/dist/src/core/contentGenerator.test.js.map +1 -1
  91. package/dist/src/core/coreToolScheduler.d.ts +1 -1
  92. package/dist/src/core/coreToolScheduler.js +76 -34
  93. package/dist/src/core/coreToolScheduler.js.map +1 -1
  94. package/dist/src/core/coreToolScheduler.test.js +135 -37
  95. package/dist/src/core/coreToolScheduler.test.js.map +1 -1
  96. package/dist/src/core/geminiChat.js +60 -29
  97. package/dist/src/core/geminiChat.js.map +1 -1
  98. package/dist/src/core/geminiChat.test.js +236 -188
  99. package/dist/src/core/geminiChat.test.js.map +1 -1
  100. package/dist/src/core/geminiChat_network_retry.test.d.ts +6 -0
  101. package/dist/src/core/geminiChat_network_retry.test.js +198 -0
  102. package/dist/src/core/geminiChat_network_retry.test.js.map +1 -0
  103. package/dist/src/core/logger.js.map +1 -1
  104. package/dist/src/core/nonInteractiveToolExecutor.test.js +4 -5
  105. package/dist/src/core/nonInteractiveToolExecutor.test.js.map +1 -1
  106. package/dist/src/core/prompts.js +13 -11
  107. package/dist/src/core/prompts.js.map +1 -1
  108. package/dist/src/core/prompts.test.js +19 -8
  109. package/dist/src/core/prompts.test.js.map +1 -1
  110. package/dist/src/core/sessionHookTriggers.d.ts +28 -0
  111. package/dist/src/core/sessionHookTriggers.js +68 -0
  112. package/dist/src/core/sessionHookTriggers.js.map +1 -0
  113. package/dist/src/core/turn.d.ts +1 -0
  114. package/dist/src/core/turn.js +1 -1
  115. package/dist/src/core/turn.js.map +1 -1
  116. package/dist/src/fallback/handler.js +56 -115
  117. package/dist/src/fallback/handler.js.map +1 -1
  118. package/dist/src/fallback/handler.test.js +124 -278
  119. package/dist/src/fallback/handler.test.js.map +1 -1
  120. package/dist/src/generated/git-commit.d.ts +2 -2
  121. package/dist/src/generated/git-commit.js +2 -2
  122. package/dist/src/hooks/hookEventHandler.js +59 -1
  123. package/dist/src/hooks/hookEventHandler.js.map +1 -1
  124. package/dist/src/hooks/hookEventHandler.test.js +8 -1
  125. package/dist/src/hooks/hookEventHandler.test.js.map +1 -1
  126. package/dist/src/hooks/hookRegistry.d.ts +0 -7
  127. package/dist/src/hooks/hookRegistry.js +8 -21
  128. package/dist/src/hooks/hookRegistry.js.map +1 -1
  129. package/dist/src/hooks/hookRegistry.test.js +2 -7
  130. package/dist/src/hooks/hookRegistry.test.js.map +1 -1
  131. package/dist/src/hooks/hookRunner.js +12 -2
  132. package/dist/src/hooks/hookRunner.js.map +1 -1
  133. package/dist/src/hooks/hookRunner.test.js +1 -1
  134. package/dist/src/hooks/hookRunner.test.js.map +1 -1
  135. package/dist/src/hooks/hookSystem.d.ts +0 -8
  136. package/dist/src/hooks/hookSystem.js +0 -18
  137. package/dist/src/hooks/hookSystem.js.map +1 -1
  138. package/dist/src/hooks/hookSystem.test.js +123 -18
  139. package/dist/src/hooks/hookSystem.test.js.map +1 -1
  140. package/dist/src/hooks/index.d.ts +3 -1
  141. package/dist/src/hooks/index.js +3 -0
  142. package/dist/src/hooks/index.js.map +1 -1
  143. package/dist/src/hooks/types.d.ts +1 -2
  144. package/dist/src/hooks/types.js +0 -1
  145. package/dist/src/hooks/types.js.map +1 -1
  146. package/dist/src/ide/detect-ide.test.js +32 -1
  147. package/dist/src/ide/detect-ide.test.js.map +1 -1
  148. package/dist/src/ide/ide-client.js +2 -0
  149. package/dist/src/ide/ide-client.js.map +1 -1
  150. package/dist/src/ide/ide-installer.test.js +1 -1
  151. package/dist/src/ide/ide-installer.test.js.map +1 -1
  152. package/dist/src/index.d.ts +8 -0
  153. package/dist/src/index.js +8 -0
  154. package/dist/src/index.js.map +1 -1
  155. package/dist/src/mcp/oauth-provider.js.map +1 -1
  156. package/dist/src/output/json-formatter.d.ts +2 -2
  157. package/dist/src/output/json-formatter.js +6 -3
  158. package/dist/src/output/json-formatter.js.map +1 -1
  159. package/dist/src/output/json-formatter.test.js +37 -9
  160. package/dist/src/output/json-formatter.test.js.map +1 -1
  161. package/dist/src/output/stream-json-formatter.js +6 -0
  162. package/dist/src/output/stream-json-formatter.js.map +1 -1
  163. package/dist/src/output/stream-json-formatter.test.js +98 -100
  164. package/dist/src/output/stream-json-formatter.test.js.map +1 -1
  165. package/dist/src/output/types.d.ts +3 -0
  166. package/dist/src/output/types.js.map +1 -1
  167. package/dist/src/policy/config.js +97 -11
  168. package/dist/src/policy/config.js.map +1 -1
  169. package/dist/src/policy/persistence.test.d.ts +6 -0
  170. package/dist/src/policy/persistence.test.js +154 -0
  171. package/dist/src/policy/persistence.test.js.map +1 -0
  172. package/dist/src/policy/policies/agent.toml +31 -0
  173. package/dist/src/policy/policy-engine.d.ts +10 -1
  174. package/dist/src/policy/policy-engine.js +79 -5
  175. package/dist/src/policy/policy-engine.js.map +1 -1
  176. package/dist/src/policy/policy-engine.test.js +26 -2
  177. package/dist/src/policy/policy-engine.test.js.map +1 -1
  178. package/dist/src/policy/policy-updater.test.d.ts +6 -0
  179. package/dist/src/policy/policy-updater.test.js +116 -0
  180. package/dist/src/policy/policy-updater.test.js.map +1 -0
  181. package/dist/src/policy/shell-safety.test.d.ts +6 -0
  182. package/dist/src/policy/shell-safety.test.js +75 -0
  183. package/dist/src/policy/shell-safety.test.js.map +1 -0
  184. package/dist/src/policy/toml-loader.d.ts +11 -5
  185. package/dist/src/policy/toml-loader.js +38 -23
  186. package/dist/src/policy/toml-loader.js.map +1 -1
  187. package/dist/src/policy/toml-loader.test.js +28 -7
  188. package/dist/src/policy/toml-loader.test.js.map +1 -1
  189. package/dist/src/policy/types.d.ts +15 -0
  190. package/dist/src/resources/resource-registry.d.ts +30 -0
  191. package/dist/src/resources/resource-registry.js +57 -0
  192. package/dist/src/resources/resource-registry.js.map +1 -0
  193. package/dist/src/resources/resource-registry.test.d.ts +6 -0
  194. package/dist/src/resources/resource-registry.test.js +54 -0
  195. package/dist/src/resources/resource-registry.test.js.map +1 -0
  196. package/dist/src/routing/modelRouterService.js +0 -15
  197. package/dist/src/routing/modelRouterService.js.map +1 -1
  198. package/dist/src/routing/modelRouterService.test.js +0 -62
  199. package/dist/src/routing/modelRouterService.test.js.map +1 -1
  200. package/dist/src/routing/strategies/classifierStrategy.js +10 -21
  201. package/dist/src/routing/strategies/classifierStrategy.js.map +1 -1
  202. package/dist/src/routing/strategies/classifierStrategy.test.js +2 -1
  203. package/dist/src/routing/strategies/classifierStrategy.test.js.map +1 -1
  204. package/dist/src/routing/strategies/fallbackStrategy.js +23 -12
  205. package/dist/src/routing/strategies/fallbackStrategy.js.map +1 -1
  206. package/dist/src/routing/strategies/fallbackStrategy.test.js +69 -39
  207. package/dist/src/routing/strategies/fallbackStrategy.test.js.map +1 -1
  208. package/dist/src/routing/strategies/overrideStrategy.js +4 -3
  209. package/dist/src/routing/strategies/overrideStrategy.js.map +1 -1
  210. package/dist/src/safety/checker-runner.js +17 -6
  211. package/dist/src/safety/checker-runner.js.map +1 -1
  212. package/dist/src/services/chatCompressionService.js +15 -1
  213. package/dist/src/services/chatCompressionService.js.map +1 -1
  214. package/dist/src/services/chatCompressionService.test.js +2 -0
  215. package/dist/src/services/chatCompressionService.test.js.map +1 -1
  216. package/dist/src/services/chatRecordingService.d.ts +14 -0
  217. package/dist/src/services/chatRecordingService.js +37 -0
  218. package/dist/src/services/chatRecordingService.js.map +1 -1
  219. package/dist/src/services/fileSystemService.d.ts +0 -9
  220. package/dist/src/services/fileSystemService.js +0 -11
  221. package/dist/src/services/fileSystemService.js.map +1 -1
  222. package/dist/src/services/gitService.js +5 -0
  223. package/dist/src/services/gitService.js.map +1 -1
  224. package/dist/src/services/gitService.test.js +28 -0
  225. package/dist/src/services/gitService.test.js.map +1 -1
  226. package/dist/src/services/loopDetectionService.js +2 -2
  227. package/dist/src/services/loopDetectionService.js.map +1 -1
  228. package/dist/src/services/modelConfig.golden.test.js +32 -0
  229. package/dist/src/services/modelConfig.golden.test.js.map +1 -1
  230. package/dist/src/services/modelConfigService.d.ts +3 -0
  231. package/dist/src/services/modelConfigService.js +3 -2
  232. package/dist/src/services/modelConfigService.js.map +1 -1
  233. package/dist/src/services/modelConfigService.test.js +110 -0
  234. package/dist/src/services/modelConfigService.test.js.map +1 -1
  235. package/dist/src/services/modelConfigServiceTestUtils.d.ts +10 -0
  236. package/dist/src/services/modelConfigServiceTestUtils.js +17 -0
  237. package/dist/src/services/modelConfigServiceTestUtils.js.map +1 -0
  238. package/dist/src/services/sessionSummaryService.d.ts +28 -0
  239. package/dist/src/services/sessionSummaryService.js +131 -0
  240. package/dist/src/services/sessionSummaryService.js.map +1 -0
  241. package/dist/src/services/sessionSummaryService.test.d.ts +6 -0
  242. package/dist/src/services/sessionSummaryService.test.js +785 -0
  243. package/dist/src/services/sessionSummaryService.test.js.map +1 -0
  244. package/dist/src/services/sessionSummaryUtils.d.ts +16 -0
  245. package/dist/src/services/sessionSummaryUtils.js +129 -0
  246. package/dist/src/services/sessionSummaryUtils.js.map +1 -0
  247. package/dist/src/services/sessionSummaryUtils.test.d.ts +6 -0
  248. package/dist/src/services/sessionSummaryUtils.test.js +137 -0
  249. package/dist/src/services/sessionSummaryUtils.test.js.map +1 -0
  250. package/dist/src/services/shellExecutionService.js +28 -22
  251. package/dist/src/services/shellExecutionService.js.map +1 -1
  252. package/dist/src/services/shellExecutionService.test.js +46 -4
  253. package/dist/src/services/shellExecutionService.test.js.map +1 -1
  254. package/dist/src/services/test-data/resolved-aliases-retry.golden.json +238 -0
  255. package/dist/src/services/test-data/resolved-aliases.golden.json +16 -0
  256. package/dist/src/telemetry/activity-detector.test.js.map +1 -1
  257. package/dist/src/telemetry/clearcut-logger/clearcut-logger.d.ts +1 -0
  258. package/dist/src/telemetry/clearcut-logger/clearcut-logger.js +28 -5
  259. package/dist/src/telemetry/clearcut-logger/clearcut-logger.js.map +1 -1
  260. package/dist/src/telemetry/clearcut-logger/clearcut-logger.test.js +67 -1
  261. package/dist/src/telemetry/clearcut-logger/clearcut-logger.test.js.map +1 -1
  262. package/dist/src/telemetry/clearcut-logger/event-metadata-key.d.ts +1 -0
  263. package/dist/src/telemetry/clearcut-logger/event-metadata-key.js +3 -1
  264. package/dist/src/telemetry/clearcut-logger/event-metadata-key.js.map +1 -1
  265. package/dist/src/telemetry/config.js +2 -0
  266. package/dist/src/telemetry/config.js.map +1 -1
  267. package/dist/src/telemetry/config.test.js +25 -0
  268. package/dist/src/telemetry/config.test.js.map +1 -1
  269. package/dist/src/telemetry/gcp-exporters.d.ts +4 -3
  270. package/dist/src/telemetry/gcp-exporters.js +8 -4
  271. package/dist/src/telemetry/gcp-exporters.js.map +1 -1
  272. package/dist/src/telemetry/index.d.ts +1 -1
  273. package/dist/src/telemetry/index.js +1 -1
  274. package/dist/src/telemetry/index.js.map +1 -1
  275. package/dist/src/telemetry/loggers.d.ts +2 -1
  276. package/dist/src/telemetry/loggers.js +345 -335
  277. package/dist/src/telemetry/loggers.js.map +1 -1
  278. package/dist/src/telemetry/loggers.test.js +20 -5
  279. package/dist/src/telemetry/loggers.test.js.map +1 -1
  280. package/dist/src/telemetry/metrics.test.js.map +1 -1
  281. package/dist/src/telemetry/sdk.d.ts +9 -2
  282. package/dist/src/telemetry/sdk.js +143 -17
  283. package/dist/src/telemetry/sdk.js.map +1 -1
  284. package/dist/src/telemetry/sdk.test.js +130 -28
  285. package/dist/src/telemetry/sdk.test.js.map +1 -1
  286. package/dist/src/telemetry/startupProfiler.js +26 -3
  287. package/dist/src/telemetry/startupProfiler.js.map +1 -1
  288. package/dist/src/telemetry/startupProfiler.test.js +49 -7
  289. package/dist/src/telemetry/startupProfiler.test.js.map +1 -1
  290. package/dist/src/telemetry/telemetry.test.js +10 -3
  291. package/dist/src/telemetry/telemetry.test.js.map +1 -1
  292. package/dist/src/telemetry/trace.js.map +1 -1
  293. package/dist/src/telemetry/types.d.ts +31 -6
  294. package/dist/src/telemetry/types.js +47 -8
  295. package/dist/src/telemetry/types.js.map +1 -1
  296. package/dist/src/telemetry/uiTelemetry.d.ts +1 -0
  297. package/dist/src/telemetry/uiTelemetry.js +2 -0
  298. package/dist/src/telemetry/uiTelemetry.js.map +1 -1
  299. package/dist/src/telemetry/uiTelemetry.test.js +4 -0
  300. package/dist/src/telemetry/uiTelemetry.test.js.map +1 -1
  301. package/dist/src/test-utils/mock-message-bus.js.map +1 -1
  302. package/dist/src/tools/confirmation-policy.test.d.ts +6 -0
  303. package/dist/src/tools/confirmation-policy.test.js +152 -0
  304. package/dist/src/tools/confirmation-policy.test.js.map +1 -0
  305. package/dist/src/tools/edit.js +5 -0
  306. package/dist/src/tools/edit.js.map +1 -1
  307. package/dist/src/tools/edit.test.js.map +1 -1
  308. package/dist/src/tools/grep.js.map +1 -1
  309. package/dist/src/tools/mcp-client-manager.d.ts +2 -1
  310. package/dist/src/tools/mcp-client-manager.js +20 -4
  311. package/dist/src/tools/mcp-client-manager.js.map +1 -1
  312. package/dist/src/tools/mcp-client-manager.test.js +13 -10
  313. package/dist/src/tools/mcp-client-manager.test.js.map +1 -1
  314. package/dist/src/tools/mcp-client.d.ts +39 -3
  315. package/dist/src/tools/mcp-client.js +433 -168
  316. package/dist/src/tools/mcp-client.js.map +1 -1
  317. package/dist/src/tools/mcp-client.test.js +648 -28
  318. package/dist/src/tools/mcp-client.test.js.map +1 -1
  319. package/dist/src/tools/mcp-tool.js +13 -0
  320. package/dist/src/tools/mcp-tool.js.map +1 -1
  321. package/dist/src/tools/mcp-tool.test.js +25 -0
  322. package/dist/src/tools/mcp-tool.test.js.map +1 -1
  323. package/dist/src/tools/memoryTool.js +1 -0
  324. package/dist/src/tools/memoryTool.js.map +1 -1
  325. package/dist/src/tools/modifiable-tool.js.map +1 -1
  326. package/dist/src/tools/modifiable-tool.test.js +22 -13
  327. package/dist/src/tools/modifiable-tool.test.js.map +1 -1
  328. package/dist/src/tools/read-file.js +1 -1
  329. package/dist/src/tools/read-file.js.map +1 -1
  330. package/dist/src/tools/read-file.test.js.map +1 -1
  331. package/dist/src/tools/read-many-files.js +6 -4
  332. package/dist/src/tools/read-many-files.js.map +1 -1
  333. package/dist/src/tools/read-many-files.test.js +1 -1
  334. package/dist/src/tools/read-many-files.test.js.map +1 -1
  335. package/dist/src/tools/shell.d.ts +2 -1
  336. package/dist/src/tools/shell.js +15 -1
  337. package/dist/src/tools/shell.js.map +1 -1
  338. package/dist/src/tools/shell.test.js +2 -1
  339. package/dist/src/tools/shell.test.js.map +1 -1
  340. package/dist/src/tools/smart-edit.js +5 -0
  341. package/dist/src/tools/smart-edit.js.map +1 -1
  342. package/dist/src/tools/smart-edit.test.js.map +1 -1
  343. package/dist/src/tools/tool-names.d.ts +2 -0
  344. package/dist/src/tools/tool-names.js +2 -0
  345. package/dist/src/tools/tool-names.js.map +1 -1
  346. package/dist/src/tools/tools.d.ts +19 -0
  347. package/dist/src/tools/tools.js +29 -8
  348. package/dist/src/tools/tools.js.map +1 -1
  349. package/dist/src/tools/web-fetch.js +17 -4
  350. package/dist/src/tools/web-fetch.js.map +1 -1
  351. package/dist/src/tools/web-fetch.test.js +1 -0
  352. package/dist/src/tools/web-fetch.test.js.map +1 -1
  353. package/dist/src/tools/write-file.js +5 -0
  354. package/dist/src/tools/write-file.js.map +1 -1
  355. package/dist/src/tools/write-file.test.js.map +1 -1
  356. package/dist/src/utils/bfsFileSearch.d.ts +8 -0
  357. package/dist/src/utils/bfsFileSearch.js +63 -23
  358. package/dist/src/utils/bfsFileSearch.js.map +1 -1
  359. package/dist/src/utils/bfsFileSearch.test.js +65 -1
  360. package/dist/src/utils/bfsFileSearch.test.js.map +1 -1
  361. package/dist/src/utils/checkpointUtils.d.ts +82 -0
  362. package/dist/src/utils/checkpointUtils.js +117 -0
  363. package/dist/src/utils/checkpointUtils.js.map +1 -0
  364. package/dist/src/utils/checkpointUtils.test.d.ts +6 -0
  365. package/dist/src/utils/checkpointUtils.test.js +229 -0
  366. package/dist/src/utils/checkpointUtils.test.js.map +1 -0
  367. package/dist/src/utils/debugLogger.d.ts +3 -0
  368. package/dist/src/utils/debugLogger.js +27 -0
  369. package/dist/src/utils/debugLogger.js.map +1 -1
  370. package/dist/src/utils/editCorrector.test.js +4 -0
  371. package/dist/src/utils/editCorrector.test.js.map +1 -1
  372. package/dist/src/utils/editor.d.ts +9 -1
  373. package/dist/src/utils/editor.js +23 -14
  374. package/dist/src/utils/editor.js.map +1 -1
  375. package/dist/src/utils/errors.d.ts +8 -0
  376. package/dist/src/utils/errors.js +39 -2
  377. package/dist/src/utils/errors.js.map +1 -1
  378. package/dist/src/utils/errors.test.d.ts +6 -0
  379. package/dist/src/utils/errors.test.js +155 -0
  380. package/dist/src/utils/errors.test.js.map +1 -0
  381. package/dist/src/utils/extensionLoader.d.ts +2 -2
  382. package/dist/src/utils/extensionLoader.js +5 -6
  383. package/dist/src/utils/extensionLoader.js.map +1 -1
  384. package/dist/src/utils/extensionLoader.test.js +11 -0
  385. package/dist/src/utils/extensionLoader.test.js.map +1 -1
  386. package/dist/src/utils/fetch.d.ts +1 -1
  387. package/dist/src/utils/fetch.js +3 -3
  388. package/dist/src/utils/fetch.js.map +1 -1
  389. package/dist/src/utils/fileUtils.test.js +15 -0
  390. package/dist/src/utils/fileUtils.test.js.map +1 -1
  391. package/dist/src/utils/filesearch/crawlCache.js.map +1 -1
  392. package/dist/src/utils/filesearch/fileSearch.js.map +1 -1
  393. package/dist/src/utils/flashFallback.test.js +1 -1
  394. package/dist/src/utils/flashFallback.test.js.map +1 -1
  395. package/dist/src/utils/googleErrors.js +31 -18
  396. package/dist/src/utils/googleErrors.js.map +1 -1
  397. package/dist/src/utils/googleErrors.test.js +10 -2
  398. package/dist/src/utils/googleErrors.test.js.map +1 -1
  399. package/dist/src/utils/googleQuotaErrors.d.ts +3 -3
  400. package/dist/src/utils/googleQuotaErrors.js +32 -6
  401. package/dist/src/utils/googleQuotaErrors.js.map +1 -1
  402. package/dist/src/utils/googleQuotaErrors.test.js +94 -2
  403. package/dist/src/utils/googleQuotaErrors.test.js.map +1 -1
  404. package/dist/src/utils/nextSpeakerChecker.test.js +4 -0
  405. package/dist/src/utils/nextSpeakerChecker.test.js.map +1 -1
  406. package/dist/src/utils/pathCorrector.js +12 -2
  407. package/dist/src/utils/pathCorrector.js.map +1 -1
  408. package/dist/src/utils/pathCorrector.test.js +6 -2
  409. package/dist/src/utils/pathCorrector.test.js.map +1 -1
  410. package/dist/src/utils/retry.d.ts +11 -0
  411. package/dist/src/utils/retry.js +54 -13
  412. package/dist/src/utils/retry.js.map +1 -1
  413. package/dist/src/utils/retry.test.js +170 -10
  414. package/dist/src/utils/retry.test.js.map +1 -1
  415. package/dist/src/utils/shell-permissions.d.ts +52 -0
  416. package/dist/src/utils/shell-permissions.js +188 -0
  417. package/dist/src/utils/shell-permissions.js.map +1 -0
  418. package/dist/src/utils/shell-permissions.test.d.ts +6 -0
  419. package/dist/src/utils/shell-permissions.test.js +347 -0
  420. package/dist/src/utils/shell-permissions.test.js.map +1 -0
  421. package/dist/src/utils/shell-utils.d.ts +10 -47
  422. package/dist/src/utils/shell-utils.js +1 -182
  423. package/dist/src/utils/shell-utils.js.map +1 -1
  424. package/dist/src/utils/shell-utils.test.js +1 -288
  425. package/dist/src/utils/shell-utils.test.js.map +1 -1
  426. package/dist/src/utils/terminalSerializer.test.js +17 -0
  427. package/dist/src/utils/terminalSerializer.test.js.map +1 -1
  428. package/dist/src/utils/tool-utils.js.map +1 -1
  429. package/dist/src/utils/version.d.ts +6 -0
  430. package/dist/src/utils/version.js +15 -0
  431. package/dist/src/utils/version.js.map +1 -0
  432. package/dist/src/utils/version.test.d.ts +6 -0
  433. package/dist/src/utils/version.test.js +39 -0
  434. package/dist/src/utils/version.test.js.map +1 -0
  435. package/dist/tsconfig.tsbuildinfo +1 -1
  436. package/package.json +1 -1
@@ -7,13 +7,16 @@ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
7
7
  import { ApiError, ThinkingLevel } from '@google/genai';
8
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, DEFAULT_GEMINI_MODEL, DEFAULT_THINKING_MODE, PREVIEW_GEMINI_MODEL, } from '../config/models.js';
10
+ import { DEFAULT_GEMINI_FLASH_MODEL, DEFAULT_THINKING_MODE, PREVIEW_GEMINI_MODEL, PREVIEW_GEMINI_FLASH_MODEL, } from '../config/models.js';
11
11
  import { AuthType } from './contentGenerator.js';
12
12
  import { TerminalQuotaError } from '../utils/googleQuotaErrors.js';
13
- import { retryWithBackoff } from '../utils/retry.js';
13
+ import {} from '../utils/retry.js';
14
14
  import { uiTelemetryService } from '../telemetry/uiTelemetry.js';
15
15
  import { HookSystem } from '../hooks/hookSystem.js';
16
16
  import { createMockMessageBus } from '../test-utils/mock-message-bus.js';
17
+ import { createAvailabilityServiceMock } from '../availability/testUtils.js';
18
+ import * as policyHelpers from '../availability/policyHelpers.js';
19
+ import { makeResolvedModelConfig } from '../services/modelConfigServiceTestUtils.js';
17
20
  // Mock fs module to prevent actual file system operations during tests
18
21
  const mockFileSystem = new Map();
19
22
  vi.mock('node:fs', () => {
@@ -31,6 +34,10 @@ vi.mock('node:fs', () => {
31
34
  });
32
35
  }),
33
36
  existsSync: vi.fn((path) => mockFileSystem.has(path)),
37
+ createWriteStream: vi.fn(() => ({
38
+ write: vi.fn(),
39
+ on: vi.fn(),
40
+ })),
34
41
  };
35
42
  return {
36
43
  default: fsModule,
@@ -44,9 +51,13 @@ const { mockHandleFallback } = vi.hoisted(() => ({
44
51
  const { mockRetryWithBackoff } = vi.hoisted(() => ({
45
52
  mockRetryWithBackoff: vi.fn(),
46
53
  }));
47
- vi.mock('../utils/retry.js', () => ({
48
- retryWithBackoff: mockRetryWithBackoff,
49
- }));
54
+ vi.mock('../utils/retry.js', async (importOriginal) => {
55
+ const actual = await importOriginal();
56
+ return {
57
+ ...actual,
58
+ retryWithBackoff: mockRetryWithBackoff,
59
+ };
60
+ });
50
61
  vi.mock('../fallback/handler.js', () => ({
51
62
  handleFallback: mockHandleFallback,
52
63
  }));
@@ -79,19 +90,32 @@ describe('GeminiChat', () => {
79
90
  };
80
91
  mockHandleFallback.mockClear();
81
92
  // Default mock implementation for tests that don't care about retry logic
82
- mockRetryWithBackoff.mockImplementation(async (apiCall) => apiCall());
93
+ mockRetryWithBackoff.mockImplementation(async (apiCall, options) => {
94
+ const result = await apiCall();
95
+ const context = options?.getAvailabilityContext?.();
96
+ if (context) {
97
+ context.service.markHealthy(context.policy.model);
98
+ }
99
+ return result;
100
+ });
101
+ let currentModel = 'gemini-pro';
102
+ let currentActiveModel = 'gemini-pro';
83
103
  mockConfig = {
84
104
  getSessionId: () => 'test-session-id',
85
105
  getTelemetryLogPromptsEnabled: () => true,
86
106
  getUsageStatisticsEnabled: () => true,
87
107
  getDebugMode: () => false,
88
108
  getPreviewFeatures: () => false,
89
- getContentGeneratorConfig: vi.fn().mockReturnValue({
90
- authType: 'oauth-personal', // Ensure this is set for fallback tests
91
- model: 'test-model',
109
+ getContentGeneratorConfig: vi.fn().mockImplementation(() => ({
110
+ authType: 'oauth-personal',
111
+ model: currentModel,
112
+ })),
113
+ getModel: vi.fn().mockImplementation(() => currentModel),
114
+ setModel: vi.fn().mockImplementation((m) => {
115
+ currentModel = m;
116
+ // When model is explicitly set, active model usually resets or updates to it
117
+ currentActiveModel = m;
92
118
  }),
93
- getModel: vi.fn().mockReturnValue('gemini-pro'),
94
- setModel: vi.fn(),
95
119
  isInFallbackMode: vi.fn().mockReturnValue(false),
96
120
  getQuotaErrorOccurred: vi.fn().mockReturnValue(false),
97
121
  setQuotaErrorOccurred: vi.fn(),
@@ -105,9 +129,11 @@ describe('GeminiChat', () => {
105
129
  }),
106
130
  getContentGenerator: vi.fn().mockReturnValue(mockContentGenerator),
107
131
  getRetryFetchErrors: vi.fn().mockReturnValue(false),
132
+ getUserTier: vi.fn().mockReturnValue(undefined),
108
133
  modelConfigService: {
109
134
  getResolvedConfig: vi.fn().mockImplementation((modelConfigKey) => {
110
- const thinkingConfig = modelConfigKey.model.startsWith('gemini-3')
135
+ const model = modelConfigKey.model ?? mockConfig.getModel();
136
+ const thinkingConfig = model.startsWith('gemini-3')
111
137
  ? {
112
138
  thinkingLevel: ThinkingLevel.HIGH,
113
139
  }
@@ -115,7 +141,7 @@ describe('GeminiChat', () => {
115
141
  thinkingBudget: DEFAULT_THINKING_MODE,
116
142
  };
117
143
  return {
118
- model: modelConfigKey.model,
144
+ model,
119
145
  generateContentConfig: {
120
146
  temperature: 0,
121
147
  thinkingConfig,
@@ -129,6 +155,12 @@ describe('GeminiChat', () => {
129
155
  setPreviewModelFallbackMode: vi.fn(),
130
156
  isInteractive: vi.fn().mockReturnValue(false),
131
157
  getEnableHooks: vi.fn().mockReturnValue(false),
158
+ isModelAvailabilityServiceEnabled: vi.fn().mockReturnValue(false),
159
+ getActiveModel: vi.fn().mockImplementation(() => currentActiveModel),
160
+ setActiveModel: vi
161
+ .fn()
162
+ .mockImplementation((m) => (currentActiveModel = m)),
163
+ getModelAvailabilityService: vi.fn(),
132
164
  };
133
165
  // Use proper MessageBus mocking for Phase 3 preparation
134
166
  const mockMessageBus = createMockMessageBus();
@@ -421,6 +453,26 @@ describe('GeminiChat', () => {
421
453
  maxAttempts: 1,
422
454
  }));
423
455
  });
456
+ it('should use maxAttempts=1 for retryWithBackoff when in Preview Model Fallback Mode (Flash)', async () => {
457
+ vi.mocked(mockConfig.isPreviewModelFallbackMode).mockReturnValue(true);
458
+ vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue((async function* () {
459
+ yield {
460
+ candidates: [
461
+ {
462
+ content: { parts: [{ text: 'Success' }] },
463
+ finishReason: 'STOP',
464
+ },
465
+ ],
466
+ };
467
+ })());
468
+ const stream = await chat.sendMessageStream({ model: PREVIEW_GEMINI_FLASH_MODEL }, 'test', 'prompt-id-fast-retry-flash', new AbortController().signal);
469
+ for await (const _ of stream) {
470
+ // consume stream
471
+ }
472
+ expect(mockRetryWithBackoff).toHaveBeenCalledWith(expect.any(Function), expect.objectContaining({
473
+ maxAttempts: 1,
474
+ }));
475
+ });
424
476
  it('should NOT use maxAttempts=1 for other models even in Preview Model Fallback Mode', async () => {
425
477
  vi.mocked(mockConfig.isPreviewModelFallbackMode).mockReturnValue(true);
426
478
  vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue((async function* () {
@@ -441,44 +493,6 @@ describe('GeminiChat', () => {
441
493
  maxAttempts: undefined, // Should use default
442
494
  }));
443
495
  });
444
- it('should pass DEFAULT_GEMINI_MODEL to handleFallback when Preview Model is bypassed (downgraded)', async () => {
445
- // ARRANGE
446
- vi.mocked(mockConfig.isPreviewModelBypassMode).mockReturnValue(true);
447
- // Mock retryWithBackoff to simulate catching the error and calling onPersistent429
448
- vi.mocked(retryWithBackoff).mockImplementation(async (apiCall, options) => {
449
- const onPersistent429 = options?.onPersistent429;
450
- try {
451
- await apiCall();
452
- }
453
- catch (error) {
454
- if (onPersistent429) {
455
- await onPersistent429(AuthType.LOGIN_WITH_GOOGLE, error);
456
- }
457
- throw error;
458
- }
459
- });
460
- // We need the API call to fail so retryWithBackoff calls the callback.
461
- vi.mocked(mockContentGenerator.generateContentStream).mockRejectedValue(new TerminalQuotaError('Simulated Quota Error', {
462
- code: 429,
463
- message: 'Simulated Quota Error',
464
- details: [],
465
- }));
466
- // ACT
467
- const consumeStream = async () => {
468
- const stream = await chat.sendMessageStream({ model: PREVIEW_GEMINI_MODEL }, 'test', 'prompt-id-bypass', new AbortController().signal);
469
- // Consume the stream to trigger execution
470
- for await (const _ of stream) {
471
- // do nothing
472
- }
473
- };
474
- await expect(consumeStream()).rejects.toThrow('Simulated Quota Error');
475
- expect(retryWithBackoff).toHaveBeenCalled();
476
- // ASSERT
477
- // handleFallback is called via onPersistent429Callback
478
- // We verify it was called with DEFAULT_GEMINI_MODEL
479
- expect(mockHandleFallback).toHaveBeenCalledWith(expect.anything(), DEFAULT_GEMINI_MODEL, // This is the key assertion
480
- expect.anything(), expect.anything());
481
- });
482
496
  it('should throw an error when a tool call is followed by an empty stream response', async () => {
483
497
  // 1. Setup: A history where the model has just made a function call.
484
498
  const initialHistory = [
@@ -1295,30 +1309,6 @@ describe('GeminiChat', () => {
1295
1309
  }
1296
1310
  expect(turn4.parts[0].text).toBe('second response');
1297
1311
  });
1298
- describe('Model Resolution', () => {
1299
- const mockResponse = {
1300
- candidates: [
1301
- {
1302
- content: { parts: [{ text: 'response' }], role: 'model' },
1303
- finishReason: 'STOP',
1304
- },
1305
- ],
1306
- };
1307
- it('should use the FLASH model when in fallback mode (sendMessageStream)', async () => {
1308
- vi.mocked(mockConfig.getModel).mockReturnValue('gemini-pro');
1309
- vi.mocked(mockConfig.isInFallbackMode).mockReturnValue(true);
1310
- vi.mocked(mockContentGenerator.generateContentStream).mockImplementation(async () => (async function* () {
1311
- yield mockResponse;
1312
- })());
1313
- const stream = await chat.sendMessageStream({ model: 'test-model' }, 'test message', 'prompt-id-res3', new AbortController().signal);
1314
- for await (const _ of stream) {
1315
- // consume stream
1316
- }
1317
- expect(mockContentGenerator.generateContentStream).toHaveBeenCalledWith(expect.objectContaining({
1318
- model: DEFAULT_GEMINI_FLASH_MODEL,
1319
- }), 'prompt-id-res3');
1320
- });
1321
- });
1322
1312
  describe('Fallback Integration (Retries)', () => {
1323
1313
  const error429 = new ApiError({
1324
1314
  message: 'API Error 429: Quota exceeded',
@@ -1385,64 +1375,6 @@ describe('GeminiChat', () => {
1385
1375
  const modelTurn = history[1];
1386
1376
  expect(modelTurn.parts[0].text).toBe('Success on retry');
1387
1377
  });
1388
- it('should switch to DEFAULT_GEMINI_FLASH_MODEL and use thinkingBudget when falling back from a gemini-3 model', async () => {
1389
- // ARRANGE
1390
- const authType = AuthType.LOGIN_WITH_GOOGLE;
1391
- vi.mocked(mockConfig.getContentGeneratorConfig).mockReturnValue({
1392
- authType,
1393
- });
1394
- // Initial state: Not in fallback mode
1395
- const isInFallbackModeSpy = vi.spyOn(mockConfig, 'isInFallbackMode');
1396
- isInFallbackModeSpy.mockReturnValue(false);
1397
- // Mock API calls:
1398
- // 1. Fails with 429 (simulating gemini-3 failure)
1399
- // 2. Succeeds (simulating fallback success)
1400
- vi.mocked(mockContentGenerator.generateContentStream)
1401
- .mockRejectedValueOnce(error429)
1402
- .mockResolvedValueOnce((async function* () {
1403
- yield {
1404
- candidates: [
1405
- {
1406
- content: { parts: [{ text: 'Fallback success' }] },
1407
- finishReason: 'STOP',
1408
- },
1409
- ],
1410
- };
1411
- })());
1412
- // Mock handleFallback to enable fallback mode and signal retry
1413
- mockHandleFallback.mockImplementation(async () => {
1414
- isInFallbackModeSpy.mockReturnValue(true); // Next call will see fallback mode = true
1415
- return true;
1416
- });
1417
- // ACT
1418
- const stream = await chat.sendMessageStream({ model: 'gemini-3-test-model' }, // Start with a gemini-3 model
1419
- 'test fallback thinking', 'prompt-id-fb3', new AbortController().signal);
1420
- for await (const _ of stream) {
1421
- // consume stream
1422
- }
1423
- // ASSERT
1424
- expect(mockContentGenerator.generateContentStream).toHaveBeenCalledTimes(2);
1425
- // First call: gemini-3 model, thinkingLevel set
1426
- expect(mockContentGenerator.generateContentStream).toHaveBeenNthCalledWith(1, expect.objectContaining({
1427
- model: 'gemini-3-test-model',
1428
- config: expect.objectContaining({
1429
- thinkingConfig: {
1430
- thinkingBudget: undefined,
1431
- thinkingLevel: ThinkingLevel.HIGH,
1432
- },
1433
- }),
1434
- }), 'prompt-id-fb3');
1435
- // Second call: DEFAULT_GEMINI_FLASH_MODEL (due to fallback), thinkingBudget set (due to fix)
1436
- expect(mockContentGenerator.generateContentStream).toHaveBeenNthCalledWith(2, expect.objectContaining({
1437
- model: DEFAULT_GEMINI_FLASH_MODEL,
1438
- config: expect.objectContaining({
1439
- thinkingConfig: {
1440
- thinkingBudget: DEFAULT_THINKING_MODE,
1441
- thinkingLevel: undefined,
1442
- },
1443
- }),
1444
- }), 'prompt-id-fb3');
1445
- });
1446
1378
  it('should stop retrying if handleFallback returns false (e.g., auth intent)', async () => {
1447
1379
  vi.mocked(mockConfig.getModel).mockReturnValue('gemini-pro');
1448
1380
  vi.mocked(mockContentGenerator.generateContentStream).mockRejectedValue(error429);
@@ -1542,61 +1474,6 @@ describe('GeminiChat', () => {
1542
1474
  ]);
1543
1475
  });
1544
1476
  });
1545
- describe('Preview Model Fallback Logic', () => {
1546
- it('should reset previewModelBypassMode to false at the start of sendMessageStream', async () => {
1547
- const stream = (async function* () {
1548
- yield {
1549
- candidates: [
1550
- {
1551
- content: { role: 'model', parts: [{ text: 'Success' }] },
1552
- finishReason: 'STOP',
1553
- },
1554
- ],
1555
- };
1556
- })();
1557
- vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue(stream);
1558
- await chat.sendMessageStream({ model: 'test-model' }, 'test', 'prompt-id-preview-model-reset', new AbortController().signal);
1559
- expect(mockConfig.setPreviewModelBypassMode).toHaveBeenCalledWith(false);
1560
- });
1561
- it('should reset previewModelFallbackMode to false upon successful Preview Model usage', async () => {
1562
- const stream = (async function* () {
1563
- yield {
1564
- candidates: [
1565
- {
1566
- content: { role: 'model', parts: [{ text: 'Success' }] },
1567
- finishReason: 'STOP',
1568
- },
1569
- ],
1570
- };
1571
- })();
1572
- vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue(stream);
1573
- const resultStream = await chat.sendMessageStream({ model: PREVIEW_GEMINI_MODEL }, 'test', 'prompt-id-preview-model-healing', new AbortController().signal);
1574
- for await (const _ of resultStream) {
1575
- // consume stream
1576
- }
1577
- expect(mockConfig.setPreviewModelFallbackMode).toHaveBeenCalledWith(false);
1578
- });
1579
- it('should NOT reset previewModelFallbackMode if Preview Model was bypassed (downgraded)', async () => {
1580
- const stream = (async function* () {
1581
- yield {
1582
- candidates: [
1583
- {
1584
- content: { role: 'model', parts: [{ text: 'Success' }] },
1585
- finishReason: 'STOP',
1586
- },
1587
- ],
1588
- };
1589
- })();
1590
- vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue(stream);
1591
- // Simulate bypass mode being active (downgrade happened)
1592
- vi.mocked(mockConfig.isPreviewModelBypassMode).mockReturnValue(true);
1593
- const resultStream = await chat.sendMessageStream({ model: PREVIEW_GEMINI_MODEL }, 'test', 'prompt-id-bypass-no-healing', new AbortController().signal);
1594
- for await (const _ of resultStream) {
1595
- // consume stream
1596
- }
1597
- expect(mockConfig.setPreviewModelFallbackMode).not.toHaveBeenCalled();
1598
- });
1599
- });
1600
1477
  describe('ensureActiveLoopHasThoughtSignatures', () => {
1601
1478
  it('should add thoughtSignature to the first functionCall in each model turn of the active loop', () => {
1602
1479
  const chat = new GeminiChat(mockConfig, '', [], []);
@@ -1676,5 +1553,176 @@ describe('GeminiChat', () => {
1676
1553
  expect(newContents).toEqual(history);
1677
1554
  });
1678
1555
  });
1556
+ describe('Availability Service Integration', () => {
1557
+ let mockAvailabilityService;
1558
+ beforeEach(async () => {
1559
+ mockAvailabilityService = createAvailabilityServiceMock();
1560
+ vi.mocked(mockConfig.getModelAvailabilityService).mockReturnValue(mockAvailabilityService);
1561
+ vi.mocked(mockConfig.isModelAvailabilityServiceEnabled).mockReturnValue(true);
1562
+ // Stateful mock for activeModel
1563
+ let activeModel = 'model-a';
1564
+ vi.mocked(mockConfig.getActiveModel).mockImplementation(() => activeModel);
1565
+ vi.mocked(mockConfig.setActiveModel).mockImplementation((model) => {
1566
+ activeModel = model;
1567
+ });
1568
+ vi.spyOn(policyHelpers, 'resolvePolicyChain').mockReturnValue([
1569
+ {
1570
+ model: 'model-a',
1571
+ isLastResort: false,
1572
+ actions: {},
1573
+ stateTransitions: {},
1574
+ },
1575
+ {
1576
+ model: 'model-b',
1577
+ isLastResort: false,
1578
+ actions: {},
1579
+ stateTransitions: {},
1580
+ },
1581
+ {
1582
+ model: 'model-c',
1583
+ isLastResort: true,
1584
+ actions: {},
1585
+ stateTransitions: {},
1586
+ },
1587
+ ]);
1588
+ });
1589
+ it('should mark healthy on successful stream', async () => {
1590
+ vi.mocked(mockAvailabilityService.selectFirstAvailable).mockReturnValue({
1591
+ selectedModel: 'model-b',
1592
+ skipped: [],
1593
+ });
1594
+ // Simulate selection happening upstream
1595
+ mockConfig.setActiveModel('model-b');
1596
+ vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue((async function* () {
1597
+ yield {
1598
+ candidates: [
1599
+ {
1600
+ content: { parts: [{ text: 'Response' }], role: 'model' },
1601
+ finishReason: 'STOP',
1602
+ },
1603
+ ],
1604
+ };
1605
+ })());
1606
+ const stream = await chat.sendMessageStream({ model: 'gemini-pro' }, 'test', 'prompt-healthy', new AbortController().signal);
1607
+ for await (const _ of stream) {
1608
+ // consume
1609
+ }
1610
+ expect(mockAvailabilityService.markHealthy).toHaveBeenCalledWith('model-b');
1611
+ });
1612
+ it('caps retries to a single attempt when selection is sticky', async () => {
1613
+ vi.mocked(mockAvailabilityService.selectFirstAvailable).mockReturnValue({
1614
+ selectedModel: 'model-a',
1615
+ attempts: 1,
1616
+ skipped: [],
1617
+ });
1618
+ vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue((async function* () {
1619
+ yield {
1620
+ candidates: [
1621
+ {
1622
+ content: { parts: [{ text: 'Response' }], role: 'model' },
1623
+ finishReason: 'STOP',
1624
+ },
1625
+ ],
1626
+ };
1627
+ })());
1628
+ const stream = await chat.sendMessageStream({ model: 'gemini-pro' }, 'test', 'prompt-sticky-once', new AbortController().signal);
1629
+ for await (const _ of stream) {
1630
+ // consume
1631
+ }
1632
+ expect(mockRetryWithBackoff).toHaveBeenCalledWith(expect.any(Function), expect.objectContaining({ maxAttempts: 1 }));
1633
+ expect(mockAvailabilityService.consumeStickyAttempt).toHaveBeenCalledWith('model-a');
1634
+ });
1635
+ it('should pass attempted model to onPersistent429 callback which calls handleFallback', async () => {
1636
+ vi.mocked(mockAvailabilityService.selectFirstAvailable).mockReturnValue({
1637
+ selectedModel: 'model-a',
1638
+ skipped: [],
1639
+ });
1640
+ // Simulate selection happening upstream
1641
+ mockConfig.setActiveModel('model-a');
1642
+ // Simulate retry logic behavior: catch error, call onPersistent429
1643
+ const error = new TerminalQuotaError('Quota', {
1644
+ code: 429,
1645
+ message: 'quota',
1646
+ details: [],
1647
+ });
1648
+ vi.mocked(mockContentGenerator.generateContentStream).mockRejectedValue(error);
1649
+ // We need retryWithBackoff to trigger the callback
1650
+ mockRetryWithBackoff.mockImplementation(async (apiCall, options) => {
1651
+ try {
1652
+ await apiCall();
1653
+ }
1654
+ catch (e) {
1655
+ if (options?.onPersistent429) {
1656
+ await options.onPersistent429(AuthType.LOGIN_WITH_GOOGLE, e);
1657
+ }
1658
+ throw e; // throw anyway to end test
1659
+ }
1660
+ });
1661
+ const consume = async () => {
1662
+ const stream = await chat.sendMessageStream({ model: 'gemini-pro' }, 'test', 'prompt-fallback-arg', new AbortController().signal);
1663
+ for await (const _ of stream) {
1664
+ // consume
1665
+ }
1666
+ };
1667
+ await expect(consume()).rejects.toThrow();
1668
+ // handleFallback is called with the ATTEMPTED model (model-a), not the requested one (gemini-pro)
1669
+ expect(mockHandleFallback).toHaveBeenCalledWith(expect.anything(), 'model-a', expect.anything(), error);
1670
+ });
1671
+ it('re-resolves generateContentConfig when active model changes between retries', async () => {
1672
+ // Availability enabled with stateful active model
1673
+ let activeModel = 'model-a';
1674
+ vi.mocked(mockConfig.getActiveModel).mockImplementation(() => activeModel);
1675
+ vi.mocked(mockConfig.setActiveModel).mockImplementation((model) => {
1676
+ activeModel = model;
1677
+ });
1678
+ // Different configs per model
1679
+ vi.mocked(mockConfig.modelConfigService.getResolvedConfig)
1680
+ .mockReturnValueOnce(makeResolvedModelConfig('model-a', { temperature: 0.1 }))
1681
+ .mockReturnValueOnce(makeResolvedModelConfig('model-b', { temperature: 0.9 }));
1682
+ // First attempt uses model-a, then simulate availability switching to model-b
1683
+ mockRetryWithBackoff.mockImplementation(async (apiCall) => {
1684
+ await apiCall(); // first attempt
1685
+ activeModel = 'model-b'; // simulate switch before retry
1686
+ return apiCall(); // second attempt
1687
+ });
1688
+ // Generators for each attempt
1689
+ const firstResponse = (async function* () {
1690
+ yield {
1691
+ candidates: [
1692
+ {
1693
+ content: { parts: [{ text: 'first' }], role: 'model' },
1694
+ finishReason: 'STOP',
1695
+ },
1696
+ ],
1697
+ };
1698
+ })();
1699
+ const secondResponse = (async function* () {
1700
+ yield {
1701
+ candidates: [
1702
+ {
1703
+ content: { parts: [{ text: 'second' }], role: 'model' },
1704
+ finishReason: 'STOP',
1705
+ },
1706
+ ],
1707
+ };
1708
+ })();
1709
+ vi.mocked(mockContentGenerator.generateContentStream)
1710
+ .mockResolvedValueOnce(firstResponse)
1711
+ .mockResolvedValueOnce(secondResponse);
1712
+ const stream = await chat.sendMessageStream({ model: 'gemini-pro' }, 'test', 'prompt-config-refresh', new AbortController().signal);
1713
+ // Consume to drive both attempts
1714
+ for await (const _ of stream) {
1715
+ // consume
1716
+ }
1717
+ expect(mockContentGenerator.generateContentStream).toHaveBeenNthCalledWith(1, expect.objectContaining({
1718
+ model: 'model-a',
1719
+ config: expect.objectContaining({ temperature: 0.1 }),
1720
+ }), expect.any(String));
1721
+ expect(mockContentGenerator.generateContentStream).toHaveBeenNthCalledWith(2, expect.objectContaining({
1722
+ model: 'model-b',
1723
+ config: expect.objectContaining({ temperature: 0.9 }),
1724
+ }), expect.any(String));
1725
+ });
1726
+ });
1679
1727
  });
1680
1728
  //# sourceMappingURL=geminiChat.test.js.map