muonroi-cli 1.8.3 → 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 (477) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +133 -122
  3. package/dist/packages/agent-harness-core/src/driver.d.ts +27 -1
  4. package/dist/packages/agent-harness-core/src/driver.js +46 -0
  5. package/dist/packages/agent-harness-core/src/event-tee.d.ts +48 -0
  6. package/dist/packages/agent-harness-core/src/event-tee.js +77 -0
  7. package/dist/packages/agent-harness-core/src/mcp-server.d.ts +11 -0
  8. package/dist/packages/agent-harness-core/src/mcp-server.js +87 -15
  9. package/dist/packages/agent-harness-core/src/protocol.d.ts +66 -2
  10. package/dist/packages/agent-harness-core/src/protocol.js +15 -0
  11. package/dist/packages/agent-harness-core/src/visual-quality.d.ts +58 -0
  12. package/dist/packages/agent-harness-core/src/visual-quality.js +141 -0
  13. package/dist/packages/agent-harness-opentui/src/agent-mode.d.ts +6 -0
  14. package/dist/packages/agent-harness-opentui/src/agent-mode.js +14 -1
  15. package/dist/packages/agent-harness-opentui/src/input-bridge.d.ts +2 -10
  16. package/dist/packages/agent-harness-opentui/src/input-bridge.js +103 -16
  17. package/dist/packages/agent-harness-opentui/src/install.d.ts +8 -0
  18. package/dist/packages/agent-harness-opentui/src/install.js +10 -0
  19. package/dist/packages/agent-harness-opentui/src/semantic.js +12 -10
  20. package/dist/packages/agent-harness-opentui/src/visual-capture.d.ts +56 -0
  21. package/dist/packages/agent-harness-opentui/src/visual-capture.js +103 -0
  22. package/dist/src/agent-harness/mock-model.d.ts +28 -0
  23. package/dist/src/agent-harness/mock-model.js +63 -1
  24. package/dist/src/agent-harness/test-spawn.js +31 -0
  25. package/dist/src/cli/config/screen-providers.js +1 -1
  26. package/dist/src/cli/cost-forensics.d.ts +10 -0
  27. package/dist/src/cli/cost-forensics.js +30 -15
  28. package/dist/src/cli/keys-bundle.d.ts +1 -1
  29. package/dist/src/cli/keys-bundle.js +1 -1
  30. package/dist/src/cli/keys.d.ts +2 -2
  31. package/dist/src/cli/keys.js +19 -81
  32. package/dist/src/council/clarifier.d.ts +28 -2
  33. package/dist/src/council/clarifier.js +81 -15
  34. package/dist/src/council/context.js +49 -15
  35. package/dist/src/council/debate-checkpoint.d.ts +129 -0
  36. package/dist/src/council/debate-checkpoint.js +176 -0
  37. package/dist/src/council/debate-planner.js +51 -3
  38. package/dist/src/council/debate-summary.d.ts +25 -0
  39. package/dist/src/council/debate-summary.js +85 -0
  40. package/dist/src/council/debate.d.ts +169 -2
  41. package/dist/src/council/debate.js +1210 -134
  42. package/dist/src/council/index.d.ts +85 -1
  43. package/dist/src/council/index.js +634 -196
  44. package/dist/src/council/leader.d.ts +26 -0
  45. package/dist/src/council/leader.js +150 -9
  46. package/dist/src/council/llm.d.ts +32 -0
  47. package/dist/src/council/llm.js +231 -38
  48. package/dist/src/council/panel-select.d.ts +30 -0
  49. package/dist/src/council/panel-select.js +72 -0
  50. package/dist/src/council/planner.js +23 -0
  51. package/dist/src/council/preflight.d.ts +7 -0
  52. package/dist/src/council/preflight.js +14 -2
  53. package/dist/src/council/prompts.d.ts +30 -3
  54. package/dist/src/council/prompts.js +254 -84
  55. package/dist/src/council/stance-recall.d.ts +42 -0
  56. package/dist/src/council/stance-recall.js +57 -0
  57. package/dist/src/council/strip-think.d.ts +17 -0
  58. package/dist/src/council/strip-think.js +33 -0
  59. package/dist/src/council/types.d.ts +128 -0
  60. package/dist/src/ee/artifact-cache.d.ts +16 -0
  61. package/dist/src/ee/artifact-cache.js +32 -0
  62. package/dist/src/ee/auth.d.ts +1 -0
  63. package/dist/src/ee/auth.js +15 -2
  64. package/dist/src/ee/bridge.d.ts +10 -0
  65. package/dist/src/ee/bridge.js +58 -0
  66. package/dist/src/ee/client.js +81 -18
  67. package/dist/src/ee/export-transcripts.d.ts +1 -0
  68. package/dist/src/ee/export-transcripts.js +8 -10
  69. package/dist/src/ee/extract-session.js +29 -0
  70. package/dist/src/ee/extract-style.d.ts +58 -0
  71. package/dist/src/ee/extract-style.js +270 -0
  72. package/dist/src/ee/recall-ledger.d.ts +9 -0
  73. package/dist/src/ee/recall-ledger.js +3 -0
  74. package/dist/src/ee/scope.d.ts +1 -0
  75. package/dist/src/ee/scope.js +26 -1
  76. package/dist/src/ee/search.d.ts +7 -0
  77. package/dist/src/ee/search.js +24 -0
  78. package/dist/src/ee/transcript-emit.js +2 -0
  79. package/dist/src/ee/types.d.ts +22 -0
  80. package/dist/src/ee/who-am-i-brain.d.ts +35 -0
  81. package/dist/src/ee/who-am-i-brain.js +220 -0
  82. package/dist/src/ee/who-am-i.d.ts +10 -3
  83. package/dist/src/ee/who-am-i.js +12 -0
  84. package/dist/src/ee/workflow-event.d.ts +48 -0
  85. package/dist/src/ee/workflow-event.js +81 -0
  86. package/dist/src/flow/compaction/compress.d.ts +3 -3
  87. package/dist/src/flow/compaction/compress.js +45 -8
  88. package/dist/src/flow/compaction/extract.d.ts +4 -7
  89. package/dist/src/flow/compaction/extract.js +50 -10
  90. package/dist/src/flow/compaction/index.d.ts +13 -1
  91. package/dist/src/flow/compaction/index.js +70 -3
  92. package/dist/src/flow/compaction/input-guard.d.ts +24 -0
  93. package/dist/src/flow/compaction/input-guard.js +43 -0
  94. package/dist/src/flow/fold-planning.d.ts +36 -0
  95. package/dist/src/flow/fold-planning.js +83 -0
  96. package/dist/src/flow/hierarchy.d.ts +146 -0
  97. package/dist/src/flow/hierarchy.js +427 -0
  98. package/dist/src/flow/index.d.ts +1 -0
  99. package/dist/src/flow/index.js +2 -0
  100. package/dist/src/flow/run-artifacts.d.ts +102 -0
  101. package/dist/src/flow/run-artifacts.js +208 -0
  102. package/dist/src/generated/version.d.ts +1 -1
  103. package/dist/src/generated/version.js +1 -1
  104. package/dist/src/gsd/assessment-schema.d.ts +44 -0
  105. package/dist/src/gsd/assessment-schema.js +134 -0
  106. package/dist/src/gsd/capability-registry.d.ts +45 -0
  107. package/dist/src/gsd/capability-registry.js +337 -0
  108. package/dist/src/gsd/complexity-assessor.d.ts +39 -0
  109. package/dist/src/gsd/complexity-assessor.js +152 -0
  110. package/dist/src/gsd/config-bridge.d.ts +7 -0
  111. package/dist/src/gsd/config-bridge.js +114 -0
  112. package/dist/src/gsd/config-loader.d.ts +27 -0
  113. package/dist/src/gsd/config-loader.js +50 -0
  114. package/dist/src/gsd/council-context.d.ts +44 -0
  115. package/dist/src/gsd/council-context.js +114 -0
  116. package/dist/src/gsd/ee-closure.d.ts +28 -0
  117. package/dist/src/gsd/ee-closure.js +49 -0
  118. package/dist/src/gsd/flags.d.ts +55 -0
  119. package/dist/src/gsd/flags.js +83 -0
  120. package/dist/src/gsd/gsd-dispatch.d.ts +58 -0
  121. package/dist/src/gsd/gsd-dispatch.js +131 -0
  122. package/dist/src/gsd/gsd-runtime.d.ts +22 -0
  123. package/dist/src/gsd/gsd-runtime.js +37 -0
  124. package/dist/src/gsd/host-adapter.d.ts +11 -0
  125. package/dist/src/gsd/host-adapter.js +29 -0
  126. package/dist/src/gsd/index.d.ts +24 -1
  127. package/dist/src/gsd/index.js +27 -0
  128. package/dist/src/gsd/loop-host-contract.d.ts +21 -0
  129. package/dist/src/gsd/loop-host-contract.js +39 -0
  130. package/dist/src/gsd/loop-host.d.ts +69 -0
  131. package/dist/src/gsd/loop-host.js +245 -0
  132. package/dist/src/gsd/loop-resolver.d.ts +36 -0
  133. package/dist/src/gsd/loop-resolver.js +79 -0
  134. package/dist/src/gsd/model-tier.d.ts +13 -0
  135. package/dist/src/gsd/model-tier.js +45 -0
  136. package/dist/src/gsd/mutation-gate.d.ts +16 -0
  137. package/dist/src/gsd/mutation-gate.js +41 -0
  138. package/dist/src/gsd/native-roadmap.d.ts +89 -0
  139. package/dist/src/gsd/native-roadmap.js +343 -0
  140. package/dist/src/gsd/native-state.d.ts +47 -0
  141. package/dist/src/gsd/native-state.js +220 -0
  142. package/dist/src/gsd/paths.d.ts +23 -0
  143. package/dist/src/gsd/paths.js +66 -0
  144. package/dist/src/gsd/phase-dag.d.ts +12 -0
  145. package/dist/src/gsd/phase-dag.js +94 -0
  146. package/dist/src/gsd/phase-sync.d.ts +42 -0
  147. package/dist/src/gsd/phase-sync.js +321 -0
  148. package/dist/src/gsd/pil-gate-context.d.ts +13 -0
  149. package/dist/src/gsd/pil-gate-context.js +64 -0
  150. package/dist/src/gsd/pil-gate-critic.d.ts +19 -0
  151. package/dist/src/gsd/pil-gate-critic.js +74 -0
  152. package/dist/src/gsd/plan-council-prompts.d.ts +25 -0
  153. package/dist/src/gsd/plan-council-prompts.js +79 -0
  154. package/dist/src/gsd/plan-council.d.ts +44 -0
  155. package/dist/src/gsd/plan-council.js +251 -0
  156. package/dist/src/gsd/plan-gate-vocabulary.d.ts +40 -0
  157. package/dist/src/gsd/plan-gate-vocabulary.js +64 -0
  158. package/dist/src/gsd/product-workspace.d.ts +13 -0
  159. package/dist/src/gsd/product-workspace.js +124 -0
  160. package/dist/src/gsd/ship-bridge.d.ts +25 -0
  161. package/dist/src/gsd/ship-bridge.js +65 -0
  162. package/dist/src/gsd/state-document.d.ts +40 -0
  163. package/dist/src/gsd/state-document.js +163 -0
  164. package/dist/src/gsd/verdict-schema.d.ts +39 -0
  165. package/dist/src/gsd/verdict-schema.js +144 -0
  166. package/dist/src/gsd/verify-context.d.ts +22 -0
  167. package/dist/src/gsd/verify-context.js +27 -0
  168. package/dist/src/gsd/verify-council-prompts.d.ts +19 -0
  169. package/dist/src/gsd/verify-council-prompts.js +85 -0
  170. package/dist/src/gsd/verify-council.d.ts +25 -0
  171. package/dist/src/gsd/verify-council.js +119 -0
  172. package/dist/src/gsd/verify-gate-vocabulary.d.ts +25 -0
  173. package/dist/src/gsd/verify-gate-vocabulary.js +46 -0
  174. package/dist/src/gsd/workflow-engine.d.ts +60 -0
  175. package/dist/src/gsd/workflow-engine.js +207 -0
  176. package/dist/src/gsd/workflow-tools.d.ts +13 -0
  177. package/dist/src/gsd/workflow-tools.js +277 -0
  178. package/dist/src/hooks/index.js +1 -1
  179. package/dist/src/index.js +44 -11
  180. package/dist/src/maintain/pr-builder.js +23 -13
  181. package/dist/src/mcp/auto-setup.js +57 -32
  182. package/dist/src/mcp/client-pool.js +1 -1
  183. package/dist/src/mcp/ee-tools.js +1 -0
  184. package/dist/src/mcp/oauth-callback.js +2 -2
  185. package/dist/src/mcp/research-onboarding.js +8 -7
  186. package/dist/src/mcp/runtime.js +34 -2
  187. package/dist/src/mcp/setup-guide-text.d.ts +1 -1
  188. package/dist/src/mcp/setup-guide-text.js +77 -76
  189. package/dist/src/models/catalog-client.d.ts +87 -0
  190. package/dist/src/models/catalog-client.js +105 -38
  191. package/dist/src/models/catalog.json +528 -265
  192. package/dist/src/models/registry.d.ts +22 -7
  193. package/dist/src/models/registry.js +73 -10
  194. package/dist/src/ops/doctor.js +8 -8
  195. package/dist/src/orchestrator/auto-commit.js +1 -1
  196. package/dist/src/orchestrator/batch-turn-runner.js +2 -2
  197. package/dist/src/orchestrator/cache-prefix.d.ts +67 -0
  198. package/dist/src/orchestrator/cache-prefix.js +83 -0
  199. package/dist/src/orchestrator/compact-request.d.ts +32 -0
  200. package/dist/src/orchestrator/compact-request.js +41 -0
  201. package/dist/src/orchestrator/compaction.d.ts +10 -0
  202. package/dist/src/orchestrator/compaction.js +27 -7
  203. package/dist/src/orchestrator/council-manager.d.ts +12 -3
  204. package/dist/src/orchestrator/council-manager.js +65 -24
  205. package/dist/src/orchestrator/cross-turn-dedup.d.ts +6 -0
  206. package/dist/src/orchestrator/cross-turn-dedup.js +22 -1
  207. package/dist/src/orchestrator/error-utils.d.ts +29 -0
  208. package/dist/src/orchestrator/error-utils.js +132 -24
  209. package/dist/src/orchestrator/grounding-check.js +39 -1
  210. package/dist/src/orchestrator/message-processor.js +242 -33
  211. package/dist/src/orchestrator/orchestrator.d.ts +39 -3
  212. package/dist/src/orchestrator/orchestrator.js +651 -102
  213. package/dist/src/orchestrator/preprocessor.js +1 -1
  214. package/dist/src/orchestrator/proactive-compact-detector.d.ts +26 -0
  215. package/dist/src/orchestrator/proactive-compact-detector.js +36 -0
  216. package/dist/src/orchestrator/prompts.js +159 -159
  217. package/dist/src/orchestrator/reactive-delegation.d.ts +39 -0
  218. package/dist/src/orchestrator/reactive-delegation.js +59 -0
  219. package/dist/src/orchestrator/retry-classifier.d.ts +2 -1
  220. package/dist/src/orchestrator/retry-classifier.js +46 -2
  221. package/dist/src/orchestrator/safety-intercept.d.ts +45 -0
  222. package/dist/src/orchestrator/safety-intercept.js +55 -0
  223. package/dist/src/orchestrator/scope-reminder.js +1 -1
  224. package/dist/src/orchestrator/session-experience.d.ts +2 -1
  225. package/dist/src/orchestrator/session-experience.js +2 -1
  226. package/dist/src/orchestrator/should-run-gate.d.ts +5 -0
  227. package/dist/src/orchestrator/should-run-gate.js +18 -0
  228. package/dist/src/orchestrator/stall-watchdog.d.ts +24 -3
  229. package/dist/src/orchestrator/stall-watchdog.js +47 -13
  230. package/dist/src/orchestrator/stream-runner.js +62 -29
  231. package/dist/src/orchestrator/sub-agent-cap.d.ts +13 -1
  232. package/dist/src/orchestrator/sub-agent-cap.js +16 -1
  233. package/dist/src/orchestrator/sub-agent-model-tier.d.ts +13 -1
  234. package/dist/src/orchestrator/sub-agent-model-tier.js +16 -2
  235. package/dist/src/orchestrator/subagent-compactor.d.ts +53 -1
  236. package/dist/src/orchestrator/subagent-compactor.js +126 -15
  237. package/dist/src/orchestrator/tool-engine.d.ts +26 -0
  238. package/dist/src/orchestrator/tool-engine.js +669 -56
  239. package/dist/src/orchestrator/tool-limit-auto-recover.d.ts +22 -0
  240. package/dist/src/orchestrator/tool-limit-auto-recover.js +30 -0
  241. package/dist/src/orchestrator/turn-runner-deps.d.ts +19 -0
  242. package/dist/src/orchestrator/turn-watchdog.d.ts +37 -0
  243. package/dist/src/orchestrator/turn-watchdog.js +55 -0
  244. package/dist/src/pil/agent-operating-contract.d.ts +1 -1
  245. package/dist/src/pil/agent-operating-contract.js +1 -1
  246. package/dist/src/pil/cheap-model-playbook.d.ts +1 -1
  247. package/dist/src/pil/cheap-model-playbook.js +5 -1
  248. package/dist/src/pil/cheap-model-workbooks.d.ts +1 -1
  249. package/dist/src/pil/cheap-model-workbooks.js +1 -1
  250. package/dist/src/pil/discovery-types.d.ts +1 -0
  251. package/dist/src/pil/discovery.js +16 -11
  252. package/dist/src/pil/layer1-intent.d.ts +18 -6
  253. package/dist/src/pil/layer1-intent.js +66 -757
  254. package/dist/src/pil/layer15-context-scan.js +15 -1
  255. package/dist/src/pil/layer2_5-ponytail.js +8 -8
  256. package/dist/src/pil/layer3-ee-injection.js +23 -8
  257. package/dist/src/pil/layer4-gsd.js +69 -16
  258. package/dist/src/pil/layer5-context.js +7 -3
  259. package/dist/src/pil/layer6-output.d.ts +23 -0
  260. package/dist/src/pil/layer6-output.js +5 -1
  261. package/dist/src/pil/llm-classify.d.ts +33 -2
  262. package/dist/src/pil/llm-classify.js +123 -131
  263. package/dist/src/pil/native-capabilities-workbook.d.ts +1 -1
  264. package/dist/src/pil/native-capabilities-workbook.js +1 -0
  265. package/dist/src/pil/pipeline.js +34 -2
  266. package/dist/src/pil/response-tools.js +5 -3
  267. package/dist/src/pil/schema.d.ts +1 -0
  268. package/dist/src/pil/schema.js +2 -0
  269. package/dist/src/pil/types.d.ts +18 -0
  270. package/dist/src/playbook/directives.d.ts +4 -0
  271. package/dist/src/playbook/directives.js +17 -5
  272. package/dist/src/product-loop/backlog-builder.d.ts +14 -1
  273. package/dist/src/product-loop/backlog-builder.js +30 -6
  274. package/dist/src/product-loop/discovery-context-format.js +3 -1
  275. package/dist/src/product-loop/discovery-ecosystem.js +4 -1
  276. package/dist/src/product-loop/discovery-interview.js +32 -3
  277. package/dist/src/product-loop/discovery-schema.js +5 -1
  278. package/dist/src/product-loop/done-gate.js +3 -3
  279. package/dist/src/product-loop/ideal-trace.d.ts +7 -0
  280. package/dist/src/product-loop/ideal-trace.js +64 -0
  281. package/dist/src/product-loop/index.d.ts +13 -1
  282. package/dist/src/product-loop/index.js +333 -52
  283. package/dist/src/product-loop/loop-driver.d.ts +7 -0
  284. package/dist/src/product-loop/loop-driver.js +327 -116
  285. package/dist/src/product-loop/phase-plan.d.ts +5 -0
  286. package/dist/src/product-loop/phase-plan.js +39 -2
  287. package/dist/src/product-loop/phase-runner.js +9 -1
  288. package/dist/src/product-loop/progress-snapshot.js +4 -4
  289. package/dist/src/product-loop/sprint-runner.d.ts +111 -0
  290. package/dist/src/product-loop/sprint-runner.js +559 -16
  291. package/dist/src/product-loop/types.d.ts +36 -5
  292. package/dist/src/providers/adapter.d.ts +1 -1
  293. package/dist/src/providers/adapter.js +3 -4
  294. package/dist/src/providers/auth/browser-flow.d.ts +1 -1
  295. package/dist/src/providers/auth/browser-flow.js +1 -1
  296. package/dist/src/providers/auth/openai-oauth.js +1 -1
  297. package/dist/src/providers/auth/registry.js +0 -34
  298. package/dist/src/providers/auth/token-store.js +4 -1
  299. package/dist/src/providers/auth/types.d.ts +1 -1
  300. package/dist/src/providers/auth/types.js +1 -1
  301. package/dist/src/providers/capabilities.d.ts +24 -5
  302. package/dist/src/providers/capabilities.js +42 -24
  303. package/dist/src/providers/endpoints.d.ts +2 -2
  304. package/dist/src/providers/endpoints.js +11 -10
  305. package/dist/src/providers/keychain.d.ts +1 -1
  306. package/dist/src/providers/keychain.js +7 -9
  307. package/dist/src/providers/mcp-vision-bridge.js +82 -172
  308. package/dist/src/providers/openai-compatible.js +8 -1
  309. package/dist/src/providers/pricing.d.ts +2 -2
  310. package/dist/src/providers/pricing.js +3 -13
  311. package/dist/src/providers/runtime.d.ts +27 -2
  312. package/dist/src/providers/runtime.js +78 -15
  313. package/dist/src/providers/strategies/base.strategy.d.ts +16 -0
  314. package/dist/src/providers/strategies/base.strategy.js +24 -1
  315. package/dist/src/providers/strategies/{siliconflow.strategy.d.ts → opencode-go.strategy.d.ts} +3 -3
  316. package/dist/src/providers/strategies/opencode-go.strategy.js +83 -0
  317. package/dist/src/providers/strategies/registry.js +4 -4
  318. package/dist/src/providers/strategies/thinking-mode.d.ts +109 -0
  319. package/dist/src/providers/strategies/thinking-mode.js +280 -1
  320. package/dist/src/providers/strategies/zai.strategy.d.ts +14 -0
  321. package/dist/src/providers/strategies/zai.strategy.js +44 -0
  322. package/dist/src/providers/types.d.ts +5 -6
  323. package/dist/src/providers/types.js +2 -2
  324. package/dist/src/providers/vision-backend.d.ts +47 -0
  325. package/dist/src/providers/vision-backend.js +258 -0
  326. package/dist/src/providers/vision-proxy.d.ts +22 -9
  327. package/dist/src/providers/vision-proxy.js +63 -132
  328. package/dist/src/providers/wire-debug.js +95 -0
  329. package/dist/src/reporter/index.js +1 -1
  330. package/dist/src/router/decide.d.ts +13 -0
  331. package/dist/src/router/decide.js +138 -36
  332. package/dist/src/router/peak-hour.d.ts +38 -0
  333. package/dist/src/router/peak-hour.js +107 -0
  334. package/dist/src/router/step-router.js +3 -2
  335. package/dist/src/router/warm.js +4 -5
  336. package/dist/src/scaffold/bb-ecosystem-apply.js +47 -47
  337. package/dist/src/scaffold/bb-quality-gate.js +5 -5
  338. package/dist/src/scaffold/continuation-prompt.d.ts +11 -0
  339. package/dist/src/scaffold/continuation-prompt.js +86 -60
  340. package/dist/src/scaffold/init-new.js +453 -453
  341. package/dist/src/scaffold/point-to-existing.d.ts +21 -0
  342. package/dist/src/scaffold/point-to-existing.js +25 -0
  343. package/dist/src/self-qa/agentic-loop.js +22 -22
  344. package/dist/src/{ui/state → state}/active-run.d.ts +19 -0
  345. package/dist/src/{ui/state → state}/active-run.js +21 -0
  346. package/dist/src/{ui/status-bar/store.d.ts → state/status-bar-store.d.ts} +4 -1
  347. package/dist/src/{ui/status-bar/store.js → state/status-bar-store.js} +12 -9
  348. package/dist/src/state/turn-trace.d.ts +43 -0
  349. package/dist/src/state/turn-trace.js +32 -0
  350. package/dist/src/storage/db.js +2 -1
  351. package/dist/src/storage/index.d.ts +1 -1
  352. package/dist/src/storage/index.js +1 -1
  353. package/dist/src/storage/interaction-log.d.ts +1 -1
  354. package/dist/src/storage/interaction-log.js +5 -5
  355. package/dist/src/storage/migrations.js +196 -126
  356. package/dist/src/storage/session-experience-store.js +4 -4
  357. package/dist/src/storage/sessions.d.ts +28 -10
  358. package/dist/src/storage/sessions.js +112 -55
  359. package/dist/src/storage/transcript-view.js +1 -1
  360. package/dist/src/storage/transcript.d.ts +51 -0
  361. package/dist/src/storage/transcript.js +383 -112
  362. package/dist/src/storage/usage.js +14 -14
  363. package/dist/src/storage/workspaces.js +12 -12
  364. package/dist/src/tools/file.d.ts +15 -0
  365. package/dist/src/tools/file.js +32 -0
  366. package/dist/src/tools/native-tools.js +5 -0
  367. package/dist/src/tools/registry.d.ts +3 -0
  368. package/dist/src/tools/registry.js +460 -22
  369. package/dist/src/tools/research.d.ts +29 -0
  370. package/dist/src/tools/research.js +233 -0
  371. package/dist/src/types/index.d.ts +118 -3
  372. package/dist/src/ui/app.js +0 -0
  373. package/dist/src/ui/cards/product-status-card.js +1 -1
  374. package/dist/src/ui/components/bubble-body-guard.d.ts +27 -0
  375. package/dist/src/ui/components/bubble-body-guard.js +50 -0
  376. package/dist/src/ui/components/context-rail.d.ts +26 -0
  377. package/dist/src/ui/components/context-rail.js +33 -0
  378. package/dist/src/ui/components/council-conclusion-card.d.ts +73 -0
  379. package/dist/src/ui/components/council-conclusion-card.js +420 -0
  380. package/dist/src/ui/components/council-debate-pill.d.ts +36 -0
  381. package/dist/src/ui/components/council-debate-pill.js +34 -0
  382. package/dist/src/ui/components/council-info-card.js +2 -2
  383. package/dist/src/ui/components/council-leader-bubble.d.ts +10 -3
  384. package/dist/src/ui/components/council-leader-bubble.js +21 -11
  385. package/dist/src/ui/components/council-message-bubble.d.ts +15 -2
  386. package/dist/src/ui/components/council-message-bubble.js +16 -15
  387. package/dist/src/ui/components/council-phase-timeline.d.ts +9 -1
  388. package/dist/src/ui/components/council-phase-timeline.js +49 -15
  389. package/dist/src/ui/components/council-placeholder-bubble.d.ts +16 -9
  390. package/dist/src/ui/components/council-placeholder-bubble.js +32 -29
  391. package/dist/src/ui/components/council-question-card.js +12 -12
  392. package/dist/src/ui/components/council-rail-rounds.d.ts +26 -0
  393. package/dist/src/ui/components/council-rail-rounds.js +57 -0
  394. package/dist/src/ui/components/council-round-group.d.ts +38 -0
  395. package/dist/src/ui/components/council-round-group.js +88 -0
  396. package/dist/src/ui/components/council-status-list.d.ts +3 -1
  397. package/dist/src/ui/components/council-status-list.js +36 -24
  398. package/dist/src/ui/components/council-synthesis-banner.d.ts +7 -2
  399. package/dist/src/ui/components/council-synthesis-banner.js +20 -5
  400. package/dist/src/ui/components/halt-recovery-card.js +9 -5
  401. package/dist/src/ui/components/jump-to-latest-pill.d.ts +11 -0
  402. package/dist/src/ui/components/jump-to-latest-pill.js +14 -0
  403. package/dist/src/ui/components/prompt-box.js +18 -16
  404. package/dist/src/ui/components/session-tree-card.d.ts +14 -0
  405. package/dist/src/ui/components/session-tree-card.js +46 -0
  406. package/dist/src/ui/components/slash-inline-menu.d.ts +12 -0
  407. package/dist/src/ui/components/slash-inline-menu.js +26 -5
  408. package/dist/src/ui/components/task-list-panel.d.ts +14 -1
  409. package/dist/src/ui/components/task-list-panel.js +22 -2
  410. package/dist/src/ui/containers/modals-layer.d.ts +2 -1
  411. package/dist/src/ui/hooks/use-session-picker.d.ts +3 -3
  412. package/dist/src/ui/mcp-modal.js +2 -4
  413. package/dist/src/ui/modals/api-key-modal.js +1 -1
  414. package/dist/src/ui/modals/connect-modal.js +4 -3
  415. package/dist/src/ui/modals/session-picker-modal.d.ts +2 -2
  416. package/dist/src/ui/modals/session-picker-modal.js +3 -5
  417. package/dist/src/ui/picker-providers.d.ts +1 -1
  418. package/dist/src/ui/picker-providers.js +1 -1
  419. package/dist/src/ui/primitives/index.d.ts +1 -0
  420. package/dist/src/ui/primitives/index.js +2 -0
  421. package/dist/src/ui/primitives/semantic-primitives.d.ts +76 -0
  422. package/dist/src/ui/primitives/semantic-primitives.js +81 -0
  423. package/dist/src/ui/slash/compact.js +5 -7
  424. package/dist/src/ui/slash/cost.js +1 -1
  425. package/dist/src/ui/slash/council-inspect.js +4 -4
  426. package/dist/src/ui/slash/council.js +19 -1
  427. package/dist/src/ui/slash/debug.d.ts +3 -31
  428. package/dist/src/ui/slash/debug.js +9 -20
  429. package/dist/src/ui/slash/ideal.d.ts +6 -2
  430. package/dist/src/ui/slash/ideal.js +97 -7
  431. package/dist/src/ui/slash/menu-items.d.ts +7 -0
  432. package/dist/src/ui/slash/menu-items.js +12 -18
  433. package/dist/src/ui/slash/registry.d.ts +2 -0
  434. package/dist/src/ui/slash/registry.js +4 -0
  435. package/dist/src/ui/status-bar/cache-hit.d.ts +6 -0
  436. package/dist/src/ui/status-bar/cache-hit.js +9 -0
  437. package/dist/src/ui/status-bar/index.d.ts +1 -1
  438. package/dist/src/ui/status-bar/index.js +7 -3
  439. package/dist/src/ui/status-bar/usd-meter.d.ts +5 -4
  440. package/dist/src/ui/status-bar/usd-meter.js +6 -4
  441. package/dist/src/ui/theme.d.ts +1 -0
  442. package/dist/src/ui/theme.js +2 -0
  443. package/dist/src/ui/types.d.ts +7 -0
  444. package/dist/src/ui/use-app-logic.js +0 -0
  445. package/dist/src/ui/utils/format.d.ts +14 -0
  446. package/dist/src/ui/utils/format.js +23 -3
  447. package/dist/src/usage/downgrade.js +2 -2
  448. package/dist/src/usage/product-ledger.js +2 -2
  449. package/dist/src/utils/clipboard-image.js +23 -23
  450. package/dist/src/utils/install-manager.js +14 -11
  451. package/dist/src/utils/logger.js +2 -2
  452. package/dist/src/utils/permission-mode.js +5 -3
  453. package/dist/src/utils/redactor.js +1 -1
  454. package/dist/src/utils/settings.d.ts +153 -5
  455. package/dist/src/utils/settings.js +233 -29
  456. package/dist/src/utils/side-question.js +2 -2
  457. package/dist/src/utils/skills.js +3 -3
  458. package/dist/src/utils/visible-retry.d.ts +11 -0
  459. package/dist/src/utils/visible-retry.js +10 -1
  460. package/dist/src/verify/entrypoint.d.ts +1 -1
  461. package/dist/src/verify/entrypoint.js +1 -1
  462. package/dist/src/verify/recipes.d.ts +13 -0
  463. package/dist/src/verify/recipes.js +15 -0
  464. package/package.json +135 -132
  465. package/dist/src/providers/auth/gcloud.d.ts +0 -28
  466. package/dist/src/providers/auth/gcloud.js +0 -102
  467. package/dist/src/providers/auth/gemini-oauth.d.ts +0 -82
  468. package/dist/src/providers/auth/gemini-oauth.js +0 -472
  469. package/dist/src/providers/gemini.d.ts +0 -11
  470. package/dist/src/providers/gemini.js +0 -45
  471. package/dist/src/providers/siliconflow-sse-repair.d.ts +0 -58
  472. package/dist/src/providers/siliconflow-sse-repair.js +0 -177
  473. package/dist/src/providers/strategies/google.strategy.d.ts +0 -22
  474. package/dist/src/providers/strategies/google.strategy.js +0 -174
  475. package/dist/src/providers/strategies/siliconflow.strategy.js +0 -29
  476. package/dist/src/ui/containers/chat-feed.d.ts +0 -40
  477. package/dist/src/ui/containers/chat-feed.js +0 -66
@@ -4,6 +4,17 @@ import type { ProviderId } from "../providers/types.js";
4
4
  import type { AgentMode, ReasoningEffort } from "../types/index.js";
5
5
  import { type ShellSettings } from "./shell.js";
6
6
  export type ModelRole = "leader" | "implement" | "verify" | "research";
7
+ export type PeakHourMode = "downgrade" | "switch";
8
+ export interface PeakHourPolicy {
9
+ /** Default true — peak-hour routing active. */
10
+ enabled?: boolean;
11
+ /**
12
+ * downgrade: same-provider only (per catalog provider_policies.peak_hour).
13
+ * switch: try catalog switch_fallback_providers first.
14
+ * Default: switch.
15
+ */
16
+ mode?: PeakHourMode;
17
+ }
7
18
  export declare function getCatalogDefaultModel(): string;
8
19
  export type TelegramStreamingMode = "off" | "partial";
9
20
  export type CouncilExperienceMode = "off" | "advisory" | "enforcing";
@@ -101,6 +112,9 @@ export interface UserSettings {
101
112
  autoCompactAfterTurn?: boolean;
102
113
  /** Minimum % of context window to trigger post-turn auto-compact (default 0.25 = 25%, range 0.05-0.50). */
103
114
  autoCompactThresholdPct?: number;
115
+ /** Minimum new tokens accumulated since the last compaction before another
116
+ * post-turn compaction may fire. Prevents cache-reset thrash. Default 20000. */
117
+ autoCompactMinNewTokens?: number;
104
118
  roleModels?: Partial<Record<ModelRole, string>>;
105
119
  councilRounds?: number;
106
120
  autoCouncil?: boolean;
@@ -118,6 +132,26 @@ export interface UserSettings {
118
132
  * preserving legacy behavior).
119
133
  */
120
134
  autoCouncilMinRoles?: number;
135
+ /**
136
+ * Whether an auto-triggered council/debate runs the pre-debate clarification
137
+ * interview (model-designed askcards) before debating, instead of jumping
138
+ * straight into the debate on the bare prompt. Default true — a broad request
139
+ * like "dùng debate mode thảo luận lên plan" is exactly the kind of ambiguous
140
+ * scope the interview is meant to chốt first (prevents the debate drifting /
141
+ * "lan man"). The clarifier is ROI-gated and returns 0 cards on already-detailed
142
+ * topics, so enabling it is safe. Set false (or env MUONROI_AUTOCOUNCIL_CLARIFY=0)
143
+ * to restore the old skip-clarification behaviour.
144
+ */
145
+ autoCouncilClarify?: boolean;
146
+ /**
147
+ * When true (default), auto-council is skipped if the current session model is
148
+ * a reasoning model (catalog `reasoning: true`). Reasoning models already
149
+ * perform an internal self-debate via extended thinking, so running an
150
+ * explicit multi-role council on the same prompt is usually low-ROI and
151
+ * expensive. Set false (or env MUONROI_AUTOCOUNCIL_SKIP_REASONING=0) to force
152
+ * council even for reasoning models.
153
+ */
154
+ autoCouncilSkipReasoning?: boolean;
121
155
  councilPreferMultiProvider?: boolean;
122
156
  /** EE involvement level in council debates. Default: advisory. CQ-19. */
123
157
  councilExperienceMode?: CouncilExperienceMode;
@@ -132,6 +166,20 @@ export interface UserSettings {
132
166
  * decide structure and quality, not throughput.
133
167
  */
134
168
  councilCostAware?: boolean;
169
+ /**
170
+ * Language the council debate is conducted in (Feature B). The chosen language
171
+ * IS the debate language — no separate translate-back pass, so no extra LLM
172
+ * rounds. Values:
173
+ * - "auto" (default) — debate + conclusion follow the language of the user's
174
+ * prompt/brief (they type Vietnamese → the debate runs in Vietnamese).
175
+ * - "english" — force the historical English-only debate (citation tags,
176
+ * JSON, cross-turn citations stay maximally machine-stable).
177
+ * - any other free-text locale label (e.g. "vietnamese", "日本語") — pin the
178
+ * debate to that language regardless of the prompt's language.
179
+ * Citation tags, JSON keys, the `type` field, code identifiers, and STACK LOCK
180
+ * technology names always stay verbatim English regardless of this setting.
181
+ */
182
+ councilLanguage?: string;
135
183
  /** Set true after the user has been prompted (or skipped) the web-research onboarding. */
136
184
  webResearchPrompted?: boolean;
137
185
  /** Set true after the user has been prompted (or skipped) the first-run Experience Engine setup. */
@@ -151,13 +199,13 @@ export interface UserSettings {
151
199
  providers?: {
152
200
  anthropic?: ProviderKeyConfig;
153
201
  openai?: ProviderKeyConfig;
154
- google?: ProviderKeyConfig;
155
202
  deepseek?: ProviderKeyConfig;
156
- siliconflow?: ProviderKeyConfig;
157
203
  xai?: ProviderKeyConfig;
158
204
  ollama?: {
159
205
  baseURL?: string;
160
206
  };
207
+ zai?: ProviderKeyConfig;
208
+ "opencode-go"?: ProviderKeyConfig;
161
209
  };
162
210
  /** Providers the user has explicitly disabled in the model picker (still configured but hidden). */
163
211
  disabledProviders?: ProviderId[];
@@ -187,6 +235,11 @@ export interface UserSettings {
187
235
  /** Switch back to premium for final synthesis. Default: false. */
188
236
  premiumSynthesis?: boolean;
189
237
  };
238
+ /**
239
+ * Peak-hour routing for Z.ai (14:00–18:00 UTC+8 per docs) and DeepSeek
240
+ * (pro→flash downgrade in the same window to ease concurrency pressure).
241
+ */
242
+ peakHourPolicy?: PeakHourPolicy;
190
243
  /**
191
244
  * Reporter auto-fire settings (B2).
192
245
  * Controls whether the reporter posts to Discord automatically on sprint
@@ -216,6 +269,27 @@ export interface UserSettings {
216
269
  * Default 400_000 (~100k tokens).
217
270
  */
218
271
  topLevelToolBudgetChars?: number;
272
+ /**
273
+ * Router tier-promotion cap. The session's default model is treated as the
274
+ * user's cost ceiling: the EE router may DOWNGRADE per turn (cheaper model)
275
+ * but may not promote to a HIGHER tier than this setting allows without an
276
+ * explicit opt-in.
277
+ *
278
+ * - `"off"`: never promote beyond the default model's own tier.
279
+ * - `"balanced"` (default): allow promotion up to balanced, never premium.
280
+ * Routine tasks the EE brain over-classifies as premium get clamped to
281
+ * balanced (or to the default model when the provider has no balanced
282
+ * option — e.g. DeepSeek native only has fast + premium).
283
+ * - `"any"`: restore legacy behavior (router may promote to any tier).
284
+ *
285
+ * Evidence: session 89b34ce9a4e8 — default deepseek-v4-flash (fast tier),
286
+ * stored session.model=flash, but EE warm path returned premium for a
287
+ * routine "check và commit files" task → 47/47 turns silently ran on
288
+ * deepseek-v4-pro ($0.353) instead of flash (~$0.06). The per-turn routing
289
+ * override never flowed through setModel so the sessions row stayed flash
290
+ * ("session.model lie"). Default cap="balanced" prevents that silent leak.
291
+ */
292
+ routingPromoteMax?: "off" | "balanced" | "any";
219
293
  }
220
294
  export interface ProjectSettings {
221
295
  model?: string;
@@ -279,6 +353,7 @@ export declare function loadPaymentSettings(): Required<PaymentSettings>;
279
353
  export declare function savePaymentSettings(partial: PaymentSettings): void;
280
354
  export declare function isAutoCompactAfterTurnEnabled(): boolean;
281
355
  export declare function getAutoCompactThresholdPct(): number;
356
+ export declare function getAutoCompactMinNewTokens(): number;
282
357
  /**
283
358
  * Per-invocation cap on cumulative tool-output chars inside a `task`
284
359
  * sub-agent. See orchestrator/sub-agent-cap.ts for the tiered compression
@@ -294,6 +369,21 @@ export declare function getSubAgentBudgetChars(): number;
294
369
  * Default 120_000 (2 min). Env override: MUONROI_PROVIDER_STALL_TIMEOUT_MS.
295
370
  */
296
371
  export declare function getProviderStallTimeoutMs(): number;
372
+ /**
373
+ * No-forward-progress watchdog timeout (ms) for streaming model calls. Distinct
374
+ * from the stall watchdog: the stall watchdog re-arms on ANY stream chunk —
375
+ * including a reasoning model's `reasoning-delta` chunks — so a model stuck in an
376
+ * endless chain-of-thought keeps petting it and it NEVER fires (observed live
377
+ * 2026-07-10: a deepseek-v4-flash sub-agent churned reasoning for 30+ min, 1.4M
378
+ * input tokens, ZERO text/tool output, and the 2-min stall watchdog never tripped
379
+ * because reasoning chunks kept arriving). This second watchdog is petted ONLY on
380
+ * REAL forward progress (a text-delta or a tool-call), so a runaway-reasoning /
381
+ * no-output loop is aborted while a legitimately long reasoning burst that DOES
382
+ * eventually emit text/tools survives. Set generously above a normal reasoning
383
+ * burst. Range 30_000–1_800_000; 0 disables. Default 300_000 (5 min). Env
384
+ * override: MUONROI_PROVIDER_PROGRESS_TIMEOUT_MS.
385
+ */
386
+ export declare function getProviderProgressTimeoutMs(): number;
297
387
  /**
298
388
  * Number of times to AUTOMATICALLY re-issue a streaming model call after the
299
389
  * stall watchdog fires WITHOUT any chunk having arrived (a time-to-first-byte
@@ -337,21 +427,48 @@ export declare function getSubAgentCompactKeepLast(): number;
337
427
  * top-level loops typically carry more useful early context.
338
428
  * Env override: MUONROI_TOP_LEVEL_COMPACT_THRESHOLD_CHARS.
339
429
  */
340
- export declare function getTopLevelCompactThresholdChars(): number;
430
+ export declare function getTopLevelCompactThresholdChars(contextWindowTokens?: number): number;
431
+ /**
432
+ * Compaction hysteresis factor for the top-level loop. Once B4 compaction has
433
+ * fired within a turn, the compacted prefix is FROZEN and only new messages are
434
+ * appended (keeping the provider prompt-cache prefix byte-stable) until the
435
+ * cumulative size grows past `lastTriggerChars * factor` — then it re-compacts.
436
+ *
437
+ * Why: measured on session 1afb2728e67a — a 24-step turn re-ran compaction every
438
+ * step, and each step's sliding keepLast boundary flipped one more tool result
439
+ * verbatim→stub, breaking the cache prefix at that position. 63% of that
440
+ * session's FRESH input came from 5 such compaction-induced cache breaks.
441
+ * Holding the boundary between compactions trades a higher peak input for far
442
+ * fewer cache-break re-bills.
443
+ *
444
+ * Range 1.0–3.0. Default 1.15 (re-compact at +15% growth). `1.0` or env `0`
445
+ * disables hysteresis → legacy per-step compaction.
446
+ * Env override: MUONROI_COMPACT_HYSTERESIS.
447
+ */
448
+ export declare function getTopLevelCompactHysteresis(): number;
341
449
  /**
342
450
  * Phase B4 — number of trailing tool turns kept verbatim during top-level
343
451
  * compaction. Higher than sub-agent default because top-level agents make
344
452
  * decisions across longer horizons.
345
453
  * Env override: MUONROI_TOP_LEVEL_COMPACT_KEEP_LAST.
346
454
  */
347
- export declare function getTopLevelCompactKeepLast(): number;
455
+ export declare function getTopLevelCompactKeepLast(contextWindowTokens?: number): number;
456
+ /**
457
+ * O2 — byte budget for the verbatim tail (last keepLast turns) in top-level B4
458
+ * compaction. The keepLast shrink is otherwise fill-ratio based, so on a
459
+ * large-context model a read-heavy tail stays verbatim at ~50% fill, pinning
460
+ * each tool round at 60-80K input (measured on July-8 sessions). This caps the
461
+ * tail's actual chars, shrinking keepLast further (floor 2) when the kept tool
462
+ * results are large. 0 disables. Env: MUONROI_TOP_LEVEL_COMPACT_TAIL_BUDGET_CHARS.
463
+ */
464
+ export declare function getTopLevelCompactTailBudgetChars(contextWindowTokens?: number): number;
348
465
  /**
349
466
  * Per-turn cap on cumulative tool-output chars inside the top-level
350
467
  * orchestrator agentic loop. Same tiered compression as the sub-agent cap,
351
468
  * higher default so single-tool turns are unaffected. Env override:
352
469
  * MUONROI_TOP_LEVEL_TOOL_BUDGET_CHARS.
353
470
  */
354
- export declare function getTopLevelToolBudgetChars(maxRounds?: number): number;
471
+ export declare function getTopLevelToolBudgetChars(maxRounds?: number, contextWindowTokens?: number): number;
355
472
  export declare function getRoleModel(role: ModelRole): string | undefined;
356
473
  export declare function getRoleModels(): Partial<Record<ModelRole, string>>;
357
474
  export declare function getCouncilRounds(): number;
@@ -361,10 +478,41 @@ export declare function normalizeAutoCouncilConfidence(val: unknown): number;
361
478
  /** Pure validator extracted for testability; clamps user input to a sane range. */
362
479
  export declare function normalizeAutoCouncilMinRoles(val: unknown): number;
363
480
  export declare function getAutoCouncilConfidence(): number;
481
+ /**
482
+ * Whether the auto-council path runs the pre-debate clarification interview
483
+ * (model-designed askcards) before debating. Default true so a broadly-scoped
484
+ * "debate mode" request is clarified first. Env override wins over the user
485
+ * setting for quick dev toggling; env "0"/"false" disables, "1"/"true" enables.
486
+ */
487
+ export declare function isAutoCouncilClarifyEnabled(): boolean;
488
+ /**
489
+ * Whether auto-council should be skipped when the session model is a reasoning
490
+ * model. Default true. Env override wins over the user setting for quick dev
491
+ * toggling; env "0"/"false" disables the skip (forces council), "1"/"true"
492
+ * enables the skip.
493
+ */
494
+ export declare function isAutoCouncilSkipReasoning(): boolean;
364
495
  export declare function getAutoCouncilMinRoles(): number;
365
496
  export declare function isCouncilMultiProviderPreferred(): boolean;
366
497
  export declare function getCouncilExperienceMode(): CouncilExperienceMode;
498
+ export declare function normalizePeakHourPolicy(raw: unknown): PeakHourPolicy;
499
+ export declare function getPeakHourPolicy(): PeakHourPolicy;
367
500
  export declare function isCouncilCostAware(): boolean;
501
+ /**
502
+ * Normalize a raw councilLanguage value (Feature B). Trims + lowercases the two
503
+ * reserved modes ("auto", "english"); any other non-empty string is preserved
504
+ * as-is (trimmed) so locale labels keep the user's exact casing (e.g. "日本語").
505
+ * Empty / non-string → "auto".
506
+ */
507
+ export declare function normalizeCouncilLanguage(raw: unknown): string;
508
+ export declare function getCouncilLanguage(): string;
509
+ /**
510
+ * Router tier-promotion ceiling. See UserSettings.routingPromoteMax.
511
+ * Default "balanced" — router may promote up to balanced but never silently
512
+ * to premium. Validated to the three allowed values; any unknown value
513
+ * falls back to the default.
514
+ */
515
+ export declare function getRoutingPromoteMax(): "off" | "balanced" | "any";
368
516
  export declare function getDisabledProviders(): ProviderId[];
369
517
  export declare function isProviderDisabled(provider: ProviderId): boolean;
370
518
  export declare function setProviderDisabled(provider: ProviderId, disabled: boolean): ProviderId[];
@@ -1,7 +1,7 @@
1
1
  import * as fs from "fs";
2
2
  import * as os from "os";
3
3
  import * as path from "path";
4
- import { getEffectiveReasoningEffort, getFirstCatalogModel, getFirstCatalogProvider, getModelByTier, getModelIds, getModelInfo, MODELS, normalizeModelId, } from "../models/registry.js";
4
+ import { getCatalogCouncilRouting, getEffectiveReasoningEffort, getFirstCatalogModel, getFirstCatalogProvider, getModelByTier, getModelIds, getModelInfo, MODELS, normalizeModelId, } from "../models/registry.js";
5
5
  import { apiBaseFor, PROVIDER_ENDPOINTS } from "../providers/endpoints.js";
6
6
  import { ALL_PROVIDER_IDS } from "../providers/types.js";
7
7
  import { logger } from "./logger.js";
@@ -72,8 +72,9 @@ export function parseSubAgentsRawList(raw) {
72
72
  export function loadValidSubAgents() {
73
73
  return parseSubAgentsRawList(loadUserSettings().subAgents);
74
74
  }
75
- const USER_DIR = path.join(os.homedir(), ".muonroi-cli");
76
- const USER_SETTINGS_PATH = path.join(USER_DIR, "user-settings.json");
75
+ function getUserSettingsPath() {
76
+ return path.join(os.homedir(), ".muonroi-cli", "user-settings.json");
77
+ }
77
78
  function ensureDir(dir) {
78
79
  if (!fs.existsSync(dir)) {
79
80
  fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
@@ -179,7 +180,7 @@ export function ensureFootprintGitignored(cwd = process.cwd()) {
179
180
  }
180
181
  }
181
182
  export function loadUserSettings() {
182
- return readJson(USER_SETTINGS_PATH) || {};
183
+ return readJson(getUserSettingsPath()) || {};
183
184
  }
184
185
  export function saveUserSettings(partial) {
185
186
  const current = loadUserSettings();
@@ -248,7 +249,7 @@ export function saveUserSettings(partial) {
248
249
  }
249
250
  : {}),
250
251
  };
251
- writeJson(USER_SETTINGS_PATH, next);
252
+ writeJson(getUserSettingsPath(), next);
252
253
  }
253
254
  export function loadProjectSettings() {
254
255
  const projectPath = path.join(process.cwd(), ".muonroi-cli", "settings.json");
@@ -310,11 +311,6 @@ export function getProviderConfigs(mainApiKey) {
310
311
  if (openaiKey) {
311
312
  configs.openai = { apiKey: openaiKey, baseURL: p.openai?.baseURL };
312
313
  }
313
- // Google Gemini
314
- const googleKey = process.env.GOOGLE_API_KEY ?? p.google?.apiKey;
315
- if (googleKey) {
316
- configs.google = { apiKey: googleKey, baseURL: p.google?.baseURL };
317
- }
318
314
  // DeepSeek
319
315
  const deepseekKey = process.env.DEEPSEEK_API_KEY ?? p.deepseek?.apiKey;
320
316
  if (deepseekKey) {
@@ -323,14 +319,6 @@ export function getProviderConfigs(mainApiKey) {
323
319
  baseURL: p.deepseek?.baseURL ?? apiBaseFor("deepseek"),
324
320
  };
325
321
  }
326
- // SiliconFlow
327
- const siliconflowKey = process.env.SILICONFLOW_API_KEY ?? p.siliconflow?.apiKey;
328
- if (siliconflowKey) {
329
- configs.siliconflow = {
330
- apiKey: siliconflowKey,
331
- baseURL: p.siliconflow?.baseURL ?? apiBaseFor("siliconflow"),
332
- };
333
- }
334
322
  // xAI / Grok (OpenAI-compatible)
335
323
  const xaiKey = process.env.XAI_API_KEY ?? p.xai?.apiKey;
336
324
  if (xaiKey) {
@@ -339,6 +327,22 @@ export function getProviderConfigs(mainApiKey) {
339
327
  baseURL: p.xai?.baseURL ?? apiBaseFor("xai"),
340
328
  };
341
329
  }
330
+ // Z.ai (OpenAI-compatible)
331
+ const zaiKey = process.env.ZAI_API_KEY ?? p.zai?.apiKey;
332
+ if (zaiKey) {
333
+ configs.zai = {
334
+ apiKey: zaiKey,
335
+ baseURL: p.zai?.baseURL ?? apiBaseFor("zai"),
336
+ };
337
+ }
338
+ // OpenCode Go (OpenAI-compatible)
339
+ const opencodeGoKey = process.env.OPENCODE_GO_API_KEY ?? p["opencode-go"]?.apiKey;
340
+ if (opencodeGoKey) {
341
+ configs["opencode-go"] = {
342
+ apiKey: opencodeGoKey,
343
+ baseURL: p["opencode-go"]?.baseURL ?? apiBaseFor("opencode-go"),
344
+ };
345
+ }
342
346
  // Ollama — no key needed, just baseURL
343
347
  const ollamaURL = process.env.OLLAMA_URL ?? p.ollama?.baseURL ?? "http://localhost:11434";
344
348
  configs.ollama = { baseURL: ollamaURL };
@@ -591,6 +595,12 @@ export function getAutoCompactThresholdPct() {
591
595
  return val;
592
596
  return 0.4; // default 40% — Reduced from 25% after session bf58d0f46b51 analysis: 13 compacts in 43min generated 1.3M uncached tokens. Higher threshold = fewer compacts = less compaction overhead. For DeepSeek 128K context: fires at 51K instead of 32K.
593
597
  }
598
+ export function getAutoCompactMinNewTokens() {
599
+ const val = loadUserSettings().autoCompactMinNewTokens;
600
+ if (typeof val === "number" && val >= 0 && val <= 200_000)
601
+ return val;
602
+ return 20_000; // Observed thrash: re-compact after ~14K new tokens (session ff932f8568e8).
603
+ }
594
604
  /**
595
605
  * Per-invocation cap on cumulative tool-output chars inside a `task`
596
606
  * sub-agent. See orchestrator/sub-agent-cap.ts for the tiered compression
@@ -627,6 +637,31 @@ export function getProviderStallTimeoutMs() {
627
637
  }
628
638
  return 120_000;
629
639
  }
640
+ /**
641
+ * No-forward-progress watchdog timeout (ms) for streaming model calls. Distinct
642
+ * from the stall watchdog: the stall watchdog re-arms on ANY stream chunk —
643
+ * including a reasoning model's `reasoning-delta` chunks — so a model stuck in an
644
+ * endless chain-of-thought keeps petting it and it NEVER fires (observed live
645
+ * 2026-07-10: a deepseek-v4-flash sub-agent churned reasoning for 30+ min, 1.4M
646
+ * input tokens, ZERO text/tool output, and the 2-min stall watchdog never tripped
647
+ * because reasoning chunks kept arriving). This second watchdog is petted ONLY on
648
+ * REAL forward progress (a text-delta or a tool-call), so a runaway-reasoning /
649
+ * no-output loop is aborted while a legitimately long reasoning burst that DOES
650
+ * eventually emit text/tools survives. Set generously above a normal reasoning
651
+ * burst. Range 30_000–1_800_000; 0 disables. Default 300_000 (5 min). Env
652
+ * override: MUONROI_PROVIDER_PROGRESS_TIMEOUT_MS.
653
+ */
654
+ export function getProviderProgressTimeoutMs() {
655
+ const envRaw = process.env.MUONROI_PROVIDER_PROGRESS_TIMEOUT_MS;
656
+ if (envRaw !== undefined && envRaw !== "") {
657
+ const n = Number(envRaw);
658
+ if (Number.isFinite(n) && n === 0)
659
+ return 0; // explicit disable
660
+ if (Number.isFinite(n) && n >= 30_000 && n <= 1_800_000)
661
+ return Math.floor(n);
662
+ }
663
+ return 300_000;
664
+ }
630
665
  /**
631
666
  * Number of times to AUTOMATICALLY re-issue a streaming model call after the
632
667
  * stall watchdog fires WITHOUT any chunk having arrived (a time-to-first-byte
@@ -702,17 +737,50 @@ export function getSubAgentCompactKeepLast() {
702
737
  * top-level loops typically carry more useful early context.
703
738
  * Env override: MUONROI_TOP_LEVEL_COMPACT_THRESHOLD_CHARS.
704
739
  */
705
- export function getTopLevelCompactThresholdChars() {
740
+ export function getTopLevelCompactThresholdChars(contextWindowTokens) {
706
741
  const envRaw = process.env.MUONROI_TOP_LEVEL_COMPACT_THRESHOLD_CHARS;
707
742
  if (envRaw) {
708
743
  const n = Number(envRaw);
709
- if (Number.isFinite(n) && n >= 50_000 && n <= 1_500_000)
744
+ if (Number.isFinite(n) && n >= 10_000 && n <= 1_500_000)
710
745
  return Math.floor(n);
711
746
  }
712
- // Phase C5 lowered from 200_000 to 100_000 chars (symmetric with the
713
- // sub-agent 80→40K reduction). Same evidence applies: tool results are
714
- // capped, so the chars threshold rarely trips while token billing climbs.
715
- return 100_000;
747
+ // For small-context models (e.g. DeepSeek 64K), scale threshold proportionally
748
+ // to prevent linear token growth during tool loops. A model with 64K context
749
+ // gets threshold = 64000 * 4 * 0.35 = 89,600 chars (~22K tokens = 35% of window).
750
+ // Large-context models (128K+) keep the original 200K default.
751
+ if (contextWindowTokens && contextWindowTokens > 0) {
752
+ const dynamicThreshold = Math.floor(contextWindowTokens * 4 * 0.35);
753
+ return Math.min(200_000, dynamicThreshold);
754
+ }
755
+ return 200_000;
756
+ }
757
+ /**
758
+ * Compaction hysteresis factor for the top-level loop. Once B4 compaction has
759
+ * fired within a turn, the compacted prefix is FROZEN and only new messages are
760
+ * appended (keeping the provider prompt-cache prefix byte-stable) until the
761
+ * cumulative size grows past `lastTriggerChars * factor` — then it re-compacts.
762
+ *
763
+ * Why: measured on session 1afb2728e67a — a 24-step turn re-ran compaction every
764
+ * step, and each step's sliding keepLast boundary flipped one more tool result
765
+ * verbatim→stub, breaking the cache prefix at that position. 63% of that
766
+ * session's FRESH input came from 5 such compaction-induced cache breaks.
767
+ * Holding the boundary between compactions trades a higher peak input for far
768
+ * fewer cache-break re-bills.
769
+ *
770
+ * Range 1.0–3.0. Default 1.15 (re-compact at +15% growth). `1.0` or env `0`
771
+ * disables hysteresis → legacy per-step compaction.
772
+ * Env override: MUONROI_COMPACT_HYSTERESIS.
773
+ */
774
+ export function getTopLevelCompactHysteresis() {
775
+ const envRaw = process.env.MUONROI_COMPACT_HYSTERESIS;
776
+ if (envRaw !== undefined && envRaw.trim() !== "") {
777
+ const n = Number(envRaw);
778
+ if (Number.isFinite(n) && n === 0)
779
+ return 1.0; // explicit disable
780
+ if (Number.isFinite(n) && n >= 1.0 && n <= 3.0)
781
+ return n;
782
+ }
783
+ return 1.15;
716
784
  }
717
785
  /**
718
786
  * Phase B4 — number of trailing tool turns kept verbatim during top-level
@@ -720,22 +788,59 @@ export function getTopLevelCompactThresholdChars() {
720
788
  * decisions across longer horizons.
721
789
  * Env override: MUONROI_TOP_LEVEL_COMPACT_KEEP_LAST.
722
790
  */
723
- export function getTopLevelCompactKeepLast() {
791
+ export function getTopLevelCompactKeepLast(contextWindowTokens) {
724
792
  const envRaw = process.env.MUONROI_TOP_LEVEL_COMPACT_KEEP_LAST;
725
793
  if (envRaw) {
726
794
  const n = Number(envRaw);
727
795
  if (Number.isFinite(n) && n >= 1 && n <= 30)
728
796
  return Math.floor(n);
729
797
  }
798
+ // Small-context models (< 100K tokens) benefit from keeping fewer trailing
799
+ // turns — each verbatim turn with tool results + reasoning tokens costs
800
+ // 5-15K tokens. Reduce from 5 to 3 for small windows.
801
+ if (contextWindowTokens && contextWindowTokens < 100_000) {
802
+ return 3;
803
+ }
730
804
  return 5;
731
805
  }
806
+ /**
807
+ * O2 — byte budget for the verbatim tail (last keepLast turns) in top-level B4
808
+ * compaction. The keepLast shrink is otherwise fill-ratio based, so on a
809
+ * large-context model a read-heavy tail stays verbatim at ~50% fill, pinning
810
+ * each tool round at 60-80K input (measured on July-8 sessions). This caps the
811
+ * tail's actual chars, shrinking keepLast further (floor 2) when the kept tool
812
+ * results are large. 0 disables. Env: MUONROI_TOP_LEVEL_COMPACT_TAIL_BUDGET_CHARS.
813
+ */
814
+ export function getTopLevelCompactTailBudgetChars(contextWindowTokens) {
815
+ const envRaw = process.env.MUONROI_TOP_LEVEL_COMPACT_TAIL_BUDGET_CHARS;
816
+ if (envRaw !== undefined && envRaw.trim() !== "") {
817
+ const n = Number(envRaw);
818
+ // 0 = explicit disable; otherwise clamp to a sane floor so a fat-fingered
819
+ // tiny value can't stub away all recent context.
820
+ if (Number.isFinite(n) && n === 0)
821
+ return 0;
822
+ if (Number.isFinite(n) && n >= 20_000 && n <= 1_000_000)
823
+ return Math.floor(n);
824
+ }
825
+ // Default 50K chars (~12.5K tokens). Chosen from a deterministic measurement:
826
+ // on a realistic read-heavy turn the keepLast=5 verbatim tail is ~70-100K
827
+ // chars, so a looser budget (e.g. 120K) never bites (no-op). 50K shrinks the
828
+ // effective tail to ~3 turns on heavy turns (matching the sub-agent keepLast
829
+ // default) — ~7K tokens/call saved — while high-value results stay verbatim
830
+ // and light turns (below the 200K compaction threshold) are untouched. For
831
+ // small windows, scale to ~20% of the window so the tail can't dominate.
832
+ if (contextWindowTokens && contextWindowTokens > 0) {
833
+ return Math.min(50_000, Math.floor(contextWindowTokens * 4 * 0.2));
834
+ }
835
+ return 50_000;
836
+ }
732
837
  /**
733
838
  * Per-turn cap on cumulative tool-output chars inside the top-level
734
839
  * orchestrator agentic loop. Same tiered compression as the sub-agent cap,
735
840
  * higher default so single-tool turns are unaffected. Env override:
736
841
  * MUONROI_TOP_LEVEL_TOOL_BUDGET_CHARS.
737
842
  */
738
- export function getTopLevelToolBudgetChars(maxRounds) {
843
+ export function getTopLevelToolBudgetChars(maxRounds, contextWindowTokens) {
739
844
  const envRaw = process.env.MUONROI_TOP_LEVEL_TOOL_BUDGET_CHARS;
740
845
  if (envRaw) {
741
846
  const n = Number(envRaw);
@@ -748,10 +853,35 @@ export function getTopLevelToolBudgetChars(maxRounds) {
748
853
  // Dynamically scale default based on maxRounds relative to default base (40)
749
854
  const baseRounds = 40;
750
855
  const scale = maxRounds && maxRounds > baseRounds ? maxRounds / baseRounds : 1;
751
- return Math.floor(400_000 * scale);
856
+ const baseDefault = Math.floor(400_000 * scale);
857
+ // For small-context models (e.g. DeepSeek 64K), scale the budget to 60% of
858
+ // the context window in chars so tiered compression kicks in before the
859
+ // cumulative tool output exceeds what the model can hold in context.
860
+ if (contextWindowTokens && contextWindowTokens > 0 && contextWindowTokens < 200_000) {
861
+ const windowBudget = Math.floor(contextWindowTokens * 4 * 0.6);
862
+ return Math.min(baseDefault, Math.max(50_000, windowBudget));
863
+ }
864
+ return baseDefault;
752
865
  }
753
866
  export function getRoleModel(role) {
754
- return loadUserSettings().roleModels?.[role];
867
+ const configured = loadUserSettings().roleModels?.[role];
868
+ if (!configured)
869
+ return undefined;
870
+ // Graceful staleness guard (mirrors getCurrentModel's pickValid): a role model
871
+ // persisted before a catalog rename/drop (e.g. "grok-build-0.1" after it was
872
+ // dropped in favor of grok-composer-2.5-fast) must NOT leak a dead id to the
873
+ // runtime, where resolveModelRuntime throws "not found in catalog — cannot
874
+ // determine provider" and takes down the whole council speaker (observed:
875
+ // Experience Auditor on the research role). If the catalog hasn't loaded yet,
876
+ // trust the normalized id; otherwise drop unresolved ids so the caller falls
877
+ // back to its own default instead of crashing.
878
+ const normalized = normalizeModelId(configured);
879
+ if (MODELS.length === 0)
880
+ return normalized;
881
+ if (getModelInfo(normalized))
882
+ return normalized;
883
+ logger.warn("cli", `roleModels.${role} = "${configured}" is not in the catalog (renamed or removed); ignoring so the caller falls back to its default. Update it via /config.`);
884
+ return undefined;
755
885
  }
756
886
  export function getRoleModels() {
757
887
  return loadUserSettings().roleModels ?? {};
@@ -778,18 +908,92 @@ export function normalizeAutoCouncilMinRoles(val) {
778
908
  export function getAutoCouncilConfidence() {
779
909
  return normalizeAutoCouncilConfidence(loadUserSettings().autoCouncilConfidence);
780
910
  }
911
+ /**
912
+ * Whether the auto-council path runs the pre-debate clarification interview
913
+ * (model-designed askcards) before debating. Default true so a broadly-scoped
914
+ * "debate mode" request is clarified first. Env override wins over the user
915
+ * setting for quick dev toggling; env "0"/"false" disables, "1"/"true" enables.
916
+ */
917
+ export function isAutoCouncilClarifyEnabled() {
918
+ const env = process.env.MUONROI_AUTOCOUNCIL_CLARIFY?.trim().toLowerCase();
919
+ if (env === "0" || env === "false")
920
+ return false;
921
+ if (env === "1" || env === "true")
922
+ return true;
923
+ return loadUserSettings().autoCouncilClarify ?? true;
924
+ }
925
+ /**
926
+ * Whether auto-council should be skipped when the session model is a reasoning
927
+ * model. Default true. Env override wins over the user setting for quick dev
928
+ * toggling; env "0"/"false" disables the skip (forces council), "1"/"true"
929
+ * enables the skip.
930
+ */
931
+ export function isAutoCouncilSkipReasoning() {
932
+ const env = process.env.MUONROI_AUTOCOUNCIL_SKIP_REASONING?.trim().toLowerCase();
933
+ if (env === "0" || env === "false")
934
+ return false;
935
+ if (env === "1" || env === "true")
936
+ return true;
937
+ return loadUserSettings().autoCouncilSkipReasoning ?? true;
938
+ }
781
939
  export function getAutoCouncilMinRoles() {
782
940
  return normalizeAutoCouncilMinRoles(loadUserSettings().autoCouncilMinRoles);
783
941
  }
784
942
  export function isCouncilMultiProviderPreferred() {
785
- return loadUserSettings().councilPreferMultiProvider ?? false;
943
+ const user = loadUserSettings().councilPreferMultiProvider;
944
+ if (user !== undefined)
945
+ return user;
946
+ return getCatalogCouncilRouting()?.prefer_multi_provider ?? true;
786
947
  }
787
948
  export function getCouncilExperienceMode() {
788
949
  return loadUserSettings().councilExperienceMode ?? "advisory";
789
950
  }
951
+ export function normalizePeakHourPolicy(raw) {
952
+ if (!raw || typeof raw !== "object") {
953
+ return { enabled: true, mode: "switch" };
954
+ }
955
+ const p = raw;
956
+ return {
957
+ enabled: p.enabled !== false,
958
+ mode: p.mode === "downgrade" ? "downgrade" : "switch",
959
+ };
960
+ }
961
+ export function getPeakHourPolicy() {
962
+ return normalizePeakHourPolicy(loadUserSettings().peakHourPolicy);
963
+ }
790
964
  export function isCouncilCostAware() {
791
965
  return loadUserSettings().councilCostAware ?? true;
792
966
  }
967
+ /**
968
+ * Normalize a raw councilLanguage value (Feature B). Trims + lowercases the two
969
+ * reserved modes ("auto", "english"); any other non-empty string is preserved
970
+ * as-is (trimmed) so locale labels keep the user's exact casing (e.g. "日本語").
971
+ * Empty / non-string → "auto".
972
+ */
973
+ export function normalizeCouncilLanguage(raw) {
974
+ if (typeof raw !== "string")
975
+ return "auto";
976
+ const trimmed = raw.trim();
977
+ if (trimmed.length === 0)
978
+ return "auto";
979
+ const lower = trimmed.toLowerCase();
980
+ if (lower === "auto" || lower === "english")
981
+ return lower;
982
+ return trimmed;
983
+ }
984
+ export function getCouncilLanguage() {
985
+ return normalizeCouncilLanguage(loadUserSettings().councilLanguage);
986
+ }
987
+ /**
988
+ * Router tier-promotion ceiling. See UserSettings.routingPromoteMax.
989
+ * Default "balanced" — router may promote up to balanced but never silently
990
+ * to premium. Validated to the three allowed values; any unknown value
991
+ * falls back to the default.
992
+ */
993
+ export function getRoutingPromoteMax() {
994
+ const raw = loadUserSettings().routingPromoteMax;
995
+ return raw === "off" || raw === "balanced" || raw === "any" ? raw : "balanced";
996
+ }
793
997
  export function getDisabledProviders() {
794
998
  const raw = loadUserSettings().disabledProviders;
795
999
  if (!Array.isArray(raw))
@@ -1,7 +1,7 @@
1
1
  import { generateText } from "ai";
2
2
  import { resolveModelRuntime } from "../providers/runtime.js";
3
- const SIDE_QUESTION_SYSTEM = `You are a helpful coding assistant answering a quick side question. The user is in the middle of a coding session and needs a fast, concise answer. Keep your response short and focused — this is a side question, not the main task.
4
-
3
+ const SIDE_QUESTION_SYSTEM = `You are a helpful coding assistant answering a quick side question. The user is in the middle of a coding session and needs a fast, concise answer. Keep your response short and focused — this is a side question, not the main task.
4
+
5
5
  If conversation context is provided below, use it to give a more relevant answer.`;
6
6
  export async function runSideQuestion(question, provider, modelId, conversationContext, signal) {
7
7
  const runtime = resolveModelRuntime(provider, modelId);
@@ -163,9 +163,9 @@ export function discoverSkills(projectRoot) {
163
163
  _skillsCache = { skills, cachedAt: now, cwd: projectRoot };
164
164
  return skills;
165
165
  }
166
- const SKILLS_INSTRUCTIONS = `AGENT SKILLS (optional):
167
- The following <available_skills> list specialized workflows. Use them when they might help the user's request — not only on exact keyword matches.
168
- If a skill's description fits the task or could improve consistency, read that skill's instructions first using read_file with the path from <location>, then follow the SKILL.md body.
166
+ const SKILLS_INSTRUCTIONS = `AGENT SKILLS (optional):
167
+ The following <available_skills> list specialized workflows. Use them when they might help the user's request — not only on exact keyword matches.
168
+ If a skill's description fits the task or could improve consistency, read that skill's instructions first using read_file with the path from <location>, then follow the SKILL.md body.
169
169
  Paths inside a skill (scripts/, references/, assets/) are relative to the skill directory (the folder containing SKILL.md); prefer absolute paths in tool calls.`;
170
170
  /** OpenCode-style XML catalog plus activation instructions for read_file. Returns null if no skills. */
171
171
  export function formatSkillsForPrompt(skills) {