muonroi-cli 1.8.4 → 1.8.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (458) hide show
  1. package/LICENSE +17 -5
  2. package/dist/packages/agent-harness-core/src/driver.d.ts +27 -1
  3. package/dist/packages/agent-harness-core/src/driver.js +46 -0
  4. package/dist/packages/agent-harness-core/src/event-tee.d.ts +48 -0
  5. package/dist/packages/agent-harness-core/src/event-tee.js +77 -0
  6. package/dist/packages/agent-harness-core/src/mcp-server.d.ts +11 -0
  7. package/dist/packages/agent-harness-core/src/mcp-server.js +87 -15
  8. package/dist/packages/agent-harness-core/src/protocol.d.ts +66 -2
  9. package/dist/packages/agent-harness-core/src/protocol.js +15 -0
  10. package/dist/packages/agent-harness-core/src/visual-quality.d.ts +58 -0
  11. package/dist/packages/agent-harness-core/src/visual-quality.js +141 -0
  12. package/dist/packages/agent-harness-opentui/src/agent-mode.d.ts +6 -0
  13. package/dist/packages/agent-harness-opentui/src/agent-mode.js +14 -1
  14. package/dist/packages/agent-harness-opentui/src/input-bridge.d.ts +2 -10
  15. package/dist/packages/agent-harness-opentui/src/input-bridge.js +103 -16
  16. package/dist/packages/agent-harness-opentui/src/install.d.ts +8 -0
  17. package/dist/packages/agent-harness-opentui/src/install.js +10 -0
  18. package/dist/packages/agent-harness-opentui/src/semantic.js +12 -10
  19. package/dist/packages/agent-harness-opentui/src/visual-capture.d.ts +56 -0
  20. package/dist/packages/agent-harness-opentui/src/visual-capture.js +103 -0
  21. package/dist/src/agent-harness/mock-model.d.ts +28 -0
  22. package/dist/src/agent-harness/mock-model.js +63 -1
  23. package/dist/src/agent-harness/test-spawn.js +31 -0
  24. package/dist/src/cli/config/screen-providers.js +1 -1
  25. package/dist/src/cli/cost-forensics.d.ts +10 -0
  26. package/dist/src/cli/cost-forensics.js +18 -3
  27. package/dist/src/cli/keys-bundle.d.ts +1 -1
  28. package/dist/src/cli/keys-bundle.js +1 -1
  29. package/dist/src/cli/keys.d.ts +2 -2
  30. package/dist/src/cli/keys.js +19 -81
  31. package/dist/src/council/clarifier.d.ts +28 -2
  32. package/dist/src/council/clarifier.js +81 -15
  33. package/dist/src/council/context.js +49 -15
  34. package/dist/src/council/debate-checkpoint.d.ts +129 -0
  35. package/dist/src/council/debate-checkpoint.js +176 -0
  36. package/dist/src/council/debate-planner.js +51 -3
  37. package/dist/src/council/debate-summary.d.ts +25 -0
  38. package/dist/src/council/debate-summary.js +85 -0
  39. package/dist/src/council/debate.d.ts +169 -2
  40. package/dist/src/council/debate.js +1210 -134
  41. package/dist/src/council/index.d.ts +85 -1
  42. package/dist/src/council/index.js +634 -196
  43. package/dist/src/council/leader.d.ts +26 -0
  44. package/dist/src/council/leader.js +150 -9
  45. package/dist/src/council/llm.d.ts +32 -0
  46. package/dist/src/council/llm.js +231 -38
  47. package/dist/src/council/panel-select.d.ts +30 -0
  48. package/dist/src/council/panel-select.js +72 -0
  49. package/dist/src/council/planner.js +23 -0
  50. package/dist/src/council/preflight.d.ts +7 -0
  51. package/dist/src/council/preflight.js +14 -2
  52. package/dist/src/council/prompts.d.ts +30 -3
  53. package/dist/src/council/prompts.js +234 -64
  54. package/dist/src/council/stance-recall.d.ts +42 -0
  55. package/dist/src/council/stance-recall.js +57 -0
  56. package/dist/src/council/strip-think.d.ts +17 -0
  57. package/dist/src/council/strip-think.js +33 -0
  58. package/dist/src/council/types.d.ts +128 -0
  59. package/dist/src/ee/artifact-cache.d.ts +16 -0
  60. package/dist/src/ee/artifact-cache.js +32 -0
  61. package/dist/src/ee/auth.d.ts +1 -0
  62. package/dist/src/ee/auth.js +15 -2
  63. package/dist/src/ee/bridge.d.ts +10 -0
  64. package/dist/src/ee/bridge.js +58 -0
  65. package/dist/src/ee/client.js +81 -18
  66. package/dist/src/ee/export-transcripts.d.ts +1 -0
  67. package/dist/src/ee/export-transcripts.js +8 -10
  68. package/dist/src/ee/extract-session.js +29 -0
  69. package/dist/src/ee/extract-style.d.ts +58 -0
  70. package/dist/src/ee/extract-style.js +270 -0
  71. package/dist/src/ee/recall-ledger.d.ts +9 -0
  72. package/dist/src/ee/recall-ledger.js +3 -0
  73. package/dist/src/ee/scope.d.ts +1 -0
  74. package/dist/src/ee/scope.js +26 -1
  75. package/dist/src/ee/search.d.ts +7 -0
  76. package/dist/src/ee/search.js +24 -0
  77. package/dist/src/ee/transcript-emit.js +2 -0
  78. package/dist/src/ee/types.d.ts +22 -0
  79. package/dist/src/ee/who-am-i-brain.d.ts +35 -0
  80. package/dist/src/ee/who-am-i-brain.js +220 -0
  81. package/dist/src/ee/who-am-i.d.ts +10 -3
  82. package/dist/src/ee/who-am-i.js +12 -0
  83. package/dist/src/ee/workflow-event.d.ts +48 -0
  84. package/dist/src/ee/workflow-event.js +81 -0
  85. package/dist/src/flow/compaction/compress.d.ts +3 -3
  86. package/dist/src/flow/compaction/compress.js +45 -8
  87. package/dist/src/flow/compaction/extract.d.ts +4 -7
  88. package/dist/src/flow/compaction/extract.js +50 -10
  89. package/dist/src/flow/compaction/index.d.ts +13 -1
  90. package/dist/src/flow/compaction/index.js +70 -3
  91. package/dist/src/flow/compaction/input-guard.d.ts +24 -0
  92. package/dist/src/flow/compaction/input-guard.js +43 -0
  93. package/dist/src/flow/fold-planning.d.ts +36 -0
  94. package/dist/src/flow/fold-planning.js +83 -0
  95. package/dist/src/flow/hierarchy.d.ts +146 -0
  96. package/dist/src/flow/hierarchy.js +427 -0
  97. package/dist/src/flow/index.d.ts +1 -0
  98. package/dist/src/flow/index.js +2 -0
  99. package/dist/src/flow/run-artifacts.d.ts +102 -0
  100. package/dist/src/flow/run-artifacts.js +208 -0
  101. package/dist/src/generated/version.d.ts +1 -1
  102. package/dist/src/generated/version.js +1 -1
  103. package/dist/src/gsd/assessment-schema.d.ts +44 -0
  104. package/dist/src/gsd/assessment-schema.js +134 -0
  105. package/dist/src/gsd/capability-registry.d.ts +45 -0
  106. package/dist/src/gsd/capability-registry.js +337 -0
  107. package/dist/src/gsd/complexity-assessor.d.ts +39 -0
  108. package/dist/src/gsd/complexity-assessor.js +152 -0
  109. package/dist/src/gsd/config-bridge.d.ts +7 -0
  110. package/dist/src/gsd/config-bridge.js +114 -0
  111. package/dist/src/gsd/config-loader.d.ts +27 -0
  112. package/dist/src/gsd/config-loader.js +50 -0
  113. package/dist/src/gsd/council-context.d.ts +44 -0
  114. package/dist/src/gsd/council-context.js +114 -0
  115. package/dist/src/gsd/ee-closure.d.ts +28 -0
  116. package/dist/src/gsd/ee-closure.js +49 -0
  117. package/dist/src/gsd/flags.d.ts +55 -0
  118. package/dist/src/gsd/flags.js +83 -0
  119. package/dist/src/gsd/gsd-dispatch.d.ts +58 -0
  120. package/dist/src/gsd/gsd-dispatch.js +131 -0
  121. package/dist/src/gsd/gsd-runtime.d.ts +22 -0
  122. package/dist/src/gsd/gsd-runtime.js +37 -0
  123. package/dist/src/gsd/host-adapter.d.ts +11 -0
  124. package/dist/src/gsd/host-adapter.js +29 -0
  125. package/dist/src/gsd/index.d.ts +24 -1
  126. package/dist/src/gsd/index.js +27 -0
  127. package/dist/src/gsd/loop-host-contract.d.ts +21 -0
  128. package/dist/src/gsd/loop-host-contract.js +39 -0
  129. package/dist/src/gsd/loop-host.d.ts +69 -0
  130. package/dist/src/gsd/loop-host.js +245 -0
  131. package/dist/src/gsd/loop-resolver.d.ts +36 -0
  132. package/dist/src/gsd/loop-resolver.js +79 -0
  133. package/dist/src/gsd/model-tier.d.ts +13 -0
  134. package/dist/src/gsd/model-tier.js +45 -0
  135. package/dist/src/gsd/mutation-gate.d.ts +16 -0
  136. package/dist/src/gsd/mutation-gate.js +41 -0
  137. package/dist/src/gsd/native-roadmap.d.ts +89 -0
  138. package/dist/src/gsd/native-roadmap.js +343 -0
  139. package/dist/src/gsd/native-state.d.ts +47 -0
  140. package/dist/src/gsd/native-state.js +220 -0
  141. package/dist/src/gsd/paths.d.ts +23 -0
  142. package/dist/src/gsd/paths.js +66 -0
  143. package/dist/src/gsd/phase-dag.d.ts +12 -0
  144. package/dist/src/gsd/phase-dag.js +94 -0
  145. package/dist/src/gsd/phase-sync.d.ts +42 -0
  146. package/dist/src/gsd/phase-sync.js +321 -0
  147. package/dist/src/gsd/pil-gate-context.d.ts +13 -0
  148. package/dist/src/gsd/pil-gate-context.js +64 -0
  149. package/dist/src/gsd/pil-gate-critic.d.ts +19 -0
  150. package/dist/src/gsd/pil-gate-critic.js +74 -0
  151. package/dist/src/gsd/plan-council-prompts.d.ts +25 -0
  152. package/dist/src/gsd/plan-council-prompts.js +79 -0
  153. package/dist/src/gsd/plan-council.d.ts +44 -0
  154. package/dist/src/gsd/plan-council.js +251 -0
  155. package/dist/src/gsd/plan-gate-vocabulary.d.ts +40 -0
  156. package/dist/src/gsd/plan-gate-vocabulary.js +64 -0
  157. package/dist/src/gsd/product-workspace.d.ts +13 -0
  158. package/dist/src/gsd/product-workspace.js +124 -0
  159. package/dist/src/gsd/ship-bridge.d.ts +25 -0
  160. package/dist/src/gsd/ship-bridge.js +65 -0
  161. package/dist/src/gsd/state-document.d.ts +40 -0
  162. package/dist/src/gsd/state-document.js +163 -0
  163. package/dist/src/gsd/verdict-schema.d.ts +39 -0
  164. package/dist/src/gsd/verdict-schema.js +144 -0
  165. package/dist/src/gsd/verify-context.d.ts +22 -0
  166. package/dist/src/gsd/verify-context.js +27 -0
  167. package/dist/src/gsd/verify-council-prompts.d.ts +19 -0
  168. package/dist/src/gsd/verify-council-prompts.js +85 -0
  169. package/dist/src/gsd/verify-council.d.ts +25 -0
  170. package/dist/src/gsd/verify-council.js +119 -0
  171. package/dist/src/gsd/verify-gate-vocabulary.d.ts +25 -0
  172. package/dist/src/gsd/verify-gate-vocabulary.js +46 -0
  173. package/dist/src/gsd/workflow-engine.d.ts +60 -0
  174. package/dist/src/gsd/workflow-engine.js +207 -0
  175. package/dist/src/gsd/workflow-tools.d.ts +13 -0
  176. package/dist/src/gsd/workflow-tools.js +277 -0
  177. package/dist/src/hooks/index.js +1 -1
  178. package/dist/src/index.js +44 -11
  179. package/dist/src/maintain/pr-builder.js +23 -13
  180. package/dist/src/mcp/auto-setup.js +57 -32
  181. package/dist/src/mcp/client-pool.js +1 -1
  182. package/dist/src/mcp/ee-tools.js +1 -0
  183. package/dist/src/mcp/research-onboarding.js +8 -7
  184. package/dist/src/mcp/runtime.js +34 -2
  185. package/dist/src/models/catalog-client.d.ts +87 -0
  186. package/dist/src/models/catalog-client.js +105 -38
  187. package/dist/src/models/catalog.json +528 -265
  188. package/dist/src/models/registry.d.ts +22 -7
  189. package/dist/src/models/registry.js +73 -10
  190. package/dist/src/ops/doctor.js +1 -1
  191. package/dist/src/orchestrator/auto-commit.js +1 -1
  192. package/dist/src/orchestrator/batch-turn-runner.js +2 -2
  193. package/dist/src/orchestrator/cache-prefix.d.ts +67 -0
  194. package/dist/src/orchestrator/cache-prefix.js +83 -0
  195. package/dist/src/orchestrator/compact-request.d.ts +32 -0
  196. package/dist/src/orchestrator/compact-request.js +41 -0
  197. package/dist/src/orchestrator/compaction.d.ts +10 -0
  198. package/dist/src/orchestrator/compaction.js +27 -7
  199. package/dist/src/orchestrator/council-manager.d.ts +12 -3
  200. package/dist/src/orchestrator/council-manager.js +65 -24
  201. package/dist/src/orchestrator/cross-turn-dedup.d.ts +6 -0
  202. package/dist/src/orchestrator/cross-turn-dedup.js +22 -1
  203. package/dist/src/orchestrator/error-utils.d.ts +29 -0
  204. package/dist/src/orchestrator/error-utils.js +132 -24
  205. package/dist/src/orchestrator/grounding-check.js +39 -1
  206. package/dist/src/orchestrator/message-processor.js +242 -33
  207. package/dist/src/orchestrator/orchestrator.d.ts +39 -3
  208. package/dist/src/orchestrator/orchestrator.js +651 -102
  209. package/dist/src/orchestrator/preprocessor.js +1 -1
  210. package/dist/src/orchestrator/proactive-compact-detector.d.ts +26 -0
  211. package/dist/src/orchestrator/proactive-compact-detector.js +36 -0
  212. package/dist/src/orchestrator/prompts.js +17 -17
  213. package/dist/src/orchestrator/reactive-delegation.d.ts +39 -0
  214. package/dist/src/orchestrator/reactive-delegation.js +59 -0
  215. package/dist/src/orchestrator/retry-classifier.d.ts +2 -1
  216. package/dist/src/orchestrator/retry-classifier.js +46 -2
  217. package/dist/src/orchestrator/safety-intercept.d.ts +45 -0
  218. package/dist/src/orchestrator/safety-intercept.js +55 -0
  219. package/dist/src/orchestrator/scope-reminder.js +1 -1
  220. package/dist/src/orchestrator/session-experience.d.ts +2 -1
  221. package/dist/src/orchestrator/session-experience.js +2 -1
  222. package/dist/src/orchestrator/should-run-gate.d.ts +5 -0
  223. package/dist/src/orchestrator/should-run-gate.js +18 -0
  224. package/dist/src/orchestrator/stall-watchdog.d.ts +24 -3
  225. package/dist/src/orchestrator/stall-watchdog.js +47 -13
  226. package/dist/src/orchestrator/stream-runner.js +62 -29
  227. package/dist/src/orchestrator/sub-agent-cap.d.ts +13 -1
  228. package/dist/src/orchestrator/sub-agent-cap.js +16 -1
  229. package/dist/src/orchestrator/sub-agent-model-tier.d.ts +13 -1
  230. package/dist/src/orchestrator/sub-agent-model-tier.js +16 -2
  231. package/dist/src/orchestrator/subagent-compactor.d.ts +53 -1
  232. package/dist/src/orchestrator/subagent-compactor.js +126 -15
  233. package/dist/src/orchestrator/tool-engine.d.ts +22 -0
  234. package/dist/src/orchestrator/tool-engine.js +620 -56
  235. package/dist/src/orchestrator/tool-limit-auto-recover.d.ts +22 -0
  236. package/dist/src/orchestrator/tool-limit-auto-recover.js +30 -0
  237. package/dist/src/orchestrator/turn-runner-deps.d.ts +19 -0
  238. package/dist/src/orchestrator/turn-watchdog.d.ts +37 -0
  239. package/dist/src/orchestrator/turn-watchdog.js +55 -0
  240. package/dist/src/pil/agent-operating-contract.d.ts +1 -1
  241. package/dist/src/pil/agent-operating-contract.js +1 -1
  242. package/dist/src/pil/cheap-model-playbook.d.ts +1 -1
  243. package/dist/src/pil/cheap-model-playbook.js +5 -1
  244. package/dist/src/pil/cheap-model-workbooks.d.ts +1 -1
  245. package/dist/src/pil/cheap-model-workbooks.js +1 -1
  246. package/dist/src/pil/discovery-types.d.ts +1 -0
  247. package/dist/src/pil/discovery.js +16 -11
  248. package/dist/src/pil/layer1-intent.d.ts +18 -6
  249. package/dist/src/pil/layer1-intent.js +66 -757
  250. package/dist/src/pil/layer15-context-scan.js +15 -1
  251. package/dist/src/pil/layer3-ee-injection.js +23 -8
  252. package/dist/src/pil/layer4-gsd.js +69 -16
  253. package/dist/src/pil/layer5-context.js +7 -3
  254. package/dist/src/pil/layer6-output.d.ts +23 -0
  255. package/dist/src/pil/layer6-output.js +5 -1
  256. package/dist/src/pil/llm-classify.d.ts +33 -2
  257. package/dist/src/pil/llm-classify.js +123 -131
  258. package/dist/src/pil/native-capabilities-workbook.d.ts +1 -1
  259. package/dist/src/pil/native-capabilities-workbook.js +1 -0
  260. package/dist/src/pil/pipeline.js +34 -2
  261. package/dist/src/pil/response-tools.js +5 -3
  262. package/dist/src/pil/schema.d.ts +1 -0
  263. package/dist/src/pil/schema.js +2 -0
  264. package/dist/src/pil/types.d.ts +18 -0
  265. package/dist/src/playbook/directives.d.ts +4 -0
  266. package/dist/src/playbook/directives.js +17 -5
  267. package/dist/src/product-loop/backlog-builder.d.ts +14 -1
  268. package/dist/src/product-loop/backlog-builder.js +30 -6
  269. package/dist/src/product-loop/discovery-context-format.js +3 -1
  270. package/dist/src/product-loop/discovery-ecosystem.js +4 -1
  271. package/dist/src/product-loop/discovery-interview.js +32 -3
  272. package/dist/src/product-loop/discovery-schema.js +5 -1
  273. package/dist/src/product-loop/ideal-trace.d.ts +7 -0
  274. package/dist/src/product-loop/ideal-trace.js +64 -0
  275. package/dist/src/product-loop/index.d.ts +13 -1
  276. package/dist/src/product-loop/index.js +333 -52
  277. package/dist/src/product-loop/loop-driver.d.ts +7 -0
  278. package/dist/src/product-loop/loop-driver.js +310 -99
  279. package/dist/src/product-loop/phase-plan.d.ts +5 -0
  280. package/dist/src/product-loop/phase-plan.js +39 -2
  281. package/dist/src/product-loop/phase-runner.js +9 -1
  282. package/dist/src/product-loop/sprint-runner.d.ts +111 -0
  283. package/dist/src/product-loop/sprint-runner.js +559 -16
  284. package/dist/src/product-loop/types.d.ts +36 -5
  285. package/dist/src/providers/adapter.d.ts +1 -1
  286. package/dist/src/providers/adapter.js +3 -4
  287. package/dist/src/providers/auth/browser-flow.d.ts +1 -1
  288. package/dist/src/providers/auth/browser-flow.js +1 -1
  289. package/dist/src/providers/auth/openai-oauth.js +1 -1
  290. package/dist/src/providers/auth/registry.js +0 -34
  291. package/dist/src/providers/auth/token-store.js +4 -1
  292. package/dist/src/providers/auth/types.d.ts +1 -1
  293. package/dist/src/providers/auth/types.js +1 -1
  294. package/dist/src/providers/capabilities.d.ts +24 -5
  295. package/dist/src/providers/capabilities.js +42 -24
  296. package/dist/src/providers/endpoints.d.ts +2 -2
  297. package/dist/src/providers/endpoints.js +11 -10
  298. package/dist/src/providers/keychain.d.ts +1 -1
  299. package/dist/src/providers/keychain.js +7 -9
  300. package/dist/src/providers/mcp-vision-bridge.js +56 -146
  301. package/dist/src/providers/openai-compatible.js +8 -1
  302. package/dist/src/providers/pricing.d.ts +2 -2
  303. package/dist/src/providers/pricing.js +3 -13
  304. package/dist/src/providers/runtime.d.ts +27 -2
  305. package/dist/src/providers/runtime.js +78 -15
  306. package/dist/src/providers/strategies/base.strategy.d.ts +16 -0
  307. package/dist/src/providers/strategies/base.strategy.js +24 -1
  308. package/dist/src/providers/strategies/{siliconflow.strategy.d.ts → opencode-go.strategy.d.ts} +3 -3
  309. package/dist/src/providers/strategies/opencode-go.strategy.js +83 -0
  310. package/dist/src/providers/strategies/registry.js +4 -4
  311. package/dist/src/providers/strategies/thinking-mode.d.ts +109 -0
  312. package/dist/src/providers/strategies/thinking-mode.js +280 -1
  313. package/dist/src/providers/strategies/zai.strategy.d.ts +14 -0
  314. package/dist/src/providers/strategies/zai.strategy.js +44 -0
  315. package/dist/src/providers/types.d.ts +5 -6
  316. package/dist/src/providers/types.js +2 -2
  317. package/dist/src/providers/vision-backend.d.ts +47 -0
  318. package/dist/src/providers/vision-backend.js +258 -0
  319. package/dist/src/providers/vision-proxy.d.ts +22 -9
  320. package/dist/src/providers/vision-proxy.js +63 -132
  321. package/dist/src/providers/wire-debug.js +95 -0
  322. package/dist/src/router/decide.d.ts +13 -0
  323. package/dist/src/router/decide.js +138 -36
  324. package/dist/src/router/peak-hour.d.ts +38 -0
  325. package/dist/src/router/peak-hour.js +107 -0
  326. package/dist/src/router/step-router.js +3 -2
  327. package/dist/src/router/warm.js +4 -5
  328. package/dist/src/scaffold/continuation-prompt.d.ts +11 -0
  329. package/dist/src/scaffold/continuation-prompt.js +26 -0
  330. package/dist/src/scaffold/point-to-existing.d.ts +21 -0
  331. package/dist/src/scaffold/point-to-existing.js +25 -0
  332. package/dist/src/self-qa/agentic-loop.js +3 -3
  333. package/dist/src/{ui/state → state}/active-run.d.ts +19 -0
  334. package/dist/src/{ui/state → state}/active-run.js +21 -0
  335. package/dist/src/{ui/status-bar/store.d.ts → state/status-bar-store.d.ts} +4 -1
  336. package/dist/src/{ui/status-bar/store.js → state/status-bar-store.js} +12 -9
  337. package/dist/src/state/turn-trace.d.ts +43 -0
  338. package/dist/src/state/turn-trace.js +32 -0
  339. package/dist/src/storage/db.js +2 -1
  340. package/dist/src/storage/index.d.ts +1 -1
  341. package/dist/src/storage/index.js +1 -1
  342. package/dist/src/storage/interaction-log.d.ts +1 -1
  343. package/dist/src/storage/migrations.js +71 -1
  344. package/dist/src/storage/sessions.d.ts +28 -10
  345. package/dist/src/storage/sessions.js +78 -21
  346. package/dist/src/storage/transcript-view.js +1 -1
  347. package/dist/src/storage/transcript.d.ts +51 -0
  348. package/dist/src/storage/transcript.js +284 -13
  349. package/dist/src/tools/file.d.ts +15 -0
  350. package/dist/src/tools/file.js +32 -0
  351. package/dist/src/tools/native-tools.js +5 -0
  352. package/dist/src/tools/registry.d.ts +3 -0
  353. package/dist/src/tools/registry.js +460 -22
  354. package/dist/src/tools/research.d.ts +29 -0
  355. package/dist/src/tools/research.js +233 -0
  356. package/dist/src/types/index.d.ts +118 -3
  357. package/dist/src/ui/app.js +0 -0
  358. package/dist/src/ui/cards/product-status-card.js +1 -1
  359. package/dist/src/ui/components/bubble-body-guard.d.ts +27 -0
  360. package/dist/src/ui/components/bubble-body-guard.js +50 -0
  361. package/dist/src/ui/components/context-rail.d.ts +26 -0
  362. package/dist/src/ui/components/context-rail.js +33 -0
  363. package/dist/src/ui/components/council-conclusion-card.d.ts +73 -0
  364. package/dist/src/ui/components/council-conclusion-card.js +420 -0
  365. package/dist/src/ui/components/council-debate-pill.d.ts +36 -0
  366. package/dist/src/ui/components/council-debate-pill.js +34 -0
  367. package/dist/src/ui/components/council-info-card.js +2 -2
  368. package/dist/src/ui/components/council-leader-bubble.d.ts +10 -3
  369. package/dist/src/ui/components/council-leader-bubble.js +21 -11
  370. package/dist/src/ui/components/council-message-bubble.d.ts +15 -2
  371. package/dist/src/ui/components/council-message-bubble.js +16 -15
  372. package/dist/src/ui/components/council-phase-timeline.d.ts +9 -1
  373. package/dist/src/ui/components/council-phase-timeline.js +49 -15
  374. package/dist/src/ui/components/council-placeholder-bubble.d.ts +16 -9
  375. package/dist/src/ui/components/council-placeholder-bubble.js +32 -29
  376. package/dist/src/ui/components/council-question-card.js +12 -12
  377. package/dist/src/ui/components/council-rail-rounds.d.ts +26 -0
  378. package/dist/src/ui/components/council-rail-rounds.js +57 -0
  379. package/dist/src/ui/components/council-round-group.d.ts +38 -0
  380. package/dist/src/ui/components/council-round-group.js +88 -0
  381. package/dist/src/ui/components/council-status-list.d.ts +3 -1
  382. package/dist/src/ui/components/council-status-list.js +36 -24
  383. package/dist/src/ui/components/council-synthesis-banner.d.ts +7 -2
  384. package/dist/src/ui/components/council-synthesis-banner.js +20 -5
  385. package/dist/src/ui/components/halt-recovery-card.js +9 -5
  386. package/dist/src/ui/components/jump-to-latest-pill.d.ts +11 -0
  387. package/dist/src/ui/components/jump-to-latest-pill.js +14 -0
  388. package/dist/src/ui/components/prompt-box.js +18 -16
  389. package/dist/src/ui/components/session-tree-card.d.ts +14 -0
  390. package/dist/src/ui/components/session-tree-card.js +46 -0
  391. package/dist/src/ui/components/slash-inline-menu.d.ts +12 -0
  392. package/dist/src/ui/components/slash-inline-menu.js +26 -5
  393. package/dist/src/ui/components/task-list-panel.d.ts +14 -1
  394. package/dist/src/ui/components/task-list-panel.js +22 -2
  395. package/dist/src/ui/containers/modals-layer.d.ts +2 -1
  396. package/dist/src/ui/hooks/use-session-picker.d.ts +3 -3
  397. package/dist/src/ui/mcp-modal.js +2 -4
  398. package/dist/src/ui/modals/api-key-modal.js +1 -1
  399. package/dist/src/ui/modals/connect-modal.js +4 -3
  400. package/dist/src/ui/modals/session-picker-modal.d.ts +2 -2
  401. package/dist/src/ui/modals/session-picker-modal.js +3 -5
  402. package/dist/src/ui/picker-providers.d.ts +1 -1
  403. package/dist/src/ui/picker-providers.js +1 -1
  404. package/dist/src/ui/primitives/index.d.ts +1 -0
  405. package/dist/src/ui/primitives/index.js +2 -0
  406. package/dist/src/ui/primitives/semantic-primitives.d.ts +76 -0
  407. package/dist/src/ui/primitives/semantic-primitives.js +81 -0
  408. package/dist/src/ui/slash/compact.js +5 -7
  409. package/dist/src/ui/slash/cost.js +1 -1
  410. package/dist/src/ui/slash/council.js +19 -1
  411. package/dist/src/ui/slash/debug.d.ts +3 -31
  412. package/dist/src/ui/slash/debug.js +9 -20
  413. package/dist/src/ui/slash/ideal.d.ts +6 -2
  414. package/dist/src/ui/slash/ideal.js +97 -7
  415. package/dist/src/ui/slash/menu-items.d.ts +7 -0
  416. package/dist/src/ui/slash/menu-items.js +12 -18
  417. package/dist/src/ui/slash/registry.d.ts +2 -0
  418. package/dist/src/ui/slash/registry.js +4 -0
  419. package/dist/src/ui/status-bar/cache-hit.d.ts +6 -0
  420. package/dist/src/ui/status-bar/cache-hit.js +9 -0
  421. package/dist/src/ui/status-bar/index.d.ts +1 -1
  422. package/dist/src/ui/status-bar/index.js +7 -3
  423. package/dist/src/ui/status-bar/usd-meter.d.ts +5 -4
  424. package/dist/src/ui/status-bar/usd-meter.js +6 -4
  425. package/dist/src/ui/theme.d.ts +1 -0
  426. package/dist/src/ui/theme.js +2 -0
  427. package/dist/src/ui/types.d.ts +7 -0
  428. package/dist/src/ui/use-app-logic.js +0 -0
  429. package/dist/src/ui/utils/format.d.ts +14 -0
  430. package/dist/src/ui/utils/format.js +23 -3
  431. package/dist/src/usage/downgrade.js +2 -2
  432. package/dist/src/usage/product-ledger.js +2 -2
  433. package/dist/src/utils/install-manager.js +2 -1
  434. package/dist/src/utils/logger.js +2 -2
  435. package/dist/src/utils/permission-mode.js +5 -3
  436. package/dist/src/utils/redactor.js +1 -1
  437. package/dist/src/utils/settings.d.ts +153 -5
  438. package/dist/src/utils/settings.js +233 -29
  439. package/dist/src/utils/visible-retry.d.ts +11 -0
  440. package/dist/src/utils/visible-retry.js +10 -1
  441. package/dist/src/verify/entrypoint.d.ts +1 -1
  442. package/dist/src/verify/entrypoint.js +1 -1
  443. package/dist/src/verify/recipes.d.ts +13 -0
  444. package/dist/src/verify/recipes.js +15 -0
  445. package/package.json +135 -132
  446. package/dist/src/providers/auth/gcloud.d.ts +0 -28
  447. package/dist/src/providers/auth/gcloud.js +0 -102
  448. package/dist/src/providers/auth/gemini-oauth.d.ts +0 -82
  449. package/dist/src/providers/auth/gemini-oauth.js +0 -472
  450. package/dist/src/providers/gemini.d.ts +0 -11
  451. package/dist/src/providers/gemini.js +0 -45
  452. package/dist/src/providers/siliconflow-sse-repair.d.ts +0 -58
  453. package/dist/src/providers/siliconflow-sse-repair.js +0 -177
  454. package/dist/src/providers/strategies/google.strategy.d.ts +0 -22
  455. package/dist/src/providers/strategies/google.strategy.js +0 -174
  456. package/dist/src/providers/strategies/siliconflow.strategy.js +0 -29
  457. package/dist/src/ui/containers/chat-feed.d.ts +0 -40
  458. package/dist/src/ui/containers/chat-feed.js +0 -66
@@ -6,7 +6,7 @@
6
6
  * If cap breach detected, downgrade chain overrides classifier output (ROUTE-06).
7
7
  */
8
8
  import { getDefaultEEClient } from "../ee/intercept.js";
9
- import { getModelByTier, getModelInfo, getModelsForProvider } from "../models/registry.js";
9
+ import { getModelInfo, getModelsForProvider } from "../models/registry.js";
10
10
  import { taskTypeToRole } from "../pil/task-tier-map.js";
11
11
  import { detectProviderForModel } from "../providers/runtime.js";
12
12
  import { ALL_PROVIDER_IDS } from "../providers/types.js";
@@ -14,9 +14,10 @@ import { downgradeChain, emitDowngrade, getDowngradeChain } from "../usage/downg
14
14
  import { release, reserve } from "../usage/ledger.js";
15
15
  import { midstreamPolicy } from "../usage/midstream.js";
16
16
  import { CapBreachError } from "../usage/types.js";
17
- import { getRoleModel, isCouncilMultiProviderPreferred, isProviderDisabled } from "../utils/settings.js";
17
+ import { getRoleModel, getRoutingPromoteMax, isCouncilMultiProviderPreferred, isProviderDisabled, } from "../utils/settings.js";
18
18
  import { classify } from "./classifier/index.js";
19
19
  import { callColdRoute } from "./cold.js";
20
+ import { adjustPeakHourModel, getRoutedModelByTier } from "./peak-hour.js";
20
21
  import { isInheritProvider } from "./provider-sentinel.js";
21
22
  import { routerStore } from "./store.js";
22
23
  import { callWarmRoute } from "./warm.js";
@@ -75,6 +76,16 @@ function buildRouteContext(cwd, pil) {
75
76
  ctx.filesTouched = pil.filesTouched;
76
77
  if (pil?.mode)
77
78
  ctx.mode = pil.mode;
79
+ if (pil?.turnIndex !== undefined)
80
+ ctx.turnIndex = pil.turnIndex;
81
+ if (pil?.messageCount !== undefined)
82
+ ctx.messageCount = pil.messageCount;
83
+ if (pil?.compactionCount !== undefined)
84
+ ctx.compactionCount = pil.compactionCount;
85
+ if (pil?.totalSavedTokens !== undefined)
86
+ ctx.totalSavedTokens = pil.totalSavedTokens;
87
+ if (pil?.compactionSummary)
88
+ ctx.compactionSummary = pil.compactionSummary;
78
89
  return ctx;
79
90
  }
80
91
  // ─── Disabled-provider guard: fallback providers ────────────────────────────
@@ -90,7 +101,7 @@ function resolveEffectiveDefaults(opts) {
90
101
  }
91
102
  for (const p of FALLBACK_PROVIDERS) {
92
103
  if (!isProviderDisabled(p)) {
93
- const m = getModelByTier("balanced", p);
104
+ const m = getRoutedModelByTier("balanced", p);
94
105
  // Guard: getModelByTier may return a model from a different provider
95
106
  // when the preferred provider has no model for the requested tier.
96
107
  if (m && m.provider === p)
@@ -114,7 +125,7 @@ function resolveTierModel(tier, defaultProvider) {
114
125
  }
115
126
  for (const p of FALLBACK_PROVIDERS) {
116
127
  if (!isProviderDisabled(p)) {
117
- const m = getModelByTier(tier, p);
128
+ const m = getRoutedModelByTier(tier, p);
118
129
  // Guard: getModelByTier may return a model from a different provider
119
130
  // when the preferred provider has no model for the requested tier.
120
131
  if (m && m.provider === p)
@@ -126,6 +137,17 @@ function resolveTierModel(tier, defaultProvider) {
126
137
  }
127
138
  return undefined;
128
139
  }
140
+ function applyPeakHourRoute(dec) {
141
+ const adj = adjustPeakHourModel(dec.model);
142
+ if (!adj.adjusted)
143
+ return dec;
144
+ return {
145
+ ...dec,
146
+ model: adj.modelId,
147
+ provider: adj.provider,
148
+ reason: `${dec.reason}|${adj.reason}`,
149
+ };
150
+ }
129
151
  // ─── Provider constraint: never route to a provider the user lacks a key for ─
130
152
  function constrainToProvider(decision, opts) {
131
153
  // Inherit-sentinel: warm path may emit an empty provider to signal
@@ -134,11 +156,11 @@ function constrainToProvider(decision, opts) {
134
156
  if (isInheritProvider(decision.provider)) {
135
157
  const resolved = detectProviderForModel(decision.model);
136
158
  if (resolved && !isProviderDisabled(resolved))
137
- return decision;
159
+ return applyPeakHourRoute(decision);
138
160
  if (resolved && isProviderDisabled(resolved)) {
139
161
  return constrainToProvider({ ...decision, provider: resolved }, opts);
140
162
  }
141
- return decision;
163
+ return applyPeakHourRoute(decision);
142
164
  }
143
165
  // Provider-only UX: the user enables N providers and pins one as default.
144
166
  // Routing rules:
@@ -150,7 +172,7 @@ function constrainToProvider(decision, opts) {
150
172
  // surface a config error elsewhere).
151
173
  const decisionDisabled = isProviderDisabled(decision.provider);
152
174
  if (!decisionDisabled) {
153
- return decision;
175
+ return applyPeakHourRoute(decision);
154
176
  }
155
177
  if (isProviderDisabled(opts.defaultProvider)) {
156
178
  return {
@@ -158,7 +180,7 @@ function constrainToProvider(decision, opts) {
158
180
  reason: `${decision.reason}|provider-not-constrained(default-also-disabled)`,
159
181
  };
160
182
  }
161
- const sameProviderModel = getModelByTier(decision.tier === "hot" ? "fast" : decision.tier === "cold" ? "premium" : "balanced", opts.defaultProvider);
183
+ const sameProviderModel = getRoutedModelByTier(decision.tier === "hot" ? "fast" : decision.tier === "cold" ? "premium" : "balanced", opts.defaultProvider);
162
184
  if (sameProviderModel && sameProviderModel.provider !== opts.defaultProvider) {
163
185
  return {
164
186
  ...decision,
@@ -167,12 +189,12 @@ function constrainToProvider(decision, opts) {
167
189
  reason: `${decision.reason}|provider-constrained(forced-default)`,
168
190
  };
169
191
  }
170
- return {
192
+ return applyPeakHourRoute({
171
193
  ...decision,
172
194
  model: sameProviderModel?.id ?? opts.defaultModel,
173
195
  provider: sameProviderModel?.provider ?? opts.defaultProvider,
174
196
  reason: `${decision.reason}|provider-constrained`,
175
- };
197
+ });
176
198
  }
177
199
  // ─── Route feedback (HTTP path) ─────────────────────────────────────────────
178
200
  /**
@@ -194,12 +216,78 @@ export function reportRouteOutcome(taskHash, outcome, duration) {
194
216
  duration: duration ?? null,
195
217
  });
196
218
  }
219
+ // ─── Tier-promotion cap (cost-ceiling guard) ───────────────────────────────
220
+ const TIER_RANK = {
221
+ fast: 0,
222
+ balanced: 1,
223
+ premium: 2,
224
+ };
225
+ /**
226
+ * Enforce the user's tier-promotion ceiling. The session default model is the
227
+ * cost ceiling: the router may downgrade per turn but may not silently promote
228
+ * beyond `routingPromoteMax` (default "balanced"). See settings.ts.
229
+ *
230
+ * When the decision would promote, clamp down to the max allowed tier on the
231
+ * SAME provider. If no same-provider model exists at the ceiling tier, fall
232
+ * back to the session default model (the user's explicit pick). The clamp is
233
+ * skipped for: the `"any"` opt-in, the role path (explicit user roleModels
234
+ * config is itself the opt-in), cap-halt decisions, and provider-constrained
235
+ * disabled-provider recoveries (those already move toward the default).
236
+ */
237
+ function applyPromotionCap(dec, defaultModel) {
238
+ if (dec.model === "HALT" || dec.model === defaultModel)
239
+ return dec;
240
+ const cap = getRoutingPromoteMax();
241
+ if (cap === "any")
242
+ return dec;
243
+ const capRank = cap === "off" ? null : TIER_RANK[cap]; // "balanced" → 1
244
+ const defaultTier = getModelInfo(defaultModel)?.tier;
245
+ // "off" means ceiling = the default model's own tier.
246
+ const maxAllowedRank = capRank === null ? (defaultTier ? TIER_RANK[defaultTier] : 0) : capRank;
247
+ const decInfo = getModelInfo(dec.model);
248
+ const decTier = decInfo?.tier;
249
+ if (!decTier || TIER_RANK[decTier] <= maxAllowedRank)
250
+ return dec;
251
+ // Promotion exceeds the ceiling — clamp DOWN to the max allowed tier on the
252
+ // same provider. Walk down from the ceiling so we pick the highest permitted.
253
+ const provider = decInfo?.provider ?? detectProviderForModel(dec.model);
254
+ const targetTiers = capRank === null
255
+ ? defaultTier
256
+ ? [defaultTier]
257
+ : ["fast"]
258
+ : maxAllowedRank >= 1
259
+ ? ["balanced", "fast"]
260
+ : ["fast"];
261
+ for (const t of targetTiers) {
262
+ if (TIER_RANK[t] > maxAllowedRank)
263
+ continue;
264
+ const m = getRoutedModelByTier(t, provider);
265
+ if (m && m.provider === provider) {
266
+ return {
267
+ ...dec,
268
+ model: m.id,
269
+ reason: `${dec.reason} | promo-cap(${decTier}→${t})`,
270
+ };
271
+ }
272
+ }
273
+ // No cheaper model on the same provider — fall back to the session default.
274
+ return {
275
+ ...dec,
276
+ model: defaultModel,
277
+ reason: `${dec.reason} | promo-cap(${decTier}→default:${defaultModel})`,
278
+ };
279
+ }
197
280
  /**
198
281
  * Apply cap-check to a RouteDecision. Walks the downgrade chain if
199
282
  * the reservation would breach the cap. Returns the (possibly downgraded) decision.
283
+ *
284
+ * `opts` carries both the ledger homeOverride and the session defaultModel used
285
+ * by the promotion cap. `exempt` skips the promotion cap — used only for the
286
+ * role path, where the user's explicit roleModels config is itself the opt-in.
200
287
  */
201
- async function capCheck(dec, homeOverride) {
202
- let current = { ...dec };
288
+ async function capCheck(dec, opts, exempt) {
289
+ let current = exempt ? { ...dec } : applyPromotionCap({ ...dec }, opts.defaultModel);
290
+ const homeOverride = opts.homeOverride;
203
291
  let attempts = 0;
204
292
  while (attempts++ < getDowngradeChain().length) {
205
293
  // If midstream policy already refuses, halt immediately
@@ -305,14 +393,15 @@ export async function decide(prompt, opts) {
305
393
  providerDisabled: isProviderDisabled(provider),
306
394
  multiProviderPreferred: isCouncilMultiProviderPreferred(),
307
395
  })) {
396
+ const peak = adjustPeakHourModel(roleModelId);
308
397
  const d = {
309
398
  tier: "hot",
310
- model: roleModelId,
311
- provider,
312
- reason: `role:${role}→${roleModelId}`,
399
+ model: peak.modelId,
400
+ provider: peak.provider,
401
+ reason: peak.adjusted ? `role:${role}→${peak.modelId}|${peak.reason}` : `role:${role}→${roleModelId}`,
313
402
  source: "role",
314
403
  };
315
- const checked = await capCheck(d, opts.homeOverride);
404
+ const checked = await capCheck(d, opts, /* exempt */ true);
316
405
  routerStore.setState({ tier: checked.tier, lastDecision: checked, taskHash: null, source: "role" });
317
406
  if (cacheKey && !checked.cap_overridden)
318
407
  setCachedRoute(cacheKey, checked);
@@ -328,7 +417,7 @@ export async function decide(prompt, opts) {
328
417
  if (pilTier && pilConf >= 0.6) {
329
418
  // Use effective (non-disabled) provider when default is disabled
330
419
  const effective = resolveTierModel(pilTier, opts.defaultProvider);
331
- let tierModel = effective ?? getModelByTier(pilTier, opts.defaultProvider);
420
+ let tierModel = effective ?? getRoutedModelByTier(pilTier, opts.defaultProvider);
332
421
  // Guard: getModelByTier may cross to another provider when defaultProvider
333
422
  // has no model for the requested tier. If that cross-provider is disabled,
334
423
  // pin to the default model on the default provider instead.
@@ -338,17 +427,21 @@ export async function decide(prompt, opts) {
338
427
  isProviderDisabled(tierModel.provider)) {
339
428
  tierModel = undefined;
340
429
  }
430
+ const pilModel = tierModel?.id ?? opts.defaultModel;
431
+ const peak = adjustPeakHourModel(pilModel);
341
432
  const d = {
342
433
  tier: "hot",
343
- model: tierModel?.id ?? opts.defaultModel,
344
- provider: tierModel?.provider ?? opts.defaultProvider,
345
- reason: effective
346
- ? `pil:${pilTier}(${pilConf.toFixed(2)})-rerouted(disabled-default)`
347
- : `pil:${pilTier}(${pilConf.toFixed(2)})`,
434
+ model: peak.modelId,
435
+ provider: tierModel?.provider ?? peak.provider,
436
+ reason: peak.adjusted
437
+ ? `${effective ? `pil:${pilTier}(${pilConf.toFixed(2)})-rerouted(disabled-default)` : `pil:${pilTier}(${pilConf.toFixed(2)})`}|${peak.reason}`
438
+ : effective
439
+ ? `pil:${pilTier}(${pilConf.toFixed(2)})-rerouted(disabled-default)`
440
+ : `pil:${pilTier}(${pilConf.toFixed(2)})`,
348
441
  confidence: pilConf,
349
442
  source: "pil",
350
443
  };
351
- const checked = await capCheck(d, opts.homeOverride);
444
+ const checked = await capCheck(d, opts);
352
445
  routerStore.setState({
353
446
  tier: checked.tier,
354
447
  lastDecision: checked,
@@ -364,7 +457,7 @@ export async function decide(prompt, opts) {
364
457
  if (c.tier === "hot") {
365
458
  // Use effective (non-disabled) provider when default is disabled
366
459
  const effective = c.tierHint ? resolveTierModel(c.tierHint, opts.defaultProvider) : undefined;
367
- let tierModel = effective ?? (c.tierHint ? getModelByTier(c.tierHint, opts.defaultProvider) : undefined);
460
+ let tierModel = effective ?? (c.tierHint ? getRoutedModelByTier(c.tierHint, opts.defaultProvider) : undefined);
368
461
  // Same guard as the PIL branch above: drop cross-provider fallback when
369
462
  // the cross-provider is disabled, so we don't switch to a provider the
370
463
  // user has turned off in the splash modal.
@@ -374,14 +467,20 @@ export async function decide(prompt, opts) {
374
467
  isProviderDisabled(tierModel.provider)) {
375
468
  tierModel = undefined;
376
469
  }
470
+ const hotModel = tierModel?.id ?? opts.defaultModel;
471
+ const peak = adjustPeakHourModel(hotModel);
377
472
  const d = {
378
473
  tier: "hot",
379
- model: tierModel?.id ?? opts.defaultModel,
380
- provider: tierModel?.provider ?? opts.defaultProvider,
381
- reason: effective ? `${c.reason}-rerouted(disabled-default)` : c.reason,
474
+ model: peak.modelId,
475
+ provider: tierModel?.provider ?? peak.provider,
476
+ reason: peak.adjusted
477
+ ? `${effective ? `${c.reason}-rerouted(disabled-default)` : c.reason}|${peak.reason}`
478
+ : effective
479
+ ? `${c.reason}-rerouted(disabled-default)`
480
+ : c.reason,
382
481
  confidence: c.confidence,
383
482
  };
384
- const checked = await capCheck(d, opts.homeOverride);
483
+ const checked = await capCheck(d, opts);
385
484
  routerStore.setState({
386
485
  tier: checked.tier,
387
486
  lastDecision: checked,
@@ -395,7 +494,7 @@ export async function decide(prompt, opts) {
395
494
  // Step 2: Warm path (EE /api/route-model, 250ms timeout)
396
495
  const w = await callWarmRoute(prompt, { ...opts, context: routeCtx });
397
496
  if (w) {
398
- const checked = await capCheck(constrainToProvider(w, opts), opts.homeOverride);
497
+ const checked = await capCheck(constrainToProvider(w, opts), opts);
399
498
  routerStore.setState({
400
499
  tier: checked.tier,
401
500
  lastDecision: checked,
@@ -409,7 +508,7 @@ export async function decide(prompt, opts) {
409
508
  // Step 3: Cold path (EE /api/cold-route, 1s timeout)
410
509
  const cd = await callColdRoute(prompt, { ...opts, context: routeCtx });
411
510
  if (cd) {
412
- const checked = await capCheck(constrainToProvider(cd, opts), opts.homeOverride);
511
+ const checked = await capCheck(constrainToProvider(cd, opts), opts);
413
512
  routerStore.setState({
414
513
  tier: "cold",
415
514
  lastDecision: checked,
@@ -422,15 +521,18 @@ export async function decide(prompt, opts) {
422
521
  }
423
522
  // Step 4: Final fallback when EE entirely unreachable
424
523
  const effective = resolveEffectiveDefaults(opts);
524
+ const peak = adjustPeakHourModel(effective.model);
425
525
  const fallback = {
426
526
  tier: routerStore.getState().degraded ? "degraded" : "hot",
427
- model: effective.model,
428
- provider: effective.provider,
429
- reason: effective.provider !== opts.defaultProvider
430
- ? "fallback:ee-unreachable+rerouted(disabled-default)"
431
- : "fallback:ee-unreachable",
527
+ model: peak.modelId,
528
+ provider: peak.provider,
529
+ reason: peak.adjusted
530
+ ? `${effective.provider !== opts.defaultProvider ? "fallback:ee-unreachable+rerouted(disabled-default)" : "fallback:ee-unreachable"}|${peak.reason}`
531
+ : effective.provider !== opts.defaultProvider
532
+ ? "fallback:ee-unreachable+rerouted(disabled-default)"
533
+ : "fallback:ee-unreachable",
432
534
  };
433
- const checked = await capCheck(fallback, opts.homeOverride);
535
+ const checked = await capCheck(fallback, opts);
434
536
  routerStore.setState({
435
537
  lastDecision: checked,
436
538
  taskHash: null,
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Peak-hour routing — rules loaded from catalog API `provider_policies.peak_hour`
3
+ * (vendor-sourced metadata). User settings only toggle enabled + switch/downgrade mode.
4
+ */
5
+ import type { CatalogProviderPeakHour } from "../models/catalog-client.js";
6
+ import type { ProviderId } from "../providers/types.js";
7
+ import type { ModelInfo } from "../types/index.js";
8
+ import { type PeakHourPolicy } from "../utils/settings.js";
9
+ export type PeakHourMode = "downgrade" | "switch";
10
+ export interface PeakHourAdjustment {
11
+ modelId: string;
12
+ provider: ProviderId;
13
+ adjusted: boolean;
14
+ reason?: string;
15
+ }
16
+ export declare function hourInTimezone(now: Date, timezone: string): number;
17
+ /** @deprecated Use hourInTimezone — kept for tests referencing UTC+8. */
18
+ export declare function hourUtc8(now: Date): number;
19
+ export declare function peakHourWindows(rule: CatalogProviderPeakHour): Array<{
20
+ start_hour: number;
21
+ end_hour: number;
22
+ }>;
23
+ export declare function isPeakHourForProvider(provider: ProviderId, now: Date, userPolicy?: PeakHourPolicy): boolean;
24
+ /** @deprecated Use isPeakHourForProvider(provider, ...) — window is per-provider from catalog. */
25
+ export declare function isPeakHourUtc8(now: Date, userPolicy: PeakHourPolicy): boolean;
26
+ /**
27
+ * Adjust a concrete model id for peak-hour policy. No-op outside the provider's
28
+ * catalog-defined window or when user policy is disabled.
29
+ */
30
+ export declare function adjustPeakHourModel(modelId: string, opts?: {
31
+ now?: Date;
32
+ policy?: PeakHourPolicy;
33
+ }): PeakHourAdjustment;
34
+ /** Tier lookup with peak-hour adjustment applied to the resolved model. */
35
+ export declare function getRoutedModelByTier(tier: "fast" | "balanced" | "premium", preferProvider?: string, opts?: {
36
+ now?: Date;
37
+ policy?: PeakHourPolicy;
38
+ }): ModelInfo | undefined;
@@ -0,0 +1,107 @@
1
+ import { getModelByTier, getModelInfo, getProviderPeakHourRule, SWITCH_PROVIDER_ORDER } from "../models/registry.js";
2
+ import { detectProviderForModel } from "../providers/runtime.js";
3
+ import { getPeakHourPolicy, isProviderDisabled } from "../utils/settings.js";
4
+ export function hourInTimezone(now, timezone) {
5
+ const parts = new Intl.DateTimeFormat("en-US", {
6
+ timeZone: timezone,
7
+ hour: "numeric",
8
+ hour12: false,
9
+ }).formatToParts(now);
10
+ const hour = parts.find((p) => p.type === "hour")?.value;
11
+ return hour ? Number(hour) : 0;
12
+ }
13
+ /** @deprecated Use hourInTimezone — kept for tests referencing UTC+8. */
14
+ export function hourUtc8(now) {
15
+ return hourInTimezone(now, "Asia/Shanghai");
16
+ }
17
+ export function peakHourWindows(rule) {
18
+ if (rule.windows?.length)
19
+ return rule.windows;
20
+ if (rule.start_hour != null && rule.end_hour != null) {
21
+ return [{ start_hour: rule.start_hour, end_hour: rule.end_hour }];
22
+ }
23
+ return [];
24
+ }
25
+ export function isPeakHourForProvider(provider, now, userPolicy = getPeakHourPolicy()) {
26
+ if (userPolicy.enabled === false)
27
+ return false;
28
+ const rule = getProviderPeakHourRule(provider);
29
+ if (!rule)
30
+ return false;
31
+ const h = hourInTimezone(now, rule.timezone);
32
+ return peakHourWindows(rule).some((w) => h >= w.start_hour && h < w.end_hour);
33
+ }
34
+ /** @deprecated Use isPeakHourForProvider(provider, ...) — window is per-provider from catalog. */
35
+ export function isPeakHourUtc8(now, userPolicy) {
36
+ return isPeakHourForProvider("zai", now, userPolicy) || isPeakHourForProvider("deepseek", now, userPolicy);
37
+ }
38
+ function sameProviderDowngrade(modelId, provider) {
39
+ const rule = getProviderPeakHourRule(provider);
40
+ if (!rule)
41
+ return null;
42
+ if (!rule.sensitive_model_ids.includes(modelId))
43
+ return null;
44
+ return rule.fallback_model_id;
45
+ }
46
+ function pickSwitchFallback(excludeProvider) {
47
+ const rule = getProviderPeakHourRule(excludeProvider);
48
+ const order = rule?.switch_fallback_providers ?? SWITCH_PROVIDER_ORDER;
49
+ for (const p of order) {
50
+ if (p === excludeProvider)
51
+ continue;
52
+ if (isProviderDisabled(p))
53
+ continue;
54
+ const m = getModelByTier("fast", p) ?? getModelByTier("balanced", p);
55
+ if (m && m.provider === p)
56
+ return m;
57
+ }
58
+ return undefined;
59
+ }
60
+ /**
61
+ * Adjust a concrete model id for peak-hour policy. No-op outside the provider's
62
+ * catalog-defined window or when user policy is disabled.
63
+ */
64
+ export function adjustPeakHourModel(modelId, opts) {
65
+ const userPolicy = opts?.policy ?? getPeakHourPolicy();
66
+ const now = opts?.now ?? new Date();
67
+ const provider = detectProviderForModel(modelId);
68
+ if (!isPeakHourForProvider(provider, now, userPolicy)) {
69
+ return { modelId, provider, adjusted: false };
70
+ }
71
+ const downgraded = sameProviderDowngrade(modelId, provider);
72
+ if (downgraded) {
73
+ if (userPolicy.mode === "switch") {
74
+ const alt = pickSwitchFallback(provider);
75
+ if (alt && alt.id !== modelId) {
76
+ return {
77
+ modelId: alt.id,
78
+ provider: alt.provider,
79
+ adjusted: true,
80
+ reason: `peak-hour(${provider}→${alt.provider}:${alt.id})`,
81
+ };
82
+ }
83
+ }
84
+ const info = getModelInfo(downgraded);
85
+ if (info?.provider === provider) {
86
+ return {
87
+ modelId: downgraded,
88
+ provider,
89
+ adjusted: true,
90
+ reason: `peak-hour(${modelId}→${downgraded})`,
91
+ };
92
+ }
93
+ }
94
+ return { modelId, provider, adjusted: false };
95
+ }
96
+ /** Tier lookup with peak-hour adjustment applied to the resolved model. */
97
+ export function getRoutedModelByTier(tier, preferProvider, opts) {
98
+ const base = getModelByTier(tier, preferProvider);
99
+ if (!base)
100
+ return undefined;
101
+ const adj = adjustPeakHourModel(base.id, opts);
102
+ if (!adj.adjusted)
103
+ return base;
104
+ const info = getModelInfo(adj.modelId);
105
+ return info ?? base;
106
+ }
107
+ //# sourceMappingURL=peak-hour.js.map
@@ -28,6 +28,7 @@ import { getModelByTier } from "../models/registry.js";
28
28
  import { detectProviderForModel } from "../providers/runtime.js";
29
29
  import { readTimeoutEnv } from "../utils/ee-logger.js";
30
30
  import { isModelDisabled, isProviderDisabled, loadUserSettings } from "../utils/settings.js";
31
+ import { getRoutedModelByTier } from "./peak-hour.js";
31
32
  // ─── EE-guided SAMR override ─────────────────────────────────────────────────
32
33
  // EE_SAMR_TIMEOUT: per-call budget for consulting the EE brain about SAMR.
33
34
  // Default 2s, range 500ms..5s. Falls back to heuristics on timeout/error.
@@ -187,7 +188,7 @@ export function decideStepRouting(phase1ModelId, defaultProvider, config) {
187
188
  // ─── Internal helpers ────────────────────────────────────────────────────────
188
189
  function resolveExecutionModel(provider, tier, excludeModelId) {
189
190
  // Try same provider first
190
- const sameProvider = getModelByTier(tier, provider);
191
+ const sameProvider = getRoutedModelByTier(tier, provider);
191
192
  if (sameProvider && sameProvider.id !== excludeModelId) {
192
193
  // Guard: check provider-disabled and model-disabled (mirrors cross-provider branch)
193
194
  if (!isProviderDisabled(sameProvider.provider) && !isModelDisabled(sameProvider.id)) {
@@ -195,7 +196,7 @@ function resolveExecutionModel(provider, tier, excludeModelId) {
195
196
  }
196
197
  }
197
198
  // If no same-provider model in this tier, try any provider
198
- const anyProvider = getModelByTier(tier);
199
+ const anyProvider = getRoutedModelByTier(tier);
199
200
  if (anyProvider && anyProvider.id !== excludeModelId) {
200
201
  // Only use cross-provider if the provider and model aren't disabled
201
202
  if (!isProviderDisabled(anyProvider.provider) && !isModelDisabled(anyProvider.id)) {
@@ -6,13 +6,13 @@
6
6
  */
7
7
  import { routeModel as bridgeRouteModel } from "../ee/bridge.js";
8
8
  import { getDefaultEEClient } from "../ee/intercept.js";
9
- import { getModelByTier } from "../models/registry.js";
9
+ import { getRoutedModelByTier } from "./peak-hour.js";
10
10
  import { PROVIDER_INHERIT } from "./provider-sentinel.js";
11
11
  /**
12
12
  * Map the session's provider to the EE runtime whose tier ladder the EE serves.
13
13
  * EE keys its `getModelTiers()` ladders by agent runtime, not provider, and only
14
- * the catalog-backed runtimes (claude/codex/gemini) resolve to a non-null model.
15
- * Providers without an EE runtime (deepseek/siliconflow/xai) map to "" — EE then
14
+ * the catalog-backed runtimes (claude/codex) resolve to a non-null model.
15
+ * Providers without an EE runtime (deepseek/xai) map to "" — EE then
16
16
  * returns the routing TIER with a null model and we resolve the concrete model
17
17
  * from the shared catalog for the session provider. This removes the old
18
18
  * hardcoded `runtime:"claude"`, which leaked EE's stale `claude-*` ids into the
@@ -21,7 +21,6 @@ import { PROVIDER_INHERIT } from "./provider-sentinel.js";
21
21
  const PROVIDER_TO_EE_RUNTIME = Object.freeze({
22
22
  anthropic: "claude",
23
23
  openai: "codex",
24
- google: "gemini",
25
24
  });
26
25
  function eeRuntimeForProvider(provider) {
27
26
  return (provider && PROVIDER_TO_EE_RUNTIME[provider]) || "";
@@ -36,7 +35,7 @@ export async function callWarmRoute(prompt, opts) {
36
35
  // EE returns a model only for catalog-backed runtimes; otherwise resolve the
37
36
  // concrete model from the catalog for the session provider + decided tier.
38
37
  const catalogTier = TIER_TO_CATALOG_TIER[bridgeResult.tier] ?? "balanced";
39
- const model = bridgeResult.model || getModelByTier(catalogTier, opts.defaultProvider)?.id;
38
+ const model = bridgeResult.model || getRoutedModelByTier(catalogTier, opts.defaultProvider)?.id;
40
39
  if (model) {
41
40
  return {
42
41
  tier: bridgeResult.tier === "fast" ? "hot" : bridgeResult.tier === "premium" ? "cold" : "warm",
@@ -10,3 +10,14 @@ export interface ContinuationPromptInput {
10
10
  installedPackages?: readonly string[];
11
11
  }
12
12
  export declare function buildIdealContinuationPrompt(input: ContinuationPromptInput): string;
13
+ export interface AdoptExistingPromptInput {
14
+ originalPrompt: string;
15
+ projectDir: string;
16
+ }
17
+ /**
18
+ * Build the continuation prompt fed to the LLM after the user adopts an EXISTING
19
+ * project via the CB-3 "Point to existing" recovery option. Unlike the init_new
20
+ * variant this makes no template/BB assumptions — the target is an arbitrary
21
+ * repo the user already has, with a verify recipe already detected there.
22
+ */
23
+ export declare function buildAdoptExistingContinuationPrompt(input: AdoptExistingPromptInput): string;
@@ -63,4 +63,30 @@ Constraints:
63
63
  - Don't ask "do you want me to continue?" — proceed end-to-end.
64
64
  - If you hit a blocker that genuinely needs the user, state EXACTLY which decision is blocking and stop.`;
65
65
  }
66
+ /**
67
+ * Build the continuation prompt fed to the LLM after the user adopts an EXISTING
68
+ * project via the CB-3 "Point to existing" recovery option. Unlike the init_new
69
+ * variant this makes no template/BB assumptions — the target is an arbitrary
70
+ * repo the user already has, with a verify recipe already detected there.
71
+ */
72
+ export function buildAdoptExistingContinuationPrompt(input) {
73
+ const { originalPrompt, projectDir } = input;
74
+ return `You are continuing a /ideal product loop in an EXISTING project the user pointed to at ${projectDir}. A verify recipe was detected there, so the project already has a runnable test/build setup — adopt it, do NOT re-scaffold.
75
+
76
+ User's original request:
77
+ "${originalPrompt}"
78
+
79
+ Implement this now against the existing codebase. Do NOT ask the user to restate the request.
80
+
81
+ 1. Discover structure (1-2 tool calls max): list the project directory, then read the manifest that exists (package.json / pyproject.toml / go.mod / Cargo.toml / *.csproj) and any README/AGENTS.md to learn the stack, entry point, and conventions. Do NOT assume files — skip whatever is absent.
82
+ 2. Design briefly (5-10 lines): the entities/modules/endpoints to add, matching the conventions you observed (naming, error-handling, DI, folder layout).
83
+ 3. Implement: create/edit files in the EXISTING layout; mirror the surrounding patterns. Never hardcode secrets or URLs — read config from the project's existing mechanism.
84
+ 4. Verify: run the project's OWN detected test/build command; it must pass. Report the exact command + an output excerpt as evidence.
85
+ 5. Summarize in 3-5 bullets: what was added, where, and how to run/test it.
86
+
87
+ Constraints:
88
+ - Your working directory is already ${projectDir}; do NOT cd into it and do NOT re-scaffold.
89
+ - Don't ask "do you want me to continue?" — proceed end-to-end.
90
+ - If a genuine blocker needs the user (missing API key, ambiguous requirement), state EXACTLY which decision is blocking and stop.`;
91
+ }
66
92
  //# sourceMappingURL=continuation-prompt.js.map
@@ -5,6 +5,7 @@
5
5
  * Designed for testability: callers inject detectVerifyRecipe so unit tests
6
6
  * never hit the real orchestrator.
7
7
  */
8
+ import type { VerifyRecipe } from "../types/index.js";
8
9
  import { detectBBFramework } from "./init-new.js";
9
10
  export interface PointToExistingOptions {
10
11
  /** Raw path string entered by the user. May be relative or absolute. */
@@ -26,6 +27,26 @@ export interface PointToExistingResult {
26
27
  targetFramework?: "muonroi-building-block" | string;
27
28
  }
28
29
  export { detectBBFramework };
30
+ /**
31
+ * Deterministic, filesystem-only verify-recipe detection for an existing project
32
+ * the user pointed to via the CB-3 recovery card.
33
+ *
34
+ * WHY NOT the orchestrator's detectVerifyRecipe: that path runs an LLM
35
+ * verify-detect sub-agent against the orchestrator's OWN (session) cwd via
36
+ * `this.bash.getCwd()` — it cannot inspect an arbitrary pointed-to directory, so
37
+ * wiring it here was deferred and the handler stubbed to `null` (point-to-existing
38
+ * therefore ALWAYS reported "no recipe", making the recovery option inert).
39
+ * `inferVerifyProjectProfile` reads the target dir's manifest/build files
40
+ * directly (package.json test/check/lint, Makefile, pyproject, go.mod, cargo,
41
+ * maven/gradle, *.csproj) — the correct detector for adopting an existing repo.
42
+ *
43
+ * Returns the recipe only when it is actually RUNNABLE (has a test/build/start
44
+ * command). The bare "unknown" fallback that `inferVerifyProjectProfile` emits
45
+ * for a directory with no recognizable project files carries empty commands →
46
+ * we return null so point-to-existing correctly reports `no_recipe` instead of
47
+ * falsely adopting an empty directory.
48
+ */
49
+ export declare function detectExistingProjectRecipe(cwd: string): VerifyRecipe | null;
29
50
  /**
30
51
  * Resolve and validate a user-provided path for sprint re-entry.
31
52
  *
@@ -7,8 +7,33 @@
7
7
  */
8
8
  import { realpathSync, statSync } from "node:fs";
9
9
  import * as path from "node:path";
10
+ import { inferVerifyProjectProfile } from "../verify/recipes.js";
10
11
  import { detectBBFramework } from "./init-new.js";
11
12
  export { detectBBFramework };
13
+ /**
14
+ * Deterministic, filesystem-only verify-recipe detection for an existing project
15
+ * the user pointed to via the CB-3 recovery card.
16
+ *
17
+ * WHY NOT the orchestrator's detectVerifyRecipe: that path runs an LLM
18
+ * verify-detect sub-agent against the orchestrator's OWN (session) cwd via
19
+ * `this.bash.getCwd()` — it cannot inspect an arbitrary pointed-to directory, so
20
+ * wiring it here was deferred and the handler stubbed to `null` (point-to-existing
21
+ * therefore ALWAYS reported "no recipe", making the recovery option inert).
22
+ * `inferVerifyProjectProfile` reads the target dir's manifest/build files
23
+ * directly (package.json test/check/lint, Makefile, pyproject, go.mod, cargo,
24
+ * maven/gradle, *.csproj) — the correct detector for adopting an existing repo.
25
+ *
26
+ * Returns the recipe only when it is actually RUNNABLE (has a test/build/start
27
+ * command). The bare "unknown" fallback that `inferVerifyProjectProfile` emits
28
+ * for a directory with no recognizable project files carries empty commands →
29
+ * we return null so point-to-existing correctly reports `no_recipe` instead of
30
+ * falsely adopting an empty directory.
31
+ */
32
+ export function detectExistingProjectRecipe(cwd) {
33
+ const { recipe } = inferVerifyProjectProfile(cwd);
34
+ const runnable = recipe.testCommands.length > 0 || recipe.buildCommands.length > 0 || Boolean(recipe.startCommand);
35
+ return runnable ? recipe : null;
36
+ }
12
37
  // ---------------------------------------------------------------------------
13
38
  // Implementation
14
39
  // ---------------------------------------------------------------------------