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
@@ -48,6 +48,34 @@ export interface MockModelFixture {
48
48
  provider?: string;
49
49
  /** Reported model id. Default "mock-model". */
50
50
  modelId?: string;
51
+ /**
52
+ * Response for PIL's model-first intent classifier (src/pil/llm-classify.ts).
53
+ * Since the no-regex refactor (2026-07-07) PIL classifies EVERY turn by
54
+ * calling the model with a distinctive system prompt ("Reply with ONE line of
55
+ * EIGHT lowercase words…") and parses a comma-separated line. The generic
56
+ * fixture stream ("Hello back!" / arbitrary chunks) does not parse → the turn
57
+ * degrades to intentKind=UNKNOWN, which skips the gsd gate and breaks routing
58
+ * in E2E specs. The mock detects that classify call by its system prompt and
59
+ * returns THIS line instead of consuming the turn fixture — so every spec gets
60
+ * a sane classification for free. Override per-fixture when a spec needs a
61
+ * specific taskType/intent/depth. Format:
62
+ * <taskType>,<style>,<intent>,<deliverable>,<depth>,<scope>,<lang>,<clarity>
63
+ */
64
+ classify?: string;
65
+ /**
66
+ * Opt in to the PIL-classifier intercept described on `classify`. When true,
67
+ * a `doStream` call carrying the classifier's system prompt is answered with
68
+ * `classify` (or DEFAULT_CLASSIFY_LINE) WITHOUT consuming a turn fixture.
69
+ *
70
+ * This is a convenience for the file-based E2E harness (`loadMockModelFromDir`
71
+ * sets it true by default) whose fixtures supply agent turn-rounds and must
72
+ * not spend a round on the classify call. Direct in-memory callers
73
+ * (`createMockModel`/`installMockModel`, e.g. llm-classify.test.ts) leave it
74
+ * OFF so the fixture stream they configure IS delivered to the classifier —
75
+ * otherwise every classification would collapse to DEFAULT_CLASSIFY_LINE.
76
+ * Setting `classify` implies opting in.
77
+ */
78
+ autoClassify?: boolean;
51
79
  }
52
80
  export interface MockModelHandle {
53
81
  /** The mock model instance — assign to `globalThis.__muonroiMockModel`. */
@@ -25,6 +25,40 @@
25
25
  import { APICallError } from "@ai-sdk/provider";
26
26
  import { simulateReadableStream } from "ai";
27
27
  import { MockLanguageModelV3 } from "ai/test";
28
+ /**
29
+ * System-prompt substring that uniquely identifies PIL's model-first intent
30
+ * classifier call (see SYSTEM_PROMPT in src/pil/llm-classify.ts). Kept in sync
31
+ * with that constant's opening line.
32
+ */
33
+ const CLASSIFY_SIGNATURE = "Reply with ONE line of EIGHT lowercase words";
34
+ /**
35
+ * Safe default classification: a non-chitchat coding task of standard depth.
36
+ * `intent=task` keeps the toolset + gsd gate active; `standard` depth avoids
37
+ * forcing heavy-only paths. First word MUST be a valid taskType.
38
+ */
39
+ const DEFAULT_CLASSIFY_LINE = "generate, balanced, task, code, standard, local, english, clear";
40
+ /** Concatenated text of every `system` message in a doStream call's prompt. */
41
+ function systemPromptText(options) {
42
+ const prompt = options.prompt;
43
+ if (!Array.isArray(prompt))
44
+ return "";
45
+ const parts = [];
46
+ for (const msg of prompt) {
47
+ const m = msg;
48
+ if (m.role !== "system")
49
+ continue;
50
+ if (typeof m.content === "string")
51
+ parts.push(m.content);
52
+ else if (Array.isArray(m.content)) {
53
+ for (const c of m.content) {
54
+ const t = c.text;
55
+ if (typeof t === "string")
56
+ parts.push(t);
57
+ }
58
+ }
59
+ }
60
+ return parts.join("\n");
61
+ }
28
62
  function isNestedArray(v) {
29
63
  return Array.isArray(v) && v.length > 0 && Array.isArray(v[0]);
30
64
  }
@@ -44,10 +78,34 @@ export function createMockModel(fx) {
44
78
  let genIdx = 0;
45
79
  const provider = fx.provider ?? "mock";
46
80
  const modelId = fx.modelId ?? "mock-model";
81
+ const classifyLine = fx.classify ?? DEFAULT_CLASSIFY_LINE;
82
+ // Intercept the PIL classifier call ONLY when the fixture opts in (E2E
83
+ // harness fixtures do, via loadMockModelFromDir). Direct in-memory callers
84
+ // (llm-classify.test.ts) leave it off so the stream they configure IS
85
+ // delivered to the classifier — intercepting would override their reply with
86
+ // DEFAULT_CLASSIFY_LINE and make every classification collapse to "generate".
87
+ const interceptClassify = fx.autoClassify === true || fx.classify !== undefined;
47
88
  const model = new MockLanguageModelV3({
48
89
  provider,
49
90
  modelId,
50
- doStream: async () => {
91
+ doStream: async (options) => {
92
+ // PIL model-first classifier call — answer with the parseable eight-word
93
+ // line and DO NOT advance the turn-fixture cursor, so the classify call
94
+ // (which fires on every turn) never steals the round chunks meant for the
95
+ // agent's actual reply. Without this the generic fixture text fails
96
+ // parseResponse → intentKind=UNKNOWN → gsd gate/routing break in E2E.
97
+ if (interceptClassify && systemPromptText(options).includes(CLASSIFY_SIGNATURE)) {
98
+ if (process.env.MUONROI_DEBUG_MOCK_MODEL === "1") {
99
+ process.stderr.write(`[mock-model] doStream classify → "${classifyLine}"\n`);
100
+ }
101
+ return {
102
+ stream: simulateReadableStream({
103
+ chunks: textOnlyStream(classifyLine),
104
+ initialDelayInMs: null,
105
+ chunkDelayInMs: null,
106
+ }),
107
+ };
108
+ }
51
109
  const chunks = streams[Math.min(callIdx, streams.length - 1)];
52
110
  callIdx += 1;
53
111
  if (process.env.MUONROI_DEBUG_MOCK_MODEL === "1") {
@@ -397,6 +455,10 @@ export async function loadMockModelFromDir(dir) {
397
455
  // to createMockModel, so the AI SDK stream-loop and orchestrator see a
398
456
  // consistent Error shape regardless of fixture serialization.
399
457
  const normalized = {
458
+ // File-based E2E fixtures supply agent turn-rounds; auto-answer the PIL
459
+ // classifier call so it never consumes a round (a fixture may still set
460
+ // autoClassify:false to opt out, or classify:"…" to force a line).
461
+ autoClassify: true,
400
462
  ...raw.model,
401
463
  stream: isNestedArray(raw.model.stream)
402
464
  ? raw.model.stream.map(normalizeStreamChunks)
@@ -19,6 +19,35 @@ import { spawn } from "node:child_process";
19
19
  import { randomUUID } from "node:crypto";
20
20
  import { createServer } from "node:net";
21
21
  // ---------------------------------------------------------------------------
22
+ // Internal: teardown-safe stream error guards
23
+ // ---------------------------------------------------------------------------
24
+ /**
25
+ * Attach `'error'` listeners to both transport streams so a broken-pipe write
26
+ * (EPIPE / UV_EPIPE -4047 on Windows) or a peer reset (ECONNRESET) during
27
+ * process teardown is logged and swallowed instead of surfacing as an
28
+ * *uncaught* stream error that crashes the whole vitest worker.
29
+ *
30
+ * Root cause of the flake: when the child is killed in `afterAll`, an in-flight
31
+ * `inWrite.write()` (driver sendKey/sendType) or a half-open `outRead` races the
32
+ * child's death. A WritableStream with no `'error'` listener rethrows EPIPE as
33
+ * an unhandled exception — under full-suite load this reliably reproduces on
34
+ * `events.spec.ts` and fails the file even though every assertion passed.
35
+ *
36
+ * Logging (not silent swallow) satisfies the No-Silent-Catch rule — teardown
37
+ * broken-pipe is expected, but a mid-test error still gets a diagnostic line.
38
+ */
39
+ function attachStreamErrorGuards(inWrite, outRead) {
40
+ const guard = (label) => (err) => {
41
+ // EPIPE / ECONNRESET / ERR_STREAM_DESTROYED are all normal teardown races.
42
+ const code = err?.code ?? "unknown";
43
+ if (code !== "EPIPE" && code !== "ECONNRESET" && code !== "ERR_STREAM_DESTROYED") {
44
+ console.error(`[test-spawn] ${label} stream error (${code}): ${err?.message}`);
45
+ }
46
+ };
47
+ inWrite.on("error", guard("inWrite"));
48
+ outRead.on("error", guard("outRead"));
49
+ }
50
+ // ---------------------------------------------------------------------------
22
51
  // Internal: Windows named-pipe transport
23
52
  // ---------------------------------------------------------------------------
24
53
  function makePipeName(role) {
@@ -143,6 +172,7 @@ async function spawnWindows(args, opts) {
143
172
  outServer.close();
144
173
  };
145
174
  proc.once("exit", cleanup);
175
+ attachStreamErrorGuards(inSocket, outSocket);
146
176
  return {
147
177
  proc,
148
178
  inWrite: inSocket, // host writes commands → child reads on MUONROI_HARNESS_IN_PIPE
@@ -161,6 +191,7 @@ function spawnPosix(args, opts) {
161
191
  const proc = spawn("bun", ["run", ...args], spawnOptions);
162
192
  const outRead = proc.stdio[3];
163
193
  const inWrite = proc.stdio[4];
194
+ attachStreamErrorGuards(inWrite, outRead);
164
195
  return { proc, inWrite, outRead, cleanup: () => { } };
165
196
  }
166
197
  // ---------------------------------------------------------------------------
@@ -9,7 +9,7 @@ import { A, captureKey, divider, enterRawMode, hiddenPrompt, maskKey } from "./t
9
9
  * until their integration is hardened — the router can still target them
10
10
  * programmatically. To re-expose one, add its id here.
11
11
  */
12
- const VISIBLE_PROVIDERS = ["deepseek", "siliconflow"];
12
+ const VISIBLE_PROVIDERS = ["deepseek", "zai", "opencode-go", "xai"];
13
13
  const ALL_PROVIDERS = [...VISIBLE_PROVIDERS, "ollama"];
14
14
  void KEYCHAIN_PROVIDER_IDS;
15
15
  async function loadRows() {
@@ -35,6 +35,16 @@ export interface CostForensicsSummary {
35
35
  totalCostUsd: number;
36
36
  cacheHitRatio: number;
37
37
  peakSingleCallInput: number;
38
+ /**
39
+ * Peak FRESH (uncached) input on any single usage event — `max(input -
40
+ * cacheRead)`. This is the cap-escape discriminator: `task`/`council` rows
41
+ * store `totalUsage` AGGREGATED across a streamText call's internal steps, so
42
+ * a high `peakSingleCallInput` on those sources is usually cached-prefix reuse
43
+ * across steps (high cache hit), NOT a single call that breached the cap. A
44
+ * genuine cold cap-escape sends >cap FRESH tokens in one call, which shows up
45
+ * here regardless of source.
46
+ */
47
+ peakSingleCallFresh: number;
38
48
  events: CostForensicsRow[];
39
49
  /** Anti-mù counters for this session (null when none recorded). */
40
50
  experience: SessionExperienceCounts | null;
@@ -83,6 +83,7 @@ export function collectCostForensics(sessionId) {
83
83
  let totalCacheCreation = 0;
84
84
  let totalCostMicros = 0;
85
85
  let peakSingleCallInput = 0;
86
+ let peakSingleCallFresh = 0;
86
87
  for (const e of events) {
87
88
  totalInput += e.inputTokens;
88
89
  totalOutput += e.outputTokens;
@@ -91,6 +92,9 @@ export function collectCostForensics(sessionId) {
91
92
  totalCostMicros += e.costMicros;
92
93
  if (e.inputTokens > peakSingleCallInput)
93
94
  peakSingleCallInput = e.inputTokens;
95
+ const fresh = Math.max(0, e.inputTokens - e.cacheReadTokens);
96
+ if (fresh > peakSingleCallFresh)
97
+ peakSingleCallFresh = fresh;
94
98
  }
95
99
  const cacheable = totalInput;
96
100
  const cacheHitRatio = cacheable > 0 ? totalCacheRead / cacheable : 0;
@@ -106,6 +110,7 @@ export function collectCostForensics(sessionId) {
106
110
  totalCostUsd: totalCostMicros / 1_000_000,
107
111
  cacheHitRatio,
108
112
  peakSingleCallInput,
113
+ peakSingleCallFresh,
109
114
  events,
110
115
  experience: selectSessionExperience(sessionId),
111
116
  };
@@ -159,7 +164,8 @@ export function printCostForensics(summary, opts = {}) {
159
164
  w(`Total output tokens: ${formatNum(summary.totalOutput)}`);
160
165
  w(`Cache read tokens: ${formatNum(summary.totalCacheRead)} (${formatPct(summary.cacheHitRatio)} of input)`);
161
166
  w(`Cache create tokens: ${formatNum(summary.totalCacheCreation)}`);
162
- w(`Peak single call: ${formatNum(summary.peakSingleCallInput)} input`);
167
+ w(`Peak single call: ${formatNum(summary.peakSingleCallInput)} input ` +
168
+ `(${formatNum(summary.peakSingleCallFresh)} fresh)`);
163
169
  w(`Estimated cost: $${summary.totalCostUsd.toFixed(4)}`);
164
170
  // Cache-cadence diagnostic — surface fast-loop prompt-cache latency loss.
165
171
  const cadence = computeCacheCadence(summary.events);
@@ -199,8 +205,17 @@ export function printCostForensics(summary, opts = {}) {
199
205
  }
200
206
  // Acceptance hints — surface anomalies relative to Phase A/B/C targets.
201
207
  const anomalies = [];
202
- if (summary.peakSingleCallInput > 80_000) {
203
- anomalies.push(`peak single-call input ${formatNum(summary.peakSingleCallInput)} > 80,000 — sub-agent cap may not have kicked in (Phase B target breach)`);
208
+ // Phase B breach = a single call that sent >80K FRESH (uncached) tokens.
209
+ // Use FRESH, not raw input: `task`/`council` usage rows store `totalUsage`
210
+ // aggregated across a streamText call's internal steps, so a high raw input
211
+ // there is normally cached-prefix reuse across steps (high cache hit), NOT a
212
+ // cold cap breach. Evidence: session 6f17a13c1591 peaked at 378,613 raw input
213
+ // but only 54,389 fresh (324,224 cache-read) — aggregate reuse, no breach.
214
+ if (summary.peakSingleCallFresh > 80_000) {
215
+ anomalies.push(`peak single-call FRESH input ${formatNum(summary.peakSingleCallFresh)} > 80,000 — sub-agent cap may not have kicked in (Phase B target breach)`);
216
+ }
217
+ else if (summary.peakSingleCallInput > 80_000) {
218
+ anomalies.push(`peak single-call input ${formatNum(summary.peakSingleCallInput)} > 80,000 but only ${formatNum(summary.peakSingleCallFresh)} FRESH — aggregate multi-step reuse (task/council totalUsage), NOT a cap breach`);
204
219
  }
205
220
  if (summary.events.some((e) => e.messageSeq === null && e.source === "message")) {
206
221
  anomalies.push(`some 'message' events have NULL message_seq — Phase A5 message write-ahead bypassed (check persistMessageWriteAhead wiring)`);
@@ -7,7 +7,7 @@
7
7
  * Format (JSON):
8
8
  * { v:1, kdf:"scrypt", salt:hex, iv:hex, tag:hex, ct:base64 }
9
9
  *
10
- * Plaintext payload (JSON): { providers: { deepseek: "sk-...", siliconflow: "sf-..." } }
10
+ * Plaintext payload (JSON): { providers: { deepseek: "sk-...", xai: "xai-..." } }
11
11
  *
12
12
  * Crypto:
13
13
  * - scrypt(passphrase, salt, 32) → 256-bit key
@@ -7,7 +7,7 @@
7
7
  * Format (JSON):
8
8
  * { v:1, kdf:"scrypt", salt:hex, iv:hex, tag:hex, ct:base64 }
9
9
  *
10
- * Plaintext payload (JSON): { providers: { deepseek: "sk-...", siliconflow: "sf-..." } }
10
+ * Plaintext payload (JSON): { providers: { deepseek: "sk-...", xai: "xai-..." } }
11
11
  *
12
12
  * Crypto:
13
13
  * - scrypt(passphrase, salt, 32) → 256-bit key
@@ -15,8 +15,8 @@
15
15
  * keys delete <provider> — remove a stored key
16
16
  * keys import-bw [providers] — pull from Bitwarden vault, store in keychain
17
17
  * keys cleanup-settings — strip plaintext keys from user-settings.json
18
- * keys login <provider> — OAuth login (openai, google/agy)
19
- * keys logout <provider> — OAuth logout (openai, google/agy)
18
+ * keys login <provider> — OAuth login (openai, xai)
19
+ * keys logout <provider> — OAuth logout (openai, xai)
20
20
  */
21
21
  import { type ChatSecretId } from "../chat/chat-keychain.js";
22
22
  export interface KeysSetOptions {
@@ -15,8 +15,8 @@
15
15
  * keys delete <provider> — remove a stored key
16
16
  * keys import-bw [providers] — pull from Bitwarden vault, store in keychain
17
17
  * keys cleanup-settings — strip plaintext keys from user-settings.json
18
- * keys login <provider> — OAuth login (openai, google/agy)
19
- * keys logout <provider> — OAuth logout (openai, google/agy)
18
+ * keys login <provider> — OAuth login (openai, xai)
19
+ * keys logout <provider> — OAuth logout (openai, xai)
20
20
  */
21
21
  import { spawnSync } from "node:child_process";
22
22
  import { promises as fs } from "node:fs";
@@ -43,7 +43,7 @@ const CHAT_SECRET_IDS = ["discord-token", "discord-guild-id", "slack-token", "sl
43
43
  function isChatSecretId(value) {
44
44
  return CHAT_SECRET_IDS.includes(value);
45
45
  }
46
- const SETTINGS_PATH = path.join(os.homedir(), ".muonroi-cli", "user-settings.json");
46
+ const getSettingsPath = () => path.join(os.homedir(), ".muonroi-cli", "user-settings.json");
47
47
  function maskKey(key) {
48
48
  if (key.length <= 10)
49
49
  return "***";
@@ -51,8 +51,6 @@ function maskKey(key) {
51
51
  }
52
52
  function normalizeProvider(p) {
53
53
  const lower = p.toLowerCase();
54
- if (lower === "agy")
55
- return "google"; // agy is alias for google (OAuth + key)
56
54
  if (KEYCHAIN_PROVIDER_IDS.includes(lower)) {
57
55
  return lower;
58
56
  }
@@ -115,7 +113,7 @@ async function promptHidden(question) {
115
113
  export async function runKeysSet(provider, options = {}) {
116
114
  const norm = normalizeProvider(provider);
117
115
  if (!norm) {
118
- console.error(`Unknown provider '${provider}'. Valid: ${KEYCHAIN_PROVIDER_IDS.join(", ")} (agy is alias for google)`);
116
+ console.error(`Unknown provider '${provider}'. Valid: ${KEYCHAIN_PROVIDER_IDS.join(", ")}`);
119
117
  process.exit(1);
120
118
  }
121
119
  const key = (await promptHidden(`Paste ${provider} API key (hidden): `)).trim();
@@ -199,30 +197,11 @@ export async function runKeysList() {
199
197
  const oauthIds = await getOAuthProviderIds();
200
198
  for (const p of oauthIds) {
201
199
  try {
202
- let tokens = await loadTokens(p);
203
- if (!tokens && p === "google") {
204
- // Import from agy storage only for fresh (non-expired) tokens.
205
- // Stale agy tokens should be cleaned by running `keys logout google` + removing old .gemini file.
206
- const { loadAgyTokensWithRefresh } = await import("../providers/auth/gemini-oauth.js");
207
- const agyTokens = await loadAgyTokensWithRefresh();
208
- if (agyTokens && Date.now() < agyTokens.expiresAt) {
209
- tokens = agyTokens;
210
- }
211
- }
212
- if (tokens) {
213
- if (Date.now() < tokens.expiresAt) {
214
- const cfg = await getOAuthProviderConfig(p);
215
- const display = cfg?.displayName ?? p;
216
- oauthRows.push({ provider: p, display, email: tokens.email, expiresAt: tokens.expiresAt });
217
- }
218
- else if (p === "google") {
219
- // auto-clean stale Agy token from our store (imported from old ~/.gemini)
220
- try {
221
- const { deleteTokens } = await import("../providers/auth/token-store.js");
222
- await deleteTokens("google");
223
- }
224
- catch { }
225
- }
200
+ const tokens = await loadTokens(p);
201
+ if (tokens && Date.now() < tokens.expiresAt) {
202
+ const cfg = await getOAuthProviderConfig(p);
203
+ const display = cfg?.displayName ?? p;
204
+ oauthRows.push({ provider: p, display, email: tokens.email, expiresAt: tokens.expiresAt });
226
205
  }
227
206
  }
228
207
  catch {
@@ -234,7 +213,6 @@ export async function runKeysList() {
234
213
  console.log("No keys stored in OS keychain.");
235
214
  console.log("Run 'muonroi-cli keys set <provider>' or 'muonroi-cli keys import-bw' to add some.");
236
215
  console.log("Run 'muonroi-cli keys login openai' to log in with your OpenAI subscription.");
237
- console.log("Run 'muonroi-cli keys login google' or 'keys login agy' to log in with Agy (Google OAuth).");
238
216
  console.log("Run 'muonroi-cli keys login xai' to log in with your SuperGrok / X Premium+ subscription.");
239
217
  return;
240
218
  }
@@ -288,35 +266,11 @@ export async function runKeysList() {
288
266
  export async function runKeysDelete(provider) {
289
267
  const norm = normalizeProvider(provider);
290
268
  if (!norm) {
291
- console.error(`Unknown provider '${provider}'. Valid: ${KEYCHAIN_PROVIDER_IDS.join(", ")} (agy is alias for google)`);
269
+ console.error(`Unknown provider '${provider}'. Valid: ${KEYCHAIN_PROVIDER_IDS.join(", ")}`);
292
270
  process.exit(1);
293
271
  }
294
272
  const ok = await deleteKeyForProvider(norm);
295
273
  console.log(ok ? `Deleted ${provider} key from keychain.` : `No ${provider} key was stored.`);
296
- // If deleting google/agy, also clean the OAuth token (Agy uses the google slot for its OAuth)
297
- if (norm === "google") {
298
- try {
299
- const { loadTokens, deleteTokens } = await import("../providers/auth/token-store.js");
300
- const { getOAuthProviderConfig } = await import("../providers/auth/registry.js");
301
- const tokens = await loadTokens("google");
302
- if (tokens) {
303
- const cfg = await getOAuthProviderConfig("google");
304
- if (cfg?.provider) {
305
- try {
306
- await cfg.provider.revoke(tokens);
307
- }
308
- catch {
309
- // best effort
310
- }
311
- }
312
- await deleteTokens("google");
313
- console.log(`Also cleared Agy/Google OAuth token.`);
314
- }
315
- }
316
- catch {
317
- // ignore secondary errors
318
- }
319
- }
320
274
  }
321
275
  export async function runKeysExport(filePath) {
322
276
  const providers = {};
@@ -679,9 +633,9 @@ export async function runChatImportBw(opts = {}) {
679
633
  */
680
634
  export async function runKeysLogin(provider) {
681
635
  const oauthIds = await getOAuthProviderIds();
682
- const norm = provider.toLowerCase() === "agy" ? "google" : provider;
636
+ const norm = provider;
683
637
  if (!oauthIds.includes(norm)) {
684
- const supported = oauthIds.map((id) => (id === "google" ? "google (or agy)" : id)).join(", ");
638
+ const supported = oauthIds.join(", ");
685
639
  console.error(`OAuth login not supported for '${provider}'. Supported: ${supported}`);
686
640
  process.exit(1);
687
641
  }
@@ -693,22 +647,6 @@ export async function runKeysLogin(provider) {
693
647
  process.exit(1);
694
648
  }
695
649
  const name = cfg.displayName;
696
- // Special handling for google: prefer importing/reusing token from agy local config
697
- // (~/.gemini/oauth_creds.json) so existing agy login "just works" without browser.
698
- if (provider === "google") {
699
- const { loadAgyTokensWithRefresh } = await import("../providers/auth/gemini-oauth.js");
700
- const existing = await loadAgyTokensWithRefresh();
701
- if (existing?.accessToken) {
702
- console.log(`Found existing agy token for ${existing.email || "(no email)"}.`);
703
- console.log("Reusing it (refreshed if near expiry). No browser needed.");
704
- const expiry = new Date(existing.expiresAt).toLocaleString();
705
- const isExpired = Date.now() > existing.expiresAt;
706
- console.log(`Token expires: ${expiry}${isExpired ? " (was expired, refresh attempted)" : ""}`);
707
- console.log("Run 'muonroi-cli keys list' to verify.");
708
- return;
709
- }
710
- // No existing agy token — fall through to normal browser flow (using agy client)
711
- }
712
650
  console.log(`Logging in to ${name} via OAuth...`);
713
651
  console.log("A browser window will open. Complete the sign-in to continue.");
714
652
  const tokens = await cfg.provider.login({
@@ -742,9 +680,9 @@ export async function runKeysLogin(provider) {
742
680
  */
743
681
  export async function runKeysLogout(provider) {
744
682
  const oauthIds = await getOAuthProviderIds();
745
- const norm = provider.toLowerCase() === "agy" ? "google" : provider;
683
+ const norm = provider;
746
684
  if (!oauthIds.includes(norm)) {
747
- const supported = oauthIds.map((id) => (id === "google" ? "google (or agy)" : id)).join(", ");
685
+ const supported = oauthIds.join(", ");
748
686
  console.error(`OAuth logout not supported for '${provider}'. Supported: ${supported}`);
749
687
  process.exit(1);
750
688
  }
@@ -766,7 +704,7 @@ export async function runKeysLogout(provider) {
766
704
  export async function runKeysCleanupSettings() {
767
705
  let raw;
768
706
  try {
769
- raw = await fs.readFile(SETTINGS_PATH, "utf8");
707
+ raw = await fs.readFile(getSettingsPath(), "utf8");
770
708
  }
771
709
  catch (e) {
772
710
  if (e.code === "ENOENT") {
@@ -780,11 +718,11 @@ export async function runKeysCleanupSettings() {
780
718
  json = JSON.parse(raw);
781
719
  }
782
720
  catch {
783
- console.error(`Settings file is not valid JSON: ${SETTINGS_PATH}`);
721
+ console.error(`Settings file is not valid JSON: ${getSettingsPath()}`);
784
722
  process.exit(1);
785
723
  }
786
724
  const ts = new Date().toISOString().replace(/[:.]/g, "-");
787
- const backup = `${SETTINGS_PATH}.bak.${ts}`;
725
+ const backup = `${getSettingsPath()}.bak.${ts}`;
788
726
  await fs.writeFile(backup, raw, "utf8");
789
727
  let removed = 0;
790
728
  if ("apiKey" in json) {
@@ -806,9 +744,9 @@ export async function runKeysCleanupSettings() {
806
744
  delete json.providers;
807
745
  }
808
746
  }
809
- await fs.writeFile(SETTINGS_PATH, `${JSON.stringify(json, null, 2)}\n`, "utf8");
747
+ await fs.writeFile(getSettingsPath(), `${JSON.stringify(json, null, 2)}\n`, "utf8");
810
748
  console.log(`Backed up to: ${backup}`);
811
- console.log(`Removed ${removed} plaintext key field(s) from: ${SETTINGS_PATH}`);
749
+ console.log(`Removed ${removed} plaintext key field(s) from: ${getSettingsPath()}`);
812
750
  if (removed === 0) {
813
751
  console.log("(File was already clean.)");
814
752
  }
@@ -69,6 +69,26 @@ export interface ClarifyOptionsResult {
69
69
  * so the UI knows to suppress the "(Recommended)" tag.
70
70
  */
71
71
  export declare function buildClarifyOptions(suggestions: string[] | undefined, recommended?: string): ClarifyOptionsResult;
72
+ /**
73
+ * Model-designed option object: a choice the user picks between, carrying its
74
+ * own explanation. `recommended:true` marks the pre-selected default. This is
75
+ * the richer shape the clarifier prompt now asks for so every choice shows a
76
+ * per-option "why" (not just the question-level `why`).
77
+ */
78
+ export interface ClarifyOptionSpec {
79
+ label: string;
80
+ description?: string;
81
+ recommended?: boolean;
82
+ }
83
+ /**
84
+ * Build council options from the model's richer `{label, description,
85
+ * recommended}` objects, preserving each choice's explanation as `description`
86
+ * and pointing `defaultIndex` at the option the model flagged `recommended`
87
+ * (falling back to the first when none is flagged). The same "Type something" /
88
+ * "Chat about this" escape-hatches are appended as in `buildClarifyOptions`.
89
+ * Pure + exported for unit testing.
90
+ */
91
+ export declare function buildClarifyOptionsRich(specs: ClarifyOptionSpec[] | undefined): ClarifyOptionsResult;
72
92
  export declare function runClarification(topic: string, leaderModelId: string, conversationContext: string, respondToQuestion: QuestionResponder, llm: CouncilLLM, signal?: AbortSignal, seedQuestions?: GrayAreaQuestion[], maxRounds?: number,
73
93
  /**
74
94
  * Optional pre-filled answers keyed by seed-question id. When the loop's
@@ -83,5 +103,11 @@ prefillAnswers?: Map<string, string>,
83
103
  * a cheaper tier model on the same provider as the leader. Falls back to
84
104
  * the leader model if no cheaper model is cataloged. Default false.
85
105
  */
86
- costAware?: boolean): AsyncGenerator<StreamChunk, ClarifiedSpec, unknown>;
87
- export declare function buildSpecFromTopic(topic: string, _conversationContext: string): ClarifiedSpec;
106
+ costAware?: boolean,
107
+ /**
108
+ * Healthy panel models to fall back to when the leader's cost-tier spec-synth
109
+ * model is on a failing proxy — keeps the spec's ≥3 observable criteria (and
110
+ * thus a meaningful leader eval) instead of degrading to one generic criterion.
111
+ */
112
+ fallbackModels?: string[]): AsyncGenerator<StreamChunk, ClarifiedSpec, unknown>;
113
+ export declare function buildSpecFromTopic(topic: string, conversationContext: string): ClarifiedSpec;