muonroi-cli 1.8.4 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (562) hide show
  1. package/LICENSE +17 -5
  2. package/dist/packages/agent-harness-core/src/driver.d.ts +27 -1
  3. package/dist/packages/agent-harness-core/src/driver.js +46 -0
  4. package/dist/packages/agent-harness-core/src/event-filter.js +11 -0
  5. package/dist/packages/agent-harness-core/src/event-redact.js +7 -0
  6. package/dist/packages/agent-harness-core/src/event-tee.d.ts +64 -0
  7. package/dist/packages/agent-harness-core/src/event-tee.js +104 -0
  8. package/dist/packages/agent-harness-core/src/mcp-server.d.ts +25 -0
  9. package/dist/packages/agent-harness-core/src/mcp-server.js +169 -21
  10. package/dist/packages/agent-harness-core/src/predicate.d.ts +1 -1
  11. package/dist/packages/agent-harness-core/src/protocol.d.ts +90 -4
  12. package/dist/packages/agent-harness-core/src/protocol.js +15 -0
  13. package/dist/packages/agent-harness-core/src/visual-quality.d.ts +58 -0
  14. package/dist/packages/agent-harness-core/src/visual-quality.js +141 -0
  15. package/dist/packages/agent-harness-opentui/src/agent-mode.d.ts +6 -0
  16. package/dist/packages/agent-harness-opentui/src/agent-mode.js +14 -1
  17. package/dist/packages/agent-harness-opentui/src/input-bridge.d.ts +2 -10
  18. package/dist/packages/agent-harness-opentui/src/input-bridge.js +103 -16
  19. package/dist/packages/agent-harness-opentui/src/install.d.ts +8 -0
  20. package/dist/packages/agent-harness-opentui/src/install.js +10 -0
  21. package/dist/packages/agent-harness-opentui/src/semantic.js +12 -10
  22. package/dist/packages/agent-harness-opentui/src/visual-capture.d.ts +56 -0
  23. package/dist/packages/agent-harness-opentui/src/visual-capture.js +103 -0
  24. package/dist/src/agent-harness/mock-model.d.ts +38 -0
  25. package/dist/src/agent-harness/mock-model.js +69 -3
  26. package/dist/src/agent-harness/test-spawn.js +31 -0
  27. package/dist/src/chat/chat-keychain.d.ts +7 -12
  28. package/dist/src/chat/chat-keychain.js +19 -86
  29. package/dist/src/cli/config/screen-providers.js +1 -1
  30. package/dist/src/cli/cost-forensics.d.ts +10 -0
  31. package/dist/src/cli/cost-forensics.js +18 -3
  32. package/dist/src/cli/keys-bundle.d.ts +1 -1
  33. package/dist/src/cli/keys-bundle.js +1 -1
  34. package/dist/src/cli/keys.d.ts +10 -47
  35. package/dist/src/cli/keys.js +31 -399
  36. package/dist/src/council/clarifier.d.ts +31 -3
  37. package/dist/src/council/clarifier.js +220 -32
  38. package/dist/src/council/context.js +49 -15
  39. package/dist/src/council/debate-checkpoint.d.ts +129 -0
  40. package/dist/src/council/debate-checkpoint.js +176 -0
  41. package/dist/src/council/debate-planner.js +54 -5
  42. package/dist/src/council/debate-summary.d.ts +25 -0
  43. package/dist/src/council/debate-summary.js +85 -0
  44. package/dist/src/council/debate.d.ts +169 -2
  45. package/dist/src/council/debate.js +1265 -135
  46. package/dist/src/council/index.d.ts +108 -1
  47. package/dist/src/council/index.js +670 -197
  48. package/dist/src/council/leader.d.ts +26 -0
  49. package/dist/src/council/leader.js +150 -9
  50. package/dist/src/council/llm.d.ts +94 -0
  51. package/dist/src/council/llm.js +348 -55
  52. package/dist/src/council/panel-select.d.ts +30 -0
  53. package/dist/src/council/panel-select.js +82 -0
  54. package/dist/src/council/planner.js +40 -0
  55. package/dist/src/council/preflight.d.ts +17 -0
  56. package/dist/src/council/preflight.js +50 -2
  57. package/dist/src/council/prompts.d.ts +39 -4
  58. package/dist/src/council/prompts.js +256 -69
  59. package/dist/src/council/stance-recall.d.ts +42 -0
  60. package/dist/src/council/stance-recall.js +57 -0
  61. package/dist/src/council/strip-think.d.ts +17 -0
  62. package/dist/src/council/strip-think.js +33 -0
  63. package/dist/src/council/types.d.ts +138 -0
  64. package/dist/src/ee/artifact-cache.d.ts +16 -0
  65. package/dist/src/ee/artifact-cache.js +32 -0
  66. package/dist/src/ee/auth.d.ts +20 -0
  67. package/dist/src/ee/auth.js +54 -2
  68. package/dist/src/ee/bridge.d.ts +10 -0
  69. package/dist/src/ee/bridge.js +58 -0
  70. package/dist/src/ee/client.js +109 -21
  71. package/dist/src/ee/ee-onboarding.js +6 -26
  72. package/dist/src/ee/export-transcripts.d.ts +1 -0
  73. package/dist/src/ee/export-transcripts.js +8 -10
  74. package/dist/src/ee/extract-session.js +29 -0
  75. package/dist/src/ee/extract-style.d.ts +58 -0
  76. package/dist/src/ee/extract-style.js +270 -0
  77. package/dist/src/ee/recall-ledger.d.ts +9 -0
  78. package/dist/src/ee/recall-ledger.js +3 -0
  79. package/dist/src/ee/scope.d.ts +1 -0
  80. package/dist/src/ee/scope.js +26 -1
  81. package/dist/src/ee/search.d.ts +7 -0
  82. package/dist/src/ee/search.js +24 -0
  83. package/dist/src/ee/transcript-emit.js +2 -0
  84. package/dist/src/ee/types.d.ts +22 -0
  85. package/dist/src/ee/who-am-i-brain.d.ts +35 -0
  86. package/dist/src/ee/who-am-i-brain.js +220 -0
  87. package/dist/src/ee/who-am-i.d.ts +10 -3
  88. package/dist/src/ee/who-am-i.js +12 -0
  89. package/dist/src/ee/workflow-event.d.ts +48 -0
  90. package/dist/src/ee/workflow-event.js +81 -0
  91. package/dist/src/flow/compaction/compress.d.ts +3 -3
  92. package/dist/src/flow/compaction/compress.js +58 -8
  93. package/dist/src/flow/compaction/extract.d.ts +4 -7
  94. package/dist/src/flow/compaction/extract.js +50 -10
  95. package/dist/src/flow/compaction/index.d.ts +14 -1
  96. package/dist/src/flow/compaction/index.js +96 -3
  97. package/dist/src/flow/compaction/input-guard.d.ts +24 -0
  98. package/dist/src/flow/compaction/input-guard.js +43 -0
  99. package/dist/src/flow/compaction/progress.d.ts +35 -0
  100. package/dist/src/flow/compaction/progress.js +35 -0
  101. package/dist/src/flow/fold-planning.d.ts +36 -0
  102. package/dist/src/flow/fold-planning.js +83 -0
  103. package/dist/src/flow/hierarchy.d.ts +146 -0
  104. package/dist/src/flow/hierarchy.js +427 -0
  105. package/dist/src/flow/index.d.ts +1 -0
  106. package/dist/src/flow/index.js +2 -0
  107. package/dist/src/flow/run-artifacts.d.ts +102 -0
  108. package/dist/src/flow/run-artifacts.js +208 -0
  109. package/dist/src/generated/version.d.ts +1 -1
  110. package/dist/src/generated/version.js +1 -1
  111. package/dist/src/gsd/assessment-schema.d.ts +44 -0
  112. package/dist/src/gsd/assessment-schema.js +134 -0
  113. package/dist/src/gsd/capability-registry.d.ts +45 -0
  114. package/dist/src/gsd/capability-registry.js +337 -0
  115. package/dist/src/gsd/complexity-assessor.d.ts +39 -0
  116. package/dist/src/gsd/complexity-assessor.js +152 -0
  117. package/dist/src/gsd/config-bridge.d.ts +7 -0
  118. package/dist/src/gsd/config-bridge.js +114 -0
  119. package/dist/src/gsd/config-loader.d.ts +27 -0
  120. package/dist/src/gsd/config-loader.js +50 -0
  121. package/dist/src/gsd/council-context.d.ts +44 -0
  122. package/dist/src/gsd/council-context.js +114 -0
  123. package/dist/src/gsd/ee-closure.d.ts +28 -0
  124. package/dist/src/gsd/ee-closure.js +49 -0
  125. package/dist/src/gsd/flags.d.ts +66 -0
  126. package/dist/src/gsd/flags.js +102 -0
  127. package/dist/src/gsd/gsd-dispatch.d.ts +58 -0
  128. package/dist/src/gsd/gsd-dispatch.js +131 -0
  129. package/dist/src/gsd/gsd-runtime.d.ts +22 -0
  130. package/dist/src/gsd/gsd-runtime.js +37 -0
  131. package/dist/src/gsd/host-adapter.d.ts +11 -0
  132. package/dist/src/gsd/host-adapter.js +29 -0
  133. package/dist/src/gsd/index.d.ts +24 -1
  134. package/dist/src/gsd/index.js +27 -0
  135. package/dist/src/gsd/loop-host-contract.d.ts +21 -0
  136. package/dist/src/gsd/loop-host-contract.js +39 -0
  137. package/dist/src/gsd/loop-host.d.ts +69 -0
  138. package/dist/src/gsd/loop-host.js +245 -0
  139. package/dist/src/gsd/loop-resolver.d.ts +36 -0
  140. package/dist/src/gsd/loop-resolver.js +79 -0
  141. package/dist/src/gsd/model-tier.d.ts +13 -0
  142. package/dist/src/gsd/model-tier.js +45 -0
  143. package/dist/src/gsd/mutation-gate.d.ts +16 -0
  144. package/dist/src/gsd/mutation-gate.js +41 -0
  145. package/dist/src/gsd/native-roadmap.d.ts +89 -0
  146. package/dist/src/gsd/native-roadmap.js +343 -0
  147. package/dist/src/gsd/native-state.d.ts +47 -0
  148. package/dist/src/gsd/native-state.js +220 -0
  149. package/dist/src/gsd/paths.d.ts +23 -0
  150. package/dist/src/gsd/paths.js +66 -0
  151. package/dist/src/gsd/phase-dag.d.ts +12 -0
  152. package/dist/src/gsd/phase-dag.js +94 -0
  153. package/dist/src/gsd/phase-sync.d.ts +42 -0
  154. package/dist/src/gsd/phase-sync.js +321 -0
  155. package/dist/src/gsd/pil-gate-context.d.ts +13 -0
  156. package/dist/src/gsd/pil-gate-context.js +64 -0
  157. package/dist/src/gsd/pil-gate-critic.d.ts +19 -0
  158. package/dist/src/gsd/pil-gate-critic.js +74 -0
  159. package/dist/src/gsd/plan-council-prompts.d.ts +25 -0
  160. package/dist/src/gsd/plan-council-prompts.js +79 -0
  161. package/dist/src/gsd/plan-council.d.ts +44 -0
  162. package/dist/src/gsd/plan-council.js +283 -0
  163. package/dist/src/gsd/plan-gate-vocabulary.d.ts +40 -0
  164. package/dist/src/gsd/plan-gate-vocabulary.js +64 -0
  165. package/dist/src/gsd/product-workspace.d.ts +13 -0
  166. package/dist/src/gsd/product-workspace.js +124 -0
  167. package/dist/src/gsd/ship-bridge.d.ts +25 -0
  168. package/dist/src/gsd/ship-bridge.js +65 -0
  169. package/dist/src/gsd/state-document.d.ts +40 -0
  170. package/dist/src/gsd/state-document.js +163 -0
  171. package/dist/src/gsd/verdict-schema.d.ts +39 -0
  172. package/dist/src/gsd/verdict-schema.js +144 -0
  173. package/dist/src/gsd/verify-context.d.ts +22 -0
  174. package/dist/src/gsd/verify-context.js +27 -0
  175. package/dist/src/gsd/verify-council-prompts.d.ts +19 -0
  176. package/dist/src/gsd/verify-council-prompts.js +85 -0
  177. package/dist/src/gsd/verify-council.d.ts +25 -0
  178. package/dist/src/gsd/verify-council.js +119 -0
  179. package/dist/src/gsd/verify-gate-vocabulary.d.ts +25 -0
  180. package/dist/src/gsd/verify-gate-vocabulary.js +46 -0
  181. package/dist/src/gsd/workflow-engine.d.ts +60 -0
  182. package/dist/src/gsd/workflow-engine.js +207 -0
  183. package/dist/src/gsd/workflow-tools.d.ts +13 -0
  184. package/dist/src/gsd/workflow-tools.js +277 -0
  185. package/dist/src/headless/council-answers.js +4 -0
  186. package/dist/src/hooks/index.js +1 -1
  187. package/dist/src/index.js +172 -270
  188. package/dist/src/lsp/builtins.js +3 -1
  189. package/dist/src/lsp/manager.d.ts +5 -1
  190. package/dist/src/lsp/manager.js +249 -3
  191. package/dist/src/lsp/npm-cache.d.ts +11 -1
  192. package/dist/src/lsp/npm-cache.js +17 -1
  193. package/dist/src/lsp/runtime.d.ts +6 -1
  194. package/dist/src/lsp/runtime.js +17 -1
  195. package/dist/src/lsp/types.d.ts +83 -1
  196. package/dist/src/lsp/types.js +10 -0
  197. package/dist/src/maintain/pr-builder.js +23 -13
  198. package/dist/src/mcp/auto-setup.js +57 -32
  199. package/dist/src/mcp/client-pool.js +44 -16
  200. package/dist/src/mcp/lsp-tools.d.ts +5 -1
  201. package/dist/src/mcp/lsp-tools.js +93 -2
  202. package/dist/src/mcp/mcp-keychain.d.ts +3 -5
  203. package/dist/src/mcp/mcp-keychain.js +9 -49
  204. package/dist/src/mcp/research-onboarding.js +8 -7
  205. package/dist/src/mcp/runtime.js +34 -2
  206. package/dist/src/mcp/setup-guide-text.d.ts +1 -1
  207. package/dist/src/mcp/setup-guide-text.js +22 -2
  208. package/dist/src/mcp/tools-server.d.ts +10 -0
  209. package/dist/src/mcp/tools-server.js +10 -2
  210. package/dist/src/models/catalog-client.d.ts +87 -0
  211. package/dist/src/models/catalog-client.js +105 -38
  212. package/dist/src/models/catalog.json +528 -265
  213. package/dist/src/models/registry.d.ts +22 -7
  214. package/dist/src/models/registry.js +73 -10
  215. package/dist/src/ops/doctor.js +1 -1
  216. package/dist/src/orchestrator/ask-user.d.ts +61 -0
  217. package/dist/src/orchestrator/ask-user.js +65 -0
  218. package/dist/src/orchestrator/auto-commit.js +1 -1
  219. package/dist/src/orchestrator/batch-turn-runner.js +2 -2
  220. package/dist/src/orchestrator/cache-prefix.d.ts +67 -0
  221. package/dist/src/orchestrator/cache-prefix.js +83 -0
  222. package/dist/src/orchestrator/compact-request.d.ts +32 -0
  223. package/dist/src/orchestrator/compact-request.js +41 -0
  224. package/dist/src/orchestrator/compaction.d.ts +12 -3
  225. package/dist/src/orchestrator/compaction.js +35 -15
  226. package/dist/src/orchestrator/council-manager.d.ts +12 -3
  227. package/dist/src/orchestrator/council-manager.js +74 -32
  228. package/dist/src/orchestrator/council-request.d.ts +49 -0
  229. package/dist/src/orchestrator/council-request.js +62 -0
  230. package/dist/src/orchestrator/cross-turn-dedup.d.ts +6 -0
  231. package/dist/src/orchestrator/cross-turn-dedup.js +22 -1
  232. package/dist/src/orchestrator/error-utils.d.ts +29 -0
  233. package/dist/src/orchestrator/error-utils.js +132 -24
  234. package/dist/src/orchestrator/grounding-check.js +39 -1
  235. package/dist/src/orchestrator/interactive-pause.d.ts +26 -0
  236. package/dist/src/orchestrator/interactive-pause.js +36 -0
  237. package/dist/src/orchestrator/message-processor.d.ts +4 -0
  238. package/dist/src/orchestrator/message-processor.js +268 -41
  239. package/dist/src/orchestrator/orchestrator.d.ts +64 -3
  240. package/dist/src/orchestrator/orchestrator.js +823 -120
  241. package/dist/src/orchestrator/preprocessor.js +3 -3
  242. package/dist/src/orchestrator/proactive-compact-detector.d.ts +26 -0
  243. package/dist/src/orchestrator/proactive-compact-detector.js +36 -0
  244. package/dist/src/orchestrator/prompts.js +17 -17
  245. package/dist/src/orchestrator/reactive-delegation.d.ts +39 -0
  246. package/dist/src/orchestrator/reactive-delegation.js +59 -0
  247. package/dist/src/orchestrator/retry-classifier.d.ts +2 -1
  248. package/dist/src/orchestrator/retry-classifier.js +46 -2
  249. package/dist/src/orchestrator/safety-askcard.d.ts +1 -1
  250. package/dist/src/orchestrator/safety-askcard.js +5 -2
  251. package/dist/src/orchestrator/safety-intercept.d.ts +50 -0
  252. package/dist/src/orchestrator/safety-intercept.js +62 -0
  253. package/dist/src/orchestrator/scope-reminder.js +1 -1
  254. package/dist/src/orchestrator/session-experience.d.ts +2 -1
  255. package/dist/src/orchestrator/session-experience.js +2 -1
  256. package/dist/src/orchestrator/should-run-gate.d.ts +5 -0
  257. package/dist/src/orchestrator/should-run-gate.js +18 -0
  258. package/dist/src/orchestrator/stall-watchdog.d.ts +31 -3
  259. package/dist/src/orchestrator/stall-watchdog.js +65 -10
  260. package/dist/src/orchestrator/stream-runner.d.ts +13 -3
  261. package/dist/src/orchestrator/stream-runner.js +115 -49
  262. package/dist/src/orchestrator/sub-agent-cap.d.ts +13 -1
  263. package/dist/src/orchestrator/sub-agent-cap.js +16 -1
  264. package/dist/src/orchestrator/sub-agent-model-tier.d.ts +13 -1
  265. package/dist/src/orchestrator/sub-agent-model-tier.js +16 -2
  266. package/dist/src/orchestrator/subagent-compactor.d.ts +53 -1
  267. package/dist/src/orchestrator/subagent-compactor.js +126 -15
  268. package/dist/src/orchestrator/tool-engine.d.ts +41 -0
  269. package/dist/src/orchestrator/tool-engine.js +846 -66
  270. package/dist/src/orchestrator/tool-limit-auto-recover.d.ts +22 -0
  271. package/dist/src/orchestrator/tool-limit-auto-recover.js +30 -0
  272. package/dist/src/orchestrator/turn-runner-deps.d.ts +19 -0
  273. package/dist/src/orchestrator/turn-watchdog.d.ts +44 -0
  274. package/dist/src/orchestrator/turn-watchdog.js +84 -0
  275. package/dist/src/pil/agent-operating-contract.d.ts +1 -1
  276. package/dist/src/pil/agent-operating-contract.js +6 -4
  277. package/dist/src/pil/cheap-model-playbook.d.ts +1 -1
  278. package/dist/src/pil/cheap-model-playbook.js +5 -1
  279. package/dist/src/pil/cheap-model-workbooks.d.ts +1 -1
  280. package/dist/src/pil/cheap-model-workbooks.js +1 -1
  281. package/dist/src/pil/discovery-types.d.ts +1 -0
  282. package/dist/src/pil/discovery.d.ts +1 -1
  283. package/dist/src/pil/discovery.js +18 -13
  284. package/dist/src/pil/layer1-intent.d.ts +18 -6
  285. package/dist/src/pil/layer1-intent.js +66 -757
  286. package/dist/src/pil/layer15-context-scan.js +15 -1
  287. package/dist/src/pil/layer1_5-complexity-size.d.ts +7 -0
  288. package/dist/src/pil/layer1_5-complexity-size.js +31 -5
  289. package/dist/src/pil/layer3-ee-injection.js +23 -8
  290. package/dist/src/pil/layer4-gsd.js +69 -16
  291. package/dist/src/pil/layer5-context.js +7 -3
  292. package/dist/src/pil/layer6-output.d.ts +23 -0
  293. package/dist/src/pil/layer6-output.js +5 -1
  294. package/dist/src/pil/llm-classify.d.ts +111 -5
  295. package/dist/src/pil/llm-classify.js +421 -189
  296. package/dist/src/pil/native-capabilities-workbook.d.ts +1 -1
  297. package/dist/src/pil/native-capabilities-workbook.js +8 -0
  298. package/dist/src/pil/pipeline.js +36 -2
  299. package/dist/src/pil/repo-grounding-probe.d.ts +15 -0
  300. package/dist/src/pil/repo-grounding-probe.js +136 -0
  301. package/dist/src/pil/repo-structure-hints.d.ts +7 -0
  302. package/dist/src/pil/repo-structure-hints.js +45 -0
  303. package/dist/src/pil/response-tools.js +5 -3
  304. package/dist/src/pil/schema.d.ts +1 -0
  305. package/dist/src/pil/schema.js +2 -0
  306. package/dist/src/pil/types.d.ts +18 -0
  307. package/dist/src/playbook/directives.d.ts +4 -0
  308. package/dist/src/playbook/directives.js +17 -5
  309. package/dist/src/product-loop/artifact-io.js +4 -0
  310. package/dist/src/product-loop/backlog-builder.d.ts +14 -1
  311. package/dist/src/product-loop/backlog-builder.js +30 -6
  312. package/dist/src/product-loop/criteria-seed.d.ts +51 -0
  313. package/dist/src/product-loop/criteria-seed.js +200 -0
  314. package/dist/src/product-loop/discovery-context-format.js +3 -1
  315. package/dist/src/product-loop/discovery-ecosystem.js +4 -1
  316. package/dist/src/product-loop/discovery-interview.d.ts +9 -0
  317. package/dist/src/product-loop/discovery-interview.js +60 -12
  318. package/dist/src/product-loop/discovery-recommender.js +2 -1
  319. package/dist/src/product-loop/discovery-schema.js +19 -2
  320. package/dist/src/product-loop/discovery-triage.d.ts +23 -0
  321. package/dist/src/product-loop/discovery-triage.js +109 -0
  322. package/dist/src/product-loop/gather.js +150 -2
  323. package/dist/src/product-loop/ideal-trace.d.ts +7 -0
  324. package/dist/src/product-loop/ideal-trace.js +64 -0
  325. package/dist/src/product-loop/index.d.ts +13 -1
  326. package/dist/src/product-loop/index.js +340 -52
  327. package/dist/src/product-loop/loop-driver.d.ts +7 -0
  328. package/dist/src/product-loop/loop-driver.js +330 -106
  329. package/dist/src/product-loop/phase-plan.d.ts +21 -0
  330. package/dist/src/product-loop/phase-plan.js +81 -6
  331. package/dist/src/product-loop/phase-rituals.d.ts +3 -0
  332. package/dist/src/product-loop/phase-rituals.js +8 -3
  333. package/dist/src/product-loop/phase-runner.js +39 -12
  334. package/dist/src/product-loop/plan-adherence-review.d.ts +26 -0
  335. package/dist/src/product-loop/plan-adherence-review.js +144 -0
  336. package/dist/src/product-loop/sprint-runner.d.ts +173 -0
  337. package/dist/src/product-loop/sprint-runner.js +863 -19
  338. package/dist/src/product-loop/types.d.ts +61 -5
  339. package/dist/src/providers/adapter.d.ts +1 -1
  340. package/dist/src/providers/adapter.js +3 -4
  341. package/dist/src/providers/anthropic.d.ts +9 -8
  342. package/dist/src/providers/anthropic.js +13 -47
  343. package/dist/src/providers/auth/browser-flow.d.ts +1 -1
  344. package/dist/src/providers/auth/browser-flow.js +1 -1
  345. package/dist/src/providers/auth/grok-oauth.d.ts +1 -0
  346. package/dist/src/providers/auth/grok-oauth.js +30 -5
  347. package/dist/src/providers/auth/openai-oauth.d.ts +1 -0
  348. package/dist/src/providers/auth/openai-oauth.js +15 -1
  349. package/dist/src/providers/auth/registry.js +0 -34
  350. package/dist/src/providers/auth/token-store.d.ts +9 -9
  351. package/dist/src/providers/auth/token-store.js +8 -67
  352. package/dist/src/providers/auth/types.d.ts +9 -1
  353. package/dist/src/providers/auth/types.js +1 -1
  354. package/dist/src/providers/capabilities.d.ts +24 -5
  355. package/dist/src/providers/capabilities.js +42 -24
  356. package/dist/src/providers/endpoints.d.ts +2 -2
  357. package/dist/src/providers/endpoints.js +11 -10
  358. package/dist/src/providers/env-store.d.ts +17 -0
  359. package/dist/src/providers/env-store.js +228 -0
  360. package/dist/src/providers/keychain.d.ts +22 -18
  361. package/dist/src/providers/keychain.js +127 -140
  362. package/dist/src/providers/mcp-vision-bridge.js +56 -146
  363. package/dist/src/providers/openai-compatible.js +8 -1
  364. package/dist/src/providers/pricing.d.ts +2 -2
  365. package/dist/src/providers/pricing.js +3 -13
  366. package/dist/src/providers/runtime.d.ts +43 -3
  367. package/dist/src/providers/runtime.js +88 -14
  368. package/dist/src/providers/strategies/base.strategy.d.ts +16 -0
  369. package/dist/src/providers/strategies/base.strategy.js +24 -1
  370. package/dist/src/providers/strategies/{siliconflow.strategy.d.ts → opencode-go.strategy.d.ts} +3 -3
  371. package/dist/src/providers/strategies/opencode-go.strategy.js +83 -0
  372. package/dist/src/providers/strategies/registry.js +4 -4
  373. package/dist/src/providers/strategies/thinking-mode.d.ts +109 -0
  374. package/dist/src/providers/strategies/thinking-mode.js +288 -1
  375. package/dist/src/providers/strategies/xai.strategy.js +27 -0
  376. package/dist/src/providers/strategies/zai.strategy.d.ts +14 -0
  377. package/dist/src/providers/strategies/zai.strategy.js +44 -0
  378. package/dist/src/providers/types.d.ts +5 -6
  379. package/dist/src/providers/types.js +2 -2
  380. package/dist/src/providers/vision-backend.d.ts +47 -0
  381. package/dist/src/providers/vision-backend.js +258 -0
  382. package/dist/src/providers/vision-proxy.d.ts +22 -9
  383. package/dist/src/providers/vision-proxy.js +63 -132
  384. package/dist/src/providers/warm.d.ts +65 -0
  385. package/dist/src/providers/warm.js +145 -0
  386. package/dist/src/providers/wire-debug.js +95 -0
  387. package/dist/src/router/decide.d.ts +13 -0
  388. package/dist/src/router/decide.js +138 -36
  389. package/dist/src/router/peak-hour.d.ts +38 -0
  390. package/dist/src/router/peak-hour.js +107 -0
  391. package/dist/src/router/step-router.js +3 -2
  392. package/dist/src/router/warm.js +4 -5
  393. package/dist/src/scaffold/continuation-prompt.d.ts +11 -0
  394. package/dist/src/scaffold/continuation-prompt.js +26 -0
  395. package/dist/src/scaffold/point-to-existing.d.ts +21 -0
  396. package/dist/src/scaffold/point-to-existing.js +25 -0
  397. package/dist/src/self-qa/agentic-loop.js +6 -5
  398. package/dist/src/{ui/state → state}/active-run.d.ts +19 -0
  399. package/dist/src/{ui/state → state}/active-run.js +21 -0
  400. package/dist/src/{ui/status-bar/store.d.ts → state/status-bar-store.d.ts} +4 -1
  401. package/dist/src/{ui/status-bar/store.js → state/status-bar-store.js} +12 -9
  402. package/dist/src/state/turn-trace.d.ts +43 -0
  403. package/dist/src/state/turn-trace.js +32 -0
  404. package/dist/src/storage/db.js +2 -1
  405. package/dist/src/storage/index.d.ts +1 -1
  406. package/dist/src/storage/index.js +1 -1
  407. package/dist/src/storage/interaction-log.d.ts +1 -1
  408. package/dist/src/storage/migrations.js +71 -1
  409. package/dist/src/storage/sessions.d.ts +28 -10
  410. package/dist/src/storage/sessions.js +78 -21
  411. package/dist/src/storage/transcript-view.js +1 -1
  412. package/dist/src/storage/transcript.d.ts +51 -0
  413. package/dist/src/storage/transcript.js +340 -15
  414. package/dist/src/tools/file.d.ts +15 -0
  415. package/dist/src/tools/file.js +32 -0
  416. package/dist/src/tools/git-safety.d.ts +19 -0
  417. package/dist/src/tools/git-safety.js +168 -0
  418. package/dist/src/tools/native-tools.d.ts +1 -1
  419. package/dist/src/tools/native-tools.js +81 -1
  420. package/dist/src/tools/registry.d.ts +20 -0
  421. package/dist/src/tools/registry.js +576 -23
  422. package/dist/src/tools/research.d.ts +29 -0
  423. package/dist/src/tools/research.js +233 -0
  424. package/dist/src/types/index.d.ts +147 -4
  425. package/dist/src/ui/app.js +0 -0
  426. package/dist/src/ui/cards/product-status-card.js +1 -1
  427. package/dist/src/ui/components/agent-rail-activities.d.ts +26 -0
  428. package/dist/src/ui/components/agent-rail-activities.js +47 -0
  429. package/dist/src/ui/components/bubble-body-guard.d.ts +27 -0
  430. package/dist/src/ui/components/bubble-body-guard.js +50 -0
  431. package/dist/src/ui/components/compact-progress-card.d.ts +24 -0
  432. package/dist/src/ui/components/compact-progress-card.js +42 -0
  433. package/dist/src/ui/components/context-rail.d.ts +26 -0
  434. package/dist/src/ui/components/context-rail.js +33 -0
  435. package/dist/src/ui/components/council-conclusion-card.d.ts +73 -0
  436. package/dist/src/ui/components/council-conclusion-card.js +420 -0
  437. package/dist/src/ui/components/council-debate-pill.d.ts +36 -0
  438. package/dist/src/ui/components/council-debate-pill.js +34 -0
  439. package/dist/src/ui/components/council-info-card.js +2 -2
  440. package/dist/src/ui/components/council-leader-bubble.d.ts +10 -3
  441. package/dist/src/ui/components/council-leader-bubble.js +21 -11
  442. package/dist/src/ui/components/council-message-bubble.d.ts +15 -2
  443. package/dist/src/ui/components/council-message-bubble.js +16 -15
  444. package/dist/src/ui/components/council-phase-timeline.d.ts +9 -1
  445. package/dist/src/ui/components/council-phase-timeline.js +66 -17
  446. package/dist/src/ui/components/council-placeholder-bubble.d.ts +16 -9
  447. package/dist/src/ui/components/council-placeholder-bubble.js +32 -29
  448. package/dist/src/ui/components/council-question-card.js +13 -12
  449. package/dist/src/ui/components/council-rail-rounds.d.ts +26 -0
  450. package/dist/src/ui/components/council-rail-rounds.js +57 -0
  451. package/dist/src/ui/components/council-round-group.d.ts +38 -0
  452. package/dist/src/ui/components/council-round-group.js +88 -0
  453. package/dist/src/ui/components/council-status-list.d.ts +3 -1
  454. package/dist/src/ui/components/council-status-list.js +36 -24
  455. package/dist/src/ui/components/council-synthesis-banner.d.ts +7 -2
  456. package/dist/src/ui/components/council-synthesis-banner.js +20 -5
  457. package/dist/src/ui/components/halt-recovery-card.js +9 -5
  458. package/dist/src/ui/components/jump-to-latest-pill.d.ts +11 -0
  459. package/dist/src/ui/components/jump-to-latest-pill.js +14 -0
  460. package/dist/src/ui/components/message-view.d.ts +15 -0
  461. package/dist/src/ui/components/message-view.js +50 -1
  462. package/dist/src/ui/components/prompt-box.js +18 -16
  463. package/dist/src/ui/components/session-tree-card.d.ts +14 -0
  464. package/dist/src/ui/components/session-tree-card.js +46 -0
  465. package/dist/src/ui/components/slash-inline-menu.d.ts +12 -0
  466. package/dist/src/ui/components/slash-inline-menu.js +26 -5
  467. package/dist/src/ui/components/task-list-panel.d.ts +14 -1
  468. package/dist/src/ui/components/task-list-panel.js +22 -2
  469. package/dist/src/ui/components/tool-group.d.ts +15 -3
  470. package/dist/src/ui/components/tool-group.js +69 -11
  471. package/dist/src/ui/containers/modals-layer.d.ts +4 -2
  472. package/dist/src/ui/containers/modals-layer.js +2 -2
  473. package/dist/src/ui/council-harness-event.d.ts +57 -0
  474. package/dist/src/ui/council-harness-event.js +46 -0
  475. package/dist/src/ui/heartbeat-debug.d.ts +29 -0
  476. package/dist/src/ui/heartbeat-debug.js +45 -0
  477. package/dist/src/ui/hooks/use-session-picker.d.ts +3 -3
  478. package/dist/src/ui/mcp-modal.js +2 -4
  479. package/dist/src/ui/modals/api-key-modal.js +1 -1
  480. package/dist/src/ui/modals/connect-modal.js +4 -3
  481. package/dist/src/ui/modals/model-picker-modal.d.ts +8 -18
  482. package/dist/src/ui/modals/model-picker-modal.js +8 -10
  483. package/dist/src/ui/modals/session-picker-modal.d.ts +2 -2
  484. package/dist/src/ui/modals/session-picker-modal.js +3 -5
  485. package/dist/src/ui/picker-providers.d.ts +1 -1
  486. package/dist/src/ui/picker-providers.js +1 -1
  487. package/dist/src/ui/primitives/index.d.ts +1 -0
  488. package/dist/src/ui/primitives/index.js +2 -0
  489. package/dist/src/ui/primitives/semantic-primitives.d.ts +76 -0
  490. package/dist/src/ui/primitives/semantic-primitives.js +81 -0
  491. package/dist/src/ui/slash/compact.js +5 -7
  492. package/dist/src/ui/slash/cost.js +1 -1
  493. package/dist/src/ui/slash/council.js +19 -1
  494. package/dist/src/ui/slash/debug.d.ts +3 -31
  495. package/dist/src/ui/slash/debug.js +9 -20
  496. package/dist/src/ui/slash/ee.js +81 -0
  497. package/dist/src/ui/slash/ideal.d.ts +6 -2
  498. package/dist/src/ui/slash/ideal.js +97 -7
  499. package/dist/src/ui/slash/menu-items.d.ts +7 -0
  500. package/dist/src/ui/slash/menu-items.js +23 -20
  501. package/dist/src/ui/slash/registry.d.ts +2 -0
  502. package/dist/src/ui/slash/registry.js +4 -0
  503. package/dist/src/ui/status-bar/cache-hit.d.ts +6 -0
  504. package/dist/src/ui/status-bar/cache-hit.js +9 -0
  505. package/dist/src/ui/status-bar/index.d.ts +1 -1
  506. package/dist/src/ui/status-bar/index.js +7 -3
  507. package/dist/src/ui/status-bar/usd-meter.d.ts +5 -4
  508. package/dist/src/ui/status-bar/usd-meter.js +6 -4
  509. package/dist/src/ui/theme.d.ts +1 -0
  510. package/dist/src/ui/theme.js +2 -0
  511. package/dist/src/ui/types.d.ts +7 -0
  512. package/dist/src/ui/use-app-logic.js +0 -0
  513. package/dist/src/ui/utils/agent-activities.d.ts +39 -0
  514. package/dist/src/ui/utils/agent-activities.js +96 -0
  515. package/dist/src/ui/utils/format.d.ts +14 -0
  516. package/dist/src/ui/utils/format.js +23 -3
  517. package/dist/src/ui/utils/group-tool-entries.d.ts +26 -0
  518. package/dist/src/ui/utils/group-tool-entries.js +111 -0
  519. package/dist/src/ui/utils/tool-summary.d.ts +21 -0
  520. package/dist/src/ui/utils/tool-summary.js +91 -0
  521. package/dist/src/usage/downgrade.js +2 -2
  522. package/dist/src/usage/product-ledger.js +2 -2
  523. package/dist/src/utils/event-loop-monitor.d.ts +85 -0
  524. package/dist/src/utils/event-loop-monitor.js +107 -0
  525. package/dist/src/utils/install-manager.js +2 -1
  526. package/dist/src/utils/llm-deadline.d.ts +14 -0
  527. package/dist/src/utils/llm-deadline.js +19 -0
  528. package/dist/src/utils/logger.js +2 -2
  529. package/dist/src/utils/loop-profiler.d.ts +102 -0
  530. package/dist/src/utils/loop-profiler.js +202 -0
  531. package/dist/src/utils/permission-mode.js +5 -3
  532. package/dist/src/utils/redactor.js +1 -1
  533. package/dist/src/utils/settings.d.ts +180 -5
  534. package/dist/src/utils/settings.js +271 -31
  535. package/dist/src/utils/side-question.d.ts +1 -2
  536. package/dist/src/utils/side-question.js +2 -2
  537. package/dist/src/utils/visible-retry.d.ts +11 -0
  538. package/dist/src/utils/visible-retry.js +10 -1
  539. package/dist/src/verify/entrypoint.d.ts +1 -1
  540. package/dist/src/verify/entrypoint.js +52 -17
  541. package/dist/src/verify/orchestrator.d.ts +1 -1
  542. package/dist/src/verify/orchestrator.js +20 -3
  543. package/dist/src/verify/recipes.d.ts +13 -0
  544. package/dist/src/verify/recipes.js +15 -0
  545. package/package.json +134 -132
  546. package/dist/src/cli/bw-vault.d.ts +0 -55
  547. package/dist/src/cli/bw-vault.js +0 -133
  548. package/dist/src/mcp/ee-tools.d.ts +0 -46
  549. package/dist/src/mcp/ee-tools.js +0 -193
  550. package/dist/src/providers/auth/gcloud.d.ts +0 -28
  551. package/dist/src/providers/auth/gcloud.js +0 -102
  552. package/dist/src/providers/auth/gemini-oauth.d.ts +0 -82
  553. package/dist/src/providers/auth/gemini-oauth.js +0 -472
  554. package/dist/src/providers/gemini.d.ts +0 -11
  555. package/dist/src/providers/gemini.js +0 -45
  556. package/dist/src/providers/siliconflow-sse-repair.d.ts +0 -58
  557. package/dist/src/providers/siliconflow-sse-repair.js +0 -177
  558. package/dist/src/providers/strategies/google.strategy.d.ts +0 -22
  559. package/dist/src/providers/strategies/google.strategy.js +0 -174
  560. package/dist/src/providers/strategies/siliconflow.strategy.js +0 -29
  561. package/dist/src/ui/containers/chat-feed.d.ts +0 -40
  562. package/dist/src/ui/containers/chat-feed.js +0 -66
@@ -1,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");
@@ -274,10 +275,12 @@ export function saveProjectSettings(partial) {
274
275
  }
275
276
  export function getApiKey() {
276
277
  // Test escape hatch (api-key harness spec): suppress all key sources so the
277
- // boot flow reaches the API-key modal. See src/index.ts resolveKeyForModel.
278
+ // boot flow reaches the provider picker. See src/index.ts resolveKeyForModel.
278
279
  if (process.env.MUONROI_TEST_NO_KEYCHAIN === "1")
279
280
  return undefined;
280
- return process.env.MUONROI_API_KEY || loadUserSettings().apiKey;
281
+ // Env-only: the legacy plaintext `settings.apiKey` is migrated to the
282
+ // env-store on first run (migrateLegacyKeysToEnv) and no longer read here.
283
+ return process.env.MUONROI_API_KEY || undefined;
281
284
  }
282
285
  export function getBaseURL(provider) {
283
286
  if (process.env.MUONROI_BASE_URL)
@@ -310,11 +313,6 @@ export function getProviderConfigs(mainApiKey) {
310
313
  if (openaiKey) {
311
314
  configs.openai = { apiKey: openaiKey, baseURL: p.openai?.baseURL };
312
315
  }
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
316
  // DeepSeek
319
317
  const deepseekKey = process.env.DEEPSEEK_API_KEY ?? p.deepseek?.apiKey;
320
318
  if (deepseekKey) {
@@ -323,14 +321,6 @@ export function getProviderConfigs(mainApiKey) {
323
321
  baseURL: p.deepseek?.baseURL ?? apiBaseFor("deepseek"),
324
322
  };
325
323
  }
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
324
  // xAI / Grok (OpenAI-compatible)
335
325
  const xaiKey = process.env.XAI_API_KEY ?? p.xai?.apiKey;
336
326
  if (xaiKey) {
@@ -339,6 +329,22 @@ export function getProviderConfigs(mainApiKey) {
339
329
  baseURL: p.xai?.baseURL ?? apiBaseFor("xai"),
340
330
  };
341
331
  }
332
+ // Z.ai (OpenAI-compatible)
333
+ const zaiKey = process.env.ZAI_API_KEY ?? p.zai?.apiKey;
334
+ if (zaiKey) {
335
+ configs.zai = {
336
+ apiKey: zaiKey,
337
+ baseURL: p.zai?.baseURL ?? apiBaseFor("zai"),
338
+ };
339
+ }
340
+ // OpenCode Go (OpenAI-compatible)
341
+ const opencodeGoKey = process.env.OPENCODE_GO_API_KEY ?? p["opencode-go"]?.apiKey;
342
+ if (opencodeGoKey) {
343
+ configs["opencode-go"] = {
344
+ apiKey: opencodeGoKey,
345
+ baseURL: p["opencode-go"]?.baseURL ?? apiBaseFor("opencode-go"),
346
+ };
347
+ }
342
348
  // Ollama — no key needed, just baseURL
343
349
  const ollamaURL = process.env.OLLAMA_URL ?? p.ollama?.baseURL ?? "http://localhost:11434";
344
350
  configs.ollama = { baseURL: ollamaURL };
@@ -591,6 +597,12 @@ export function getAutoCompactThresholdPct() {
591
597
  return val;
592
598
  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
599
  }
600
+ export function getAutoCompactMinNewTokens() {
601
+ const val = loadUserSettings().autoCompactMinNewTokens;
602
+ if (typeof val === "number" && val >= 0 && val <= 200_000)
603
+ return val;
604
+ return 20_000; // Observed thrash: re-compact after ~14K new tokens (session ff932f8568e8).
605
+ }
594
606
  /**
595
607
  * Per-invocation cap on cumulative tool-output chars inside a `task`
596
608
  * sub-agent. See orchestrator/sub-agent-cap.ts for the tiered compression
@@ -627,6 +639,65 @@ export function getProviderStallTimeoutMs() {
627
639
  }
628
640
  return 120_000;
629
641
  }
642
+ /**
643
+ * No-forward-progress watchdog timeout (ms) for streaming model calls. Distinct
644
+ * from the stall watchdog: the stall watchdog re-arms on ANY stream chunk —
645
+ * including a reasoning model's `reasoning-delta` chunks — so a model stuck in an
646
+ * endless chain-of-thought keeps petting it and it NEVER fires (observed live
647
+ * 2026-07-10: a deepseek-v4-flash sub-agent churned reasoning for 30+ min, 1.4M
648
+ * input tokens, ZERO text/tool output, and the 2-min stall watchdog never tripped
649
+ * because reasoning chunks kept arriving). This second watchdog is petted ONLY on
650
+ * REAL forward progress (a text-delta or a tool-call), so a runaway-reasoning /
651
+ * no-output loop is aborted while a legitimately long reasoning burst that DOES
652
+ * eventually emit text/tools survives. Set generously above a normal reasoning
653
+ * burst. Range 30_000–1_800_000; 0 disables. Default 300_000 (5 min). Env
654
+ * override: MUONROI_PROVIDER_PROGRESS_TIMEOUT_MS.
655
+ */
656
+ export function getProviderProgressTimeoutMs() {
657
+ const envRaw = process.env.MUONROI_PROVIDER_PROGRESS_TIMEOUT_MS;
658
+ if (envRaw !== undefined && envRaw !== "") {
659
+ const n = Number(envRaw);
660
+ if (Number.isFinite(n) && n === 0)
661
+ return 0; // explicit disable
662
+ if (Number.isFinite(n) && n >= 30_000 && n <= 1_800_000)
663
+ return Math.floor(n);
664
+ }
665
+ return 300_000;
666
+ }
667
+ /**
668
+ * Event-loop block reporting threshold (ms). The monitor
669
+ * (`src/utils/event-loop-monitor.ts`) reports a block when its own tick runs at
670
+ * least this late — evidence that the JS thread was stuck and NO timer could
671
+ * fire, which is why the provider stall watchdogs above cannot see this class
672
+ * of freeze (measured live 2026-07-16: a 304.5s TUI freeze that the 120s stall
673
+ * watchdog only rescued 6.7s AFTER the loop recovered).
674
+ *
675
+ * 2s is well clear of ordinary GC pauses and heavy renders while still catching
676
+ * anything a user would call a freeze. Range 250–60_000; 0 disables.
677
+ * Default 2_000. Env override: MUONROI_LOOP_BLOCK_THRESHOLD_MS.
678
+ */
679
+ export function getLoopBlockThresholdMs() {
680
+ const envRaw = process.env.MUONROI_LOOP_BLOCK_THRESHOLD_MS;
681
+ if (envRaw !== undefined && envRaw !== "") {
682
+ const n = Number(envRaw);
683
+ if (Number.isFinite(n) && n === 0)
684
+ return 0; // explicit disable
685
+ if (Number.isFinite(n) && n >= 250 && n <= 60_000)
686
+ return Math.floor(n);
687
+ }
688
+ return 2_000;
689
+ }
690
+ /**
691
+ * Whether to run the rolling CPU profiler (`src/utils/loop-profiler.ts`) so a
692
+ * reported event-loop block comes with the culprit's stack rather than just a
693
+ * duration. OFF by default: continuous V8 sampling costs a little on every
694
+ * session, and this is a diagnostic for a sporadic bug, not a feature. Arm it
695
+ * when hunting a freeze. Env: MUONROI_LOOP_PROFILE=1 (0/unset disables).
696
+ */
697
+ export function isLoopProfileEnabled() {
698
+ const raw = process.env.MUONROI_LOOP_PROFILE?.trim().toLowerCase();
699
+ return raw === "1" || raw === "true" || raw === "yes" || raw === "on";
700
+ }
630
701
  /**
631
702
  * Number of times to AUTOMATICALLY re-issue a streaming model call after the
632
703
  * stall watchdog fires WITHOUT any chunk having arrived (a time-to-first-byte
@@ -702,17 +773,50 @@ export function getSubAgentCompactKeepLast() {
702
773
  * top-level loops typically carry more useful early context.
703
774
  * Env override: MUONROI_TOP_LEVEL_COMPACT_THRESHOLD_CHARS.
704
775
  */
705
- export function getTopLevelCompactThresholdChars() {
776
+ export function getTopLevelCompactThresholdChars(contextWindowTokens) {
706
777
  const envRaw = process.env.MUONROI_TOP_LEVEL_COMPACT_THRESHOLD_CHARS;
707
778
  if (envRaw) {
708
779
  const n = Number(envRaw);
709
- if (Number.isFinite(n) && n >= 50_000 && n <= 1_500_000)
780
+ if (Number.isFinite(n) && n >= 10_000 && n <= 1_500_000)
710
781
  return Math.floor(n);
711
782
  }
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;
783
+ // For small-context models (e.g. DeepSeek 64K), scale threshold proportionally
784
+ // to prevent linear token growth during tool loops. A model with 64K context
785
+ // gets threshold = 64000 * 4 * 0.35 = 89,600 chars (~22K tokens = 35% of window).
786
+ // Large-context models (128K+) keep the original 200K default.
787
+ if (contextWindowTokens && contextWindowTokens > 0) {
788
+ const dynamicThreshold = Math.floor(contextWindowTokens * 4 * 0.35);
789
+ return Math.min(200_000, dynamicThreshold);
790
+ }
791
+ return 200_000;
792
+ }
793
+ /**
794
+ * Compaction hysteresis factor for the top-level loop. Once B4 compaction has
795
+ * fired within a turn, the compacted prefix is FROZEN and only new messages are
796
+ * appended (keeping the provider prompt-cache prefix byte-stable) until the
797
+ * cumulative size grows past `lastTriggerChars * factor` — then it re-compacts.
798
+ *
799
+ * Why: measured on session 1afb2728e67a — a 24-step turn re-ran compaction every
800
+ * step, and each step's sliding keepLast boundary flipped one more tool result
801
+ * verbatim→stub, breaking the cache prefix at that position. 63% of that
802
+ * session's FRESH input came from 5 such compaction-induced cache breaks.
803
+ * Holding the boundary between compactions trades a higher peak input for far
804
+ * fewer cache-break re-bills.
805
+ *
806
+ * Range 1.0–3.0. Default 1.15 (re-compact at +15% growth). `1.0` or env `0`
807
+ * disables hysteresis → legacy per-step compaction.
808
+ * Env override: MUONROI_COMPACT_HYSTERESIS.
809
+ */
810
+ export function getTopLevelCompactHysteresis() {
811
+ const envRaw = process.env.MUONROI_COMPACT_HYSTERESIS;
812
+ if (envRaw !== undefined && envRaw.trim() !== "") {
813
+ const n = Number(envRaw);
814
+ if (Number.isFinite(n) && n === 0)
815
+ return 1.0; // explicit disable
816
+ if (Number.isFinite(n) && n >= 1.0 && n <= 3.0)
817
+ return n;
818
+ }
819
+ return 1.15;
716
820
  }
717
821
  /**
718
822
  * Phase B4 — number of trailing tool turns kept verbatim during top-level
@@ -720,22 +824,59 @@ export function getTopLevelCompactThresholdChars() {
720
824
  * decisions across longer horizons.
721
825
  * Env override: MUONROI_TOP_LEVEL_COMPACT_KEEP_LAST.
722
826
  */
723
- export function getTopLevelCompactKeepLast() {
827
+ export function getTopLevelCompactKeepLast(contextWindowTokens) {
724
828
  const envRaw = process.env.MUONROI_TOP_LEVEL_COMPACT_KEEP_LAST;
725
829
  if (envRaw) {
726
830
  const n = Number(envRaw);
727
831
  if (Number.isFinite(n) && n >= 1 && n <= 30)
728
832
  return Math.floor(n);
729
833
  }
834
+ // Small-context models (< 100K tokens) benefit from keeping fewer trailing
835
+ // turns — each verbatim turn with tool results + reasoning tokens costs
836
+ // 5-15K tokens. Reduce from 5 to 3 for small windows.
837
+ if (contextWindowTokens && contextWindowTokens < 100_000) {
838
+ return 3;
839
+ }
730
840
  return 5;
731
841
  }
842
+ /**
843
+ * O2 — byte budget for the verbatim tail (last keepLast turns) in top-level B4
844
+ * compaction. The keepLast shrink is otherwise fill-ratio based, so on a
845
+ * large-context model a read-heavy tail stays verbatim at ~50% fill, pinning
846
+ * each tool round at 60-80K input (measured on July-8 sessions). This caps the
847
+ * tail's actual chars, shrinking keepLast further (floor 2) when the kept tool
848
+ * results are large. 0 disables. Env: MUONROI_TOP_LEVEL_COMPACT_TAIL_BUDGET_CHARS.
849
+ */
850
+ export function getTopLevelCompactTailBudgetChars(contextWindowTokens) {
851
+ const envRaw = process.env.MUONROI_TOP_LEVEL_COMPACT_TAIL_BUDGET_CHARS;
852
+ if (envRaw !== undefined && envRaw.trim() !== "") {
853
+ const n = Number(envRaw);
854
+ // 0 = explicit disable; otherwise clamp to a sane floor so a fat-fingered
855
+ // tiny value can't stub away all recent context.
856
+ if (Number.isFinite(n) && n === 0)
857
+ return 0;
858
+ if (Number.isFinite(n) && n >= 20_000 && n <= 1_000_000)
859
+ return Math.floor(n);
860
+ }
861
+ // Default 50K chars (~12.5K tokens). Chosen from a deterministic measurement:
862
+ // on a realistic read-heavy turn the keepLast=5 verbatim tail is ~70-100K
863
+ // chars, so a looser budget (e.g. 120K) never bites (no-op). 50K shrinks the
864
+ // effective tail to ~3 turns on heavy turns (matching the sub-agent keepLast
865
+ // default) — ~7K tokens/call saved — while high-value results stay verbatim
866
+ // and light turns (below the 200K compaction threshold) are untouched. For
867
+ // small windows, scale to ~20% of the window so the tail can't dominate.
868
+ if (contextWindowTokens && contextWindowTokens > 0) {
869
+ return Math.min(50_000, Math.floor(contextWindowTokens * 4 * 0.2));
870
+ }
871
+ return 50_000;
872
+ }
732
873
  /**
733
874
  * Per-turn cap on cumulative tool-output chars inside the top-level
734
875
  * orchestrator agentic loop. Same tiered compression as the sub-agent cap,
735
876
  * higher default so single-tool turns are unaffected. Env override:
736
877
  * MUONROI_TOP_LEVEL_TOOL_BUDGET_CHARS.
737
878
  */
738
- export function getTopLevelToolBudgetChars(maxRounds) {
879
+ export function getTopLevelToolBudgetChars(maxRounds, contextWindowTokens) {
739
880
  const envRaw = process.env.MUONROI_TOP_LEVEL_TOOL_BUDGET_CHARS;
740
881
  if (envRaw) {
741
882
  const n = Number(envRaw);
@@ -748,10 +889,35 @@ export function getTopLevelToolBudgetChars(maxRounds) {
748
889
  // Dynamically scale default based on maxRounds relative to default base (40)
749
890
  const baseRounds = 40;
750
891
  const scale = maxRounds && maxRounds > baseRounds ? maxRounds / baseRounds : 1;
751
- return Math.floor(400_000 * scale);
892
+ const baseDefault = Math.floor(400_000 * scale);
893
+ // For small-context models (e.g. DeepSeek 64K), scale the budget to 60% of
894
+ // the context window in chars so tiered compression kicks in before the
895
+ // cumulative tool output exceeds what the model can hold in context.
896
+ if (contextWindowTokens && contextWindowTokens > 0 && contextWindowTokens < 200_000) {
897
+ const windowBudget = Math.floor(contextWindowTokens * 4 * 0.6);
898
+ return Math.min(baseDefault, Math.max(50_000, windowBudget));
899
+ }
900
+ return baseDefault;
752
901
  }
753
902
  export function getRoleModel(role) {
754
- return loadUserSettings().roleModels?.[role];
903
+ const configured = loadUserSettings().roleModels?.[role];
904
+ if (!configured)
905
+ return undefined;
906
+ // Graceful staleness guard (mirrors getCurrentModel's pickValid): a role model
907
+ // persisted before a catalog rename/drop (e.g. "grok-build-0.1" after it was
908
+ // dropped in favor of grok-composer-2.5-fast) must NOT leak a dead id to the
909
+ // runtime, where resolveModelRuntime throws "not found in catalog — cannot
910
+ // determine provider" and takes down the whole council speaker (observed:
911
+ // Experience Auditor on the research role). If the catalog hasn't loaded yet,
912
+ // trust the normalized id; otherwise drop unresolved ids so the caller falls
913
+ // back to its own default instead of crashing.
914
+ const normalized = normalizeModelId(configured);
915
+ if (MODELS.length === 0)
916
+ return normalized;
917
+ if (getModelInfo(normalized))
918
+ return normalized;
919
+ 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.`);
920
+ return undefined;
755
921
  }
756
922
  export function getRoleModels() {
757
923
  return loadUserSettings().roleModels ?? {};
@@ -778,18 +944,92 @@ export function normalizeAutoCouncilMinRoles(val) {
778
944
  export function getAutoCouncilConfidence() {
779
945
  return normalizeAutoCouncilConfidence(loadUserSettings().autoCouncilConfidence);
780
946
  }
947
+ /**
948
+ * Whether the auto-council path runs the pre-debate clarification interview
949
+ * (model-designed askcards) before debating. Default true so a broadly-scoped
950
+ * "debate mode" request is clarified first. Env override wins over the user
951
+ * setting for quick dev toggling; env "0"/"false" disables, "1"/"true" enables.
952
+ */
953
+ export function isAutoCouncilClarifyEnabled() {
954
+ const env = process.env.MUONROI_AUTOCOUNCIL_CLARIFY?.trim().toLowerCase();
955
+ if (env === "0" || env === "false")
956
+ return false;
957
+ if (env === "1" || env === "true")
958
+ return true;
959
+ return loadUserSettings().autoCouncilClarify ?? true;
960
+ }
961
+ /**
962
+ * Whether auto-council should be skipped when the session model is a reasoning
963
+ * model. Default true. Env override wins over the user setting for quick dev
964
+ * toggling; env "0"/"false" disables the skip (forces council), "1"/"true"
965
+ * enables the skip.
966
+ */
967
+ export function isAutoCouncilSkipReasoning() {
968
+ const env = process.env.MUONROI_AUTOCOUNCIL_SKIP_REASONING?.trim().toLowerCase();
969
+ if (env === "0" || env === "false")
970
+ return false;
971
+ if (env === "1" || env === "true")
972
+ return true;
973
+ return loadUserSettings().autoCouncilSkipReasoning ?? true;
974
+ }
781
975
  export function getAutoCouncilMinRoles() {
782
976
  return normalizeAutoCouncilMinRoles(loadUserSettings().autoCouncilMinRoles);
783
977
  }
784
978
  export function isCouncilMultiProviderPreferred() {
785
- return loadUserSettings().councilPreferMultiProvider ?? false;
979
+ const user = loadUserSettings().councilPreferMultiProvider;
980
+ if (user !== undefined)
981
+ return user;
982
+ return getCatalogCouncilRouting()?.prefer_multi_provider ?? true;
786
983
  }
787
984
  export function getCouncilExperienceMode() {
788
985
  return loadUserSettings().councilExperienceMode ?? "advisory";
789
986
  }
987
+ export function normalizePeakHourPolicy(raw) {
988
+ if (!raw || typeof raw !== "object") {
989
+ return { enabled: true, mode: "switch" };
990
+ }
991
+ const p = raw;
992
+ return {
993
+ enabled: p.enabled !== false,
994
+ mode: p.mode === "downgrade" ? "downgrade" : "switch",
995
+ };
996
+ }
997
+ export function getPeakHourPolicy() {
998
+ return normalizePeakHourPolicy(loadUserSettings().peakHourPolicy);
999
+ }
790
1000
  export function isCouncilCostAware() {
791
1001
  return loadUserSettings().councilCostAware ?? true;
792
1002
  }
1003
+ /**
1004
+ * Normalize a raw councilLanguage value (Feature B). Trims + lowercases the two
1005
+ * reserved modes ("auto", "english"); any other non-empty string is preserved
1006
+ * as-is (trimmed) so locale labels keep the user's exact casing (e.g. "日本語").
1007
+ * Empty / non-string → "auto".
1008
+ */
1009
+ export function normalizeCouncilLanguage(raw) {
1010
+ if (typeof raw !== "string")
1011
+ return "auto";
1012
+ const trimmed = raw.trim();
1013
+ if (trimmed.length === 0)
1014
+ return "auto";
1015
+ const lower = trimmed.toLowerCase();
1016
+ if (lower === "auto" || lower === "english")
1017
+ return lower;
1018
+ return trimmed;
1019
+ }
1020
+ export function getCouncilLanguage() {
1021
+ return normalizeCouncilLanguage(loadUserSettings().councilLanguage);
1022
+ }
1023
+ /**
1024
+ * Router tier-promotion ceiling. See UserSettings.routingPromoteMax.
1025
+ * Default "balanced" — router may promote up to balanced but never silently
1026
+ * to premium. Validated to the three allowed values; any unknown value
1027
+ * falls back to the default.
1028
+ */
1029
+ export function getRoutingPromoteMax() {
1030
+ const raw = loadUserSettings().routingPromoteMax;
1031
+ return raw === "off" || raw === "balanced" || raw === "any" ? raw : "balanced";
1032
+ }
793
1033
  export function getDisabledProviders() {
794
1034
  const raw = loadUserSettings().disabledProviders;
795
1035
  if (!Array.isArray(raw))
@@ -1,4 +1,3 @@
1
- import type { ProviderFactory as LegacyProvider } from "../providers/runtime.js";
2
1
  export interface SideQuestionResult {
3
2
  response: string;
4
3
  usage?: {
@@ -7,4 +6,4 @@ export interface SideQuestionResult {
7
6
  outputTokens?: number;
8
7
  };
9
8
  }
10
- export declare function runSideQuestion(question: string, provider: LegacyProvider, modelId: string, conversationContext: string, signal?: AbortSignal): Promise<SideQuestionResult>;
9
+ export declare function runSideQuestion(question: string, modelId: string, conversationContext: string, signal?: AbortSignal): Promise<SideQuestionResult>;
@@ -3,8 +3,8 @@ import { resolveModelRuntime } from "../providers/runtime.js";
3
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
4
 
5
5
  If conversation context is provided below, use it to give a more relevant answer.`;
6
- export async function runSideQuestion(question, provider, modelId, conversationContext, signal) {
7
- const runtime = resolveModelRuntime(provider, modelId);
6
+ export async function runSideQuestion(question, modelId, conversationContext, signal) {
7
+ const runtime = resolveModelRuntime(modelId);
8
8
  const system = conversationContext
9
9
  ? `${SIDE_QUESTION_SYSTEM}\n\n<conversation_context>\n${conversationContext}\n</conversation_context>`
10
10
  : SIDE_QUESTION_SYSTEM;
@@ -17,4 +17,15 @@ export interface VisibleRetryOpts {
17
17
  /** Hook called before each delay; default writes to stderr. */
18
18
  onRetry?: (attempt: number, totalAttempts: number, delayMs: number, error: Error) => void;
19
19
  }
20
+ /**
21
+ * Optional UI sink for retry progress. The TUI registers one via
22
+ * {@link setRetryReporter}; without it (headless/CLI, tests) retries fall back
23
+ * to stderr. This exists because a raw `process.stderr.write` under OpenTUI's
24
+ * raw-mode alt-screen paints over wherever the cursor sits — the retry line was
25
+ * bleeding into the composer input frame (user-reported). Routing through a
26
+ * toast keeps it in the proper surface.
27
+ */
28
+ type RetryReporter = (message: string, level: "warn" | "info") => void;
29
+ export declare function setRetryReporter(fn: RetryReporter | null): void;
20
30
  export declare function withVisibleRetry<T>(fn: () => Promise<T>, opts?: VisibleRetryOpts): Promise<T>;
31
+ export {};
@@ -6,9 +6,18 @@ function isRetryableError(err) {
6
6
  const msg = (e?.message ?? "").toLowerCase();
7
7
  return msg.includes("rate limit") || msg.includes("too many requests") || msg.includes("timeout");
8
8
  }
9
+ let retryReporter = null;
10
+ export function setRetryReporter(fn) {
11
+ retryReporter = fn;
12
+ }
9
13
  function defaultOnRetry(attempt, total, delayMs, error) {
10
14
  const reason = error.statusCode === 429 ? "rate-limited (429)" : error.message.slice(0, 80);
11
- process.stderr.write(`[retry] ${reason} — waiting ${Math.round(delayMs / 1000)}s before attempt ${attempt + 1}/${total}\n`);
15
+ const message = `[retry] ${reason} — waiting ${Math.round(delayMs / 1000)}s before attempt ${attempt + 1}/${total}`;
16
+ if (retryReporter) {
17
+ retryReporter(message, "warn");
18
+ return;
19
+ }
20
+ process.stderr.write(`${message}\n`);
12
21
  }
13
22
  export async function withVisibleRetry(fn, opts = {}) {
14
23
  const delays = opts.delaysMs ?? [2000, 4000, 8000, 16000, 32000];
@@ -27,4 +27,4 @@ export declare function getVerifyCliError(options: {
27
27
  hasPrompt?: boolean;
28
28
  hasMessageArgs?: boolean;
29
29
  }): string | null;
30
- export { defaultShellInit, inferVerifyProjectProfile, inferVerifySmokeUrl, normalizeVerifyRecipe, type VerifyProjectProfile, } from "./recipes.js";
30
+ export { defaultShellInit, inferVerifyProjectProfile, inferVerifySmokeUrl, normalizeVerifyRecipe, shouldTrustDeterministicRecipe, type VerifyProjectProfile, } from "./recipes.js";
@@ -1,3 +1,4 @@
1
+ import { VERIFY_FAIL_MARKER, VERIFY_PASS_MARKER } from "../product-loop/verify-result.js";
1
2
  import { ensureVerifyCheckpoint } from "./checkpoint.js";
2
3
  import { loadVerifyEnvironment } from "./environment.js";
3
4
  import { buildBrowserGuidance, buildEvidenceGuidance, buildReadinessGuidance } from "./evidence.js";
@@ -69,6 +70,51 @@ export function buildVerifyTaskPrompt(cwd, settings, recipeOverride, sandboxMode
69
70
  "Phase 1 — Setup:",
70
71
  "- Probe the host for runtimes. If missing, attempt to install them or report as blockers.",
71
72
  ];
73
+ // Does this project actually expose a long-running app to start + smoke-test in
74
+ // a browser? Only then are Phase 3 (start app) / Phase 4 (browser QA) applicable.
75
+ // A CLI/library/script project (pytest-only Python, a Go/Rust binary, a plain
76
+ // script) has smokeKind "cli"/"none" and no startCommand — forcing "Start the
77
+ // app (REQUIRED)" + "browser QA (REQUIRED)" there is contradictory: the sub-agent
78
+ // cannot satisfy them, so it emits NEITHER verdict marker → parseVerifyResult →
79
+ // UNKNOWN → engineering-floor fails → the sprint retries forever on a passing
80
+ // build. Gate the runtime/browser phases and the verdict rule on this flag.
81
+ const hasRuntimeSmoke = profile.recipe.smokeKind === "http" && Boolean(profile.recipe.startCommand);
82
+ const runtimePhases = hasRuntimeSmoke
83
+ ? [
84
+ "Phase 3 — Start the app (REQUIRED, do not skip):",
85
+ "- Start the app using startCommand from the recipe, running it in the background.",
86
+ "- Wait for the app to be ready: use a curl readiness loop or `agent-browser wait --load networkidle`.",
87
+ "- If the app fails to start, report the error but still attempt to capture evidence (logs, screenshots).",
88
+ "",
89
+ "Phase 4 — Browser QA testing (REQUIRED, do not skip):",
90
+ "- You are a QA tester. Open the app in the browser and test it like a human would.",
91
+ `- agent-browser commands run on the HOST${sandboxMode === "shuru" ? ", not the sandbox" : ""}. They WILL work. Do not skip them.`,
92
+ "- Record a video of the entire browser session.",
93
+ "- Navigate the app: click links, buttons, menus. Verify pages load correctly.",
94
+ "- Check for JavaScript console errors.",
95
+ "- Spend 3-5 interactions testing the critical path. Take screenshots after each.",
96
+ "- This is the most important phase. Build/lint passing means nothing if the app doesn't actually work.",
97
+ "",
98
+ "Phase 5 — Teardown:",
99
+ "- Stop recording, close browser, THEN stop the dev server.",
100
+ ]
101
+ : [
102
+ "Phase 3 — Runtime smoke (CLI / library / script — there is NO long-running app to start):",
103
+ '- This project exposes no HTTP server or startable app (smokeKind is not "http"), so there is NOTHING to start in the background and NO browser QA to run.',
104
+ "- Do the equivalent CLI smoke instead: run the built binary / script (or the recipe's smoke command) once and confirm it produces the expected output and a zero exit code.",
105
+ "- Capture the exact command and its output as evidence.",
106
+ "- Browser and app-start phases are NOT APPLICABLE here — skipping them is CORRECT and is NOT a failure. Do NOT treat their absence as a blocker.",
107
+ ];
108
+ const verdictRule = hasRuntimeSmoke
109
+ ? [
110
+ `- After the report, emit the verdict on its own final line: exactly \`${VERIFY_PASS_MARKER}\` if install/build/test and the smoke/QA phases all succeeded, otherwise exactly \`${VERIFY_FAIL_MARKER}\`.`,
111
+ `- Emit \`${VERIFY_FAIL_MARKER}\` on ANY failed or skipped required phase (build error, failing test, app did not start, blocking console error). Do NOT emit \`${VERIFY_PASS_MARKER}\` if you could not actually run the recipe.`,
112
+ ]
113
+ : [
114
+ `- After the report, emit the verdict on its own final line: exactly \`${VERIFY_PASS_MARKER}\` if install/build/test (and the CLI smoke, if any) all succeeded, otherwise exactly \`${VERIFY_FAIL_MARKER}\`.`,
115
+ `- This is a CLI/library/script project with no app to start: the browser and app-start phases are N/A, NOT failures. Do NOT withhold \`${VERIFY_PASS_MARKER}\` merely because they were not run.`,
116
+ `- Emit \`${VERIFY_FAIL_MARKER}\` ONLY on a real failure: an install/build error, a failing test, or a CLI smoke that produced the wrong result. Do NOT emit \`${VERIFY_PASS_MARKER}\` if you could not actually run the recipe's tests.`,
117
+ ];
72
118
  return [
73
119
  "Run a local verification pass for the current workspace.",
74
120
  "",
@@ -90,22 +136,7 @@ export function buildVerifyTaskPrompt(cwd, settings, recipeOverride, sandboxMode
90
136
  "Phase 2 — Build and test:",
91
137
  "- Run installCommands, buildCommands, and testCommands from the recipe.",
92
138
  "",
93
- "Phase 3 — Start the app (REQUIRED, do not skip):",
94
- "- Start the app using startCommand from the recipe, running it in the background.",
95
- "- Wait for the app to be ready: use a curl readiness loop or `agent-browser wait --load networkidle`.",
96
- "- If the app fails to start, report the error but still attempt to capture evidence (logs, screenshots).",
97
- "",
98
- "Phase 4 — Browser QA testing (REQUIRED, do not skip):",
99
- "- You are a QA tester. Open the app in the browser and test it like a human would.",
100
- `- agent-browser commands run on the HOST${sandboxMode === "shuru" ? ", not the sandbox" : ""}. They WILL work. Do not skip them.`,
101
- "- Record a video of the entire browser session.",
102
- "- Navigate the app: click links, buttons, menus. Verify pages load correctly.",
103
- "- Check for JavaScript console errors.",
104
- "- Spend 3-5 interactions testing the critical path. Take screenshots after each.",
105
- "- This is the most important phase. Build/lint passing means nothing if the app doesn't actually work.",
106
- "",
107
- "Phase 5 — Teardown:",
108
- "- Stop recording, close browser, THEN stop the dev server.",
139
+ ...runtimePhases,
109
140
  ...buildReadinessGuidance(profile),
110
141
  ...buildBrowserGuidance(profile),
111
142
  ...buildRetryGuidance(profile),
@@ -122,6 +153,10 @@ export function buildVerifyTaskPrompt(cwd, settings, recipeOverride, sandboxMode
122
153
  "- The Summary must say what recipe/source of truth you used and whether you changed the inferred/default one.",
123
154
  "- Evidence is mandatory even on failure. If you captured screenshots, video, or logs, include their exact workspace-relative file paths in the Evidence section.",
124
155
  "- Use markdown links for artifact paths when practical, otherwise include the plain relative paths.",
156
+ "",
157
+ "Verdict marker (MANDATORY — the loop parses this, an absent marker scores the sprint 0.00):",
158
+ ...verdictRule,
159
+ "- The marker is the last line, nothing after it.",
125
160
  ].join("\n");
126
161
  }
127
162
  export function createVerifyTaskRequest(cwd, settings, recipeOverride, sandboxMode = "shuru") {
@@ -382,5 +417,5 @@ export function getVerifyCliError(options) {
382
417
  }
383
418
  return null;
384
419
  }
385
- export { defaultShellInit, inferVerifyProjectProfile, inferVerifySmokeUrl, normalizeVerifyRecipe, } from "./recipes.js";
420
+ export { defaultShellInit, inferVerifyProjectProfile, inferVerifySmokeUrl, normalizeVerifyRecipe, shouldTrustDeterministicRecipe, } from "./recipes.js";
386
421
  //# sourceMappingURL=entrypoint.js.map
@@ -1,5 +1,5 @@
1
1
  import type { TaskRequest, ToolResult, VerifyRecipe } from "../types/index.js";
2
- import type { SandboxSettings } from "../utils/settings.js";
2
+ import { type SandboxSettings } from "../utils/settings.js";
3
3
  import { type PreparedVerifyCheckpoint } from "./checkpoint.js";
4
4
  import { type VerifyProjectProfile } from "./recipes.js";
5
5
  export interface VerifyAgentLike {
@@ -1,3 +1,4 @@
1
+ import { getCurrentSandboxMode } from "../utils/settings.js";
1
2
  import { ensureVerifyCheckpoint } from "./checkpoint.js";
2
3
  import { buildVerifyTaskPrompt } from "./entrypoint.js";
3
4
  import { loadVerifyEnvironment, saveVerifyEnvironment } from "./environment.js";
@@ -39,8 +40,21 @@ export async function prepareVerifyRun(agent, options = {}) {
39
40
  }
40
41
  }
41
42
  const sandboxSettings = buildRuntimeSandboxSettings(profile);
42
- options.onProgress?.("Preparing verify checkpoint");
43
- const checkpoint = await ensureVerifyCheckpoint(cwd, profile, sandboxSettings);
43
+ // Sandbox "off" run the recipe directly on the host; do NOT bootstrap a
44
+ // `shuru` checkpoint. ensureVerifyCheckpoint spawns `shuru checkpoint …`
45
+ // whenever the recipe has installCommands, regardless of mode — on a host
46
+ // without shuru installed that throws "Executable not found in $PATH: shuru",
47
+ // which parseVerifyResult maps to ERROR (never PASS), pinning the sprint score
48
+ // at 0.00. Skip it when the sole source of truth (getCurrentSandboxMode) is off.
49
+ const checkpoint = getCurrentSandboxMode() === "off"
50
+ ? { created: false }
51
+ : await (async () => {
52
+ options.onProgress?.("Preparing verify checkpoint");
53
+ return ensureVerifyCheckpoint(cwd, profile, sandboxSettings);
54
+ })();
55
+ if (getCurrentSandboxMode() === "off") {
56
+ options.onProgress?.("Sandbox off — running verify on host (no shuru checkpoint)");
57
+ }
44
58
  if (checkpoint.checkpointName) {
45
59
  sandboxSettings.from = checkpoint.checkpointName;
46
60
  if (checkpoint.guestWorkdir) {
@@ -57,7 +71,10 @@ export async function prepareVerifyRun(agent, options = {}) {
57
71
  const taskRequest = {
58
72
  agent: "verify",
59
73
  description: "Run local verification",
60
- prompt: buildVerifyTaskPrompt(cwd, sandboxSettings, profile.recipe),
74
+ // Thread the resolved mode so the prompt tells the sub-agent to run "on the
75
+ // host" instead of "inside the active Shuru sandbox" when sandbox is off
76
+ // (the param defaults to "shuru", which was wrong for an off host).
77
+ prompt: buildVerifyTaskPrompt(cwd, sandboxSettings, profile.recipe, getCurrentSandboxMode()),
61
78
  };
62
79
  return {
63
80
  profile,