muonroi-cli 1.8.4 → 1.8.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 (458) hide show
  1. package/LICENSE +17 -5
  2. package/dist/packages/agent-harness-core/src/driver.d.ts +27 -1
  3. package/dist/packages/agent-harness-core/src/driver.js +46 -0
  4. package/dist/packages/agent-harness-core/src/event-tee.d.ts +48 -0
  5. package/dist/packages/agent-harness-core/src/event-tee.js +77 -0
  6. package/dist/packages/agent-harness-core/src/mcp-server.d.ts +11 -0
  7. package/dist/packages/agent-harness-core/src/mcp-server.js +87 -15
  8. package/dist/packages/agent-harness-core/src/protocol.d.ts +66 -2
  9. package/dist/packages/agent-harness-core/src/protocol.js +15 -0
  10. package/dist/packages/agent-harness-core/src/visual-quality.d.ts +58 -0
  11. package/dist/packages/agent-harness-core/src/visual-quality.js +141 -0
  12. package/dist/packages/agent-harness-opentui/src/agent-mode.d.ts +6 -0
  13. package/dist/packages/agent-harness-opentui/src/agent-mode.js +14 -1
  14. package/dist/packages/agent-harness-opentui/src/input-bridge.d.ts +2 -10
  15. package/dist/packages/agent-harness-opentui/src/input-bridge.js +103 -16
  16. package/dist/packages/agent-harness-opentui/src/install.d.ts +8 -0
  17. package/dist/packages/agent-harness-opentui/src/install.js +10 -0
  18. package/dist/packages/agent-harness-opentui/src/semantic.js +12 -10
  19. package/dist/packages/agent-harness-opentui/src/visual-capture.d.ts +56 -0
  20. package/dist/packages/agent-harness-opentui/src/visual-capture.js +103 -0
  21. package/dist/src/agent-harness/mock-model.d.ts +28 -0
  22. package/dist/src/agent-harness/mock-model.js +63 -1
  23. package/dist/src/agent-harness/test-spawn.js +31 -0
  24. package/dist/src/cli/config/screen-providers.js +1 -1
  25. package/dist/src/cli/cost-forensics.d.ts +10 -0
  26. package/dist/src/cli/cost-forensics.js +18 -3
  27. package/dist/src/cli/keys-bundle.d.ts +1 -1
  28. package/dist/src/cli/keys-bundle.js +1 -1
  29. package/dist/src/cli/keys.d.ts +2 -2
  30. package/dist/src/cli/keys.js +19 -81
  31. package/dist/src/council/clarifier.d.ts +28 -2
  32. package/dist/src/council/clarifier.js +81 -15
  33. package/dist/src/council/context.js +49 -15
  34. package/dist/src/council/debate-checkpoint.d.ts +129 -0
  35. package/dist/src/council/debate-checkpoint.js +176 -0
  36. package/dist/src/council/debate-planner.js +51 -3
  37. package/dist/src/council/debate-summary.d.ts +25 -0
  38. package/dist/src/council/debate-summary.js +85 -0
  39. package/dist/src/council/debate.d.ts +169 -2
  40. package/dist/src/council/debate.js +1210 -134
  41. package/dist/src/council/index.d.ts +85 -1
  42. package/dist/src/council/index.js +634 -196
  43. package/dist/src/council/leader.d.ts +26 -0
  44. package/dist/src/council/leader.js +150 -9
  45. package/dist/src/council/llm.d.ts +32 -0
  46. package/dist/src/council/llm.js +231 -38
  47. package/dist/src/council/panel-select.d.ts +30 -0
  48. package/dist/src/council/panel-select.js +72 -0
  49. package/dist/src/council/planner.js +23 -0
  50. package/dist/src/council/preflight.d.ts +7 -0
  51. package/dist/src/council/preflight.js +14 -2
  52. package/dist/src/council/prompts.d.ts +30 -3
  53. package/dist/src/council/prompts.js +234 -64
  54. package/dist/src/council/stance-recall.d.ts +42 -0
  55. package/dist/src/council/stance-recall.js +57 -0
  56. package/dist/src/council/strip-think.d.ts +17 -0
  57. package/dist/src/council/strip-think.js +33 -0
  58. package/dist/src/council/types.d.ts +128 -0
  59. package/dist/src/ee/artifact-cache.d.ts +16 -0
  60. package/dist/src/ee/artifact-cache.js +32 -0
  61. package/dist/src/ee/auth.d.ts +1 -0
  62. package/dist/src/ee/auth.js +15 -2
  63. package/dist/src/ee/bridge.d.ts +10 -0
  64. package/dist/src/ee/bridge.js +58 -0
  65. package/dist/src/ee/client.js +81 -18
  66. package/dist/src/ee/export-transcripts.d.ts +1 -0
  67. package/dist/src/ee/export-transcripts.js +8 -10
  68. package/dist/src/ee/extract-session.js +29 -0
  69. package/dist/src/ee/extract-style.d.ts +58 -0
  70. package/dist/src/ee/extract-style.js +270 -0
  71. package/dist/src/ee/recall-ledger.d.ts +9 -0
  72. package/dist/src/ee/recall-ledger.js +3 -0
  73. package/dist/src/ee/scope.d.ts +1 -0
  74. package/dist/src/ee/scope.js +26 -1
  75. package/dist/src/ee/search.d.ts +7 -0
  76. package/dist/src/ee/search.js +24 -0
  77. package/dist/src/ee/transcript-emit.js +2 -0
  78. package/dist/src/ee/types.d.ts +22 -0
  79. package/dist/src/ee/who-am-i-brain.d.ts +35 -0
  80. package/dist/src/ee/who-am-i-brain.js +220 -0
  81. package/dist/src/ee/who-am-i.d.ts +10 -3
  82. package/dist/src/ee/who-am-i.js +12 -0
  83. package/dist/src/ee/workflow-event.d.ts +48 -0
  84. package/dist/src/ee/workflow-event.js +81 -0
  85. package/dist/src/flow/compaction/compress.d.ts +3 -3
  86. package/dist/src/flow/compaction/compress.js +45 -8
  87. package/dist/src/flow/compaction/extract.d.ts +4 -7
  88. package/dist/src/flow/compaction/extract.js +50 -10
  89. package/dist/src/flow/compaction/index.d.ts +13 -1
  90. package/dist/src/flow/compaction/index.js +70 -3
  91. package/dist/src/flow/compaction/input-guard.d.ts +24 -0
  92. package/dist/src/flow/compaction/input-guard.js +43 -0
  93. package/dist/src/flow/fold-planning.d.ts +36 -0
  94. package/dist/src/flow/fold-planning.js +83 -0
  95. package/dist/src/flow/hierarchy.d.ts +146 -0
  96. package/dist/src/flow/hierarchy.js +427 -0
  97. package/dist/src/flow/index.d.ts +1 -0
  98. package/dist/src/flow/index.js +2 -0
  99. package/dist/src/flow/run-artifacts.d.ts +102 -0
  100. package/dist/src/flow/run-artifacts.js +208 -0
  101. package/dist/src/generated/version.d.ts +1 -1
  102. package/dist/src/generated/version.js +1 -1
  103. package/dist/src/gsd/assessment-schema.d.ts +44 -0
  104. package/dist/src/gsd/assessment-schema.js +134 -0
  105. package/dist/src/gsd/capability-registry.d.ts +45 -0
  106. package/dist/src/gsd/capability-registry.js +337 -0
  107. package/dist/src/gsd/complexity-assessor.d.ts +39 -0
  108. package/dist/src/gsd/complexity-assessor.js +152 -0
  109. package/dist/src/gsd/config-bridge.d.ts +7 -0
  110. package/dist/src/gsd/config-bridge.js +114 -0
  111. package/dist/src/gsd/config-loader.d.ts +27 -0
  112. package/dist/src/gsd/config-loader.js +50 -0
  113. package/dist/src/gsd/council-context.d.ts +44 -0
  114. package/dist/src/gsd/council-context.js +114 -0
  115. package/dist/src/gsd/ee-closure.d.ts +28 -0
  116. package/dist/src/gsd/ee-closure.js +49 -0
  117. package/dist/src/gsd/flags.d.ts +55 -0
  118. package/dist/src/gsd/flags.js +83 -0
  119. package/dist/src/gsd/gsd-dispatch.d.ts +58 -0
  120. package/dist/src/gsd/gsd-dispatch.js +131 -0
  121. package/dist/src/gsd/gsd-runtime.d.ts +22 -0
  122. package/dist/src/gsd/gsd-runtime.js +37 -0
  123. package/dist/src/gsd/host-adapter.d.ts +11 -0
  124. package/dist/src/gsd/host-adapter.js +29 -0
  125. package/dist/src/gsd/index.d.ts +24 -1
  126. package/dist/src/gsd/index.js +27 -0
  127. package/dist/src/gsd/loop-host-contract.d.ts +21 -0
  128. package/dist/src/gsd/loop-host-contract.js +39 -0
  129. package/dist/src/gsd/loop-host.d.ts +69 -0
  130. package/dist/src/gsd/loop-host.js +245 -0
  131. package/dist/src/gsd/loop-resolver.d.ts +36 -0
  132. package/dist/src/gsd/loop-resolver.js +79 -0
  133. package/dist/src/gsd/model-tier.d.ts +13 -0
  134. package/dist/src/gsd/model-tier.js +45 -0
  135. package/dist/src/gsd/mutation-gate.d.ts +16 -0
  136. package/dist/src/gsd/mutation-gate.js +41 -0
  137. package/dist/src/gsd/native-roadmap.d.ts +89 -0
  138. package/dist/src/gsd/native-roadmap.js +343 -0
  139. package/dist/src/gsd/native-state.d.ts +47 -0
  140. package/dist/src/gsd/native-state.js +220 -0
  141. package/dist/src/gsd/paths.d.ts +23 -0
  142. package/dist/src/gsd/paths.js +66 -0
  143. package/dist/src/gsd/phase-dag.d.ts +12 -0
  144. package/dist/src/gsd/phase-dag.js +94 -0
  145. package/dist/src/gsd/phase-sync.d.ts +42 -0
  146. package/dist/src/gsd/phase-sync.js +321 -0
  147. package/dist/src/gsd/pil-gate-context.d.ts +13 -0
  148. package/dist/src/gsd/pil-gate-context.js +64 -0
  149. package/dist/src/gsd/pil-gate-critic.d.ts +19 -0
  150. package/dist/src/gsd/pil-gate-critic.js +74 -0
  151. package/dist/src/gsd/plan-council-prompts.d.ts +25 -0
  152. package/dist/src/gsd/plan-council-prompts.js +79 -0
  153. package/dist/src/gsd/plan-council.d.ts +44 -0
  154. package/dist/src/gsd/plan-council.js +251 -0
  155. package/dist/src/gsd/plan-gate-vocabulary.d.ts +40 -0
  156. package/dist/src/gsd/plan-gate-vocabulary.js +64 -0
  157. package/dist/src/gsd/product-workspace.d.ts +13 -0
  158. package/dist/src/gsd/product-workspace.js +124 -0
  159. package/dist/src/gsd/ship-bridge.d.ts +25 -0
  160. package/dist/src/gsd/ship-bridge.js +65 -0
  161. package/dist/src/gsd/state-document.d.ts +40 -0
  162. package/dist/src/gsd/state-document.js +163 -0
  163. package/dist/src/gsd/verdict-schema.d.ts +39 -0
  164. package/dist/src/gsd/verdict-schema.js +144 -0
  165. package/dist/src/gsd/verify-context.d.ts +22 -0
  166. package/dist/src/gsd/verify-context.js +27 -0
  167. package/dist/src/gsd/verify-council-prompts.d.ts +19 -0
  168. package/dist/src/gsd/verify-council-prompts.js +85 -0
  169. package/dist/src/gsd/verify-council.d.ts +25 -0
  170. package/dist/src/gsd/verify-council.js +119 -0
  171. package/dist/src/gsd/verify-gate-vocabulary.d.ts +25 -0
  172. package/dist/src/gsd/verify-gate-vocabulary.js +46 -0
  173. package/dist/src/gsd/workflow-engine.d.ts +60 -0
  174. package/dist/src/gsd/workflow-engine.js +207 -0
  175. package/dist/src/gsd/workflow-tools.d.ts +13 -0
  176. package/dist/src/gsd/workflow-tools.js +277 -0
  177. package/dist/src/hooks/index.js +1 -1
  178. package/dist/src/index.js +44 -11
  179. package/dist/src/maintain/pr-builder.js +23 -13
  180. package/dist/src/mcp/auto-setup.js +57 -32
  181. package/dist/src/mcp/client-pool.js +1 -1
  182. package/dist/src/mcp/ee-tools.js +1 -0
  183. package/dist/src/mcp/research-onboarding.js +8 -7
  184. package/dist/src/mcp/runtime.js +34 -2
  185. package/dist/src/models/catalog-client.d.ts +87 -0
  186. package/dist/src/models/catalog-client.js +105 -38
  187. package/dist/src/models/catalog.json +528 -265
  188. package/dist/src/models/registry.d.ts +22 -7
  189. package/dist/src/models/registry.js +73 -10
  190. package/dist/src/ops/doctor.js +1 -1
  191. package/dist/src/orchestrator/auto-commit.js +1 -1
  192. package/dist/src/orchestrator/batch-turn-runner.js +2 -2
  193. package/dist/src/orchestrator/cache-prefix.d.ts +67 -0
  194. package/dist/src/orchestrator/cache-prefix.js +83 -0
  195. package/dist/src/orchestrator/compact-request.d.ts +32 -0
  196. package/dist/src/orchestrator/compact-request.js +41 -0
  197. package/dist/src/orchestrator/compaction.d.ts +10 -0
  198. package/dist/src/orchestrator/compaction.js +27 -7
  199. package/dist/src/orchestrator/council-manager.d.ts +12 -3
  200. package/dist/src/orchestrator/council-manager.js +65 -24
  201. package/dist/src/orchestrator/cross-turn-dedup.d.ts +6 -0
  202. package/dist/src/orchestrator/cross-turn-dedup.js +22 -1
  203. package/dist/src/orchestrator/error-utils.d.ts +29 -0
  204. package/dist/src/orchestrator/error-utils.js +132 -24
  205. package/dist/src/orchestrator/grounding-check.js +39 -1
  206. package/dist/src/orchestrator/message-processor.js +242 -33
  207. package/dist/src/orchestrator/orchestrator.d.ts +39 -3
  208. package/dist/src/orchestrator/orchestrator.js +651 -102
  209. package/dist/src/orchestrator/preprocessor.js +1 -1
  210. package/dist/src/orchestrator/proactive-compact-detector.d.ts +26 -0
  211. package/dist/src/orchestrator/proactive-compact-detector.js +36 -0
  212. package/dist/src/orchestrator/prompts.js +17 -17
  213. package/dist/src/orchestrator/reactive-delegation.d.ts +39 -0
  214. package/dist/src/orchestrator/reactive-delegation.js +59 -0
  215. package/dist/src/orchestrator/retry-classifier.d.ts +2 -1
  216. package/dist/src/orchestrator/retry-classifier.js +46 -2
  217. package/dist/src/orchestrator/safety-intercept.d.ts +45 -0
  218. package/dist/src/orchestrator/safety-intercept.js +55 -0
  219. package/dist/src/orchestrator/scope-reminder.js +1 -1
  220. package/dist/src/orchestrator/session-experience.d.ts +2 -1
  221. package/dist/src/orchestrator/session-experience.js +2 -1
  222. package/dist/src/orchestrator/should-run-gate.d.ts +5 -0
  223. package/dist/src/orchestrator/should-run-gate.js +18 -0
  224. package/dist/src/orchestrator/stall-watchdog.d.ts +24 -3
  225. package/dist/src/orchestrator/stall-watchdog.js +47 -13
  226. package/dist/src/orchestrator/stream-runner.js +62 -29
  227. package/dist/src/orchestrator/sub-agent-cap.d.ts +13 -1
  228. package/dist/src/orchestrator/sub-agent-cap.js +16 -1
  229. package/dist/src/orchestrator/sub-agent-model-tier.d.ts +13 -1
  230. package/dist/src/orchestrator/sub-agent-model-tier.js +16 -2
  231. package/dist/src/orchestrator/subagent-compactor.d.ts +53 -1
  232. package/dist/src/orchestrator/subagent-compactor.js +126 -15
  233. package/dist/src/orchestrator/tool-engine.d.ts +22 -0
  234. package/dist/src/orchestrator/tool-engine.js +620 -56
  235. package/dist/src/orchestrator/tool-limit-auto-recover.d.ts +22 -0
  236. package/dist/src/orchestrator/tool-limit-auto-recover.js +30 -0
  237. package/dist/src/orchestrator/turn-runner-deps.d.ts +19 -0
  238. package/dist/src/orchestrator/turn-watchdog.d.ts +37 -0
  239. package/dist/src/orchestrator/turn-watchdog.js +55 -0
  240. package/dist/src/pil/agent-operating-contract.d.ts +1 -1
  241. package/dist/src/pil/agent-operating-contract.js +1 -1
  242. package/dist/src/pil/cheap-model-playbook.d.ts +1 -1
  243. package/dist/src/pil/cheap-model-playbook.js +5 -1
  244. package/dist/src/pil/cheap-model-workbooks.d.ts +1 -1
  245. package/dist/src/pil/cheap-model-workbooks.js +1 -1
  246. package/dist/src/pil/discovery-types.d.ts +1 -0
  247. package/dist/src/pil/discovery.js +16 -11
  248. package/dist/src/pil/layer1-intent.d.ts +18 -6
  249. package/dist/src/pil/layer1-intent.js +66 -757
  250. package/dist/src/pil/layer15-context-scan.js +15 -1
  251. package/dist/src/pil/layer3-ee-injection.js +23 -8
  252. package/dist/src/pil/layer4-gsd.js +69 -16
  253. package/dist/src/pil/layer5-context.js +7 -3
  254. package/dist/src/pil/layer6-output.d.ts +23 -0
  255. package/dist/src/pil/layer6-output.js +5 -1
  256. package/dist/src/pil/llm-classify.d.ts +33 -2
  257. package/dist/src/pil/llm-classify.js +123 -131
  258. package/dist/src/pil/native-capabilities-workbook.d.ts +1 -1
  259. package/dist/src/pil/native-capabilities-workbook.js +1 -0
  260. package/dist/src/pil/pipeline.js +34 -2
  261. package/dist/src/pil/response-tools.js +5 -3
  262. package/dist/src/pil/schema.d.ts +1 -0
  263. package/dist/src/pil/schema.js +2 -0
  264. package/dist/src/pil/types.d.ts +18 -0
  265. package/dist/src/playbook/directives.d.ts +4 -0
  266. package/dist/src/playbook/directives.js +17 -5
  267. package/dist/src/product-loop/backlog-builder.d.ts +14 -1
  268. package/dist/src/product-loop/backlog-builder.js +30 -6
  269. package/dist/src/product-loop/discovery-context-format.js +3 -1
  270. package/dist/src/product-loop/discovery-ecosystem.js +4 -1
  271. package/dist/src/product-loop/discovery-interview.js +32 -3
  272. package/dist/src/product-loop/discovery-schema.js +5 -1
  273. package/dist/src/product-loop/ideal-trace.d.ts +7 -0
  274. package/dist/src/product-loop/ideal-trace.js +64 -0
  275. package/dist/src/product-loop/index.d.ts +13 -1
  276. package/dist/src/product-loop/index.js +333 -52
  277. package/dist/src/product-loop/loop-driver.d.ts +7 -0
  278. package/dist/src/product-loop/loop-driver.js +310 -99
  279. package/dist/src/product-loop/phase-plan.d.ts +5 -0
  280. package/dist/src/product-loop/phase-plan.js +39 -2
  281. package/dist/src/product-loop/phase-runner.js +9 -1
  282. package/dist/src/product-loop/sprint-runner.d.ts +111 -0
  283. package/dist/src/product-loop/sprint-runner.js +559 -16
  284. package/dist/src/product-loop/types.d.ts +36 -5
  285. package/dist/src/providers/adapter.d.ts +1 -1
  286. package/dist/src/providers/adapter.js +3 -4
  287. package/dist/src/providers/auth/browser-flow.d.ts +1 -1
  288. package/dist/src/providers/auth/browser-flow.js +1 -1
  289. package/dist/src/providers/auth/openai-oauth.js +1 -1
  290. package/dist/src/providers/auth/registry.js +0 -34
  291. package/dist/src/providers/auth/token-store.js +4 -1
  292. package/dist/src/providers/auth/types.d.ts +1 -1
  293. package/dist/src/providers/auth/types.js +1 -1
  294. package/dist/src/providers/capabilities.d.ts +24 -5
  295. package/dist/src/providers/capabilities.js +42 -24
  296. package/dist/src/providers/endpoints.d.ts +2 -2
  297. package/dist/src/providers/endpoints.js +11 -10
  298. package/dist/src/providers/keychain.d.ts +1 -1
  299. package/dist/src/providers/keychain.js +7 -9
  300. package/dist/src/providers/mcp-vision-bridge.js +56 -146
  301. package/dist/src/providers/openai-compatible.js +8 -1
  302. package/dist/src/providers/pricing.d.ts +2 -2
  303. package/dist/src/providers/pricing.js +3 -13
  304. package/dist/src/providers/runtime.d.ts +27 -2
  305. package/dist/src/providers/runtime.js +78 -15
  306. package/dist/src/providers/strategies/base.strategy.d.ts +16 -0
  307. package/dist/src/providers/strategies/base.strategy.js +24 -1
  308. package/dist/src/providers/strategies/{siliconflow.strategy.d.ts → opencode-go.strategy.d.ts} +3 -3
  309. package/dist/src/providers/strategies/opencode-go.strategy.js +83 -0
  310. package/dist/src/providers/strategies/registry.js +4 -4
  311. package/dist/src/providers/strategies/thinking-mode.d.ts +109 -0
  312. package/dist/src/providers/strategies/thinking-mode.js +280 -1
  313. package/dist/src/providers/strategies/zai.strategy.d.ts +14 -0
  314. package/dist/src/providers/strategies/zai.strategy.js +44 -0
  315. package/dist/src/providers/types.d.ts +5 -6
  316. package/dist/src/providers/types.js +2 -2
  317. package/dist/src/providers/vision-backend.d.ts +47 -0
  318. package/dist/src/providers/vision-backend.js +258 -0
  319. package/dist/src/providers/vision-proxy.d.ts +22 -9
  320. package/dist/src/providers/vision-proxy.js +63 -132
  321. package/dist/src/providers/wire-debug.js +95 -0
  322. package/dist/src/router/decide.d.ts +13 -0
  323. package/dist/src/router/decide.js +138 -36
  324. package/dist/src/router/peak-hour.d.ts +38 -0
  325. package/dist/src/router/peak-hour.js +107 -0
  326. package/dist/src/router/step-router.js +3 -2
  327. package/dist/src/router/warm.js +4 -5
  328. package/dist/src/scaffold/continuation-prompt.d.ts +11 -0
  329. package/dist/src/scaffold/continuation-prompt.js +26 -0
  330. package/dist/src/scaffold/point-to-existing.d.ts +21 -0
  331. package/dist/src/scaffold/point-to-existing.js +25 -0
  332. package/dist/src/self-qa/agentic-loop.js +3 -3
  333. package/dist/src/{ui/state → state}/active-run.d.ts +19 -0
  334. package/dist/src/{ui/state → state}/active-run.js +21 -0
  335. package/dist/src/{ui/status-bar/store.d.ts → state/status-bar-store.d.ts} +4 -1
  336. package/dist/src/{ui/status-bar/store.js → state/status-bar-store.js} +12 -9
  337. package/dist/src/state/turn-trace.d.ts +43 -0
  338. package/dist/src/state/turn-trace.js +32 -0
  339. package/dist/src/storage/db.js +2 -1
  340. package/dist/src/storage/index.d.ts +1 -1
  341. package/dist/src/storage/index.js +1 -1
  342. package/dist/src/storage/interaction-log.d.ts +1 -1
  343. package/dist/src/storage/migrations.js +71 -1
  344. package/dist/src/storage/sessions.d.ts +28 -10
  345. package/dist/src/storage/sessions.js +78 -21
  346. package/dist/src/storage/transcript-view.js +1 -1
  347. package/dist/src/storage/transcript.d.ts +51 -0
  348. package/dist/src/storage/transcript.js +284 -13
  349. package/dist/src/tools/file.d.ts +15 -0
  350. package/dist/src/tools/file.js +32 -0
  351. package/dist/src/tools/native-tools.js +5 -0
  352. package/dist/src/tools/registry.d.ts +3 -0
  353. package/dist/src/tools/registry.js +460 -22
  354. package/dist/src/tools/research.d.ts +29 -0
  355. package/dist/src/tools/research.js +233 -0
  356. package/dist/src/types/index.d.ts +118 -3
  357. package/dist/src/ui/app.js +0 -0
  358. package/dist/src/ui/cards/product-status-card.js +1 -1
  359. package/dist/src/ui/components/bubble-body-guard.d.ts +27 -0
  360. package/dist/src/ui/components/bubble-body-guard.js +50 -0
  361. package/dist/src/ui/components/context-rail.d.ts +26 -0
  362. package/dist/src/ui/components/context-rail.js +33 -0
  363. package/dist/src/ui/components/council-conclusion-card.d.ts +73 -0
  364. package/dist/src/ui/components/council-conclusion-card.js +420 -0
  365. package/dist/src/ui/components/council-debate-pill.d.ts +36 -0
  366. package/dist/src/ui/components/council-debate-pill.js +34 -0
  367. package/dist/src/ui/components/council-info-card.js +2 -2
  368. package/dist/src/ui/components/council-leader-bubble.d.ts +10 -3
  369. package/dist/src/ui/components/council-leader-bubble.js +21 -11
  370. package/dist/src/ui/components/council-message-bubble.d.ts +15 -2
  371. package/dist/src/ui/components/council-message-bubble.js +16 -15
  372. package/dist/src/ui/components/council-phase-timeline.d.ts +9 -1
  373. package/dist/src/ui/components/council-phase-timeline.js +49 -15
  374. package/dist/src/ui/components/council-placeholder-bubble.d.ts +16 -9
  375. package/dist/src/ui/components/council-placeholder-bubble.js +32 -29
  376. package/dist/src/ui/components/council-question-card.js +12 -12
  377. package/dist/src/ui/components/council-rail-rounds.d.ts +26 -0
  378. package/dist/src/ui/components/council-rail-rounds.js +57 -0
  379. package/dist/src/ui/components/council-round-group.d.ts +38 -0
  380. package/dist/src/ui/components/council-round-group.js +88 -0
  381. package/dist/src/ui/components/council-status-list.d.ts +3 -1
  382. package/dist/src/ui/components/council-status-list.js +36 -24
  383. package/dist/src/ui/components/council-synthesis-banner.d.ts +7 -2
  384. package/dist/src/ui/components/council-synthesis-banner.js +20 -5
  385. package/dist/src/ui/components/halt-recovery-card.js +9 -5
  386. package/dist/src/ui/components/jump-to-latest-pill.d.ts +11 -0
  387. package/dist/src/ui/components/jump-to-latest-pill.js +14 -0
  388. package/dist/src/ui/components/prompt-box.js +18 -16
  389. package/dist/src/ui/components/session-tree-card.d.ts +14 -0
  390. package/dist/src/ui/components/session-tree-card.js +46 -0
  391. package/dist/src/ui/components/slash-inline-menu.d.ts +12 -0
  392. package/dist/src/ui/components/slash-inline-menu.js +26 -5
  393. package/dist/src/ui/components/task-list-panel.d.ts +14 -1
  394. package/dist/src/ui/components/task-list-panel.js +22 -2
  395. package/dist/src/ui/containers/modals-layer.d.ts +2 -1
  396. package/dist/src/ui/hooks/use-session-picker.d.ts +3 -3
  397. package/dist/src/ui/mcp-modal.js +2 -4
  398. package/dist/src/ui/modals/api-key-modal.js +1 -1
  399. package/dist/src/ui/modals/connect-modal.js +4 -3
  400. package/dist/src/ui/modals/session-picker-modal.d.ts +2 -2
  401. package/dist/src/ui/modals/session-picker-modal.js +3 -5
  402. package/dist/src/ui/picker-providers.d.ts +1 -1
  403. package/dist/src/ui/picker-providers.js +1 -1
  404. package/dist/src/ui/primitives/index.d.ts +1 -0
  405. package/dist/src/ui/primitives/index.js +2 -0
  406. package/dist/src/ui/primitives/semantic-primitives.d.ts +76 -0
  407. package/dist/src/ui/primitives/semantic-primitives.js +81 -0
  408. package/dist/src/ui/slash/compact.js +5 -7
  409. package/dist/src/ui/slash/cost.js +1 -1
  410. package/dist/src/ui/slash/council.js +19 -1
  411. package/dist/src/ui/slash/debug.d.ts +3 -31
  412. package/dist/src/ui/slash/debug.js +9 -20
  413. package/dist/src/ui/slash/ideal.d.ts +6 -2
  414. package/dist/src/ui/slash/ideal.js +97 -7
  415. package/dist/src/ui/slash/menu-items.d.ts +7 -0
  416. package/dist/src/ui/slash/menu-items.js +12 -18
  417. package/dist/src/ui/slash/registry.d.ts +2 -0
  418. package/dist/src/ui/slash/registry.js +4 -0
  419. package/dist/src/ui/status-bar/cache-hit.d.ts +6 -0
  420. package/dist/src/ui/status-bar/cache-hit.js +9 -0
  421. package/dist/src/ui/status-bar/index.d.ts +1 -1
  422. package/dist/src/ui/status-bar/index.js +7 -3
  423. package/dist/src/ui/status-bar/usd-meter.d.ts +5 -4
  424. package/dist/src/ui/status-bar/usd-meter.js +6 -4
  425. package/dist/src/ui/theme.d.ts +1 -0
  426. package/dist/src/ui/theme.js +2 -0
  427. package/dist/src/ui/types.d.ts +7 -0
  428. package/dist/src/ui/use-app-logic.js +0 -0
  429. package/dist/src/ui/utils/format.d.ts +14 -0
  430. package/dist/src/ui/utils/format.js +23 -3
  431. package/dist/src/usage/downgrade.js +2 -2
  432. package/dist/src/usage/product-ledger.js +2 -2
  433. package/dist/src/utils/install-manager.js +2 -1
  434. package/dist/src/utils/logger.js +2 -2
  435. package/dist/src/utils/permission-mode.js +5 -3
  436. package/dist/src/utils/redactor.js +1 -1
  437. package/dist/src/utils/settings.d.ts +153 -5
  438. package/dist/src/utils/settings.js +233 -29
  439. package/dist/src/utils/visible-retry.d.ts +11 -0
  440. package/dist/src/utils/visible-retry.js +10 -1
  441. package/dist/src/verify/entrypoint.d.ts +1 -1
  442. package/dist/src/verify/entrypoint.js +1 -1
  443. package/dist/src/verify/recipes.d.ts +13 -0
  444. package/dist/src/verify/recipes.js +15 -0
  445. package/package.json +135 -132
  446. package/dist/src/providers/auth/gcloud.d.ts +0 -28
  447. package/dist/src/providers/auth/gcloud.js +0 -102
  448. package/dist/src/providers/auth/gemini-oauth.d.ts +0 -82
  449. package/dist/src/providers/auth/gemini-oauth.js +0 -472
  450. package/dist/src/providers/gemini.d.ts +0 -11
  451. package/dist/src/providers/gemini.js +0 -45
  452. package/dist/src/providers/siliconflow-sse-repair.d.ts +0 -58
  453. package/dist/src/providers/siliconflow-sse-repair.js +0 -177
  454. package/dist/src/providers/strategies/google.strategy.d.ts +0 -22
  455. package/dist/src/providers/strategies/google.strategy.js +0 -174
  456. package/dist/src/providers/strategies/siliconflow.strategy.js +0 -29
  457. package/dist/src/ui/containers/chat-feed.d.ts +0 -40
  458. package/dist/src/ui/containers/chat-feed.js +0 -66
@@ -12,16 +12,8 @@
12
12
  */
13
13
  import { existsSync, readFileSync } from "node:fs";
14
14
  import { extname, resolve } from "node:path";
15
- import { apiBaseFor } from "./endpoints.js";
16
- import { loadKeyForProvider } from "./keychain.js";
15
+ import { callVisionBackend, formatNativeVisionObservation, formatNativeVisionUnavailable, looksLikeOcrIntent, resolveAvailableVisionChain, wrapAnalyzerInstructions, } from "./vision-backend.js";
17
16
  import { needsVisionProxy } from "./vision-proxy.js";
18
- const VISION_MODELS = [
19
- "Qwen/Qwen3-VL-8B-Instruct",
20
- "Qwen/Qwen3-VL-30B-A3B-Instruct",
21
- "Qwen/Qwen3-VL-32B-Instruct",
22
- ];
23
- const SILICONFLOW_BASE = apiBaseFor("siliconflow");
24
- const REQUEST_TIMEOUT_MS = 90_000;
25
17
  const IMAGE_CACHE_MAX = 20;
26
18
  const IMAGE_CACHE_TTL_MS = 15 * 60 * 1000; // 15 minutes
27
19
  /**
@@ -174,8 +166,8 @@ export async function bridgeMcpToolResult(toolName, toolOutput, modelId, signal,
174
166
  }
175
167
  // Detect context for better analysis prompt
176
168
  const context = detectImageContext(toolName, toolOutput);
177
- const description = await analyzeImages(images, context, signal);
178
- if (!description) {
169
+ const rawObservation = await analyzeImages(images, context, signal);
170
+ if (!rawObservation) {
179
171
  // Even when the vision proxy fails (no API key / network error), we MUST
180
172
  // still strip the base64 payload from the tool output. Otherwise a single
181
173
  // Playwright screenshot (~1.5MB of base64) lands in conversation history
@@ -187,15 +179,21 @@ export async function bridgeMcpToolResult(toolName, toolOutput, modelId, signal,
187
179
  proxied: true,
188
180
  };
189
181
  }
190
- const cachedIds = addToCache(images.map((img) => ({ ...img, source: baseName || toolName })), description, `${baseName} result`);
182
+ const cachedIds = addToCache(images.map((img) => ({ ...img, source: baseName || toolName })), rawObservation, `${baseName} result`);
191
183
  const cleanOutput = stripBase64FromOutput(toolOutput);
192
- const cacheHint = `\n[Cached as ${cachedIds.join(", ")} — use ask_vision_proxy for follow-up questions]`;
193
- const fullDescription = `${description}${cacheHint}`;
184
+ const fullDescription = formatNativeVisionObservation(rawObservation, {
185
+ imageCount: images.length,
186
+ cachedIds,
187
+ });
194
188
  const enhanced = typeof cleanOutput === "string"
195
189
  ? `${cleanOutput}\n\n${fullDescription}`
196
- : { ...cleanOutput, _visionDescription: description, _cachedImageIds: cachedIds };
190
+ : {
191
+ ...cleanOutput,
192
+ _visionDescription: fullDescription,
193
+ _cachedImageIds: cachedIds,
194
+ };
197
195
  recordBridgedDescription(toolCallId, fullDescription);
198
- return { output: enhanced, proxied: true, description, cachedImageIds: cachedIds };
196
+ return { output: enhanced, proxied: true, description: fullDescription, cachedImageIds: cachedIds };
199
197
  }
200
198
  /**
201
199
  * Proactively analyze an image from a file path, URL, or raw base64.
@@ -246,12 +244,13 @@ export async function analyzeImageFromSource(source, question, cwd, signal) {
246
244
  }
247
245
  const context = question ? { type: "user-query", hint: question } : { type: "generic" };
248
246
  const prompt = question ? buildFollowUpPrompt(question, images.length) : undefined;
249
- const description = await analyzeImages(images, context, signal, prompt);
250
- if (!description) {
251
- return "Vision proxy could not analyze the image. Check SILICONFLOW_API_KEY configuration.";
247
+ const rawObservation = await analyzeImages(images, context, signal, prompt);
248
+ if (!rawObservation) {
249
+ const { collectVisionUnavailableReasons } = await import("./vision-backend.js");
250
+ return formatNativeVisionUnavailable(1, await collectVisionUnavailableReasons());
252
251
  }
253
- const cachedIds = addToCache(images, description, source);
254
- return `${description}\n[Cached as ${cachedIds.join(", ")} — use ask_vision_proxy for follow-up questions]`;
252
+ const cachedIds = addToCache(images, rawObservation, source);
253
+ return formatNativeVisionObservation(rawObservation, { imageCount: images.length, cachedIds });
255
254
  }
256
255
  /**
257
256
  * Ask a follow-up question about a previously cached image,
@@ -278,17 +277,10 @@ export async function askVisionProxy(question, imageIdOrPath, cwd, signal) {
278
277
  }
279
278
  return `No matching image. Available:\n${cached.map((c) => `- ${c.id}: ${c.label} (${c.age})`).join("\n")}\n\nSpecify image_id, or provide a file_path to analyze a new image.`;
280
279
  }
281
- let apiKey;
282
- try {
283
- apiKey = await loadKeyForProvider("siliconflow");
284
- }
285
- catch {
286
- return "Vision proxy unavailable — SILICONFLOW_API_KEY not configured.";
287
- }
288
280
  const visionContent = [];
289
281
  visionContent.push({
290
282
  type: "text",
291
- text: buildFollowUpPrompt(question, targets.length),
283
+ text: wrapAnalyzerInstructions(buildFollowUpPrompt(question, targets.length), looksLikeOcrIntent(question) ? "ocr" : "default"),
292
284
  });
293
285
  for (const img of targets) {
294
286
  visionContent.push({
@@ -299,19 +291,15 @@ export async function askVisionProxy(question, imageIdOrPath, cwd, signal) {
299
291
  },
300
292
  });
301
293
  }
302
- for (const model of VISION_MODELS) {
303
- try {
304
- const result = await callVisionModel(model, visionContent, apiKey, signal);
305
- if (result) {
306
- return `[Vision Proxy Answer — via ${model}]\n${result}\n[/Vision Proxy Answer]`;
307
- }
308
- }
309
- catch (err) {
310
- if (signal?.aborted)
311
- throw err;
312
- }
294
+ const kind = looksLikeOcrIntent(question) ? "ocr" : "default";
295
+ const result = await callVisionBackend(await resolveAvailableVisionChain(kind), visionContent, signal);
296
+ if (result.ok) {
297
+ return formatNativeVisionObservation(result.text, {
298
+ imageCount: targets.length,
299
+ cachedIds: targets.map((t) => t.id),
300
+ });
313
301
  }
314
- return "Vision proxy failed. Try again or describe what you need help with.";
302
+ return formatNativeVisionUnavailable(targets.length, [result.reason]);
315
303
  }
316
304
  /**
317
305
  * System prompt guidance for text-only models — UNIVERSAL, not Playwright-specific.
@@ -320,32 +308,27 @@ export function getVisionGuidanceForTextOnly(modelId) {
320
308
  if (!needsVisionProxy(modelId))
321
309
  return "";
322
310
  return `
323
- VISION PROXY (text-only model):
324
- You cannot see images directly. You have vision proxy tools to work with ANY image:
311
+ VISION (extended capability):
312
+ You have full visual understanding via auto-injected <vision-observation> blocks and vision tools.
313
+ Treat every <vision-observation> as YOUR direct sight — respond naturally as if you saw the image yourself.
325
314
 
326
- TOOLS:
327
- - analyze_image: Proactively analyze an image from a file path, URL, or base64. Use this FIRST when you encounter or need to work with an image.
328
- - ask_vision_proxy: Ask a specific follow-up question about any cached image (or provide a file path for a new image).
329
- - list_vision_cache: See all cached images available for querying.
315
+ TOOLS (when you need a closer look or a new image):
316
+ - analyze_image: Inspect an image file path, data URI, or base64. Use when you encounter a new image or need a fresh screenshot analyzed.
317
+ - ask_vision_proxy: Ask a follow-up about a cached image (by ID) or a file path zoom in on a region, read text, compare colors.
318
+ - list_vision_cache: List cached image IDs available for follow-up questions.
330
319
 
331
- WHEN TO USE (be PROACTIVE, not passive):
332
- - User mentions an image file → analyze_image immediately
333
- - User pastes or references a screenshot it's auto-analyzed, use ask_vision_proxy for details
334
- - Working with UI/web pagesprefer browser_snapshot (text-based), use screenshot + ask_vision_proxy when visual details matter
335
- - Reviewing design mockups, diagrams, charts → analyze_image the file
336
- - Debugging visual issues (CSS, layout, colors) → take screenshot, then ask_vision_proxy specific questions
337
- - Comparing before/after → analyze both images, ask about differences
338
- - Reading text from images (OCR) → analyze_image with a question like "transcribe all text"
339
- - Any file with image extension (.png, .jpg, .gif, .webp, .svg, etc.) → analyze_image
320
+ WHEN TO ACT:
321
+ - User mentions an image → analyze_image immediately (do not guess)
322
+ - Tool results include <vision-observation> use that as your sight; ask_vision_proxy if any detail is unclear
323
+ - UI debugging → screenshot + ask_vision_proxy for specific elements
324
+ - OCR / transcribe → analyze_image with question "transcribe all visible text"
340
325
 
341
- WORKFLOW:
342
- 1. Encounter image analyze_image (or it's auto-analyzed from tool results)
343
- 2. Need more detail ask_vision_proxy with specific question
344
- 3. Need to verify changes take new screenshot/analyze new image → compare
326
+ IF STILL UNCLEAR after an observation:
327
+ 1. ask_vision_proxy with a precise question about the unclear region or text
328
+ 2. analyze_image again on a new or higher-resolution file
329
+ 3. Ask the user to share another screenshot or clarify what to focus on
345
330
 
346
- Images are cached (up to ${IMAGE_CACHE_MAX}, ${IMAGE_CACHE_TTL_MS / 60000}min TTL). You can reference them by ID for follow-ups.
347
-
348
- IMPORTANT: Do NOT guess what an image contains. Always use the proxy to get accurate information.
331
+ Never invent visual details. Cached images: up to ${IMAGE_CACHE_MAX}, ${IMAGE_CACHE_TTL_MS / 60000}min TTL.
349
332
  `;
350
333
  }
351
334
  // Keep backward compat export
@@ -380,17 +363,11 @@ async function analyzeImages(images, context, signal, customPrompt) {
380
363
  const svgFast = trySvgFastPath(images, context);
381
364
  if (svgFast)
382
365
  return svgFast;
383
- let apiKey;
384
- try {
385
- apiKey = await loadKeyForProvider("siliconflow");
386
- }
387
- catch {
388
- return null;
389
- }
366
+ const kind = context.type === "design" ? "design" : customPrompt && looksLikeOcrIntent(customPrompt) ? "ocr" : "default";
390
367
  const visionContent = [];
391
368
  visionContent.push({
392
369
  type: "text",
393
- text: customPrompt ?? buildContextualPrompt(images.length, context),
370
+ text: wrapAnalyzerInstructions(customPrompt ?? buildContextualPrompt(images.length, context), kind),
394
371
  });
395
372
  for (const img of images) {
396
373
  visionContent.push({
@@ -401,22 +378,10 @@ async function analyzeImages(images, context, signal, customPrompt) {
401
378
  },
402
379
  });
403
380
  }
404
- // Force JSON output for the design contract. Markdown narrative is fine for
405
- // debug-style contexts (terminal, code) where the human reads the result,
406
- // but design output is consumed machine-to-machine.
407
381
  const responseFormat = context.type === "design" ? { type: "json_object" } : undefined;
408
- for (const model of VISION_MODELS) {
409
- try {
410
- const result = await callVisionModel(model, visionContent, apiKey, signal, responseFormat);
411
- if (result) {
412
- return formatBridgeResult(result, images.length, model, context.type);
413
- }
414
- }
415
- catch (err) {
416
- if (signal?.aborted)
417
- throw err;
418
- }
419
- }
382
+ const result = await callVisionBackend(await resolveAvailableVisionChain(kind), visionContent, signal, responseFormat);
383
+ if (result.ok)
384
+ return result.text;
420
385
  return null;
421
386
  }
422
387
  function trySvgFastPath(images, context) {
@@ -437,11 +402,10 @@ function trySvgFastPath(images, context) {
437
402
  return svg.length > 32_000 ? `${svg.slice(0, 32_000)}\n<!-- truncated -->` : svg;
438
403
  })
439
404
  .join("\n\n");
440
- const header = `[Vision Bridge — ${images.length} SVG source(s) passed through (${context.type}, fast-path, no vision call)]`;
441
- const guidance = context.type === "design"
442
- ? `\nThe raw SVG below IS the layout contract. Coordinates and colors are exact. Map <rect>/<text>/<g> nodes to the schema:\n${UI_LAYOUT_SCHEMA_HINT}\n`
443
- : "\nThe raw SVG below is vector text — read element attributes (x, y, width, fill, text content) directly.\n";
444
- return `\n${header}${guidance}\n\`\`\`svg\n${decoded}\n\`\`\`\n[/Vision Bridge]\n`;
405
+ const observation = context.type === "design"
406
+ ? `I see ${images.length} SVG source(s). Vector markup is exact — map nodes to:\n${UI_LAYOUT_SCHEMA_HINT}\n\n\`\`\`svg\n${decoded}\n\`\`\``
407
+ : `I see ${images.length} SVG source(s). Vector markup:\n\n\`\`\`svg\n${decoded}\n\`\`\``;
408
+ return observation;
445
409
  }
446
410
  function buildContextualPrompt(imageCount, context) {
447
411
  const plural = imageCount > 1 ? `${imageCount} images` : "the image";
@@ -799,63 +763,9 @@ function walkAndStrip(obj) {
799
763
  }
800
764
  }
801
765
  function wrapWithFallback(output, imageCount) {
802
- const notice = `\n[Vision Bridge — ${imageCount} image(s) could not be analyzed. Use analyze_image or ask_vision_proxy with a file path to retry.]\n`;
766
+ const notice = formatNativeVisionUnavailable(imageCount, ["auto-analysis failed"]);
803
767
  if (typeof output === "string")
804
768
  return `${output}\n${notice}`;
805
769
  return { ...output, _visionNotice: notice };
806
770
  }
807
- async function callVisionModel(model, content, apiKey, signal, responseFormat) {
808
- const controller = new AbortController();
809
- let timedOut = false;
810
- const timeout = setTimeout(() => {
811
- timedOut = true;
812
- controller.abort();
813
- }, REQUEST_TIMEOUT_MS);
814
- if (signal) {
815
- signal.addEventListener("abort", () => controller.abort(), { once: true });
816
- }
817
- let res;
818
- try {
819
- res = await fetch(`${SILICONFLOW_BASE}/chat/completions`, {
820
- method: "POST",
821
- headers: {
822
- "Content-Type": "application/json",
823
- Authorization: `Bearer ${apiKey}`,
824
- },
825
- body: JSON.stringify({
826
- model,
827
- messages: [{ role: "user", content }],
828
- max_tokens: 3072,
829
- temperature: 0.1,
830
- ...(responseFormat ? { response_format: responseFormat } : {}),
831
- }),
832
- signal: controller.signal,
833
- });
834
- }
835
- catch (err) {
836
- clearTimeout(timeout);
837
- if (timedOut) {
838
- console.warn(`[vision-bridge] ${model} timed out after ${REQUEST_TIMEOUT_MS}ms`);
839
- return null;
840
- }
841
- if (signal?.aborted)
842
- throw err;
843
- console.warn(`[vision-bridge] ${model} network error: ${err instanceof Error ? err.message : String(err)}`);
844
- return null;
845
- }
846
- clearTimeout(timeout);
847
- if (!res.ok) {
848
- const errText = await res.text().catch(() => "");
849
- console.warn(`[vision-bridge] ${model} HTTP ${res.status}: ${errText.slice(0, 200)}`);
850
- return null;
851
- }
852
- const data = (await res.json().catch(() => null));
853
- return data?.choices?.[0]?.message?.content ?? null;
854
- }
855
- function formatBridgeResult(description, imageCount, model, contextType) {
856
- const header = imageCount > 1
857
- ? `[Vision Bridge — ${imageCount} images analyzed (${contextType}) via ${model}]`
858
- : `[Vision Bridge — image analyzed (${contextType}) via ${model}]`;
859
- return `\n${header}\n${description}\n[/Vision Bridge]\n`;
860
- }
861
771
  //# sourceMappingURL=mcp-vision-bridge.js.map
@@ -28,6 +28,12 @@ export function createOpenAICompatibleAdapter(config) {
28
28
  // placeholder because the SDK always emits an Authorization header from it.
29
29
  apiKey: config.apiKey ?? (config.oauthHeaders ? "oauth" : undefined),
30
30
  ...(config.oauthHeaders ? { headers: config.oauthHeaders } : {}),
31
+ transformRequestBody: (body) => {
32
+ const next = { ...body };
33
+ delete next.reasoning_effort;
34
+ delete next.verbosity;
35
+ return next;
36
+ },
31
37
  });
32
38
  return {
33
39
  id: config.id,
@@ -44,8 +50,9 @@ export function createOpenAICompatibleAdapter(config) {
44
50
  : undefined,
45
51
  });
46
52
  }
53
+ const cleanModel = config.model.startsWith("opencode/") ? config.model.slice(9) : config.model;
47
54
  const result = streamText({
48
- model: provider(config.model),
55
+ model: provider(cleanModel),
49
56
  messages: req.messages,
50
57
  tools: req.tools,
51
58
  toolChoice: req.toolChoice,
@@ -2,7 +2,7 @@
2
2
  * src/providers/pricing.ts
3
3
  *
4
4
  * Pricing lookup: catalog-first, then static fallback for providers not in catalog
5
- * (anthropic, openai, google — they're not in catalog.json).
5
+ * (anthropic, openai — they're not in catalog.json).
6
6
  * Phase 1 ships static; Phase 4 (WEB-02) adds remote fetch.
7
7
  * PROV-06 requirement.
8
8
  *
@@ -31,7 +31,7 @@ export declare const STATIC_PRICING_FALLBACK: Record<string, Record<string, Pric
31
31
  /**
32
32
  * Look up pricing for a (provider, model) pair.
33
33
  * Checks catalog first (preferred — single source of truth).
34
- * Falls back to static table for models not in catalog (anthropic, openai, google, legacy providers).
34
+ * Falls back to static table for models not in catalog (anthropic, openai, legacy providers).
35
35
  * Returns undefined if provider or model is not found in either source.
36
36
  * Ollama uses a '*' wildcard in the static table that matches any model.
37
37
  */
@@ -2,7 +2,7 @@
2
2
  * src/providers/pricing.ts
3
3
  *
4
4
  * Pricing lookup: catalog-first, then static fallback for providers not in catalog
5
- * (anthropic, openai, google — they're not in catalog.json).
5
+ * (anthropic, openai — they're not in catalog.json).
6
6
  * Phase 1 ships static; Phase 4 (WEB-02) adds remote fetch.
7
7
  * PROV-06 requirement.
8
8
  *
@@ -10,7 +10,7 @@
10
10
  * Freshness policy: re-verify every 60 days.
11
11
  */
12
12
  import { MODELS } from "../models/registry.js";
13
- // Static fallback for providers not in catalog (anthropic, openai, google).
13
+ // Static fallback for providers not in catalog (anthropic, openai).
14
14
  // verified 2026-04-29 from official provider pricing pages; cache prices
15
15
  // re-verified 2026-05-08 against deepseek-platform / openai / anthropic docs
16
16
  export const STATIC_PRICING_FALLBACK = {
@@ -51,10 +51,6 @@ export const STATIC_PRICING_FALLBACK = {
51
51
  cached_input_per_million_usd: 7.5,
52
52
  },
53
53
  },
54
- google: {
55
- "gemini-2.5-flash": { input_per_million_usd: 0.3, output_per_million_usd: 2.5 }, // ai.google.dev/pricing
56
- "gemini-pro-latest": { input_per_million_usd: 1.25, output_per_million_usd: 10.0 }, // ai.google.dev/pricing
57
- },
58
54
  deepseek: {
59
55
  // DeepSeek V4 chat: $0.27/M input miss, $0.027/M input hit, $1.10/M output
60
56
  // (api-docs.deepseek.com/quick_start/pricing). Flash/Pro split below mirrors
@@ -70,12 +66,6 @@ export const STATIC_PRICING_FALLBACK = {
70
66
  output_per_million_usd: 2.19,
71
67
  },
72
68
  },
73
- siliconflow: {
74
- "Qwen/Qwen2.5-Coder-32B-Instruct": { input_per_million_usd: 0.18, output_per_million_usd: 0.18 }, // siliconflow.com/pricing
75
- // DeepSeek models served via SiliconFlow — keep in sync with catalog.json.
76
- "deepseek-ai/DeepSeek-V4-Flash": { input_per_million_usd: 0.1, output_per_million_usd: 0.4 },
77
- "deepseek-ai/DeepSeek-V4-Pro": { input_per_million_usd: 2.0, output_per_million_usd: 8.0 },
78
- },
79
69
  ollama: {
80
70
  // local-only — zero variable cost
81
71
  "*": { input_per_million_usd: 0, output_per_million_usd: 0 },
@@ -84,7 +74,7 @@ export const STATIC_PRICING_FALLBACK = {
84
74
  /**
85
75
  * Look up pricing for a (provider, model) pair.
86
76
  * Checks catalog first (preferred — single source of truth).
87
- * Falls back to static table for models not in catalog (anthropic, openai, google, legacy providers).
77
+ * Falls back to static table for models not in catalog (anthropic, openai, legacy providers).
88
78
  * Returns undefined if provider or model is not found in either source.
89
79
  * Ollama uses a '*' wildcard in the static table that matches any model.
90
80
  */
@@ -6,6 +6,15 @@ export type ProviderFactory = ((modelId: string) => any) & {
6
6
  defaultProviderOptions?: Record<string, unknown>;
7
7
  /** AI SDK top-level call params to strip (backend doesn't accept them). */
8
8
  unsupportedParams?: ReadonlyArray<"maxOutputTokens" | "temperature" | "topP">;
9
+ /**
10
+ * The provider this factory talks to. Stamped by createProviderFactory so a
11
+ * caller that blindly reuses one provider's factory with a model from a
12
+ * DIFFERENT provider can be detected and auto-corrected in resolveModelRuntime
13
+ * (see the factory registry below) — otherwise the request would hit the wrong
14
+ * endpoint (e.g. a gateway-routed model POSTed straight to a native API,
15
+ * bypassing the configured gateway).
16
+ */
17
+ providerId?: ProviderId;
9
18
  };
10
19
  export interface ProviderFactoryResult {
11
20
  id: ProviderId;
@@ -19,6 +28,8 @@ export interface ResolvedModelRuntime {
19
28
  /** Top-level streamText params to omit (backend doesn't accept them). */
20
29
  unsupportedParams?: ReadonlyArray<"maxOutputTokens" | "temperature" | "topP">;
21
30
  }
31
+ /** Test seam: clear the registry between cases so entries never leak across specs. */
32
+ export declare function __resetProviderFactoryRegistry(): void;
22
33
  /**
23
34
  * Phase 12.2-G4: thin dispatcher delegating to the provider strategy registry.
24
35
  * Each provider's SDK wiring + `factory.responses` + `defaultProviderOptions`
@@ -34,8 +45,6 @@ export declare function createProviderFactory(id: ProviderId, opts: {
34
45
  * For OpenAI: loads stored OAuth tokens (auto-refreshing if expiring) and injects
35
46
  * them as Authorization / ChatGPT-Account-ID headers so subscription-backed
36
47
  * ChatGPT Plus/Pro accounts work without an API key.
37
- * For Google: loads stored Agy OAuth tokens and injects Authorization header
38
- * so users can authenticate via their Google account without a GOOGLE_API_KEY.
39
48
  * Falls back to API-key path when no tokens are stored.
40
49
  * All other providers: identical to createProviderFactory.
41
50
  */
@@ -87,5 +96,21 @@ export declare function buildTurnProviderOptions(runtime: ResolvedModelRuntime,
87
96
  * top-level path, and cost-leak specs cannot drift apart.
88
97
  */
89
98
  export declare function shouldDropParam(runtime: ResolvedModelRuntime, param: "maxOutputTokens" | "temperature" | "topP"): boolean;
99
+ /**
100
+ * Resolve the `temperature` spread for a streamText/generateText call.
101
+ *
102
+ * Returns `{}` (omit the param) when the model does not accept temperature
103
+ * (reasoning models, OAuth `unsupportedParams`), `{ temperature: <fixed> }`
104
+ * when the catalog pins a `fixed_temperature` (e.g. Moonshot/Kimi via
105
+ * opencode-go reject any value but `1` with "invalid temperature: only 1 is
106
+ * allowed for this model"), otherwise `{ temperature: <desired> }`.
107
+ *
108
+ * Every orchestrator call site that sets a temperature MUST go through this
109
+ * helper — inlining `temperature: 0.7` is what made every Kimi tool-loop turn
110
+ * fail wholesale (mirrors `resolveTemperature` used in src/council/llm.ts).
111
+ */
112
+ export declare function resolveTemperatureParam(runtime: ResolvedModelRuntime, desired: number): {
113
+ temperature?: number;
114
+ };
90
115
  export declare function detectProviderForModel(modelId: string): ProviderId;
91
116
  export declare function requireRuntimeProvider(runtime: ResolvedModelRuntime): ProviderId;
@@ -1,8 +1,24 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { getModelInfo } from "../models/registry.js";
3
+ import { logger } from "../utils/logger.js";
3
4
  import { getReasoningEffortForModel } from "../utils/settings.js";
4
5
  import { getProviderCapabilities } from "./capabilities.js";
5
6
  import { getProviderStrategy } from "./strategies/registry.js";
7
+ /**
8
+ * Session-scoped registry of the most-recently-built factory per provider.
9
+ * resolveModelRuntime consults this to auto-correct a factory/model provider
10
+ * mismatch: when a caller reuses provider A's factory to run a model that
11
+ * belongs to provider B (a real hazard in sub-task paths like compaction that
12
+ * inherit the parent session's factory), we substitute B's real factory if one
13
+ * was built this session. Single-orchestrator invariant (v1, see CQ-16a) makes
14
+ * a module-level map safe. Last-built wins, so a /model key change that rebuilds
15
+ * a provider's factory transparently refreshes the entry.
16
+ */
17
+ const providerFactoryRegistry = new Map();
18
+ /** Test seam: clear the registry between cases so entries never leak across specs. */
19
+ export function __resetProviderFactoryRegistry() {
20
+ providerFactoryRegistry.clear();
21
+ }
6
22
  /**
7
23
  * Phase 12.2-G4: thin dispatcher delegating to the provider strategy registry.
8
24
  * Each provider's SDK wiring + `factory.responses` + `defaultProviderOptions`
@@ -10,15 +26,16 @@ import { getProviderStrategy } from "./strategies/registry.js";
10
26
  */
11
27
  export function createProviderFactory(id, opts) {
12
28
  const strategy = getProviderStrategy(id);
13
- return { id, factory: strategy.createFactory(opts) };
29
+ const factory = strategy.createFactory(opts);
30
+ factory.providerId = id;
31
+ providerFactoryRegistry.set(id, factory);
32
+ return { id, factory };
14
33
  }
15
34
  /**
16
35
  * Async variant of createProviderFactory.
17
36
  * For OpenAI: loads stored OAuth tokens (auto-refreshing if expiring) and injects
18
37
  * them as Authorization / ChatGPT-Account-ID headers so subscription-backed
19
38
  * ChatGPT Plus/Pro accounts work without an API key.
20
- * For Google: loads stored Agy OAuth tokens and injects Authorization header
21
- * so users can authenticate via their Google account without a GOOGLE_API_KEY.
22
39
  * Falls back to API-key path when no tokens are stored.
23
40
  * All other providers: identical to createProviderFactory.
24
41
  */
@@ -38,13 +55,12 @@ export async function createProviderFactoryAsync(id, opts) {
38
55
  // override the OAuth backend and produce a 401 "Missing scopes:
39
56
  // api.responses.write" when the subscription token hits the platform
40
57
  // API. Fall back to opts.baseURL only when the OAuth provider declares
41
- // no dedicated backend (cfg.baseURL undefined, e.g. some Gemini flows).
58
+ // no dedicated backend (cfg.baseURL undefined).
42
59
  //
43
60
  // However, a user-specified baseURL in settings (e.g. switching to a
44
61
  // different backend after an OAuth provider is killed) MUST override
45
- // the hardcoded OAuth default. This lets users migrate from the old
46
- // cloudcode-pa.googleapis.com endpoint to a new proxy/backend without
47
- // modifying source.
62
+ // the hardcoded OAuth default. This lets users migrate to a new
63
+ // proxy/backend without modifying source.
48
64
  const { loadUserSettings } = await import("../utils/settings.js");
49
65
  const userSettings = loadUserSettings();
50
66
  const userBaseURL = userSettings?.providers?.[id]?.baseURL;
@@ -68,8 +84,8 @@ export async function createProviderFactoryAsync(id, opts) {
68
84
  }
69
85
  export function resolveModelRuntime(factory, modelId) {
70
86
  // Resolve aliases (e.g. "deepseek-v4-flash") to the provider-native id
71
- // (e.g. "deepseek-ai/DeepSeek-V4-Flash") BEFORE invoking the factory.
72
- // Without this, SiliconFlow / DeepSeek / xAI reject the request because
87
+ // (e.g. "deepseek-v4-flash") BEFORE invoking the factory.
88
+ // Without this, DeepSeek / xAI reject the request because
73
89
  // the alias is not a valid model id on their API.
74
90
  const mockGlobals = globalThis;
75
91
  const modelInfo = getModelInfo(modelId);
@@ -78,6 +94,32 @@ export function resolveModelRuntime(factory, modelId) {
78
94
  if (!providerId && !mockGlobals.__muonroiMockModel) {
79
95
  throw new Error(`Model "${modelId}" not found in catalog — cannot determine provider.`);
80
96
  }
97
+ // Factory/model provider guard (Layer 2). A caller that reuses one provider's
98
+ // factory to run a model from another provider (e.g. compaction inheriting the
99
+ // parent session's native factory for a gateway-routed model) would otherwise
100
+ // POST to the wrong endpoint. If the passed factory is stamped for a different
101
+ // provider AND we built the correct one this session, substitute it so the
102
+ // request lands on the right backend; otherwise keep going (Part 3's wire-id
103
+ // normalization keeps it valid) but log the mismatch so it is never silent.
104
+ let effectiveFactory = factory;
105
+ if (providerId && factory.providerId && factory.providerId !== providerId) {
106
+ const correct = providerFactoryRegistry.get(providerId);
107
+ if (correct) {
108
+ effectiveFactory = correct;
109
+ logger.debug("orchestrator", "Redirected model to its own provider factory", {
110
+ modelId: canonicalId,
111
+ modelProvider: providerId,
112
+ factoryProvider: factory.providerId,
113
+ });
114
+ }
115
+ else {
116
+ logger.warn("orchestrator", "Factory/model provider mismatch; no factory for model's provider", {
117
+ modelId: canonicalId,
118
+ modelProvider: providerId,
119
+ factoryProvider: factory.providerId,
120
+ });
121
+ }
122
+ }
81
123
  const userEffort = getReasoningEffortForModel(modelId);
82
124
  const mockModel = mockGlobals.__muonroiMockModel;
83
125
  // Determine the language model + unsupportedParams + provider-level defaults.
@@ -107,12 +149,12 @@ export function resolveModelRuntime(factory, modelId) {
107
149
  else {
108
150
  const strategy = getProviderStrategy(providerId);
109
151
  resolved = strategy.resolve({
110
- factory,
152
+ factory: effectiveFactory,
111
153
  modelId: canonicalId,
112
154
  modelInfo,
113
155
  reasoningEffort: userEffort,
114
156
  });
115
- providerLevelDefaults = factory.defaultProviderOptions;
157
+ providerLevelDefaults = effectiveFactory.defaultProviderOptions;
116
158
  }
117
159
  // Merge provider-level defaults from the factory (e.g. OAuth backends inject
118
160
  // `instructions` + `store: false` for the ChatGPT Codex API). This keeps
@@ -229,6 +271,27 @@ export function shouldDropParam(runtime, param) {
229
271
  }
230
272
  return runtime.unsupportedParams?.includes(param) ?? false;
231
273
  }
274
+ /**
275
+ * Resolve the `temperature` spread for a streamText/generateText call.
276
+ *
277
+ * Returns `{}` (omit the param) when the model does not accept temperature
278
+ * (reasoning models, OAuth `unsupportedParams`), `{ temperature: <fixed> }`
279
+ * when the catalog pins a `fixed_temperature` (e.g. Moonshot/Kimi via
280
+ * opencode-go reject any value but `1` with "invalid temperature: only 1 is
281
+ * allowed for this model"), otherwise `{ temperature: <desired> }`.
282
+ *
283
+ * Every orchestrator call site that sets a temperature MUST go through this
284
+ * helper — inlining `temperature: 0.7` is what made every Kimi tool-loop turn
285
+ * fail wholesale (mirrors `resolveTemperature` used in src/council/llm.ts).
286
+ */
287
+ export function resolveTemperatureParam(runtime, desired) {
288
+ if (shouldDropParam(runtime, "temperature"))
289
+ return {};
290
+ const fixed = runtime.modelInfo?.fixedTemperature;
291
+ if (typeof fixed === "number")
292
+ return { temperature: fixed };
293
+ return { temperature: desired };
294
+ }
232
295
  export function detectProviderForModel(modelId) {
233
296
  const info = getModelInfo(modelId);
234
297
  if (info?.provider) {
@@ -240,12 +303,12 @@ export function detectProviderForModel(modelId) {
240
303
  return "deepseek";
241
304
  if (id.startsWith("gpt-") || id.startsWith("o1") || id.startsWith("o3") || id.startsWith("o4"))
242
305
  return "openai";
243
- if (id.startsWith("gemini") || id.startsWith("models/gemini"))
244
- return "google";
245
306
  if (id.startsWith("grok"))
246
307
  return "xai";
247
- if (id.includes("qwen") || id.includes("glm") || id.includes("internlm"))
248
- return "siliconflow";
308
+ if (id.includes("glm") || id.startsWith("z-ai") || id.startsWith("zai"))
309
+ return "zai";
310
+ if (id.startsWith("opencode"))
311
+ return "opencode-go";
249
312
  if (id.startsWith("llama") || id.startsWith("mistral") || id.startsWith("phi-"))
250
313
  return "ollama";
251
314
  if (id.startsWith("claude"))
@@ -17,6 +17,22 @@ import type { ModelInfo } from "../../types/index.js";
17
17
  import type { ProviderCapabilities } from "../capabilities.js";
18
18
  import type { ProviderFactory, ResolvedModelRuntime } from "../runtime.js";
19
19
  import type { ProviderId } from "../types.js";
20
+ /**
21
+ * Catalog ids for models reached through the OpenCode Go (Console Go) gateway
22
+ * carry an `opencode/` routing prefix (e.g. `opencode/deepseek-v4-flash`). That
23
+ * prefix is a *routing* marker, not part of the wire model name any upstream
24
+ * accepts — the gateway itself strips it before forwarding (see
25
+ * OpenCodeGoStrategy.createFactory). When a task sub-model resolved from that
26
+ * catalog id is run through a DIFFERENT provider's factory (e.g. the compaction
27
+ * proposer reusing the parent's native DeepSeek factory), the raw prefixed id
28
+ * would otherwise be POSTed to api.deepseek.com and rejected with HTTP 400
29
+ * "The supported API model names are deepseek-v4-pro or deepseek-v4-flash, but
30
+ * you passed opencode/deepseek-v4-flash". Stripping here — the single chokepoint
31
+ * every provider's resolve() flows through — makes the wire name always native.
32
+ * The returned `modelId` keeps the catalog id so usage/pricing attribution is
33
+ * unchanged; only the id handed to factory() is normalized.
34
+ */
35
+ export declare function toWireModelId(modelId: string): string;
20
36
  export interface CreateFactoryOpts {
21
37
  apiKey?: string;
22
38
  baseURL?: string;