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,11 +1,18 @@
1
1
  import * as path from "node:path";
2
2
  import { runDebate } from "../council/debate.js";
3
+ import { deleteDebateInputs, readDebateCheckpoint, readDebateInputs, writeDebateInputs, } from "../council/debate-checkpoint.js";
4
+ import { resolveDebateSummary } from "../council/debate-summary.js";
3
5
  import { resolveLeaderModelDetailed, resolveParticipants } from "../council/leader.js";
4
6
  import { phaseStart } from "../council/phase-events.js";
5
7
  import { runPreflight } from "../council/preflight.js";
8
+ import { makeStanceRecall } from "../council/stance-recall.js";
6
9
  import { fetchBBContext, inferBBFromPrompt, renderBBContextBlock } from "../ee/bb-retrieval.js";
10
+ import { getDefaultEEClient } from "../ee/intercept.js";
11
+ import { fireAndForgetWorkflowEvent } from "../ee/workflow-event.js";
7
12
  import { readArtifact, writeArtifact } from "../flow/artifact-io.js";
8
- import { logInteraction, recordUsageEvent } from "../storage/index.js";
13
+ import { ensureRunScoped } from "../flow/hierarchy.js";
14
+ import { renderResumeDigest, writeContextDoc, writeResearchDoc } from "../flow/run-artifacts.js";
15
+ import { logInteraction } from "../storage/index.js";
9
16
  import { isCouncilMultiProviderPreferred } from "../utils/settings.js";
10
17
  import { extractAssumptionsFromDebate, mergeAssumptions, renderLedgerSummary } from "./assumption-ledger.js";
11
18
  import { buildPriorContext } from "./cross-run-memory.js";
@@ -17,60 +24,58 @@ import { recordPhaseEnd, recordPhaseStart } from "./phase-budget.js";
17
24
  import { additionalPrefills, auditAsContextBlock, auditRepo } from "./repo-audit.js";
18
25
  import { SEED_DIMENSIONS } from "./seed-questions.js";
19
26
  import { deriveTasksFromSpec, writeTasks } from "./typed-artifacts.js";
27
+ // Council usage_events recording (source="council") now happens at the single
28
+ // source of truth inside createCouncilLLM (src/council/llm.ts → recordCouncilUsage),
29
+ // so it covers EVERY council entry point — the /council slash path, auto-council,
30
+ // and all /ideal phases (clarifier, research, generate, sprint) — not just this
31
+ // driver's runDebate call site. The former loop-driver-local wrapper only wrapped
32
+ // the debate llm and double-counted nothing else; recording at the source removes
33
+ // both the leak and the risk of a double count here.
20
34
  /**
21
- * Wraps a CouncilLLM so every debate/research/generate call records a
22
- * usage_events row with source="council". debate.ts callers don't pass an
23
- * onUsage callback today, so the only place we can intercept is here, by
24
- * injecting our recorder into each method invocation before forwarding.
25
- * The original onUsage (if any) is still called.
35
+ * Best-effort interaction_logs writer for the loop-driver. Logs and continues on
36
+ * failure so a broken DB never blocks the FSM. `ctx.sessionId` falls back to
37
+ * runId for legacy callers that don't pass a chat session id. `cols` carries
38
+ * fields that belong in real columns rather than metadata_json.
26
39
  */
27
- function wrapLLMForUsageTracking(llm, ctx) {
28
- const sid = ctx.sessionId ?? ctx.runId;
29
- const recorder = (modelId) => (usage) => {
30
- try {
31
- recordUsageEvent(sid, "council", modelId, {
32
- inputTokens: usage.inputTokens,
33
- outputTokens: usage.outputTokens,
34
- cacheReadTokens: usage.cachedInputTokens,
35
- });
36
- }
37
- catch {
38
- /* best-effort — never block the debate */
39
- }
40
- };
41
- return {
42
- generate: (modelId, system, prompt, maxTokens, onUsage, signal) => llm.generate(modelId, system, prompt, maxTokens, (u) => {
43
- recorder(modelId)(u);
44
- onUsage?.(u);
45
- }, signal),
46
- debate: (modelId, system, prompt, signal, persistTrace, options, onUsage) => llm.debate(modelId, system, prompt, signal, persistTrace, options, (u) => {
47
- recorder(modelId)(u);
48
- onUsage?.(u);
49
- }),
50
- research: (modelId, topic, conversationContext, signal, persistTrace, options, onUsage) => llm.research(modelId, topic, conversationContext, signal, persistTrace, options, (u) => {
51
- recorder(modelId)(u);
52
- onUsage?.(u);
53
- }),
54
- };
55
- }
56
- /**
57
- * Best-effort interaction_logs writer for the loop-driver. Swallows failures
58
- * so a broken DB never blocks the FSM. `ctx.sessionId` falls back to runId
59
- * for legacy callers that don't pass a chat session id.
60
- */
61
- function logLoopEvent(ctx, subtype, data) {
40
+ function logLoopEvent(ctx, subtype, data, cols) {
62
41
  try {
63
42
  const sid = ctx.sessionId ?? ctx.runId;
64
43
  // Stamp runId on every row so multi-run sessions can be demuxed during
65
44
  // forensics — previously only route_decision carried this field. Caller
66
45
  // overrides win if the payload already contained a runId.
67
46
  const enriched = "runId" in data ? data : { ...data, runId: ctx.runId };
68
- logInteraction(sid, "council", { eventSubtype: subtype, data: enriched });
47
+ // `cols` promotes fields out of metadata_json into real columns so they are
48
+ // aggregatable in SQL. Without it every council_message row landed with
49
+ // duration_ms NULL, leaving `debate_complete` (the whole debate) as the only
50
+ // timed row — an aggregate that cannot name which speaker was slow.
51
+ logInteraction(sid, "council", {
52
+ eventSubtype: subtype,
53
+ data: enriched,
54
+ ...(cols?.model ? { model: cols.model } : {}),
55
+ ...(typeof cols?.durationMs === "number" ? { durationMs: cols.durationMs } : {}),
56
+ });
69
57
  }
70
- catch {
71
- /* non-criticalaudit trail only */
58
+ catch (err) {
59
+ // Audit trail only never block the FSM. Logged so a broken DB surfaces
60
+ // instead of silently emptying the forensics table. (No-Silent-Catch.)
61
+ console.error(`[loop-driver] logLoopEvent failed for subtype "${subtype}": ${err instanceof Error ? err.message : String(err)}`);
72
62
  }
73
63
  }
64
+ /**
65
+ * Clamp a title for the milestone/phase index (F12): truncate at a word
66
+ * boundary when possible and strip trailing punctuation/whitespace so a raw
67
+ * slice can't leave a dangling "(" or a half word. Falls back to a hard slice
68
+ * for a single long token.
69
+ */
70
+ export function clampTitle(raw, max) {
71
+ const s = (raw ?? "").trim();
72
+ if (s.length <= max)
73
+ return s.replace(/[\s([{<"'.,;:–—-]+$/u, "").trim() || s;
74
+ const hard = s.slice(0, max);
75
+ const lastSpace = hard.lastIndexOf(" ");
76
+ const base = lastSpace >= Math.floor(max * 0.6) ? hard.slice(0, lastSpace) : hard;
77
+ return base.replace(/[\s([{<"'.,;:–—-]+$/u, "").trim() || hard.trim();
78
+ }
74
79
  function buildWorkspaceDiscoveryCard(d, a, priorRunCount) {
75
80
  const sections = [];
76
81
  if (!d.hasProject) {
@@ -157,6 +162,12 @@ export async function* runLoopDriver(ctx) {
157
162
  let state = "idle";
158
163
  let clarifiedSpec;
159
164
  let debateState;
165
+ // Resolved once the research debate completes (F9): runningSummary, or a
166
+ // faithful fallback synthesized from participant positions when the debate
167
+ // returned after openings without a running summary. Reused across the
168
+ // research artifacts AND the scoping synthesis prompt so neither loses the
169
+ // debate. Empty until the research phase runs.
170
+ let resolvedDebateSummary = "";
160
171
  let discovery;
161
172
  let audit;
162
173
  let productSpec;
@@ -168,6 +179,28 @@ export async function* runLoopDriver(ctx) {
168
179
  const leaderResolution = await resolveLeaderModelDetailed(ctx.sessionModelId);
169
180
  const leaderModelId = leaderResolution.modelId;
170
181
  const councilParticipants = await resolveParticipants(ctx.sessionModelId, isCouncilMultiProviderPreferred());
182
+ // C-v2 cross-session resume — a persisted debate checkpoint means the council
183
+ // debate was interrupted in a PRIOR session (the process died mid-round; the
184
+ // in-process C-v1 retry only covers same-session throws). Skip the already-
185
+ // completed + persisted discovery + interview and jump straight to the
186
+ // research/debate stage with the restored gather outputs; the `research` case
187
+ // reads the checkpoint and resumes the debate from its last completed round.
188
+ const priorCheckpoint = await readDebateCheckpoint(runDir);
189
+ if (priorCheckpoint) {
190
+ const inputs = await readDebateInputs(runDir);
191
+ if (inputs) {
192
+ clarifiedSpec = inputs.clarifiedSpec;
193
+ conversationContext = inputs.conversationContext;
194
+ state = "research";
195
+ yield {
196
+ type: "content",
197
+ content: `\n> Resuming an interrupted council debate (round ${priorCheckpoint.roundCount + 1}) — skipping discovery + interview.\n`,
198
+ };
199
+ }
200
+ else {
201
+ console.error(`[loop-driver] debate checkpoint present but debate-inputs.json missing (run ${ctx.runId}); running the council FSM fresh.`);
202
+ }
203
+ }
171
204
  while (true) {
172
205
  switch (state) {
173
206
  case "idle": {
@@ -218,6 +251,21 @@ export async function* runLoopDriver(ctx) {
218
251
  if (discoveryCard) {
219
252
  yield { type: "council_info_card", councilInfoCard: discoveryCard };
220
253
  }
254
+ // Part A — persist prior-run context as a first-class context.md. The
255
+ // digest is intentionally NOT re-injected into the live system prompt
256
+ // (PIL Layer 3 already does semantic injection), but writing it to disk
257
+ // makes it a reviewable, resumable surface — the run's inherited memory.
258
+ try {
259
+ const ctxParts = [`Prior runs on this workspace: ${prior.runs.length}`];
260
+ if (prior.digest?.trim())
261
+ ctxParts.push("", prior.digest.trim());
262
+ if (audit.hasProject)
263
+ ctxParts.push("", "## Repo audit", conversationContext);
264
+ await writeContextDoc(ctx.flowDir, ctx.runId, ctxParts.join("\n"));
265
+ }
266
+ catch {
267
+ /* non-critical — context.md is a review surface, never blocks the FSM */
268
+ }
221
269
  // Persist discovery evidence + repo audit so resume can replay.
222
270
  const stateMap = (await readArtifact(runDir, "state.md")) ?? { preamble: "", sections: new Map() };
223
271
  if (discovery.evidence.length > 0) {
@@ -266,7 +314,12 @@ export async function* runLoopDriver(ctx) {
266
314
  });
267
315
  // Write Resume Digest to state.md
268
316
  const stateMap = (await readArtifact(runDir, "state.md")) ?? { preamble: "", sections: new Map() };
269
- stateMap.sections.set("Resume Digest", "Stage: Gather - Defining product dimensions");
317
+ stateMap.sections.set("Resume Digest", renderResumeDigest({
318
+ stage: "gather",
319
+ lastCompleted: "discover",
320
+ nextAction: "Answer clarification questions to define product dimensions",
321
+ updatedAt: new Date().toISOString(),
322
+ }));
270
323
  await writeArtifact(runDir, "state.md", stateMap);
271
324
  // runGatherPhase is async (not a generator), but the discovery
272
325
  // interview emits askcard chunks via the io.emit callback. We collect
@@ -390,6 +443,9 @@ export async function* runLoopDriver(ctx) {
390
443
  if (!clarifiedSpec) {
391
444
  return { runId: ctx.runId, stage: "error", success: false, reason: "missing_spec_for_research" };
392
445
  }
446
+ // Part E — web-research confidence for this run, filled when the
447
+ // Researcher stance is (re)assigned to a web-capable model below.
448
+ let researchWebConfidence;
393
449
  const researchPhaseStartMs = Date.now();
394
450
  yield phaseStart({
395
451
  phaseId: "loop:research",
@@ -402,7 +458,12 @@ export async function* runLoopDriver(ctx) {
402
458
  phase: "research",
403
459
  });
404
460
  const stateMap = (await readArtifact(runDir, "state.md")) ?? { preamble: "", sections: new Map() };
405
- stateMap.sections.set("Resume Digest", "Stage: Research - Multi-expert debate");
461
+ stateMap.sections.set("Resume Digest", renderResumeDigest({
462
+ stage: "research",
463
+ lastCompleted: "gather",
464
+ nextAction: "Run / resume the multi-expert council debate",
465
+ updatedAt: new Date().toISOString(),
466
+ }));
406
467
  await writeArtifact(runDir, "state.md", stateMap);
407
468
  // inside research phase, before building councilTopic:
408
469
  const projectCtx = await readProjectContext(ctx.flowDir, ctx.runId);
@@ -456,6 +517,42 @@ export async function* runLoopDriver(ctx) {
456
517
  stance: s,
457
518
  };
458
519
  });
520
+ // Part E — the Researcher stance MUST prefer a model with NATIVE online
521
+ // web research (its own web_search/browsing), so the research phase gets
522
+ // real online facts rather than codebase-only reasoning. If a web-capable
523
+ // model is reachable this session, route the Researcher to it; otherwise
524
+ // record degraded confidence into research.md (the fallback add-in path
525
+ // — Tavily/MCP — is untrusted per the owner's Part E principle).
526
+ const { modelHasNativeWebResearch, getWebResearchModel } = await import("../models/registry.js");
527
+ const reachableIds = new Set([...councilParticipants.map((p) => p.model), leaderModelId].filter(Boolean));
528
+ const researcherIdx = participants.findIndex((p) => p.stance?.name === "Researcher");
529
+ if (researcherIdx >= 0) {
530
+ const current = participants[researcherIdx].model;
531
+ if (!modelHasNativeWebResearch(current)) {
532
+ // ONLY reroute to a web-native model that is actually REACHABLE this
533
+ // session. A model whose provider has no factory wired (e.g. grok/xai
534
+ // when only opencode-go is authed) would fail council participant
535
+ // creation ("no factory for model's provider") and wedge the debate —
536
+ // so if no reachable web-native model exists, degrade gracefully and
537
+ // KEEP the current reachable model rather than swap in a dead id.
538
+ const webModel = getWebResearchModel(reachableIds);
539
+ if (webModel) {
540
+ participants[researcherIdx].model = webModel.id;
541
+ researchWebConfidence = { confidence: "native", model: webModel.id };
542
+ }
543
+ else {
544
+ researchWebConfidence = { confidence: "degraded" };
545
+ logLoopEvent(ctx, "research_web_degraded", {
546
+ phase: "research",
547
+ reason: "no reachable native_web_research model",
548
+ researcherModel: current,
549
+ });
550
+ }
551
+ }
552
+ else {
553
+ researchWebConfidence = { confidence: "native", model: current };
554
+ }
555
+ }
459
556
  // CB-1 — BB-aware context injection. Runs before council debate fires so
460
557
  // the research stances have access to relevant BB recipes and rules.
461
558
  //
@@ -503,67 +600,125 @@ export async function* runLoopDriver(ctx) {
503
600
  /* graceful degrade — never block the research phase */
504
601
  }
505
602
  }
506
- const debateGen = runDebate(clarifiedSpec, {
507
- topic: ctx.idea,
603
+ // C-v2 persist the minimal gather outputs so a fresh session can
604
+ // re-enter the debate (loop-driver resume entry above) without re-running
605
+ // the interactive discovery + interview. Written once here at debate start,
606
+ // captures the final conversationContext (after project + BB context).
607
+ // Deleted after scoping writes the spec. Non-fatal on failure.
608
+ await writeDebateInputs(runDir, {
609
+ version: 1,
610
+ problemStatement: clarifiedSpec.problemStatement,
611
+ clarifiedSpec,
508
612
  conversationContext,
509
- leaderModelId,
510
- participants,
511
- }, wrapLLMForUsageTracking(ctx.llm, ctx));
512
- // Suppress raw debate content so the user is not confused by inter-role
513
- // monologue ("Researcher Architect ... Question back to you?") which
514
- // is NOT addressed to them. We still pass through phase/status events
515
- // so the UI keeps a live progress indicator. After the debate completes
516
- // we emit a single condensed summary.
517
- try {
518
- while (true) {
519
- const { value, done } = await debateGen.next();
520
- if (done) {
521
- debateState = value;
522
- break;
613
+ savedAt: new Date().toISOString(),
614
+ });
615
+ // C (mid-debate checkpoint) — runDebate snapshots its per-round state to
616
+ // `<runDir>/debate-checkpoint.json` after each completed round and deletes
617
+ // it on normal completion. If a prior attempt (this process, or a crashed
618
+ // earlier run of the SAME runId) left a checkpoint, seed it so the debate
619
+ // resumes from the last completed round instead of re-running rounds 1..N.
620
+ let resumeCheckpoint = (await readDebateCheckpoint(runDir)) ?? undefined;
621
+ // In-process resilience: a mid-debate throw (provider 5xx after the
622
+ // fallback budget, transient network) re-runs runDebate ONCE (default)
623
+ // from the freshly-written checkpoint, so a round-5 break loses one round,
624
+ // not five. Only retried when a checkpoint exists (≥1 round completed)
625
+ // a pre-round-1 failure has nothing to resume and rethrows immediately.
626
+ const maxDebateRetries = (() => {
627
+ const raw = process.env.MUONROI_DEBATE_RESUME_RETRIES;
628
+ const n = raw ? Number.parseInt(raw, 10) : Number.NaN;
629
+ return Number.isFinite(n) && n >= 0 ? n : 1;
630
+ })();
631
+ for (let attempt = 0;; attempt++) {
632
+ const debateGen = runDebate(clarifiedSpec, {
633
+ topic: ctx.idea,
634
+ conversationContext,
635
+ leaderModelId,
636
+ participants,
637
+ runId: ctx.sessionId ?? ctx.runId,
638
+ checkpointDir: runDir,
639
+ resumeCheckpoint,
640
+ // Item 3 — per-stance recall: each participant opens grounded in the
641
+ // stance-weighted slice of the brain its lens cares about. Bounded +
642
+ // failure-tolerant; unavailable EE leaves openings unchanged.
643
+ stanceRecall: makeStanceRecall(getDefaultEEClient(), {
644
+ cwd: ctx.flowDir,
645
+ sourceSession: ctx.sessionId ?? ctx.runId,
646
+ }),
647
+ }, ctx.llm);
648
+ // Suppress raw debate content so the user is not confused by inter-role
649
+ // monologue ("Researcher → Architect ... Question back to you?") which
650
+ // is NOT addressed to them. We still pass through phase/status events
651
+ // so the UI keeps a live progress indicator. After the debate completes
652
+ // we emit a single condensed summary.
653
+ try {
654
+ while (true) {
655
+ const { value, done } = await debateGen.next();
656
+ if (done) {
657
+ debateState = value;
658
+ break;
659
+ }
660
+ const chunk = value;
661
+ if (chunk.type === "content")
662
+ continue;
663
+ // Persist debate speaker turns to interaction_logs so forensics
664
+ // can replay the debate text without relying on TUI scrollback
665
+ // (which currently holds the only copy — messages/usage_events
666
+ // tables stay empty for the debate path).
667
+ if (chunk.type === "council_message" && chunk.councilMessage) {
668
+ const cm = chunk.councilMessage;
669
+ logLoopEvent(ctx, "council_message", {
670
+ phase: "research",
671
+ kind: cm.kind,
672
+ speakerRole: cm.speaker.role,
673
+ speakerModel: cm.speaker.model,
674
+ partnerRole: cm.partner?.role ?? null,
675
+ round: cm.round ?? null,
676
+ attempts: cm.attempts ?? 1,
677
+ failureReason: cm.failureReason ?? null,
678
+ toolCalls: cm.toolCalls?.map((tc) => tc.name) ?? [],
679
+ // Cap at 4000 chars so a single row stays well under SQLite
680
+ // text limits even for the most verbose speaker turn.
681
+ textExcerpt: cm.text.slice(0, 4000),
682
+ textLength: cm.text.length,
683
+ durationMs: cm.durationMs ?? null,
684
+ }, { model: cm.speaker.model, durationMs: cm.durationMs });
685
+ }
686
+ yield chunk;
523
687
  }
524
- const chunk = value;
525
- if (chunk.type === "content")
688
+ break; // debate completed — exit the retry loop.
689
+ }
690
+ catch (err) {
691
+ // The debate iterator hit an exception (e.g. provider 5xx after the
692
+ // retry budget). Persist an audit row so we have a forensics trail —
693
+ // without it the FSM unwinds silently and the session looks like
694
+ // "research = 0 word" in the DB.
695
+ const freshCp = (await readDebateCheckpoint(runDir)) ?? undefined;
696
+ logLoopEvent(ctx, "council_error", {
697
+ phase: "research",
698
+ stage: "debate",
699
+ error: err instanceof Error ? err.message : String(err),
700
+ roundCount: debateState?.roundCount ?? freshCp?.roundCount ?? 0,
701
+ participantCount: participants.length,
702
+ elapsedMs: Date.now() - researchPhaseStartMs,
703
+ });
704
+ // C — resume-from-checkpoint retry: only when a completed round was
705
+ // checkpointed and retries remain. Otherwise rethrow unchanged.
706
+ if (attempt < maxDebateRetries && freshCp && freshCp.roundCount >= 1) {
707
+ resumeCheckpoint = freshCp;
708
+ yield {
709
+ type: "content",
710
+ content: `\n> Debate interrupted (${err instanceof Error ? err.message : String(err)}); resuming from round ${freshCp.roundCount + 1}…\n`,
711
+ };
526
712
  continue;
527
- // Persist debate speaker turns to interaction_logs so forensics
528
- // can replay the debate text without relying on TUI scrollback
529
- // (which currently holds the only copy — messages/usage_events
530
- // tables stay empty for the debate path).
531
- if (chunk.type === "council_message" && chunk.councilMessage) {
532
- const cm = chunk.councilMessage;
533
- logLoopEvent(ctx, "council_message", {
534
- phase: "research",
535
- kind: cm.kind,
536
- speakerRole: cm.speaker.role,
537
- speakerModel: cm.speaker.model,
538
- partnerRole: cm.partner?.role ?? null,
539
- round: cm.round ?? null,
540
- attempts: cm.attempts ?? 1,
541
- failureReason: cm.failureReason ?? null,
542
- toolCalls: cm.toolCalls?.map((tc) => tc.name) ?? [],
543
- // Cap at 4000 chars so a single row stays well under SQLite
544
- // text limits even for the most verbose speaker turn.
545
- textExcerpt: cm.text.slice(0, 4000),
546
- textLength: cm.text.length,
547
- });
548
713
  }
549
- yield chunk;
714
+ throw err;
550
715
  }
551
716
  }
552
- catch (err) {
553
- // The debate iterator hit an exception (e.g. provider 5xx after the
554
- // retry budget). Persist an audit row before re-throwing so we have
555
- // a forensics trail — without it the FSM unwinds silently and the
556
- // session looks like "research = 0 word" in the DB.
557
- logLoopEvent(ctx, "council_error", {
558
- phase: "research",
559
- stage: "debate",
560
- error: err instanceof Error ? err.message : String(err),
561
- roundCount: debateState?.roundCount ?? 0,
562
- participantCount: participants.length,
563
- elapsedMs: Date.now() - researchPhaseStartMs,
564
- });
565
- throw err;
566
- }
717
+ // F9 — resolve the debate summary once (runningSummary or a fallback
718
+ // synthesized from participant positions) for reuse across the research
719
+ // artifacts + scoping synthesis.
720
+ if (debateState)
721
+ resolvedDebateSummary = resolveDebateSummary(debateState);
567
722
  // Forensics row mirrors the council/index.ts council_summary record
568
723
  // (which only fires for sprint planning via runCouncil). The /ideal
569
724
  // initial debate goes through runDebate here and was previously
@@ -592,18 +747,47 @@ export async function* runLoopDriver(ctx) {
592
747
  durationMs: Date.now() - researchPhaseStartMs,
593
748
  });
594
749
  }
595
- const summaryText = debateState?.runningSummary?.trim() || "(debate produced no summary — using empty research findings)";
750
+ const summaryText = resolvedDebateSummary || "(debate produced no summary — using empty research findings)";
596
751
  yield {
597
752
  type: "council_info_card",
598
753
  councilInfoCard: buildResearchSummaryCard(summaryText, debateState?.researchFindings),
599
754
  };
600
- // Append research summary to delegations.md
755
+ // Append research summary to delegations.md (kept for back-compat: the
756
+ // EE transcript extractor + legacy readers still read these sections).
757
+ // A debate that returns after openings (leader routes straight to the
758
+ // preflight gate) leaves runningSummary empty — synthesize a faithful
759
+ // fallback from the participants' positions so the canonical research
760
+ // artifacts never silently drop the whole debate.
601
761
  const delegationsMap = (await readArtifact(runDir, "delegations.md")) ?? { preamble: "", sections: new Map() };
602
- delegationsMap.sections.set("Research Summary", debateState.runningSummary);
762
+ delegationsMap.sections.set("Research Summary", resolvedDebateSummary);
603
763
  if (debateState.researchFindings) {
604
764
  delegationsMap.sections.set("Research Findings", debateState.researchFindings);
605
765
  }
606
766
  await writeArtifact(runDir, "delegations.md", delegationsMap);
767
+ // Part A — promote the debate output to a first-class research.md. This
768
+ // is the canonical, reviewable surface (`/ideal review`) and the home
769
+ // for the EE recall seed once per-turn grounding lands (deferred).
770
+ try {
771
+ await writeResearchDoc(ctx.flowDir, ctx.runId, {
772
+ summary: resolvedDebateSummary,
773
+ findings: debateState.researchFindings ?? undefined,
774
+ webResearch: researchWebConfidence,
775
+ });
776
+ // Part C — persist the debate as a workflow_debate experience so a
777
+ // future run on a similar topic can seed its stances with what this
778
+ // council concluded (gate-on-outcome: fired after the debate produced
779
+ // a summary, not per-turn — Kill #4/#5).
780
+ fireAndForgetWorkflowEvent({
781
+ kind: "council-debate",
782
+ phaseRef: `runs/${ctx.runId}#research`,
783
+ sessionId: ctx.sessionId ?? ctx.runId,
784
+ text: (debateState.runningSummary ?? "").slice(0, 2000) || `debate on: ${ctx.idea.slice(0, 200)}`,
785
+ payload: { topic: ctx.idea.slice(0, 200), roundCount: debateState.roundCount ?? 0 },
786
+ });
787
+ }
788
+ catch {
789
+ /* non-critical — research.md is a review surface, never blocks the FSM */
790
+ }
607
791
  // P6 - extract assumptions from the research debate so foundational
608
792
  // claims (perf budgets, SDK contracts, etc.) become trackable across
609
793
  // sprints rather than buried in stance prose. Silent skip on
@@ -667,7 +851,12 @@ export async function* runLoopDriver(ctx) {
667
851
  phase: "scoping",
668
852
  });
669
853
  const stateMap = (await readArtifact(runDir, "state.md")) ?? { preamble: "", sections: new Map() };
670
- stateMap.sections.set("Resume Digest", "Stage: Scoping - Synthesizing product roadmap");
854
+ stateMap.sections.set("Resume Digest", renderResumeDigest({
855
+ stage: "scoping",
856
+ lastCompleted: "research",
857
+ nextAction: "Synthesize the ProductSpec and confirm the roadmap at the preflight gate",
858
+ updatedAt: new Date().toISOString(),
859
+ }));
671
860
  await writeArtifact(runDir, "state.md", stateMap);
672
861
  // Visible progress indicator — without this the TUI sits silent for
673
862
  // 30-60s while ctx.llm.generate runs the synthesis call. Users read
@@ -687,7 +876,7 @@ export async function* runLoopDriver(ctx) {
687
876
  const synthesisPrompt = `Synthesize a ProductSpec JSON based on the following:
688
877
  Idea: ${ctx.idea}
689
878
  Clarified Spec: ${JSON.stringify(clarifiedSpec)}
690
- Debate Summary: ${debateState.runningSummary}
879
+ Debate Summary: ${resolvedDebateSummary || debateState.runningSummary}
691
880
  Research Findings: ${debateState.researchFindings ?? "N/A"}
692
881
 
693
882
  Output ONLY a JSON object matching this interface:
@@ -758,6 +947,41 @@ interface ProductSpec {
758
947
  const roadmapMap = (await readArtifact(runDir, "roadmap.md")) ?? { preamble: "", sections: new Map() };
759
948
  roadmapMap.sections.set("Product Specification", JSON.stringify(productSpec, null, 2));
760
949
  await writeArtifact(runDir, "roadmap.md", roadmapMap);
950
+ // Part C — the synthesized spec IS the council's decision. Persist it as
951
+ // a workflow_decision experience so future runs don't re-litigate a
952
+ // settled architectural/scoping choice.
953
+ fireAndForgetWorkflowEvent({
954
+ kind: "decision",
955
+ phaseRef: `runs/${ctx.runId}#scoping`,
956
+ sessionId: ctx.sessionId ?? ctx.runId,
957
+ text: `Scoped "${ctx.idea.slice(0, 120)}": ${(productSpec?.architecture ?? "").slice(0, 400)}`,
958
+ payload: {
959
+ persona: productSpec?.persona ?? null,
960
+ mvp: productSpec?.mvp ?? [],
961
+ sprintEstimate: productSpec?.sprintEstimate ?? null,
962
+ },
963
+ });
964
+ // C-v2 — debate + scoping are done and the spec is persisted, so the
965
+ // cross-session resume inputs are obsolete (the checkpoint was already
966
+ // deleted by runDebate on completion). Clean them up so a later
967
+ // `/ideal resume` of this run does not re-enter the debate FSM.
968
+ await deleteDebateInputs(runDir);
969
+ // Hierarchy index — place this run under a milestone (the product idea)
970
+ // and a phase (this scoped iteration). Idempotent across resume; purely
971
+ // an index over runs/, never rewrites run artifacts or ROADMAP phases.
972
+ try {
973
+ const mvpHead = Array.isArray(productSpec?.mvp) && productSpec.mvp.length > 0 ? productSpec.mvp[0] : "";
974
+ await ensureRunScoped(ctx.flowDir, {
975
+ runId: ctx.runId,
976
+ milestoneTitle: clampTitle(ctx.idea, 60),
977
+ milestoneGoal: (productSpec?.architecture ?? "").slice(0, 200),
978
+ phaseTitle: clampTitle(mvpHead || ctx.idea, 50),
979
+ phaseGoal: (productSpec?.persona ?? "").slice(0, 200),
980
+ }, new Date().toISOString());
981
+ }
982
+ catch {
983
+ /* non-critical — hierarchy is an index; failure must not block scoping */
984
+ }
761
985
  // P8 - derive tasks.json from the spec (canonical machine-readable
762
986
  // surface for downstream /execute consumption). MVP items get
763
987
  // sprint=1, phase2 gets sprint=2. Re-deriving the same spec is
@@ -3,6 +3,27 @@ import type { Migrator } from "./discovery-migrations.js";
3
3
  import type { LeaderLike } from "./discovery-prompt-parser.js";
4
4
  import type { PhasePlanArtifact, ProductRunManifest } from "./types.js";
5
5
  export declare const PHASE_PLANNER_SYSTEM: string;
6
+ /**
7
+ * Normalise `id` + `dependsOn` on every phase to the canonical string form.
8
+ * Mutates and returns the same plan object. Safe on already-normalised plans.
9
+ */
10
+ /**
11
+ * Coerce a plan's per-phase `maxSprints` to a usable sprint COUNT: an integer
12
+ * in [1, 20]. This is a pure VALIDITY FLOOR — it does NOT allocate or divide any
13
+ * budget. The PO/leader decides each phase's `maxSprints` from that phase's
14
+ * difficulty (see PHASE_PLANNER_SYSTEM); this only defends the executor against a
15
+ * malformed value the model might still emit.
16
+ *
17
+ * The sprint loop is `for (sprintN = 1; sprintN <= phase.maxSprints; sprintN++)`,
18
+ * so any value < 1 makes `1 <= 0.n` false → the loop body NEVER runs → `runSprint`
19
+ * is never called → implementation is silently skipped (the historical
20
+ * "plan is great but implement never starts / hangs" bug — a small `--max-sprints`
21
+ * once made the old "divide the budget across phases" prompt emit fractions like
22
+ * `0.3 / 0.2 / 0.5`). Flooring to an integer ≥ 1 guarantees every planned phase
23
+ * executes at least one sprint regardless of what the model returns.
24
+ */
25
+ export declare function clampMaxSprints(raw: unknown): number;
26
+ export declare function normalizePhasePlan(plan: PhasePlanArtifact): PhasePlanArtifact;
6
27
  export declare function parsePhasePlanJson(raw: string): PhasePlanArtifact;
7
28
  export declare function validatePhasePlan(plan: PhasePlanArtifact, spec: ClarifiedSpec): void;
8
29
  export declare function fallbackSinglePhase(spec: ClarifiedSpec, manifest: ProductRunManifest): PhasePlanArtifact;