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
@@ -1,18 +1,24 @@
1
1
  import * as fs from "node:fs";
2
- import { generateText, stepCountIs } from "ai";
2
+ import { generateText, stepCountIs, streamText } from "ai";
3
3
  import { getDefaultEEClient } from "../ee/intercept.js";
4
4
  import { emitMatches } from "../ee/render.js";
5
+ import { getMcpKey } from "../mcp/mcp-keychain.js";
5
6
  import { buildMcpToolSet } from "../mcp/runtime.js";
6
- import { getProviderCapabilities } from "../providers/capabilities.js";
7
+ import { getModelInfo } from "../models/registry.js";
8
+ import { getProviderCapabilities, resolveTemperature } from "../providers/capabilities.js";
7
9
  import { loadKeyForProvider, ProviderKeyMissingError } from "../providers/keychain.js";
8
10
  import { createProviderFactoryAsync, detectProviderForModel, resolveModelRuntime } from "../providers/runtime.js";
11
+ import { statusBarStore } from "../state/status-bar-store.js";
12
+ import { recordUsageEvent } from "../storage/index.js";
9
13
  import { createBuiltinTools as createTools } from "../tools/registry.js";
10
14
  import { appendCostLog } from "../usage/cost-log.js";
11
15
  import { projectCostUSD } from "../usage/estimator.js";
12
16
  import { withDeadlineRace, withTimeoutSignal } from "../utils/llm-deadline.js";
17
+ import { logger } from "../utils/logger.js";
13
18
  import { loadMcpServers } from "../utils/settings.js";
14
19
  import { withVisibleRetry } from "../utils/visible-retry.js";
15
20
  import { buildResearchSystemPrompt } from "./prompts.js";
21
+ import { stripThinkBlocks } from "./strip-think.js";
16
22
  /**
17
23
  * Resolve a provider factory for a council sub-call, OAuth-aware.
18
24
  *
@@ -186,6 +192,62 @@ function logCouncilCost(args) {
186
192
  }).catch(() => undefined);
187
193
  return usage;
188
194
  }
195
+ /**
196
+ * Record a council LLM call into the `usage_events` table with source="council".
197
+ *
198
+ * This is the SINGLE source of truth for council usage accounting: it fires from
199
+ * inside every council generate/debate/research call so ALL council entry points
200
+ * (the /council slash path, auto-council, and every /ideal phase — clarifier,
201
+ * research, generate, sprint) land in usage_events. Before this existed, only
202
+ * the loop-driver's `runDebate` call site externally wrapped usage recording, so
203
+ * `runCouncilV2` (/council + auto-council) and the non-debate /ideal phases never
204
+ * recorded — their token cost was invisible to session totals, the StatusBar,
205
+ * `usage forensics`, and the cost caps (root cause of session f24c28b6dcb3:
206
+ * council ran but produced 0 source="council" usage_events).
207
+ *
208
+ * Best-effort: a null sessionId (no chat session FK) or a DB failure must never
209
+ * break the council run, but per the No-Silent-Catch rule we log the failure.
210
+ */
211
+ export function recordCouncilUsage(sessionId, modelId, usage) {
212
+ if (!sessionId)
213
+ return;
214
+ try {
215
+ recordUsageEvent(sessionId, "council", modelId, {
216
+ inputTokens: usage.inputTokens,
217
+ outputTokens: usage.outputTokens,
218
+ cacheReadTokens: usage.cachedInputTokens,
219
+ });
220
+ // Mirror council billing into the live StatusBar so cache-hit% / tokens /
221
+ // cost reflect the WHOLE session (council calls have ~0 cache; excluding
222
+ // them overstated the message-only cache ratio — session f24c28b6dcb3).
223
+ // ONLY the five cumulative billing counters — NEVER ctx_tokens/ctx_pct/
224
+ // model/provider, which describe the current MAIN-conversation call and
225
+ // would be clobbered by a small council sub-call.
226
+ const totalInput = usage.inputTokens;
227
+ const output = usage.outputTokens;
228
+ const cacheRead = usage.cachedInputTokens;
229
+ const info = getModelInfo(modelId);
230
+ const priceIn = info?.inputPrice ?? 0;
231
+ const priceCached = info?.cachedInputPrice ?? priceIn * 0.1;
232
+ const priceOut = info?.outputPrice ?? 0;
233
+ const nonCachedInput = Math.max(0, totalInput - cacheRead);
234
+ const turnCostMicros = nonCachedInput * priceIn + cacheRead * priceCached + output * priceOut;
235
+ const prev = statusBarStore.getState();
236
+ statusBarStore.setState({
237
+ in_tokens: prev.in_tokens + totalInput,
238
+ out_tokens: prev.out_tokens + output,
239
+ cache_read_tokens: (prev.cache_read_tokens ?? 0) + cacheRead,
240
+ session_usd: prev.session_usd + turnCostMicros / 1_000_000,
241
+ });
242
+ }
243
+ catch (err) {
244
+ logger.error("storage", "recordUsageEvent(council) failed — token cost not accounted", {
245
+ sessionId,
246
+ modelId,
247
+ message: err instanceof Error ? err.message : String(err),
248
+ });
249
+ }
250
+ }
189
251
  function getMockLlm() {
190
252
  // biome-ignore lint/suspicious/noExplicitAny: intentionally untyped global access
191
253
  return globalThis.__muonroiMockLlm ?? null;
@@ -209,6 +271,87 @@ const COUNCIL_LLM_TIMEOUT_MS = (() => {
209
271
  // withTimeoutSignal + withDeadlineRace moved to ../utils/llm-deadline.js so all
210
272
  // pre-flight LLM call sites (council, debate-planner, scope-ceiling) share one
211
273
  // implementation. Imported at the top of this file.
274
+ /**
275
+ * Run a single-shot LLM call over the STREAMING transport and collect the full
276
+ * result, returning a `generateText`-shaped object ({ text, usage, finishReason,
277
+ * reasoningText }).
278
+ *
279
+ * Why stream a one-shot generate? The OpenAI codex/oauth endpoint
280
+ * (chatgpt.com/backend-api/codex/responses, used by gpt-5.*-codex subscription
281
+ * auth) HARD-REJECTS non-streaming requests with 400 `{"detail":"Stream must be
282
+ * set to true"}`. `generateText` issues a non-stream POST, so every council
283
+ * sub-task that ran through it (leader round evaluation, clarify, spec
284
+ * synthesis, running summary) failed on a codex session — surfacing to the user
285
+ * as the opaque "Decision: evaluation unavailable" round card (diagnosed from
286
+ * session 8191ecaee149: gpt-5.4-mini → codex/responses → "Stream must be set to
287
+ * true"). The panel debate never hit this because it streams. Streaming is the
288
+ * universal transport (every provider + the whole TUI already use it), so
289
+ * collecting a streamed result fixes codex without special-casing it.
290
+ *
291
+ * A provider `error` part is re-thrown so the caller's retry / cross-provider
292
+ * fallback treats it as a failure exactly as a thrown `generateText` did.
293
+ *
294
+ * Also used by `debate()` (the panel pair turns) with the tiny verification
295
+ * toolset: pass `tools`/`stopWhen`/`prepareStep` and the collected `toolCalls`
296
+ * (toolName + input + matched result) come back in the same shape the old
297
+ * `generateText` result exposed. A debater on the codex/oauth endpoint hit the
298
+ * exact same non-stream 400 as the eval path — streaming fixes it uniformly.
299
+ */
300
+ async function collectStreamText(args) {
301
+ const hasTools = !!args.tools && Object.keys(args.tools).length > 0;
302
+ const result = streamText({
303
+ model: args.model,
304
+ system: args.system,
305
+ prompt: args.prompt,
306
+ maxOutputTokens: args.maxOutputTokens,
307
+ maxRetries: 0,
308
+ ...(args.temperature === undefined ? {} : { temperature: args.temperature }),
309
+ ...(args.providerOptions ? { providerOptions: args.providerOptions } : {}),
310
+ ...(hasTools ? { tools: args.tools, stopWhen: args.stopWhen, prepareStep: args.prepareStep } : {}),
311
+ abortSignal: args.abortSignal,
312
+ });
313
+ let text = "";
314
+ let reasoningText = "";
315
+ let usage;
316
+ let finishReason;
317
+ const toolCalls = [];
318
+ const byId = new Map();
319
+ for await (const part of result.fullStream) {
320
+ switch (part.type) {
321
+ case "text-delta":
322
+ text += part.text ?? "";
323
+ break;
324
+ case "reasoning-delta":
325
+ reasoningText += part.text ?? "";
326
+ break;
327
+ case "tool-call": {
328
+ const p = part;
329
+ const tc = { toolName: p.toolName, input: p.input };
330
+ byId.set(p.toolCallId, tc);
331
+ toolCalls.push(tc);
332
+ break;
333
+ }
334
+ case "tool-result": {
335
+ const p = part;
336
+ const tc = byId.get(p.toolCallId);
337
+ if (tc)
338
+ tc.result = p.output;
339
+ break;
340
+ }
341
+ case "finish":
342
+ usage = part.totalUsage ?? part.usage;
343
+ finishReason = part.finishReason;
344
+ break;
345
+ case "error": {
346
+ const raw = part.error;
347
+ throw raw instanceof Error ? raw : new Error(String(raw));
348
+ }
349
+ default:
350
+ break;
351
+ }
352
+ }
353
+ return { text, usage, finishReason, reasoningText: reasoningText || undefined, toolCalls };
354
+ }
212
355
  export function createCouncilLLM(bash, mode, sessionId, stats) {
213
356
  return {
214
357
  async generate(modelId, system, prompt, maxTokens = 4096, onUsage, signal) {
@@ -216,7 +359,7 @@ export function createCouncilLLM(bash, mode, sessionId, stats) {
216
359
  if (mock) {
217
360
  stats.calls++;
218
361
  const result = await mock.complete({ prompt });
219
- return result.text;
362
+ return stripThinkBlocks(result.text);
220
363
  }
221
364
  const providerId = detectProviderForModel(modelId);
222
365
  const factory = await resolveCouncilFactory(providerId);
@@ -228,18 +371,23 @@ export function createCouncilLLM(bash, mode, sessionId, stats) {
228
371
  // eval) was a no-op — the call ran to completion or hit the 5-min timeout.
229
372
  const { signal: timedSignal, cleanup: cleanupTimeout } = withTimeoutSignal(signal, COUNCIL_LLM_TIMEOUT_MS);
230
373
  try {
231
- const result = await withDeadlineRace(() => withVisibleRetry(() => generateText({
374
+ const result = await withDeadlineRace(() => withVisibleRetry(() =>
375
+ // Stream + collect (NOT generateText). The codex/oauth endpoint
376
+ // 400s on non-stream requests ("Stream must be set to true"),
377
+ // which nulled every council eval/clarify/synthesis on a codex
378
+ // session → the opaque "evaluation unavailable" card. See
379
+ // collectStreamText's doc for the full diagnosis.
380
+ collectStreamText({
232
381
  model: runtime.model,
233
382
  system,
234
383
  prompt,
235
384
  maxOutputTokens: maxTokens,
236
- temperature: 0.7,
237
- // Visible retry (src/utils/visible-retry.ts) replaces SDK's silent
238
- // exponential backoff (2,4,8,16,32s). When SiliconFlow rate-limits
239
- // with 429, user now sees "[retry] rate-limited (429) waiting Xs
240
- // before attempt N/6" instead of a 62s blank window that looks hung.
241
- maxRetries: 0,
242
- ...(runtime.providerOptions ? { providerOptions: runtime.providerOptions } : {}),
385
+ // Never hardcode temperature: some upstreams (Moonshot/Kimi via
386
+ // opencode-go) reject any value but their pinned one, which
387
+ // failed every clarify/spec call on a Kimi session. resolveTemperature
388
+ // omits the field or clamps to the model's fixed value.
389
+ temperature: resolveTemperature(providerId, runtime.modelInfo, 0.7),
390
+ providerOptions: runtime.providerOptions,
243
391
  abortSignal: timedSignal,
244
392
  }), { label: "council.generate" }), COUNCIL_LLM_TIMEOUT_MS + 5_000, "council.generate", signal);
245
393
  cleanupTimeout();
@@ -255,6 +403,7 @@ export function createCouncilLLM(bash, mode, sessionId, stats) {
255
403
  durationMs: durMs,
256
404
  });
257
405
  onUsage?.(callUsage);
406
+ recordCouncilUsage(sessionId, modelId, callUsage);
258
407
  writeDebugRecord({
259
408
  ts: new Date().toISOString(),
260
409
  kind: "generate",
@@ -273,7 +422,7 @@ export function createCouncilLLM(bash, mode, sessionId, stats) {
273
422
  finishReason: result.finishReason,
274
423
  usage: result.usage,
275
424
  });
276
- return result.text;
425
+ return stripThinkBlocks(result.text);
277
426
  }
278
427
  catch (err) {
279
428
  cleanupTimeout();
@@ -300,7 +449,7 @@ export function createCouncilLLM(bash, mode, sessionId, stats) {
300
449
  if (mock) {
301
450
  stats.calls++;
302
451
  const result = await mock.complete({ prompt });
303
- return { text: result.text, toolCalls: [] };
452
+ return { text: stripThinkBlocks(result.text), toolCalls: [] };
304
453
  }
305
454
  const providerId = detectProviderForModel(modelId);
306
455
  const factory = await resolveCouncilFactory(providerId);
@@ -351,10 +500,26 @@ export function createCouncilLLM(bash, mode, sessionId, stats) {
351
500
  const t0 = Date.now();
352
501
  const { signal: timedSignal, cleanup: cleanupTimeout } = withTimeoutSignal(signal, COUNCIL_LLM_TIMEOUT_MS);
353
502
  try {
354
- const result = await withDeadlineRace(() => withVisibleRetry(() => generateText({
503
+ const result = await withDeadlineRace(() => withVisibleRetry(() =>
504
+ // Stream + collect (NOT generateText). A debater on the codex/oauth
505
+ // endpoint hits the same non-stream 400 ("Stream must be set to
506
+ // true") that nulled the eval path; streaming is uniform across
507
+ // providers. Tools (when the tier + circuit breaker allow) ride
508
+ // through with stepCountIs(2) + the same sanitizeHistory prepareStep.
509
+ collectStreamText({
355
510
  model: runtime.model,
356
511
  system,
357
512
  prompt,
513
+ // Reasoning models (deepseek-v4-*, anthropic thinking) consume part
514
+ // of this budget on reasoning_tokens before producing user-visible
515
+ // text. E2E showed 2048 caused finishReason=length on 3KB debate
516
+ // prompts. 6144 leaves ~4000 tokens for text after typical reasoning
517
+ // overhead and avoids cuts mid-thought.
518
+ maxOutputTokens: 6144,
519
+ // See generate(): capability-aware temperature (omit / clamp).
520
+ temperature: resolveTemperature(providerId, runtime.modelInfo, 0.7),
521
+ providerOptions: runtime.providerOptions,
522
+ abortSignal: timedSignal,
358
523
  ...(verificationTools && Object.keys(verificationTools).length > 0
359
524
  ? {
360
525
  tools: verificationTools,
@@ -367,20 +532,6 @@ export function createCouncilLLM(bash, mode, sessionId, stats) {
367
532
  },
368
533
  }
369
534
  : {}),
370
- // Reasoning models (deepseek-v4-*, anthropic thinking) consume part
371
- // of this budget on reasoning_tokens before producing user-visible
372
- // text. E2E showed 2048 caused finishReason=length on 3KB debate
373
- // prompts. 6144 leaves ~4000 tokens for text after typical reasoning
374
- // overhead and avoids cuts mid-thought.
375
- maxOutputTokens: 6144,
376
- temperature: 0.7,
377
- // Visible retry (src/utils/visible-retry.ts) replaces SDK's silent
378
- // exponential backoff (2,4,8,16,32s). When SiliconFlow rate-limits
379
- // with 429, user now sees "[retry] rate-limited (429) — waiting Xs
380
- // before attempt N/6" instead of a 62s blank window that looks hung.
381
- maxRetries: 0,
382
- ...(runtime.providerOptions ? { providerOptions: runtime.providerOptions } : {}),
383
- abortSignal: timedSignal,
384
535
  }), { label: "council.debate" }), COUNCIL_LLM_TIMEOUT_MS + 5_000, "council.debate", signal);
385
536
  cleanupTimeout();
386
537
  stats.calls++;
@@ -402,6 +553,7 @@ export function createCouncilLLM(bash, mode, sessionId, stats) {
402
553
  stepCount: toolCalls.length,
403
554
  });
404
555
  onUsage?.(debateUsage);
556
+ recordCouncilUsage(sessionId, modelId, debateUsage);
405
557
  writeDebugRecord({
406
558
  ts: new Date().toISOString(),
407
559
  kind: "debate",
@@ -423,7 +575,7 @@ export function createCouncilLLM(bash, mode, sessionId, stats) {
423
575
  usage: result.usage,
424
576
  });
425
577
  return {
426
- text: result.text,
578
+ text: stripThinkBlocks(result.text),
427
579
  toolCalls: toolCalls,
428
580
  };
429
581
  }
@@ -456,7 +608,7 @@ export function createCouncilLLM(bash, mode, sessionId, stats) {
456
608
  if (mock) {
457
609
  stats.calls++;
458
610
  const result = await mock.complete({ prompt: topic });
459
- return result.text;
611
+ return stripThinkBlocks(result.text);
460
612
  }
461
613
  const providerId = detectProviderForModel(modelId);
462
614
  const factory = await resolveCouncilFactory(providerId);
@@ -476,12 +628,21 @@ export function createCouncilLLM(bash, mode, sessionId, stats) {
476
628
  const hasUrl = /https?:\/\/\S+/.test(topic);
477
629
  const internetFirst = options?.internetFirst === true;
478
630
  const systemPrompt = buildResearchSystemPrompt(hasUrl, internetFirst);
479
- // Warn early if internet-first mode is requested but no internet/browser tools are loaded.
480
- const internetToolAvailable = Object.keys(allTools).some((n) => /tavily|web[_-]?fetch|web[_-]?search|playwright|chrome|context7|firecrawl|exa/i.test(n));
631
+ // Warn early if internet-first mode is requested but no *working* web
632
+ // research capability exists. The builtin `web_search`/`fetch_url` tools
633
+ // are ALWAYS registered, so the old tool-NAME check was always true — even
634
+ // when `web_search` will just return `ERROR no_tavily_key`, so the warning
635
+ // never fired and internet-first research failed silently. Gate on real
636
+ // capability: a Tavily key, or an MCP search/browser tool actually loaded
637
+ // (builtins alone don't count — open-ended search needs the key).
638
+ const hasTavilyKey = ((await getMcpKey("tavily")) || process.env.TAVILY_API_KEY || "").trim().length >= 10;
639
+ const mcpSearchTool = Object.keys(mcpBundle?.tools ?? {}).some((n) => /tavily|web[_-]?search|web[_-]?fetch|playwright|chrome|context7|firecrawl|exa|browser/i.test(n));
640
+ const internetToolAvailable = hasTavilyKey || mcpSearchTool;
481
641
  const internetGapWarning = internetFirst && !internetToolAvailable
482
- ? `\n\n## Research Gap\n- Internet-first mode requested but no browser/search tool ` +
483
- `(tavily, web-fetch, playwright, chrome-devtools, context7) is available. ` +
484
- `Findings will be limited to what the model already knows.`
642
+ ? `\n\n## Research Gap\n- Internet-first mode requested but no working web-search capability ` +
643
+ `(a Tavily API key or an MCP search/browser tool) is available. ` +
644
+ `\`fetch_url\` still works for explicit URLs, but open-ended search is unavailable — ` +
645
+ `findings will be limited to what the model already knows.`
485
646
  : "";
486
647
  const userPrompt = conversationContext
487
648
  ? `## Context\n${conversationContext}\n\n---\n\n## Research Topic\n${topic}\n\nInvestigate and report findings.`
@@ -508,7 +669,11 @@ export function createCouncilLLM(bash, mode, sessionId, stats) {
508
669
  return stripped === messages ? {} : { messages: stripped };
509
670
  },
510
671
  maxOutputTokens: 4096,
511
- temperature: 0.3,
672
+ // See generate(): capability-aware temperature (omit / clamp).
673
+ ...(() => {
674
+ const t = resolveTemperature(providerId, runtime.modelInfo, 0.3);
675
+ return t === undefined ? {} : { temperature: t };
676
+ })(),
512
677
  // Visible retry (src/utils/visible-retry.ts) replaces SDK's silent
513
678
  // exponential backoff (2,4,8,16,32s). When SiliconFlow rate-limits
514
679
  // with 429, user now sees "[retry] rate-limited (429) — waiting Xs
@@ -530,6 +695,7 @@ export function createCouncilLLM(bash, mode, sessionId, stats) {
530
695
  stepCount: (result.toolCalls ?? []).length,
531
696
  });
532
697
  onUsage?.(researchUsage);
698
+ recordCouncilUsage(sessionId, modelId, researchUsage);
533
699
  writeDebugRecord({
534
700
  ts: new Date().toISOString(),
535
701
  kind: "research",
@@ -558,7 +724,7 @@ export function createCouncilLLM(bash, mode, sessionId, stats) {
558
724
  // CQ-04: When URL present, verify at least one browser tool was invoked
559
725
  if (hasUrl) {
560
726
  // Use result.toolCalls (flat array across all steps) — more reliably typed than steps[].toolCalls
561
- const browserUsed = researchToolCalls.some((tc) => tc.toolName.includes("playwright") || tc.toolName.includes("chrome"));
727
+ const browserUsed = researchToolCalls.some((tc) => tc.toolName.includes("playwright") || tc.toolName.includes("chrome") || tc.toolName === "fetch_url");
562
728
  if (!browserUsed) {
563
729
  stats.calls++;
564
730
  return (result.text +
@@ -568,7 +734,7 @@ export function createCouncilLLM(bash, mode, sessionId, stats) {
568
734
  }
569
735
  }
570
736
  stats.calls++;
571
- return result.text + internetGapWarning;
737
+ return stripThinkBlocks(result.text) + internetGapWarning;
572
738
  }
573
739
  catch (err) {
574
740
  cleanupTimeout();
@@ -691,6 +857,33 @@ export async function* tracedGenerate(llm, args) {
691
857
  };
692
858
  return resultText;
693
859
  }
860
+ /**
861
+ * Run {@link tracedGenerate} across a list of models, returning the first
862
+ * non-empty completion. A model that throws (e.g. a flaky proxy returning
863
+ * "Upstream request failed") or yields only whitespace advances to the next
864
+ * candidate; fallback attempts are labelled so the timeline shows the retry.
865
+ * Returns null when every candidate fails — the caller decides the degraded
866
+ * fallback (e.g. a single-criterion spec). Dedupes the model list in order.
867
+ */
868
+ export async function* tracedGenerateWithFallback(llm, args) {
869
+ const seen = new Set();
870
+ const models = args.models.filter((m) => m && !seen.has(m) && (seen.add(m), true));
871
+ for (let i = 0; i < models.length; i++) {
872
+ try {
873
+ const raw = yield* tracedGenerate(llm, {
874
+ ...args,
875
+ modelId: models[i],
876
+ label: i > 0 ? `${args.label} (fallback: ${models[i]})` : args.label,
877
+ });
878
+ if (raw?.trim())
879
+ return raw;
880
+ }
881
+ catch {
882
+ /* try the next candidate */
883
+ }
884
+ }
885
+ return null;
886
+ }
694
887
  /**
695
888
  * Generic version of {@link tracedGenerate} for arbitrary async work
696
889
  * (e.g. `llm.research`, `Promise.all` over multiple model calls).
@@ -0,0 +1,30 @@
1
+ /**
2
+ * src/council/panel-select.ts
3
+ *
4
+ * U3 — task-aware debate panel selection. Instead of a fixed capability roster
5
+ * (resolveParticipants picks by tier, blind to the prompt), the leader reads the
6
+ * task and CHOOSES which reachable models should debate it. Fail-open: any parse
7
+ * / provider failure returns null and the caller keeps the default roster. The
8
+ * internal implement/verify/research routing slots are assigned here purely for
9
+ * downstream compatibility — they are never surfaced to the user (the UI shows
10
+ * the task-adaptive persona / model, per U1.1).
11
+ */
12
+ import type { StreamChunk } from "../types/index.js";
13
+ import type { ModelRole } from "../utils/settings.js";
14
+ import type { CouncilCandidate } from "./leader.js";
15
+ import type { CouncilLLM } from "./types.js";
16
+ /**
17
+ * Ask the leader to select a task-appropriate debate panel from `pool`.
18
+ * Returns a roster (models mapped onto internal routing roles) or null when the
19
+ * leader is unavailable / returns an unusable selection — the caller then keeps
20
+ * its default roster.
21
+ */
22
+ export declare function selectTaskAwarePanel(opts: {
23
+ topic: string;
24
+ pool: CouncilCandidate[];
25
+ leaderModelId: string;
26
+ llm: CouncilLLM;
27
+ }): AsyncGenerator<StreamChunk, Array<{
28
+ role: ModelRole;
29
+ model: string;
30
+ }> | null, unknown>;
@@ -0,0 +1,72 @@
1
+ /**
2
+ * src/council/panel-select.ts
3
+ *
4
+ * U3 — task-aware debate panel selection. Instead of a fixed capability roster
5
+ * (resolveParticipants picks by tier, blind to the prompt), the leader reads the
6
+ * task and CHOOSES which reachable models should debate it. Fail-open: any parse
7
+ * / provider failure returns null and the caller keeps the default roster. The
8
+ * internal implement/verify/research routing slots are assigned here purely for
9
+ * downstream compatibility — they are never surfaced to the user (the UI shows
10
+ * the task-adaptive persona / model, per U1.1).
11
+ */
12
+ import { tracedGenerate } from "./llm.js";
13
+ const ALL_ROLES = ["implement", "verify", "research"];
14
+ const SELECT_SYSTEM = "You are the council leader assembling a debate panel for a specific task. From the AVAILABLE MODELS, " +
15
+ "pick the 2-4 that will produce the most rigorous, diverse debate for THIS task — prefer provider and " +
16
+ "tier diversity, and match model strengths to what the task actually needs (e.g. a decision/analysis task " +
17
+ "wants strong reasoners; a build task wants a coding-capable model plus a critical reviewer). " +
18
+ 'Return ONLY JSON: {"members":[{"model":"<exact id from the list>","why":"<short reason>"}]}. ' +
19
+ "Use exact model ids from the list; never invent an id.";
20
+ /**
21
+ * Ask the leader to select a task-appropriate debate panel from `pool`.
22
+ * Returns a roster (models mapped onto internal routing roles) or null when the
23
+ * leader is unavailable / returns an unusable selection — the caller then keeps
24
+ * its default roster.
25
+ */
26
+ export async function* selectTaskAwarePanel(opts) {
27
+ const { topic, pool, leaderModelId, llm } = opts;
28
+ if (pool.length < 2)
29
+ return null;
30
+ const poolList = pool
31
+ .map((c) => `- ${c.model}${c.tier ? ` [${c.tier}]` : ""}${c.provider ? ` (${c.provider})` : ""}: ${c.description.slice(0, 120)}`)
32
+ .join("\n");
33
+ const prompt = `## Task\n${topic}\n\n## Available models\n${poolList}\n\nSelect the 2-4 models best suited to debate this task.`;
34
+ let raw;
35
+ try {
36
+ raw = yield* tracedGenerate(llm, {
37
+ phase: "panel_select",
38
+ label: "Leader selecting debate panel",
39
+ modelId: leaderModelId,
40
+ system: SELECT_SYSTEM,
41
+ prompt,
42
+ maxTokens: 512,
43
+ });
44
+ }
45
+ catch {
46
+ return null;
47
+ }
48
+ const match = raw.match(/\{[\s\S]*\}/);
49
+ if (!match)
50
+ return null;
51
+ let parsed;
52
+ try {
53
+ parsed = JSON.parse(match[0]);
54
+ }
55
+ catch {
56
+ return null;
57
+ }
58
+ const valid = new Set(pool.map((c) => c.model));
59
+ const picked = [];
60
+ for (const m of parsed.members ?? []) {
61
+ const id = typeof m?.model === "string" ? m.model.trim() : "";
62
+ if (id && valid.has(id) && !picked.includes(id))
63
+ picked.push(id);
64
+ if (picked.length >= 4)
65
+ break;
66
+ }
67
+ if (picked.length < 2)
68
+ return null;
69
+ // Roles are internal cost-tier routing slots (hidden from UI); assign cyclically.
70
+ return picked.map((model, i) => ({ role: ALL_ROLES[i % ALL_ROLES.length], model }));
71
+ }
72
+ //# sourceMappingURL=panel-select.js.map
@@ -1,3 +1,4 @@
1
+ import { getCouncilLanguage } from "../utils/settings.js";
1
2
  import { tracedGenerate } from "./llm.js";
2
3
  import { phaseDone, phaseError, phaseStart } from "./phase-events.js";
3
4
  import { buildSynthesisPrompt } from "./prompts.js";
@@ -33,6 +34,9 @@ planEmphasis) {
33
34
  outputStyle: outputStyle ?? undefined,
34
35
  refineContext,
35
36
  planEmphasis,
37
+ // Feature B — synthesis output language follows the resolved council
38
+ // debate language (auto → detect from the brief; pinned → that locale).
39
+ language: getCouncilLanguage(),
36
40
  };
37
41
  const first = buildSynthesisPrompt(baseArgs);
38
42
  synthesisText = yield* tracedGenerate(llm, {
@@ -233,10 +237,29 @@ function parseOutcome(synthesisText, debatePlan) {
233
237
  sections[s.key] = parsed[s.key];
234
238
  }
235
239
  }
240
+ // Model-first post-debate options. Keep only entries whose action is in
241
+ // the wired vocabulary; drop malformed items so a hallucinated action id
242
+ // can't reach the handler switch. Empty → index.ts uses its fallback set.
243
+ const VALID_ACTIONS = new Set(["ask_followup", "generate_plan", "implement", "save_exit", "continue_session"]);
244
+ const nextActions = Array.isArray(parsed.nextActions)
245
+ ? parsed.nextActions
246
+ .filter((a) => !!a &&
247
+ typeof a === "object" &&
248
+ typeof a.action === "string" &&
249
+ VALID_ACTIONS.has(a.action) &&
250
+ typeof a.label === "string" &&
251
+ a.label.trim().length > 0)
252
+ .map((a) => ({
253
+ action: a.action,
254
+ label: a.label.trim(),
255
+ reason: typeof a.reason === "string" ? a.reason.trim() : undefined,
256
+ }))
257
+ : undefined;
236
258
  return {
237
259
  type,
238
260
  summary,
239
261
  sections: Object.keys(sections).length > 0 ? sections : undefined,
262
+ nextActions: nextActions && nextActions.length > 0 ? nextActions : undefined,
240
263
  // Legacy fields — synthesizer may still emit them when shape calls for them.
241
264
  agreed: Array.isArray(parsed.agreed) ? parsed.agreed : undefined,
242
265
  tradeoffs: Array.isArray(parsed.tradeoffs) ? parsed.tradeoffs : undefined,
@@ -3,6 +3,13 @@ import type { ClarifiedSpec, PreflightResponder } from "./types.js";
3
3
  export interface RunPreflightOptions {
4
4
  repoEmpty?: boolean;
5
5
  researchOverridable?: boolean;
6
+ /**
7
+ * ROI: when the clarifier judged the spec ready (high confidence, no gaps),
8
+ * the approve card is a rubber-stamp. Show the Discussion Brief for
9
+ * transparency but auto-approve without blocking on a gate. Default false —
10
+ * an unready/low-confidence spec still surfaces the approve card.
11
+ */
12
+ autoApprove?: boolean;
6
13
  }
7
14
  export declare function runPreflight(spec: ClarifiedSpec, participants: Array<{
8
15
  role: string;
@@ -31,13 +31,25 @@ export async function* runPreflight(spec, participants, researchNeeded, respondT
31
31
  },
32
32
  { heading: "Scope", body: spec.scope || "Not specified" },
33
33
  {
34
- heading: "Participants",
35
- body: participants.map((p) => `- ${p.role} → ${p.model}`).join("\n"),
34
+ heading: "Panel",
35
+ body: participants.map((p) => `- ${p.model}`).join("\n"),
36
36
  },
37
37
  { heading: "Research phase", body: `${researchMode}${researchNote}` },
38
38
  ],
39
39
  },
40
40
  };
41
+ // ROI: skip the approve gate when the spec is already judged ready — show the
42
+ // brief above for transparency but don't block on a rubber-stamp.
43
+ if (options?.autoApprove === true) {
44
+ yield phaseDone({
45
+ phaseId: "phase:preflight",
46
+ kind: "preflight",
47
+ label: "Pre-flight review",
48
+ startedAt,
49
+ detail: "auto-approved (spec ready)",
50
+ });
51
+ return true;
52
+ }
41
53
  yield {
42
54
  type: "council_preflight",
43
55
  content: "Review the discussion brief above. Approve to start debate, or reject to revise.",