muonroi-cli 1.8.4 → 1.9.0

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 (562) 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-filter.js +11 -0
  5. package/dist/packages/agent-harness-core/src/event-redact.js +7 -0
  6. package/dist/packages/agent-harness-core/src/event-tee.d.ts +64 -0
  7. package/dist/packages/agent-harness-core/src/event-tee.js +104 -0
  8. package/dist/packages/agent-harness-core/src/mcp-server.d.ts +25 -0
  9. package/dist/packages/agent-harness-core/src/mcp-server.js +169 -21
  10. package/dist/packages/agent-harness-core/src/predicate.d.ts +1 -1
  11. package/dist/packages/agent-harness-core/src/protocol.d.ts +90 -4
  12. package/dist/packages/agent-harness-core/src/protocol.js +15 -0
  13. package/dist/packages/agent-harness-core/src/visual-quality.d.ts +58 -0
  14. package/dist/packages/agent-harness-core/src/visual-quality.js +141 -0
  15. package/dist/packages/agent-harness-opentui/src/agent-mode.d.ts +6 -0
  16. package/dist/packages/agent-harness-opentui/src/agent-mode.js +14 -1
  17. package/dist/packages/agent-harness-opentui/src/input-bridge.d.ts +2 -10
  18. package/dist/packages/agent-harness-opentui/src/input-bridge.js +103 -16
  19. package/dist/packages/agent-harness-opentui/src/install.d.ts +8 -0
  20. package/dist/packages/agent-harness-opentui/src/install.js +10 -0
  21. package/dist/packages/agent-harness-opentui/src/semantic.js +12 -10
  22. package/dist/packages/agent-harness-opentui/src/visual-capture.d.ts +56 -0
  23. package/dist/packages/agent-harness-opentui/src/visual-capture.js +103 -0
  24. package/dist/src/agent-harness/mock-model.d.ts +38 -0
  25. package/dist/src/agent-harness/mock-model.js +69 -3
  26. package/dist/src/agent-harness/test-spawn.js +31 -0
  27. package/dist/src/chat/chat-keychain.d.ts +7 -12
  28. package/dist/src/chat/chat-keychain.js +19 -86
  29. package/dist/src/cli/config/screen-providers.js +1 -1
  30. package/dist/src/cli/cost-forensics.d.ts +10 -0
  31. package/dist/src/cli/cost-forensics.js +18 -3
  32. package/dist/src/cli/keys-bundle.d.ts +1 -1
  33. package/dist/src/cli/keys-bundle.js +1 -1
  34. package/dist/src/cli/keys.d.ts +10 -47
  35. package/dist/src/cli/keys.js +31 -399
  36. package/dist/src/council/clarifier.d.ts +31 -3
  37. package/dist/src/council/clarifier.js +220 -32
  38. package/dist/src/council/context.js +49 -15
  39. package/dist/src/council/debate-checkpoint.d.ts +129 -0
  40. package/dist/src/council/debate-checkpoint.js +176 -0
  41. package/dist/src/council/debate-planner.js +54 -5
  42. package/dist/src/council/debate-summary.d.ts +25 -0
  43. package/dist/src/council/debate-summary.js +85 -0
  44. package/dist/src/council/debate.d.ts +169 -2
  45. package/dist/src/council/debate.js +1265 -135
  46. package/dist/src/council/index.d.ts +108 -1
  47. package/dist/src/council/index.js +670 -197
  48. package/dist/src/council/leader.d.ts +26 -0
  49. package/dist/src/council/leader.js +150 -9
  50. package/dist/src/council/llm.d.ts +94 -0
  51. package/dist/src/council/llm.js +348 -55
  52. package/dist/src/council/panel-select.d.ts +30 -0
  53. package/dist/src/council/panel-select.js +82 -0
  54. package/dist/src/council/planner.js +40 -0
  55. package/dist/src/council/preflight.d.ts +17 -0
  56. package/dist/src/council/preflight.js +50 -2
  57. package/dist/src/council/prompts.d.ts +39 -4
  58. package/dist/src/council/prompts.js +256 -69
  59. package/dist/src/council/stance-recall.d.ts +42 -0
  60. package/dist/src/council/stance-recall.js +57 -0
  61. package/dist/src/council/strip-think.d.ts +17 -0
  62. package/dist/src/council/strip-think.js +33 -0
  63. package/dist/src/council/types.d.ts +138 -0
  64. package/dist/src/ee/artifact-cache.d.ts +16 -0
  65. package/dist/src/ee/artifact-cache.js +32 -0
  66. package/dist/src/ee/auth.d.ts +20 -0
  67. package/dist/src/ee/auth.js +54 -2
  68. package/dist/src/ee/bridge.d.ts +10 -0
  69. package/dist/src/ee/bridge.js +58 -0
  70. package/dist/src/ee/client.js +109 -21
  71. package/dist/src/ee/ee-onboarding.js +6 -26
  72. package/dist/src/ee/export-transcripts.d.ts +1 -0
  73. package/dist/src/ee/export-transcripts.js +8 -10
  74. package/dist/src/ee/extract-session.js +29 -0
  75. package/dist/src/ee/extract-style.d.ts +58 -0
  76. package/dist/src/ee/extract-style.js +270 -0
  77. package/dist/src/ee/recall-ledger.d.ts +9 -0
  78. package/dist/src/ee/recall-ledger.js +3 -0
  79. package/dist/src/ee/scope.d.ts +1 -0
  80. package/dist/src/ee/scope.js +26 -1
  81. package/dist/src/ee/search.d.ts +7 -0
  82. package/dist/src/ee/search.js +24 -0
  83. package/dist/src/ee/transcript-emit.js +2 -0
  84. package/dist/src/ee/types.d.ts +22 -0
  85. package/dist/src/ee/who-am-i-brain.d.ts +35 -0
  86. package/dist/src/ee/who-am-i-brain.js +220 -0
  87. package/dist/src/ee/who-am-i.d.ts +10 -3
  88. package/dist/src/ee/who-am-i.js +12 -0
  89. package/dist/src/ee/workflow-event.d.ts +48 -0
  90. package/dist/src/ee/workflow-event.js +81 -0
  91. package/dist/src/flow/compaction/compress.d.ts +3 -3
  92. package/dist/src/flow/compaction/compress.js +58 -8
  93. package/dist/src/flow/compaction/extract.d.ts +4 -7
  94. package/dist/src/flow/compaction/extract.js +50 -10
  95. package/dist/src/flow/compaction/index.d.ts +14 -1
  96. package/dist/src/flow/compaction/index.js +96 -3
  97. package/dist/src/flow/compaction/input-guard.d.ts +24 -0
  98. package/dist/src/flow/compaction/input-guard.js +43 -0
  99. package/dist/src/flow/compaction/progress.d.ts +35 -0
  100. package/dist/src/flow/compaction/progress.js +35 -0
  101. package/dist/src/flow/fold-planning.d.ts +36 -0
  102. package/dist/src/flow/fold-planning.js +83 -0
  103. package/dist/src/flow/hierarchy.d.ts +146 -0
  104. package/dist/src/flow/hierarchy.js +427 -0
  105. package/dist/src/flow/index.d.ts +1 -0
  106. package/dist/src/flow/index.js +2 -0
  107. package/dist/src/flow/run-artifacts.d.ts +102 -0
  108. package/dist/src/flow/run-artifacts.js +208 -0
  109. package/dist/src/generated/version.d.ts +1 -1
  110. package/dist/src/generated/version.js +1 -1
  111. package/dist/src/gsd/assessment-schema.d.ts +44 -0
  112. package/dist/src/gsd/assessment-schema.js +134 -0
  113. package/dist/src/gsd/capability-registry.d.ts +45 -0
  114. package/dist/src/gsd/capability-registry.js +337 -0
  115. package/dist/src/gsd/complexity-assessor.d.ts +39 -0
  116. package/dist/src/gsd/complexity-assessor.js +152 -0
  117. package/dist/src/gsd/config-bridge.d.ts +7 -0
  118. package/dist/src/gsd/config-bridge.js +114 -0
  119. package/dist/src/gsd/config-loader.d.ts +27 -0
  120. package/dist/src/gsd/config-loader.js +50 -0
  121. package/dist/src/gsd/council-context.d.ts +44 -0
  122. package/dist/src/gsd/council-context.js +114 -0
  123. package/dist/src/gsd/ee-closure.d.ts +28 -0
  124. package/dist/src/gsd/ee-closure.js +49 -0
  125. package/dist/src/gsd/flags.d.ts +66 -0
  126. package/dist/src/gsd/flags.js +102 -0
  127. package/dist/src/gsd/gsd-dispatch.d.ts +58 -0
  128. package/dist/src/gsd/gsd-dispatch.js +131 -0
  129. package/dist/src/gsd/gsd-runtime.d.ts +22 -0
  130. package/dist/src/gsd/gsd-runtime.js +37 -0
  131. package/dist/src/gsd/host-adapter.d.ts +11 -0
  132. package/dist/src/gsd/host-adapter.js +29 -0
  133. package/dist/src/gsd/index.d.ts +24 -1
  134. package/dist/src/gsd/index.js +27 -0
  135. package/dist/src/gsd/loop-host-contract.d.ts +21 -0
  136. package/dist/src/gsd/loop-host-contract.js +39 -0
  137. package/dist/src/gsd/loop-host.d.ts +69 -0
  138. package/dist/src/gsd/loop-host.js +245 -0
  139. package/dist/src/gsd/loop-resolver.d.ts +36 -0
  140. package/dist/src/gsd/loop-resolver.js +79 -0
  141. package/dist/src/gsd/model-tier.d.ts +13 -0
  142. package/dist/src/gsd/model-tier.js +45 -0
  143. package/dist/src/gsd/mutation-gate.d.ts +16 -0
  144. package/dist/src/gsd/mutation-gate.js +41 -0
  145. package/dist/src/gsd/native-roadmap.d.ts +89 -0
  146. package/dist/src/gsd/native-roadmap.js +343 -0
  147. package/dist/src/gsd/native-state.d.ts +47 -0
  148. package/dist/src/gsd/native-state.js +220 -0
  149. package/dist/src/gsd/paths.d.ts +23 -0
  150. package/dist/src/gsd/paths.js +66 -0
  151. package/dist/src/gsd/phase-dag.d.ts +12 -0
  152. package/dist/src/gsd/phase-dag.js +94 -0
  153. package/dist/src/gsd/phase-sync.d.ts +42 -0
  154. package/dist/src/gsd/phase-sync.js +321 -0
  155. package/dist/src/gsd/pil-gate-context.d.ts +13 -0
  156. package/dist/src/gsd/pil-gate-context.js +64 -0
  157. package/dist/src/gsd/pil-gate-critic.d.ts +19 -0
  158. package/dist/src/gsd/pil-gate-critic.js +74 -0
  159. package/dist/src/gsd/plan-council-prompts.d.ts +25 -0
  160. package/dist/src/gsd/plan-council-prompts.js +79 -0
  161. package/dist/src/gsd/plan-council.d.ts +44 -0
  162. package/dist/src/gsd/plan-council.js +283 -0
  163. package/dist/src/gsd/plan-gate-vocabulary.d.ts +40 -0
  164. package/dist/src/gsd/plan-gate-vocabulary.js +64 -0
  165. package/dist/src/gsd/product-workspace.d.ts +13 -0
  166. package/dist/src/gsd/product-workspace.js +124 -0
  167. package/dist/src/gsd/ship-bridge.d.ts +25 -0
  168. package/dist/src/gsd/ship-bridge.js +65 -0
  169. package/dist/src/gsd/state-document.d.ts +40 -0
  170. package/dist/src/gsd/state-document.js +163 -0
  171. package/dist/src/gsd/verdict-schema.d.ts +39 -0
  172. package/dist/src/gsd/verdict-schema.js +144 -0
  173. package/dist/src/gsd/verify-context.d.ts +22 -0
  174. package/dist/src/gsd/verify-context.js +27 -0
  175. package/dist/src/gsd/verify-council-prompts.d.ts +19 -0
  176. package/dist/src/gsd/verify-council-prompts.js +85 -0
  177. package/dist/src/gsd/verify-council.d.ts +25 -0
  178. package/dist/src/gsd/verify-council.js +119 -0
  179. package/dist/src/gsd/verify-gate-vocabulary.d.ts +25 -0
  180. package/dist/src/gsd/verify-gate-vocabulary.js +46 -0
  181. package/dist/src/gsd/workflow-engine.d.ts +60 -0
  182. package/dist/src/gsd/workflow-engine.js +207 -0
  183. package/dist/src/gsd/workflow-tools.d.ts +13 -0
  184. package/dist/src/gsd/workflow-tools.js +277 -0
  185. package/dist/src/headless/council-answers.js +4 -0
  186. package/dist/src/hooks/index.js +1 -1
  187. package/dist/src/index.js +172 -270
  188. package/dist/src/lsp/builtins.js +3 -1
  189. package/dist/src/lsp/manager.d.ts +5 -1
  190. package/dist/src/lsp/manager.js +249 -3
  191. package/dist/src/lsp/npm-cache.d.ts +11 -1
  192. package/dist/src/lsp/npm-cache.js +17 -1
  193. package/dist/src/lsp/runtime.d.ts +6 -1
  194. package/dist/src/lsp/runtime.js +17 -1
  195. package/dist/src/lsp/types.d.ts +83 -1
  196. package/dist/src/lsp/types.js +10 -0
  197. package/dist/src/maintain/pr-builder.js +23 -13
  198. package/dist/src/mcp/auto-setup.js +57 -32
  199. package/dist/src/mcp/client-pool.js +44 -16
  200. package/dist/src/mcp/lsp-tools.d.ts +5 -1
  201. package/dist/src/mcp/lsp-tools.js +93 -2
  202. package/dist/src/mcp/mcp-keychain.d.ts +3 -5
  203. package/dist/src/mcp/mcp-keychain.js +9 -49
  204. package/dist/src/mcp/research-onboarding.js +8 -7
  205. package/dist/src/mcp/runtime.js +34 -2
  206. package/dist/src/mcp/setup-guide-text.d.ts +1 -1
  207. package/dist/src/mcp/setup-guide-text.js +22 -2
  208. package/dist/src/mcp/tools-server.d.ts +10 -0
  209. package/dist/src/mcp/tools-server.js +10 -2
  210. package/dist/src/models/catalog-client.d.ts +87 -0
  211. package/dist/src/models/catalog-client.js +105 -38
  212. package/dist/src/models/catalog.json +528 -265
  213. package/dist/src/models/registry.d.ts +22 -7
  214. package/dist/src/models/registry.js +73 -10
  215. package/dist/src/ops/doctor.js +1 -1
  216. package/dist/src/orchestrator/ask-user.d.ts +61 -0
  217. package/dist/src/orchestrator/ask-user.js +65 -0
  218. package/dist/src/orchestrator/auto-commit.js +1 -1
  219. package/dist/src/orchestrator/batch-turn-runner.js +2 -2
  220. package/dist/src/orchestrator/cache-prefix.d.ts +67 -0
  221. package/dist/src/orchestrator/cache-prefix.js +83 -0
  222. package/dist/src/orchestrator/compact-request.d.ts +32 -0
  223. package/dist/src/orchestrator/compact-request.js +41 -0
  224. package/dist/src/orchestrator/compaction.d.ts +12 -3
  225. package/dist/src/orchestrator/compaction.js +35 -15
  226. package/dist/src/orchestrator/council-manager.d.ts +12 -3
  227. package/dist/src/orchestrator/council-manager.js +74 -32
  228. package/dist/src/orchestrator/council-request.d.ts +49 -0
  229. package/dist/src/orchestrator/council-request.js +62 -0
  230. package/dist/src/orchestrator/cross-turn-dedup.d.ts +6 -0
  231. package/dist/src/orchestrator/cross-turn-dedup.js +22 -1
  232. package/dist/src/orchestrator/error-utils.d.ts +29 -0
  233. package/dist/src/orchestrator/error-utils.js +132 -24
  234. package/dist/src/orchestrator/grounding-check.js +39 -1
  235. package/dist/src/orchestrator/interactive-pause.d.ts +26 -0
  236. package/dist/src/orchestrator/interactive-pause.js +36 -0
  237. package/dist/src/orchestrator/message-processor.d.ts +4 -0
  238. package/dist/src/orchestrator/message-processor.js +268 -41
  239. package/dist/src/orchestrator/orchestrator.d.ts +64 -3
  240. package/dist/src/orchestrator/orchestrator.js +823 -120
  241. package/dist/src/orchestrator/preprocessor.js +3 -3
  242. package/dist/src/orchestrator/proactive-compact-detector.d.ts +26 -0
  243. package/dist/src/orchestrator/proactive-compact-detector.js +36 -0
  244. package/dist/src/orchestrator/prompts.js +17 -17
  245. package/dist/src/orchestrator/reactive-delegation.d.ts +39 -0
  246. package/dist/src/orchestrator/reactive-delegation.js +59 -0
  247. package/dist/src/orchestrator/retry-classifier.d.ts +2 -1
  248. package/dist/src/orchestrator/retry-classifier.js +46 -2
  249. package/dist/src/orchestrator/safety-askcard.d.ts +1 -1
  250. package/dist/src/orchestrator/safety-askcard.js +5 -2
  251. package/dist/src/orchestrator/safety-intercept.d.ts +50 -0
  252. package/dist/src/orchestrator/safety-intercept.js +62 -0
  253. package/dist/src/orchestrator/scope-reminder.js +1 -1
  254. package/dist/src/orchestrator/session-experience.d.ts +2 -1
  255. package/dist/src/orchestrator/session-experience.js +2 -1
  256. package/dist/src/orchestrator/should-run-gate.d.ts +5 -0
  257. package/dist/src/orchestrator/should-run-gate.js +18 -0
  258. package/dist/src/orchestrator/stall-watchdog.d.ts +31 -3
  259. package/dist/src/orchestrator/stall-watchdog.js +65 -10
  260. package/dist/src/orchestrator/stream-runner.d.ts +13 -3
  261. package/dist/src/orchestrator/stream-runner.js +115 -49
  262. package/dist/src/orchestrator/sub-agent-cap.d.ts +13 -1
  263. package/dist/src/orchestrator/sub-agent-cap.js +16 -1
  264. package/dist/src/orchestrator/sub-agent-model-tier.d.ts +13 -1
  265. package/dist/src/orchestrator/sub-agent-model-tier.js +16 -2
  266. package/dist/src/orchestrator/subagent-compactor.d.ts +53 -1
  267. package/dist/src/orchestrator/subagent-compactor.js +126 -15
  268. package/dist/src/orchestrator/tool-engine.d.ts +41 -0
  269. package/dist/src/orchestrator/tool-engine.js +846 -66
  270. package/dist/src/orchestrator/tool-limit-auto-recover.d.ts +22 -0
  271. package/dist/src/orchestrator/tool-limit-auto-recover.js +30 -0
  272. package/dist/src/orchestrator/turn-runner-deps.d.ts +19 -0
  273. package/dist/src/orchestrator/turn-watchdog.d.ts +44 -0
  274. package/dist/src/orchestrator/turn-watchdog.js +84 -0
  275. package/dist/src/pil/agent-operating-contract.d.ts +1 -1
  276. package/dist/src/pil/agent-operating-contract.js +6 -4
  277. package/dist/src/pil/cheap-model-playbook.d.ts +1 -1
  278. package/dist/src/pil/cheap-model-playbook.js +5 -1
  279. package/dist/src/pil/cheap-model-workbooks.d.ts +1 -1
  280. package/dist/src/pil/cheap-model-workbooks.js +1 -1
  281. package/dist/src/pil/discovery-types.d.ts +1 -0
  282. package/dist/src/pil/discovery.d.ts +1 -1
  283. package/dist/src/pil/discovery.js +18 -13
  284. package/dist/src/pil/layer1-intent.d.ts +18 -6
  285. package/dist/src/pil/layer1-intent.js +66 -757
  286. package/dist/src/pil/layer15-context-scan.js +15 -1
  287. package/dist/src/pil/layer1_5-complexity-size.d.ts +7 -0
  288. package/dist/src/pil/layer1_5-complexity-size.js +31 -5
  289. package/dist/src/pil/layer3-ee-injection.js +23 -8
  290. package/dist/src/pil/layer4-gsd.js +69 -16
  291. package/dist/src/pil/layer5-context.js +7 -3
  292. package/dist/src/pil/layer6-output.d.ts +23 -0
  293. package/dist/src/pil/layer6-output.js +5 -1
  294. package/dist/src/pil/llm-classify.d.ts +111 -5
  295. package/dist/src/pil/llm-classify.js +421 -189
  296. package/dist/src/pil/native-capabilities-workbook.d.ts +1 -1
  297. package/dist/src/pil/native-capabilities-workbook.js +8 -0
  298. package/dist/src/pil/pipeline.js +36 -2
  299. package/dist/src/pil/repo-grounding-probe.d.ts +15 -0
  300. package/dist/src/pil/repo-grounding-probe.js +136 -0
  301. package/dist/src/pil/repo-structure-hints.d.ts +7 -0
  302. package/dist/src/pil/repo-structure-hints.js +45 -0
  303. package/dist/src/pil/response-tools.js +5 -3
  304. package/dist/src/pil/schema.d.ts +1 -0
  305. package/dist/src/pil/schema.js +2 -0
  306. package/dist/src/pil/types.d.ts +18 -0
  307. package/dist/src/playbook/directives.d.ts +4 -0
  308. package/dist/src/playbook/directives.js +17 -5
  309. package/dist/src/product-loop/artifact-io.js +4 -0
  310. package/dist/src/product-loop/backlog-builder.d.ts +14 -1
  311. package/dist/src/product-loop/backlog-builder.js +30 -6
  312. package/dist/src/product-loop/criteria-seed.d.ts +51 -0
  313. package/dist/src/product-loop/criteria-seed.js +200 -0
  314. package/dist/src/product-loop/discovery-context-format.js +3 -1
  315. package/dist/src/product-loop/discovery-ecosystem.js +4 -1
  316. package/dist/src/product-loop/discovery-interview.d.ts +9 -0
  317. package/dist/src/product-loop/discovery-interview.js +60 -12
  318. package/dist/src/product-loop/discovery-recommender.js +2 -1
  319. package/dist/src/product-loop/discovery-schema.js +19 -2
  320. package/dist/src/product-loop/discovery-triage.d.ts +23 -0
  321. package/dist/src/product-loop/discovery-triage.js +109 -0
  322. package/dist/src/product-loop/gather.js +150 -2
  323. package/dist/src/product-loop/ideal-trace.d.ts +7 -0
  324. package/dist/src/product-loop/ideal-trace.js +64 -0
  325. package/dist/src/product-loop/index.d.ts +13 -1
  326. package/dist/src/product-loop/index.js +340 -52
  327. package/dist/src/product-loop/loop-driver.d.ts +7 -0
  328. package/dist/src/product-loop/loop-driver.js +330 -106
  329. package/dist/src/product-loop/phase-plan.d.ts +21 -0
  330. package/dist/src/product-loop/phase-plan.js +81 -6
  331. package/dist/src/product-loop/phase-rituals.d.ts +3 -0
  332. package/dist/src/product-loop/phase-rituals.js +8 -3
  333. package/dist/src/product-loop/phase-runner.js +39 -12
  334. package/dist/src/product-loop/plan-adherence-review.d.ts +26 -0
  335. package/dist/src/product-loop/plan-adherence-review.js +144 -0
  336. package/dist/src/product-loop/sprint-runner.d.ts +173 -0
  337. package/dist/src/product-loop/sprint-runner.js +863 -19
  338. package/dist/src/product-loop/types.d.ts +61 -5
  339. package/dist/src/providers/adapter.d.ts +1 -1
  340. package/dist/src/providers/adapter.js +3 -4
  341. package/dist/src/providers/anthropic.d.ts +9 -8
  342. package/dist/src/providers/anthropic.js +13 -47
  343. package/dist/src/providers/auth/browser-flow.d.ts +1 -1
  344. package/dist/src/providers/auth/browser-flow.js +1 -1
  345. package/dist/src/providers/auth/grok-oauth.d.ts +1 -0
  346. package/dist/src/providers/auth/grok-oauth.js +30 -5
  347. package/dist/src/providers/auth/openai-oauth.d.ts +1 -0
  348. package/dist/src/providers/auth/openai-oauth.js +15 -1
  349. package/dist/src/providers/auth/registry.js +0 -34
  350. package/dist/src/providers/auth/token-store.d.ts +9 -9
  351. package/dist/src/providers/auth/token-store.js +8 -67
  352. package/dist/src/providers/auth/types.d.ts +9 -1
  353. package/dist/src/providers/auth/types.js +1 -1
  354. package/dist/src/providers/capabilities.d.ts +24 -5
  355. package/dist/src/providers/capabilities.js +42 -24
  356. package/dist/src/providers/endpoints.d.ts +2 -2
  357. package/dist/src/providers/endpoints.js +11 -10
  358. package/dist/src/providers/env-store.d.ts +17 -0
  359. package/dist/src/providers/env-store.js +228 -0
  360. package/dist/src/providers/keychain.d.ts +22 -18
  361. package/dist/src/providers/keychain.js +127 -140
  362. package/dist/src/providers/mcp-vision-bridge.js +56 -146
  363. package/dist/src/providers/openai-compatible.js +8 -1
  364. package/dist/src/providers/pricing.d.ts +2 -2
  365. package/dist/src/providers/pricing.js +3 -13
  366. package/dist/src/providers/runtime.d.ts +43 -3
  367. package/dist/src/providers/runtime.js +88 -14
  368. package/dist/src/providers/strategies/base.strategy.d.ts +16 -0
  369. package/dist/src/providers/strategies/base.strategy.js +24 -1
  370. package/dist/src/providers/strategies/{siliconflow.strategy.d.ts → opencode-go.strategy.d.ts} +3 -3
  371. package/dist/src/providers/strategies/opencode-go.strategy.js +83 -0
  372. package/dist/src/providers/strategies/registry.js +4 -4
  373. package/dist/src/providers/strategies/thinking-mode.d.ts +109 -0
  374. package/dist/src/providers/strategies/thinking-mode.js +288 -1
  375. package/dist/src/providers/strategies/xai.strategy.js +27 -0
  376. package/dist/src/providers/strategies/zai.strategy.d.ts +14 -0
  377. package/dist/src/providers/strategies/zai.strategy.js +44 -0
  378. package/dist/src/providers/types.d.ts +5 -6
  379. package/dist/src/providers/types.js +2 -2
  380. package/dist/src/providers/vision-backend.d.ts +47 -0
  381. package/dist/src/providers/vision-backend.js +258 -0
  382. package/dist/src/providers/vision-proxy.d.ts +22 -9
  383. package/dist/src/providers/vision-proxy.js +63 -132
  384. package/dist/src/providers/warm.d.ts +65 -0
  385. package/dist/src/providers/warm.js +145 -0
  386. package/dist/src/providers/wire-debug.js +95 -0
  387. package/dist/src/router/decide.d.ts +13 -0
  388. package/dist/src/router/decide.js +138 -36
  389. package/dist/src/router/peak-hour.d.ts +38 -0
  390. package/dist/src/router/peak-hour.js +107 -0
  391. package/dist/src/router/step-router.js +3 -2
  392. package/dist/src/router/warm.js +4 -5
  393. package/dist/src/scaffold/continuation-prompt.d.ts +11 -0
  394. package/dist/src/scaffold/continuation-prompt.js +26 -0
  395. package/dist/src/scaffold/point-to-existing.d.ts +21 -0
  396. package/dist/src/scaffold/point-to-existing.js +25 -0
  397. package/dist/src/self-qa/agentic-loop.js +6 -5
  398. package/dist/src/{ui/state → state}/active-run.d.ts +19 -0
  399. package/dist/src/{ui/state → state}/active-run.js +21 -0
  400. package/dist/src/{ui/status-bar/store.d.ts → state/status-bar-store.d.ts} +4 -1
  401. package/dist/src/{ui/status-bar/store.js → state/status-bar-store.js} +12 -9
  402. package/dist/src/state/turn-trace.d.ts +43 -0
  403. package/dist/src/state/turn-trace.js +32 -0
  404. package/dist/src/storage/db.js +2 -1
  405. package/dist/src/storage/index.d.ts +1 -1
  406. package/dist/src/storage/index.js +1 -1
  407. package/dist/src/storage/interaction-log.d.ts +1 -1
  408. package/dist/src/storage/migrations.js +71 -1
  409. package/dist/src/storage/sessions.d.ts +28 -10
  410. package/dist/src/storage/sessions.js +78 -21
  411. package/dist/src/storage/transcript-view.js +1 -1
  412. package/dist/src/storage/transcript.d.ts +51 -0
  413. package/dist/src/storage/transcript.js +340 -15
  414. package/dist/src/tools/file.d.ts +15 -0
  415. package/dist/src/tools/file.js +32 -0
  416. package/dist/src/tools/git-safety.d.ts +19 -0
  417. package/dist/src/tools/git-safety.js +168 -0
  418. package/dist/src/tools/native-tools.d.ts +1 -1
  419. package/dist/src/tools/native-tools.js +81 -1
  420. package/dist/src/tools/registry.d.ts +20 -0
  421. package/dist/src/tools/registry.js +576 -23
  422. package/dist/src/tools/research.d.ts +29 -0
  423. package/dist/src/tools/research.js +233 -0
  424. package/dist/src/types/index.d.ts +147 -4
  425. package/dist/src/ui/app.js +0 -0
  426. package/dist/src/ui/cards/product-status-card.js +1 -1
  427. package/dist/src/ui/components/agent-rail-activities.d.ts +26 -0
  428. package/dist/src/ui/components/agent-rail-activities.js +47 -0
  429. package/dist/src/ui/components/bubble-body-guard.d.ts +27 -0
  430. package/dist/src/ui/components/bubble-body-guard.js +50 -0
  431. package/dist/src/ui/components/compact-progress-card.d.ts +24 -0
  432. package/dist/src/ui/components/compact-progress-card.js +42 -0
  433. package/dist/src/ui/components/context-rail.d.ts +26 -0
  434. package/dist/src/ui/components/context-rail.js +33 -0
  435. package/dist/src/ui/components/council-conclusion-card.d.ts +73 -0
  436. package/dist/src/ui/components/council-conclusion-card.js +420 -0
  437. package/dist/src/ui/components/council-debate-pill.d.ts +36 -0
  438. package/dist/src/ui/components/council-debate-pill.js +34 -0
  439. package/dist/src/ui/components/council-info-card.js +2 -2
  440. package/dist/src/ui/components/council-leader-bubble.d.ts +10 -3
  441. package/dist/src/ui/components/council-leader-bubble.js +21 -11
  442. package/dist/src/ui/components/council-message-bubble.d.ts +15 -2
  443. package/dist/src/ui/components/council-message-bubble.js +16 -15
  444. package/dist/src/ui/components/council-phase-timeline.d.ts +9 -1
  445. package/dist/src/ui/components/council-phase-timeline.js +66 -17
  446. package/dist/src/ui/components/council-placeholder-bubble.d.ts +16 -9
  447. package/dist/src/ui/components/council-placeholder-bubble.js +32 -29
  448. package/dist/src/ui/components/council-question-card.js +13 -12
  449. package/dist/src/ui/components/council-rail-rounds.d.ts +26 -0
  450. package/dist/src/ui/components/council-rail-rounds.js +57 -0
  451. package/dist/src/ui/components/council-round-group.d.ts +38 -0
  452. package/dist/src/ui/components/council-round-group.js +88 -0
  453. package/dist/src/ui/components/council-status-list.d.ts +3 -1
  454. package/dist/src/ui/components/council-status-list.js +36 -24
  455. package/dist/src/ui/components/council-synthesis-banner.d.ts +7 -2
  456. package/dist/src/ui/components/council-synthesis-banner.js +20 -5
  457. package/dist/src/ui/components/halt-recovery-card.js +9 -5
  458. package/dist/src/ui/components/jump-to-latest-pill.d.ts +11 -0
  459. package/dist/src/ui/components/jump-to-latest-pill.js +14 -0
  460. package/dist/src/ui/components/message-view.d.ts +15 -0
  461. package/dist/src/ui/components/message-view.js +50 -1
  462. package/dist/src/ui/components/prompt-box.js +18 -16
  463. package/dist/src/ui/components/session-tree-card.d.ts +14 -0
  464. package/dist/src/ui/components/session-tree-card.js +46 -0
  465. package/dist/src/ui/components/slash-inline-menu.d.ts +12 -0
  466. package/dist/src/ui/components/slash-inline-menu.js +26 -5
  467. package/dist/src/ui/components/task-list-panel.d.ts +14 -1
  468. package/dist/src/ui/components/task-list-panel.js +22 -2
  469. package/dist/src/ui/components/tool-group.d.ts +15 -3
  470. package/dist/src/ui/components/tool-group.js +69 -11
  471. package/dist/src/ui/containers/modals-layer.d.ts +4 -2
  472. package/dist/src/ui/containers/modals-layer.js +2 -2
  473. package/dist/src/ui/council-harness-event.d.ts +57 -0
  474. package/dist/src/ui/council-harness-event.js +46 -0
  475. package/dist/src/ui/heartbeat-debug.d.ts +29 -0
  476. package/dist/src/ui/heartbeat-debug.js +45 -0
  477. package/dist/src/ui/hooks/use-session-picker.d.ts +3 -3
  478. package/dist/src/ui/mcp-modal.js +2 -4
  479. package/dist/src/ui/modals/api-key-modal.js +1 -1
  480. package/dist/src/ui/modals/connect-modal.js +4 -3
  481. package/dist/src/ui/modals/model-picker-modal.d.ts +8 -18
  482. package/dist/src/ui/modals/model-picker-modal.js +8 -10
  483. package/dist/src/ui/modals/session-picker-modal.d.ts +2 -2
  484. package/dist/src/ui/modals/session-picker-modal.js +3 -5
  485. package/dist/src/ui/picker-providers.d.ts +1 -1
  486. package/dist/src/ui/picker-providers.js +1 -1
  487. package/dist/src/ui/primitives/index.d.ts +1 -0
  488. package/dist/src/ui/primitives/index.js +2 -0
  489. package/dist/src/ui/primitives/semantic-primitives.d.ts +76 -0
  490. package/dist/src/ui/primitives/semantic-primitives.js +81 -0
  491. package/dist/src/ui/slash/compact.js +5 -7
  492. package/dist/src/ui/slash/cost.js +1 -1
  493. package/dist/src/ui/slash/council.js +19 -1
  494. package/dist/src/ui/slash/debug.d.ts +3 -31
  495. package/dist/src/ui/slash/debug.js +9 -20
  496. package/dist/src/ui/slash/ee.js +81 -0
  497. package/dist/src/ui/slash/ideal.d.ts +6 -2
  498. package/dist/src/ui/slash/ideal.js +97 -7
  499. package/dist/src/ui/slash/menu-items.d.ts +7 -0
  500. package/dist/src/ui/slash/menu-items.js +23 -20
  501. package/dist/src/ui/slash/registry.d.ts +2 -0
  502. package/dist/src/ui/slash/registry.js +4 -0
  503. package/dist/src/ui/status-bar/cache-hit.d.ts +6 -0
  504. package/dist/src/ui/status-bar/cache-hit.js +9 -0
  505. package/dist/src/ui/status-bar/index.d.ts +1 -1
  506. package/dist/src/ui/status-bar/index.js +7 -3
  507. package/dist/src/ui/status-bar/usd-meter.d.ts +5 -4
  508. package/dist/src/ui/status-bar/usd-meter.js +6 -4
  509. package/dist/src/ui/theme.d.ts +1 -0
  510. package/dist/src/ui/theme.js +2 -0
  511. package/dist/src/ui/types.d.ts +7 -0
  512. package/dist/src/ui/use-app-logic.js +0 -0
  513. package/dist/src/ui/utils/agent-activities.d.ts +39 -0
  514. package/dist/src/ui/utils/agent-activities.js +96 -0
  515. package/dist/src/ui/utils/format.d.ts +14 -0
  516. package/dist/src/ui/utils/format.js +23 -3
  517. package/dist/src/ui/utils/group-tool-entries.d.ts +26 -0
  518. package/dist/src/ui/utils/group-tool-entries.js +111 -0
  519. package/dist/src/ui/utils/tool-summary.d.ts +21 -0
  520. package/dist/src/ui/utils/tool-summary.js +91 -0
  521. package/dist/src/usage/downgrade.js +2 -2
  522. package/dist/src/usage/product-ledger.js +2 -2
  523. package/dist/src/utils/event-loop-monitor.d.ts +85 -0
  524. package/dist/src/utils/event-loop-monitor.js +107 -0
  525. package/dist/src/utils/install-manager.js +2 -1
  526. package/dist/src/utils/llm-deadline.d.ts +14 -0
  527. package/dist/src/utils/llm-deadline.js +19 -0
  528. package/dist/src/utils/logger.js +2 -2
  529. package/dist/src/utils/loop-profiler.d.ts +102 -0
  530. package/dist/src/utils/loop-profiler.js +202 -0
  531. package/dist/src/utils/permission-mode.js +5 -3
  532. package/dist/src/utils/redactor.js +1 -1
  533. package/dist/src/utils/settings.d.ts +180 -5
  534. package/dist/src/utils/settings.js +271 -31
  535. package/dist/src/utils/side-question.d.ts +1 -2
  536. package/dist/src/utils/side-question.js +2 -2
  537. package/dist/src/utils/visible-retry.d.ts +11 -0
  538. package/dist/src/utils/visible-retry.js +10 -1
  539. package/dist/src/verify/entrypoint.d.ts +1 -1
  540. package/dist/src/verify/entrypoint.js +52 -17
  541. package/dist/src/verify/orchestrator.d.ts +1 -1
  542. package/dist/src/verify/orchestrator.js +20 -3
  543. package/dist/src/verify/recipes.d.ts +13 -0
  544. package/dist/src/verify/recipes.js +15 -0
  545. package/package.json +134 -132
  546. package/dist/src/cli/bw-vault.d.ts +0 -55
  547. package/dist/src/cli/bw-vault.js +0 -133
  548. package/dist/src/mcp/ee-tools.d.ts +0 -46
  549. package/dist/src/mcp/ee-tools.js +0 -193
  550. package/dist/src/providers/auth/gcloud.d.ts +0 -28
  551. package/dist/src/providers/auth/gcloud.js +0 -102
  552. package/dist/src/providers/auth/gemini-oauth.d.ts +0 -82
  553. package/dist/src/providers/auth/gemini-oauth.js +0 -472
  554. package/dist/src/providers/gemini.d.ts +0 -11
  555. package/dist/src/providers/gemini.js +0 -45
  556. package/dist/src/providers/siliconflow-sse-repair.d.ts +0 -58
  557. package/dist/src/providers/siliconflow-sse-repair.js +0 -177
  558. package/dist/src/providers/strategies/google.strategy.d.ts +0 -22
  559. package/dist/src/providers/strategies/google.strategy.js +0 -174
  560. package/dist/src/providers/strategies/siliconflow.strategy.js +0 -29
  561. package/dist/src/ui/containers/chat-feed.d.ts +0 -40
  562. package/dist/src/ui/containers/chat-feed.js +0 -66
@@ -1,49 +1,30 @@
1
1
  /**
2
2
  * src/providers/keychain.ts
3
3
  *
4
- * Per-provider keychain loader with env-var fallback.
5
- * Extends the Phase 0 loadAnthropicKey pattern to all 6 providers.
6
- * PROV-02 / PROV-03 requirements.
4
+ * Per-provider key store. Backed by the env-store (`.env` file + process.env +
5
+ * Windows registry mirror) the OS keychain (keytar) has been removed.
7
6
  *
8
- * Priority: OS keychain (keytar) > environment variable > settings.json > ProviderKeyMissingError.
7
+ * Source of truth: `process.env[ENV_BY_PROVIDER[provider]]`. Writes go through
8
+ * the env-store; reads come straight from process.env.
9
9
  * Exception: ollama is keyless by default.
10
10
  */
11
11
  import { redactor } from "../utils/redactor.js";
12
+ import { clearEnvVar, persistEnvVar } from "./env-store.js";
12
13
  import { ALL_PROVIDER_IDS } from "./types.js";
13
14
  function normalizeKeychainProvider(p) {
14
15
  const lower = p.toLowerCase();
15
- if (lower === "agy")
16
- return "google"; // agy alias for google provider
17
16
  if (ALL_PROVIDER_IDS.includes(lower))
18
17
  return lower;
19
18
  return null;
20
19
  }
21
- const SETTINGS_KEY_MAP = {
22
- anthropic: "anthropic",
23
- openai: "openai",
24
- google: "google",
25
- deepseek: "deepseek",
26
- siliconflow: "siliconflow",
27
- xai: "xai",
28
- };
29
- const KEYCHAIN_SERVICE = "muonroi-cli";
30
- const ACCOUNT_BY_PROVIDER = {
31
- anthropic: "anthropic",
32
- openai: "openai",
33
- google: "google",
34
- deepseek: "deepseek",
35
- siliconflow: "siliconflow",
36
- xai: "xai",
37
- ollama: "ollama",
38
- };
39
- const ENV_BY_PROVIDER = {
20
+ export const ENV_BY_PROVIDER = {
40
21
  anthropic: "ANTHROPIC_API_KEY",
41
22
  openai: "OPENAI_API_KEY",
42
- google: "GOOGLE_API_KEY",
43
23
  deepseek: "DEEPSEEK_API_KEY",
44
- siliconflow: "SILICONFLOW_API_KEY",
45
24
  xai: "XAI_API_KEY",
46
25
  ollama: "OLLAMA_API_KEY",
26
+ zai: "ZAI_API_KEY",
27
+ "opencode-go": "OPENCODE_GO_API_KEY",
47
28
  };
48
29
  /**
49
30
  * Thrown when no API key can be found for a provider.
@@ -56,147 +37,83 @@ export class ProviderKeyMissingError extends Error {
56
37
  this.name = "ProviderKeyMissingError";
57
38
  }
58
39
  }
59
- async function loadKeytar() {
60
- try {
61
- return (await import("keytar"));
62
- }
63
- catch {
64
- return null;
65
- }
66
- }
67
40
  /**
68
- * Providers that store an API key in the OS keychain.
69
- * Phase 12.2-G5: derived from `ALL_PROVIDER_IDS` by excluding ollama
70
- * (keyless local server). Preserves the original ordering.
41
+ * Providers that store an API key (all except keyless ollama). Derived from
42
+ * `ALL_PROVIDER_IDS`; preserves the original ordering.
71
43
  */
72
44
  export const KEYCHAIN_PROVIDER_IDS = ALL_PROVIDER_IDS.filter((p) => p !== "ollama");
73
45
  /**
74
- * Store a provider API key in the OS keychain. Returns true on success.
75
- * Falls back to false (silent) if keytar is unavailable on this platform.
46
+ * Store a provider API key in the OS environment (via the env-store: `.env`
47
+ * file + process.env + Windows registry mirror). Returns true on success.
76
48
  */
77
49
  export async function setKeyForProvider(provider, key) {
78
50
  const norm = normalizeKeychainProvider(provider) ?? provider;
79
51
  if (!key || key.length < 20) {
80
52
  throw new Error(`Key for '${provider}' is too short (< 20 chars).`);
81
53
  }
82
- const kt = await loadKeytar();
83
- if (!kt?.setPassword)
84
- return false;
85
- redactor.enrollSecret(key);
54
+ persistEnvVar(ENV_BY_PROVIDER[norm], key);
55
+ // One auth mode per provider (OAuth XOR API key): setting an API key logs out
56
+ // any OAuth session for this provider so the two can never coexist.
86
57
  try {
87
- await kt.setPassword(KEYCHAIN_SERVICE, ACCOUNT_BY_PROVIDER[norm], key);
88
- return true;
58
+ const { listOAuthProviderIds } = await import("./auth/registry.js");
59
+ if ((await listOAuthProviderIds()).includes(norm)) {
60
+ const { deleteTokens } = await import("./auth/token-store.js");
61
+ await deleteTokens(norm);
62
+ }
89
63
  }
90
64
  catch (err) {
91
- // Runtime backend failure is common on Linux when libsecret / secret service
92
- // (gnome-keyring, kwallet/ksecretd, etc.) is not installed, the collection is
93
- // locked, or no D-Bus session/keyring is active for this user.
94
- // The caller (e.g. keys import-bw / keys set) will print the friendly message.
95
- if (process.env.DEBUG || process.env.MUONROI_DEBUG_KEYCHAIN) {
96
- console.error(`[keychain] setPassword backend error for ${provider}:`, err?.message || err);
65
+ // Best-effort: the key is already persisted; failing to clear tokens is
66
+ // non-fatal (resolution still prefers the freshly-set key path only if no
67
+ // token but we log so a lingering token is diagnosable).
68
+ if (process.env.MUONROI_DEBUG_ENVSTORE) {
69
+ console.error(`[keychain] failed to clear OAuth for ${norm}: ${err instanceof Error ? err.message : String(err)}`);
97
70
  }
98
- return false;
99
71
  }
72
+ return true;
100
73
  }
101
74
  /**
102
- * Delete a stored key. Returns true if a key was deleted, false if none was
103
- * present or keytar is unavailable.
75
+ * Delete a stored key from the env-store. Returns true if a key was present.
104
76
  */
105
77
  export async function deleteKeyForProvider(provider) {
106
78
  const norm = normalizeKeychainProvider(provider) ?? provider;
107
- const kt = await loadKeytar();
108
- if (!kt?.deletePassword)
109
- return false;
110
- try {
111
- return await kt.deletePassword(KEYCHAIN_SERVICE, ACCOUNT_BY_PROVIDER[norm]);
112
- }
113
- catch (err) {
114
- if (process.env.DEBUG || process.env.MUONROI_DEBUG_KEYCHAIN) {
115
- console.error(`[keychain] deletePassword backend error for ${provider}:`, err?.message || err);
116
- }
117
- return false;
118
- }
79
+ const had = !!process.env[ENV_BY_PROVIDER[norm]];
80
+ clearEnvVar(ENV_BY_PROVIDER[norm]);
81
+ return had;
119
82
  }
120
83
  /**
121
- * List provider IDs that currently have a key stored in the keychain.
122
- * Empty array if keytar is unavailable.
84
+ * List provider IDs that currently have a key set in the environment.
123
85
  */
124
86
  export async function listStoredProviders() {
125
- const kt = await loadKeytar();
126
- if (!kt?.findCredentials)
127
- return [];
128
- try {
129
- const creds = await kt.findCredentials(KEYCHAIN_SERVICE);
130
- const validAccounts = new Set(Object.values(ACCOUNT_BY_PROVIDER));
131
- return creds.filter((c) => validAccounts.has(c.account)).map((c) => c.account);
132
- }
133
- catch {
134
- return [];
135
- }
87
+ return KEYCHAIN_PROVIDER_IDS.filter((p) => {
88
+ const v = process.env[ENV_BY_PROVIDER[p]];
89
+ return !!v && v.length >= 20;
90
+ });
136
91
  }
137
92
  /**
138
- * Load the API key for a given provider.
139
- * Priority: OS keychain (keytar) > environment variable.
93
+ * Load the API key for a given provider from the environment.
140
94
  * Ollama returns '' when no key is found (keyless).
141
95
  *
142
96
  * @throws {ProviderKeyMissingError} when no key found for non-ollama providers.
143
97
  */
144
98
  export async function loadKeyForProvider(provider) {
145
- const kt = await loadKeytar();
146
- if (kt) {
147
- try {
148
- const k = await kt.getPassword(KEYCHAIN_SERVICE, ACCOUNT_BY_PROVIDER[provider]);
149
- if (k && k.length >= 20) {
150
- redactor.enrollSecret(k);
151
- return k;
152
- }
153
- }
154
- catch {
155
- /* ignore keytar backend failures */
156
- }
157
- }
158
99
  const envKey = process.env[ENV_BY_PROVIDER[provider]];
159
100
  if (envKey && envKey.length >= 20) {
160
101
  redactor.enrollSecret(envKey);
161
102
  return envKey;
162
103
  }
163
- // Fallback: check user-settings.json providers config (lazy import to avoid circular deps)
164
- const settingsField = SETTINGS_KEY_MAP[provider];
165
- if (settingsField) {
166
- try {
167
- const { loadUserSettings } = await import("../utils/settings.js");
168
- const providers = loadUserSettings().providers;
169
- const settingsKey = providers?.[settingsField]?.apiKey;
170
- if (settingsKey && settingsKey.length >= 20) {
171
- redactor.enrollSecret(settingsKey);
172
- return settingsKey;
173
- }
174
- }
175
- catch {
176
- /* settings load failed — continue to error */
177
- }
178
- }
179
104
  // Ollama may be keyless
180
105
  if (provider === "ollama")
181
106
  return "";
182
107
  throw new ProviderKeyMissingError(provider);
183
108
  }
184
109
  /**
185
- * Return the list of providers that currently have credentials available — checked across
186
- * OS keychain, environment variables, and user-settings.json. `ollama` is always included
110
+ * Return the list of providers that currently have credentials available —
111
+ * either an env API key or a stored OAuth token. `ollama` is always included
187
112
  * because it is keyless. Order is stable for UI rendering.
188
113
  */
189
114
  export async function getConfiguredProviders() {
190
115
  const order = ALL_PROVIDER_IDS;
191
116
  const stored = new Set(await listStoredProviders());
192
- let settingsProviders = {};
193
- try {
194
- const { loadUserSettings } = await import("../utils/settings.js");
195
- settingsProviders = (loadUserSettings().providers ?? {});
196
- }
197
- catch {
198
- /* settings unreadable — keychain + env still work */
199
- }
200
117
  // OAuth-authenticated providers (no API key, but tokens stored via `keys
201
118
  // login`) need to count as configured — otherwise the model picker silently
202
119
  // hides them. Source the eligible provider list from the OAuth registry so
@@ -216,25 +133,8 @@ export async function getConfiguredProviders() {
216
133
  }
217
134
  const configured = [];
218
135
  for (const p of order) {
219
- if (p === "ollama") {
136
+ if (p === "ollama" || stored.has(p) || oauthAuthenticated.has(p)) {
220
137
  configured.push(p);
221
- continue;
222
- }
223
- if (stored.has(p) || oauthAuthenticated.has(p)) {
224
- configured.push(p);
225
- continue;
226
- }
227
- const envKey = process.env[ENV_BY_PROVIDER[p]];
228
- if (envKey && envKey.length >= 20) {
229
- configured.push(p);
230
- continue;
231
- }
232
- const settingsField = SETTINGS_KEY_MAP[p];
233
- if (settingsField) {
234
- const k = settingsProviders[settingsField]?.apiKey;
235
- if (k && k.length >= 20) {
236
- configured.push(p);
237
- }
238
138
  }
239
139
  }
240
140
  return configured;
@@ -258,7 +158,7 @@ export async function getOAuthTokens(provider) {
258
158
  }
259
159
  /**
260
160
  * Find the first provider with an available API key.
261
- * Checks in priority order: anthropic, openai, google, deepseek, siliconflow, ollama.
161
+ * Checks in priority order: anthropic, openai, deepseek, ollama.
262
162
  * Returns null if no provider has a key (unlikely — ollama is keyless fallback).
263
163
  */
264
164
  export async function firstAvailableProvider() {
@@ -277,4 +177,91 @@ export async function firstAvailableProvider() {
277
177
  }
278
178
  return null;
279
179
  }
180
+ /**
181
+ * One-time migration: move any legacy API keys — OS keychain (keytar) or
182
+ * `settings.json` (`providers.<p>.apiKey`) — into the env-store, then remove
183
+ * the legacy copies. Best-effort, idempotent, and never throws. Guarded by
184
+ * `settings.keysMigratedToEnv`. Runs once at startup before key resolution.
185
+ */
186
+ export async function migrateLegacyKeysToEnv() {
187
+ let settingsMod;
188
+ try {
189
+ settingsMod = await import("../utils/settings.js");
190
+ }
191
+ catch (err) {
192
+ if (process.env.MUONROI_DEBUG_ENVSTORE) {
193
+ console.error(`[keychain] migration: settings unavailable: ${err instanceof Error ? err.message : String(err)}`);
194
+ }
195
+ return;
196
+ }
197
+ let settings;
198
+ try {
199
+ settings = settingsMod.loadUserSettings();
200
+ }
201
+ catch {
202
+ return;
203
+ }
204
+ if (settings.keysMigratedToEnv)
205
+ return;
206
+ // Legacy keytar reader — the dependency has been removed, so absence is the
207
+ // normal case. The specifier is assembled at runtime so the bundler
208
+ // (`bun build --compile`) does not try to statically resolve the missing
209
+ // module; when keytar isn't installed this import simply throws → null.
210
+ let keytar = null;
211
+ try {
212
+ const legacyKeytarSpecifier = ["key", "tar"].join("");
213
+ keytar = (await import(legacyKeytarSpecifier));
214
+ }
215
+ catch {
216
+ keytar = null;
217
+ }
218
+ const providersPatch = { ...(settings.providers ?? {}) };
219
+ let providersDirty = false;
220
+ for (const p of KEYCHAIN_PROVIDER_IDS) {
221
+ const envName = ENV_BY_PROVIDER[p];
222
+ if (process.env[envName])
223
+ continue; // already in env
224
+ let legacy = null;
225
+ if (keytar?.getPassword) {
226
+ try {
227
+ legacy = await keytar.getPassword("muonroi-cli", p);
228
+ }
229
+ catch {
230
+ legacy = null;
231
+ }
232
+ }
233
+ if (!legacy)
234
+ legacy = providersPatch[p]?.apiKey ?? null;
235
+ if (legacy && legacy.length >= 20) {
236
+ persistEnvVar(envName, legacy);
237
+ if (keytar?.deletePassword) {
238
+ try {
239
+ await keytar.deletePassword("muonroi-cli", p);
240
+ }
241
+ catch {
242
+ /* best-effort keychain cleanup */
243
+ }
244
+ }
245
+ if (providersPatch[p]?.apiKey) {
246
+ delete providersPatch[p].apiKey;
247
+ providersDirty = true;
248
+ }
249
+ }
250
+ }
251
+ const patch = { keysMigratedToEnv: true };
252
+ // Strip the legacy plaintext main key — it is no longer read (getApiKey is
253
+ // env-only). Passing undefined drops it from the persisted JSON.
254
+ if (settings.apiKey)
255
+ patch.apiKey = undefined;
256
+ if (providersDirty)
257
+ patch.providers = providersPatch;
258
+ try {
259
+ settingsMod.saveUserSettings(patch);
260
+ }
261
+ catch (err) {
262
+ if (process.env.MUONROI_DEBUG_ENVSTORE) {
263
+ console.error(`[keychain] migration: save failed: ${err instanceof Error ? err.message : String(err)}`);
264
+ }
265
+ }
266
+ }
280
267
  //# sourceMappingURL=keychain.js.map
@@ -12,16 +12,8 @@
12
12
  */
13
13
  import { existsSync, readFileSync } from "node:fs";
14
14
  import { extname, resolve } from "node:path";
15
- import { apiBaseFor } from "./endpoints.js";
16
- import { loadKeyForProvider } from "./keychain.js";
15
+ import { callVisionBackend, formatNativeVisionObservation, formatNativeVisionUnavailable, looksLikeOcrIntent, resolveAvailableVisionChain, wrapAnalyzerInstructions, } from "./vision-backend.js";
17
16
  import { needsVisionProxy } from "./vision-proxy.js";
18
- const VISION_MODELS = [
19
- "Qwen/Qwen3-VL-8B-Instruct",
20
- "Qwen/Qwen3-VL-30B-A3B-Instruct",
21
- "Qwen/Qwen3-VL-32B-Instruct",
22
- ];
23
- const SILICONFLOW_BASE = apiBaseFor("siliconflow");
24
- const REQUEST_TIMEOUT_MS = 90_000;
25
17
  const IMAGE_CACHE_MAX = 20;
26
18
  const IMAGE_CACHE_TTL_MS = 15 * 60 * 1000; // 15 minutes
27
19
  /**
@@ -174,8 +166,8 @@ export async function bridgeMcpToolResult(toolName, toolOutput, modelId, signal,
174
166
  }
175
167
  // Detect context for better analysis prompt
176
168
  const context = detectImageContext(toolName, toolOutput);
177
- const description = await analyzeImages(images, context, signal);
178
- if (!description) {
169
+ const rawObservation = await analyzeImages(images, context, signal);
170
+ if (!rawObservation) {
179
171
  // Even when the vision proxy fails (no API key / network error), we MUST
180
172
  // still strip the base64 payload from the tool output. Otherwise a single
181
173
  // Playwright screenshot (~1.5MB of base64) lands in conversation history
@@ -187,15 +179,21 @@ export async function bridgeMcpToolResult(toolName, toolOutput, modelId, signal,
187
179
  proxied: true,
188
180
  };
189
181
  }
190
- const cachedIds = addToCache(images.map((img) => ({ ...img, source: baseName || toolName })), description, `${baseName} result`);
182
+ const cachedIds = addToCache(images.map((img) => ({ ...img, source: baseName || toolName })), rawObservation, `${baseName} result`);
191
183
  const cleanOutput = stripBase64FromOutput(toolOutput);
192
- const cacheHint = `\n[Cached as ${cachedIds.join(", ")} — use ask_vision_proxy for follow-up questions]`;
193
- const fullDescription = `${description}${cacheHint}`;
184
+ const fullDescription = formatNativeVisionObservation(rawObservation, {
185
+ imageCount: images.length,
186
+ cachedIds,
187
+ });
194
188
  const enhanced = typeof cleanOutput === "string"
195
189
  ? `${cleanOutput}\n\n${fullDescription}`
196
- : { ...cleanOutput, _visionDescription: description, _cachedImageIds: cachedIds };
190
+ : {
191
+ ...cleanOutput,
192
+ _visionDescription: fullDescription,
193
+ _cachedImageIds: cachedIds,
194
+ };
197
195
  recordBridgedDescription(toolCallId, fullDescription);
198
- return { output: enhanced, proxied: true, description, cachedImageIds: cachedIds };
196
+ return { output: enhanced, proxied: true, description: fullDescription, cachedImageIds: cachedIds };
199
197
  }
200
198
  /**
201
199
  * Proactively analyze an image from a file path, URL, or raw base64.
@@ -246,12 +244,13 @@ export async function analyzeImageFromSource(source, question, cwd, signal) {
246
244
  }
247
245
  const context = question ? { type: "user-query", hint: question } : { type: "generic" };
248
246
  const prompt = question ? buildFollowUpPrompt(question, images.length) : undefined;
249
- const description = await analyzeImages(images, context, signal, prompt);
250
- if (!description) {
251
- return "Vision proxy could not analyze the image. Check SILICONFLOW_API_KEY configuration.";
247
+ const rawObservation = await analyzeImages(images, context, signal, prompt);
248
+ if (!rawObservation) {
249
+ const { collectVisionUnavailableReasons } = await import("./vision-backend.js");
250
+ return formatNativeVisionUnavailable(1, await collectVisionUnavailableReasons());
252
251
  }
253
- const cachedIds = addToCache(images, description, source);
254
- return `${description}\n[Cached as ${cachedIds.join(", ")} — use ask_vision_proxy for follow-up questions]`;
252
+ const cachedIds = addToCache(images, rawObservation, source);
253
+ return formatNativeVisionObservation(rawObservation, { imageCount: images.length, cachedIds });
255
254
  }
256
255
  /**
257
256
  * Ask a follow-up question about a previously cached image,
@@ -278,17 +277,10 @@ export async function askVisionProxy(question, imageIdOrPath, cwd, signal) {
278
277
  }
279
278
  return `No matching image. Available:\n${cached.map((c) => `- ${c.id}: ${c.label} (${c.age})`).join("\n")}\n\nSpecify image_id, or provide a file_path to analyze a new image.`;
280
279
  }
281
- let apiKey;
282
- try {
283
- apiKey = await loadKeyForProvider("siliconflow");
284
- }
285
- catch {
286
- return "Vision proxy unavailable — SILICONFLOW_API_KEY not configured.";
287
- }
288
280
  const visionContent = [];
289
281
  visionContent.push({
290
282
  type: "text",
291
- text: buildFollowUpPrompt(question, targets.length),
283
+ text: wrapAnalyzerInstructions(buildFollowUpPrompt(question, targets.length), looksLikeOcrIntent(question) ? "ocr" : "default"),
292
284
  });
293
285
  for (const img of targets) {
294
286
  visionContent.push({
@@ -299,19 +291,15 @@ export async function askVisionProxy(question, imageIdOrPath, cwd, signal) {
299
291
  },
300
292
  });
301
293
  }
302
- for (const model of VISION_MODELS) {
303
- try {
304
- const result = await callVisionModel(model, visionContent, apiKey, signal);
305
- if (result) {
306
- return `[Vision Proxy Answer — via ${model}]\n${result}\n[/Vision Proxy Answer]`;
307
- }
308
- }
309
- catch (err) {
310
- if (signal?.aborted)
311
- throw err;
312
- }
294
+ const kind = looksLikeOcrIntent(question) ? "ocr" : "default";
295
+ const result = await callVisionBackend(await resolveAvailableVisionChain(kind), visionContent, signal);
296
+ if (result.ok) {
297
+ return formatNativeVisionObservation(result.text, {
298
+ imageCount: targets.length,
299
+ cachedIds: targets.map((t) => t.id),
300
+ });
313
301
  }
314
- return "Vision proxy failed. Try again or describe what you need help with.";
302
+ return formatNativeVisionUnavailable(targets.length, [result.reason]);
315
303
  }
316
304
  /**
317
305
  * System prompt guidance for text-only models — UNIVERSAL, not Playwright-specific.
@@ -320,32 +308,27 @@ export function getVisionGuidanceForTextOnly(modelId) {
320
308
  if (!needsVisionProxy(modelId))
321
309
  return "";
322
310
  return `
323
- VISION PROXY (text-only model):
324
- You cannot see images directly. You have vision proxy tools to work with ANY image:
311
+ VISION (extended capability):
312
+ You have full visual understanding via auto-injected <vision-observation> blocks and vision tools.
313
+ Treat every <vision-observation> as YOUR direct sight — respond naturally as if you saw the image yourself.
325
314
 
326
- TOOLS:
327
- - analyze_image: Proactively analyze an image from a file path, URL, or base64. Use this FIRST when you encounter or need to work with an image.
328
- - ask_vision_proxy: Ask a specific follow-up question about any cached image (or provide a file path for a new image).
329
- - list_vision_cache: See all cached images available for querying.
315
+ TOOLS (when you need a closer look or a new image):
316
+ - analyze_image: Inspect an image file path, data URI, or base64. Use when you encounter a new image or need a fresh screenshot analyzed.
317
+ - ask_vision_proxy: Ask a follow-up about a cached image (by ID) or a file path zoom in on a region, read text, compare colors.
318
+ - list_vision_cache: List cached image IDs available for follow-up questions.
330
319
 
331
- WHEN TO USE (be PROACTIVE, not passive):
332
- - User mentions an image file → analyze_image immediately
333
- - User pastes or references a screenshot it's auto-analyzed, use ask_vision_proxy for details
334
- - Working with UI/web pagesprefer browser_snapshot (text-based), use screenshot + ask_vision_proxy when visual details matter
335
- - Reviewing design mockups, diagrams, charts → analyze_image the file
336
- - Debugging visual issues (CSS, layout, colors) → take screenshot, then ask_vision_proxy specific questions
337
- - Comparing before/after → analyze both images, ask about differences
338
- - Reading text from images (OCR) → analyze_image with a question like "transcribe all text"
339
- - Any file with image extension (.png, .jpg, .gif, .webp, .svg, etc.) → analyze_image
320
+ WHEN TO ACT:
321
+ - User mentions an image → analyze_image immediately (do not guess)
322
+ - Tool results include <vision-observation> use that as your sight; ask_vision_proxy if any detail is unclear
323
+ - UI debugging → screenshot + ask_vision_proxy for specific elements
324
+ - OCR / transcribe → analyze_image with question "transcribe all visible text"
340
325
 
341
- WORKFLOW:
342
- 1. Encounter image analyze_image (or it's auto-analyzed from tool results)
343
- 2. Need more detail ask_vision_proxy with specific question
344
- 3. Need to verify changes take new screenshot/analyze new image → compare
326
+ IF STILL UNCLEAR after an observation:
327
+ 1. ask_vision_proxy with a precise question about the unclear region or text
328
+ 2. analyze_image again on a new or higher-resolution file
329
+ 3. Ask the user to share another screenshot or clarify what to focus on
345
330
 
346
- Images are cached (up to ${IMAGE_CACHE_MAX}, ${IMAGE_CACHE_TTL_MS / 60000}min TTL). You can reference them by ID for follow-ups.
347
-
348
- IMPORTANT: Do NOT guess what an image contains. Always use the proxy to get accurate information.
331
+ Never invent visual details. Cached images: up to ${IMAGE_CACHE_MAX}, ${IMAGE_CACHE_TTL_MS / 60000}min TTL.
349
332
  `;
350
333
  }
351
334
  // Keep backward compat export
@@ -380,17 +363,11 @@ async function analyzeImages(images, context, signal, customPrompt) {
380
363
  const svgFast = trySvgFastPath(images, context);
381
364
  if (svgFast)
382
365
  return svgFast;
383
- let apiKey;
384
- try {
385
- apiKey = await loadKeyForProvider("siliconflow");
386
- }
387
- catch {
388
- return null;
389
- }
366
+ const kind = context.type === "design" ? "design" : customPrompt && looksLikeOcrIntent(customPrompt) ? "ocr" : "default";
390
367
  const visionContent = [];
391
368
  visionContent.push({
392
369
  type: "text",
393
- text: customPrompt ?? buildContextualPrompt(images.length, context),
370
+ text: wrapAnalyzerInstructions(customPrompt ?? buildContextualPrompt(images.length, context), kind),
394
371
  });
395
372
  for (const img of images) {
396
373
  visionContent.push({
@@ -401,22 +378,10 @@ async function analyzeImages(images, context, signal, customPrompt) {
401
378
  },
402
379
  });
403
380
  }
404
- // Force JSON output for the design contract. Markdown narrative is fine for
405
- // debug-style contexts (terminal, code) where the human reads the result,
406
- // but design output is consumed machine-to-machine.
407
381
  const responseFormat = context.type === "design" ? { type: "json_object" } : undefined;
408
- for (const model of VISION_MODELS) {
409
- try {
410
- const result = await callVisionModel(model, visionContent, apiKey, signal, responseFormat);
411
- if (result) {
412
- return formatBridgeResult(result, images.length, model, context.type);
413
- }
414
- }
415
- catch (err) {
416
- if (signal?.aborted)
417
- throw err;
418
- }
419
- }
382
+ const result = await callVisionBackend(await resolveAvailableVisionChain(kind), visionContent, signal, responseFormat);
383
+ if (result.ok)
384
+ return result.text;
420
385
  return null;
421
386
  }
422
387
  function trySvgFastPath(images, context) {
@@ -437,11 +402,10 @@ function trySvgFastPath(images, context) {
437
402
  return svg.length > 32_000 ? `${svg.slice(0, 32_000)}\n<!-- truncated -->` : svg;
438
403
  })
439
404
  .join("\n\n");
440
- const header = `[Vision Bridge — ${images.length} SVG source(s) passed through (${context.type}, fast-path, no vision call)]`;
441
- const guidance = context.type === "design"
442
- ? `\nThe raw SVG below IS the layout contract. Coordinates and colors are exact. Map <rect>/<text>/<g> nodes to the schema:\n${UI_LAYOUT_SCHEMA_HINT}\n`
443
- : "\nThe raw SVG below is vector text — read element attributes (x, y, width, fill, text content) directly.\n";
444
- return `\n${header}${guidance}\n\`\`\`svg\n${decoded}\n\`\`\`\n[/Vision Bridge]\n`;
405
+ const observation = context.type === "design"
406
+ ? `I see ${images.length} SVG source(s). Vector markup is exact — map nodes to:\n${UI_LAYOUT_SCHEMA_HINT}\n\n\`\`\`svg\n${decoded}\n\`\`\``
407
+ : `I see ${images.length} SVG source(s). Vector markup:\n\n\`\`\`svg\n${decoded}\n\`\`\``;
408
+ return observation;
445
409
  }
446
410
  function buildContextualPrompt(imageCount, context) {
447
411
  const plural = imageCount > 1 ? `${imageCount} images` : "the image";
@@ -799,63 +763,9 @@ function walkAndStrip(obj) {
799
763
  }
800
764
  }
801
765
  function wrapWithFallback(output, imageCount) {
802
- const notice = `\n[Vision Bridge — ${imageCount} image(s) could not be analyzed. Use analyze_image or ask_vision_proxy with a file path to retry.]\n`;
766
+ const notice = formatNativeVisionUnavailable(imageCount, ["auto-analysis failed"]);
803
767
  if (typeof output === "string")
804
768
  return `${output}\n${notice}`;
805
769
  return { ...output, _visionNotice: notice };
806
770
  }
807
- async function callVisionModel(model, content, apiKey, signal, responseFormat) {
808
- const controller = new AbortController();
809
- let timedOut = false;
810
- const timeout = setTimeout(() => {
811
- timedOut = true;
812
- controller.abort();
813
- }, REQUEST_TIMEOUT_MS);
814
- if (signal) {
815
- signal.addEventListener("abort", () => controller.abort(), { once: true });
816
- }
817
- let res;
818
- try {
819
- res = await fetch(`${SILICONFLOW_BASE}/chat/completions`, {
820
- method: "POST",
821
- headers: {
822
- "Content-Type": "application/json",
823
- Authorization: `Bearer ${apiKey}`,
824
- },
825
- body: JSON.stringify({
826
- model,
827
- messages: [{ role: "user", content }],
828
- max_tokens: 3072,
829
- temperature: 0.1,
830
- ...(responseFormat ? { response_format: responseFormat } : {}),
831
- }),
832
- signal: controller.signal,
833
- });
834
- }
835
- catch (err) {
836
- clearTimeout(timeout);
837
- if (timedOut) {
838
- console.warn(`[vision-bridge] ${model} timed out after ${REQUEST_TIMEOUT_MS}ms`);
839
- return null;
840
- }
841
- if (signal?.aborted)
842
- throw err;
843
- console.warn(`[vision-bridge] ${model} network error: ${err instanceof Error ? err.message : String(err)}`);
844
- return null;
845
- }
846
- clearTimeout(timeout);
847
- if (!res.ok) {
848
- const errText = await res.text().catch(() => "");
849
- console.warn(`[vision-bridge] ${model} HTTP ${res.status}: ${errText.slice(0, 200)}`);
850
- return null;
851
- }
852
- const data = (await res.json().catch(() => null));
853
- return data?.choices?.[0]?.message?.content ?? null;
854
- }
855
- function formatBridgeResult(description, imageCount, model, contextType) {
856
- const header = imageCount > 1
857
- ? `[Vision Bridge — ${imageCount} images analyzed (${contextType}) via ${model}]`
858
- : `[Vision Bridge — image analyzed (${contextType}) via ${model}]`;
859
- return `\n${header}\n${description}\n[/Vision Bridge]\n`;
860
- }
861
771
  //# sourceMappingURL=mcp-vision-bridge.js.map