muonroi-cli 1.8.4 → 1.8.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (458) hide show
  1. package/LICENSE +17 -5
  2. package/dist/packages/agent-harness-core/src/driver.d.ts +27 -1
  3. package/dist/packages/agent-harness-core/src/driver.js +46 -0
  4. package/dist/packages/agent-harness-core/src/event-tee.d.ts +48 -0
  5. package/dist/packages/agent-harness-core/src/event-tee.js +77 -0
  6. package/dist/packages/agent-harness-core/src/mcp-server.d.ts +11 -0
  7. package/dist/packages/agent-harness-core/src/mcp-server.js +87 -15
  8. package/dist/packages/agent-harness-core/src/protocol.d.ts +66 -2
  9. package/dist/packages/agent-harness-core/src/protocol.js +15 -0
  10. package/dist/packages/agent-harness-core/src/visual-quality.d.ts +58 -0
  11. package/dist/packages/agent-harness-core/src/visual-quality.js +141 -0
  12. package/dist/packages/agent-harness-opentui/src/agent-mode.d.ts +6 -0
  13. package/dist/packages/agent-harness-opentui/src/agent-mode.js +14 -1
  14. package/dist/packages/agent-harness-opentui/src/input-bridge.d.ts +2 -10
  15. package/dist/packages/agent-harness-opentui/src/input-bridge.js +103 -16
  16. package/dist/packages/agent-harness-opentui/src/install.d.ts +8 -0
  17. package/dist/packages/agent-harness-opentui/src/install.js +10 -0
  18. package/dist/packages/agent-harness-opentui/src/semantic.js +12 -10
  19. package/dist/packages/agent-harness-opentui/src/visual-capture.d.ts +56 -0
  20. package/dist/packages/agent-harness-opentui/src/visual-capture.js +103 -0
  21. package/dist/src/agent-harness/mock-model.d.ts +28 -0
  22. package/dist/src/agent-harness/mock-model.js +63 -1
  23. package/dist/src/agent-harness/test-spawn.js +31 -0
  24. package/dist/src/cli/config/screen-providers.js +1 -1
  25. package/dist/src/cli/cost-forensics.d.ts +10 -0
  26. package/dist/src/cli/cost-forensics.js +18 -3
  27. package/dist/src/cli/keys-bundle.d.ts +1 -1
  28. package/dist/src/cli/keys-bundle.js +1 -1
  29. package/dist/src/cli/keys.d.ts +2 -2
  30. package/dist/src/cli/keys.js +19 -81
  31. package/dist/src/council/clarifier.d.ts +28 -2
  32. package/dist/src/council/clarifier.js +81 -15
  33. package/dist/src/council/context.js +49 -15
  34. package/dist/src/council/debate-checkpoint.d.ts +129 -0
  35. package/dist/src/council/debate-checkpoint.js +176 -0
  36. package/dist/src/council/debate-planner.js +51 -3
  37. package/dist/src/council/debate-summary.d.ts +25 -0
  38. package/dist/src/council/debate-summary.js +85 -0
  39. package/dist/src/council/debate.d.ts +169 -2
  40. package/dist/src/council/debate.js +1210 -134
  41. package/dist/src/council/index.d.ts +85 -1
  42. package/dist/src/council/index.js +634 -196
  43. package/dist/src/council/leader.d.ts +26 -0
  44. package/dist/src/council/leader.js +150 -9
  45. package/dist/src/council/llm.d.ts +32 -0
  46. package/dist/src/council/llm.js +231 -38
  47. package/dist/src/council/panel-select.d.ts +30 -0
  48. package/dist/src/council/panel-select.js +72 -0
  49. package/dist/src/council/planner.js +23 -0
  50. package/dist/src/council/preflight.d.ts +7 -0
  51. package/dist/src/council/preflight.js +14 -2
  52. package/dist/src/council/prompts.d.ts +30 -3
  53. package/dist/src/council/prompts.js +234 -64
  54. package/dist/src/council/stance-recall.d.ts +42 -0
  55. package/dist/src/council/stance-recall.js +57 -0
  56. package/dist/src/council/strip-think.d.ts +17 -0
  57. package/dist/src/council/strip-think.js +33 -0
  58. package/dist/src/council/types.d.ts +128 -0
  59. package/dist/src/ee/artifact-cache.d.ts +16 -0
  60. package/dist/src/ee/artifact-cache.js +32 -0
  61. package/dist/src/ee/auth.d.ts +1 -0
  62. package/dist/src/ee/auth.js +15 -2
  63. package/dist/src/ee/bridge.d.ts +10 -0
  64. package/dist/src/ee/bridge.js +58 -0
  65. package/dist/src/ee/client.js +81 -18
  66. package/dist/src/ee/export-transcripts.d.ts +1 -0
  67. package/dist/src/ee/export-transcripts.js +8 -10
  68. package/dist/src/ee/extract-session.js +29 -0
  69. package/dist/src/ee/extract-style.d.ts +58 -0
  70. package/dist/src/ee/extract-style.js +270 -0
  71. package/dist/src/ee/recall-ledger.d.ts +9 -0
  72. package/dist/src/ee/recall-ledger.js +3 -0
  73. package/dist/src/ee/scope.d.ts +1 -0
  74. package/dist/src/ee/scope.js +26 -1
  75. package/dist/src/ee/search.d.ts +7 -0
  76. package/dist/src/ee/search.js +24 -0
  77. package/dist/src/ee/transcript-emit.js +2 -0
  78. package/dist/src/ee/types.d.ts +22 -0
  79. package/dist/src/ee/who-am-i-brain.d.ts +35 -0
  80. package/dist/src/ee/who-am-i-brain.js +220 -0
  81. package/dist/src/ee/who-am-i.d.ts +10 -3
  82. package/dist/src/ee/who-am-i.js +12 -0
  83. package/dist/src/ee/workflow-event.d.ts +48 -0
  84. package/dist/src/ee/workflow-event.js +81 -0
  85. package/dist/src/flow/compaction/compress.d.ts +3 -3
  86. package/dist/src/flow/compaction/compress.js +45 -8
  87. package/dist/src/flow/compaction/extract.d.ts +4 -7
  88. package/dist/src/flow/compaction/extract.js +50 -10
  89. package/dist/src/flow/compaction/index.d.ts +13 -1
  90. package/dist/src/flow/compaction/index.js +70 -3
  91. package/dist/src/flow/compaction/input-guard.d.ts +24 -0
  92. package/dist/src/flow/compaction/input-guard.js +43 -0
  93. package/dist/src/flow/fold-planning.d.ts +36 -0
  94. package/dist/src/flow/fold-planning.js +83 -0
  95. package/dist/src/flow/hierarchy.d.ts +146 -0
  96. package/dist/src/flow/hierarchy.js +427 -0
  97. package/dist/src/flow/index.d.ts +1 -0
  98. package/dist/src/flow/index.js +2 -0
  99. package/dist/src/flow/run-artifacts.d.ts +102 -0
  100. package/dist/src/flow/run-artifacts.js +208 -0
  101. package/dist/src/generated/version.d.ts +1 -1
  102. package/dist/src/generated/version.js +1 -1
  103. package/dist/src/gsd/assessment-schema.d.ts +44 -0
  104. package/dist/src/gsd/assessment-schema.js +134 -0
  105. package/dist/src/gsd/capability-registry.d.ts +45 -0
  106. package/dist/src/gsd/capability-registry.js +337 -0
  107. package/dist/src/gsd/complexity-assessor.d.ts +39 -0
  108. package/dist/src/gsd/complexity-assessor.js +152 -0
  109. package/dist/src/gsd/config-bridge.d.ts +7 -0
  110. package/dist/src/gsd/config-bridge.js +114 -0
  111. package/dist/src/gsd/config-loader.d.ts +27 -0
  112. package/dist/src/gsd/config-loader.js +50 -0
  113. package/dist/src/gsd/council-context.d.ts +44 -0
  114. package/dist/src/gsd/council-context.js +114 -0
  115. package/dist/src/gsd/ee-closure.d.ts +28 -0
  116. package/dist/src/gsd/ee-closure.js +49 -0
  117. package/dist/src/gsd/flags.d.ts +55 -0
  118. package/dist/src/gsd/flags.js +83 -0
  119. package/dist/src/gsd/gsd-dispatch.d.ts +58 -0
  120. package/dist/src/gsd/gsd-dispatch.js +131 -0
  121. package/dist/src/gsd/gsd-runtime.d.ts +22 -0
  122. package/dist/src/gsd/gsd-runtime.js +37 -0
  123. package/dist/src/gsd/host-adapter.d.ts +11 -0
  124. package/dist/src/gsd/host-adapter.js +29 -0
  125. package/dist/src/gsd/index.d.ts +24 -1
  126. package/dist/src/gsd/index.js +27 -0
  127. package/dist/src/gsd/loop-host-contract.d.ts +21 -0
  128. package/dist/src/gsd/loop-host-contract.js +39 -0
  129. package/dist/src/gsd/loop-host.d.ts +69 -0
  130. package/dist/src/gsd/loop-host.js +245 -0
  131. package/dist/src/gsd/loop-resolver.d.ts +36 -0
  132. package/dist/src/gsd/loop-resolver.js +79 -0
  133. package/dist/src/gsd/model-tier.d.ts +13 -0
  134. package/dist/src/gsd/model-tier.js +45 -0
  135. package/dist/src/gsd/mutation-gate.d.ts +16 -0
  136. package/dist/src/gsd/mutation-gate.js +41 -0
  137. package/dist/src/gsd/native-roadmap.d.ts +89 -0
  138. package/dist/src/gsd/native-roadmap.js +343 -0
  139. package/dist/src/gsd/native-state.d.ts +47 -0
  140. package/dist/src/gsd/native-state.js +220 -0
  141. package/dist/src/gsd/paths.d.ts +23 -0
  142. package/dist/src/gsd/paths.js +66 -0
  143. package/dist/src/gsd/phase-dag.d.ts +12 -0
  144. package/dist/src/gsd/phase-dag.js +94 -0
  145. package/dist/src/gsd/phase-sync.d.ts +42 -0
  146. package/dist/src/gsd/phase-sync.js +321 -0
  147. package/dist/src/gsd/pil-gate-context.d.ts +13 -0
  148. package/dist/src/gsd/pil-gate-context.js +64 -0
  149. package/dist/src/gsd/pil-gate-critic.d.ts +19 -0
  150. package/dist/src/gsd/pil-gate-critic.js +74 -0
  151. package/dist/src/gsd/plan-council-prompts.d.ts +25 -0
  152. package/dist/src/gsd/plan-council-prompts.js +79 -0
  153. package/dist/src/gsd/plan-council.d.ts +44 -0
  154. package/dist/src/gsd/plan-council.js +251 -0
  155. package/dist/src/gsd/plan-gate-vocabulary.d.ts +40 -0
  156. package/dist/src/gsd/plan-gate-vocabulary.js +64 -0
  157. package/dist/src/gsd/product-workspace.d.ts +13 -0
  158. package/dist/src/gsd/product-workspace.js +124 -0
  159. package/dist/src/gsd/ship-bridge.d.ts +25 -0
  160. package/dist/src/gsd/ship-bridge.js +65 -0
  161. package/dist/src/gsd/state-document.d.ts +40 -0
  162. package/dist/src/gsd/state-document.js +163 -0
  163. package/dist/src/gsd/verdict-schema.d.ts +39 -0
  164. package/dist/src/gsd/verdict-schema.js +144 -0
  165. package/dist/src/gsd/verify-context.d.ts +22 -0
  166. package/dist/src/gsd/verify-context.js +27 -0
  167. package/dist/src/gsd/verify-council-prompts.d.ts +19 -0
  168. package/dist/src/gsd/verify-council-prompts.js +85 -0
  169. package/dist/src/gsd/verify-council.d.ts +25 -0
  170. package/dist/src/gsd/verify-council.js +119 -0
  171. package/dist/src/gsd/verify-gate-vocabulary.d.ts +25 -0
  172. package/dist/src/gsd/verify-gate-vocabulary.js +46 -0
  173. package/dist/src/gsd/workflow-engine.d.ts +60 -0
  174. package/dist/src/gsd/workflow-engine.js +207 -0
  175. package/dist/src/gsd/workflow-tools.d.ts +13 -0
  176. package/dist/src/gsd/workflow-tools.js +277 -0
  177. package/dist/src/hooks/index.js +1 -1
  178. package/dist/src/index.js +44 -11
  179. package/dist/src/maintain/pr-builder.js +23 -13
  180. package/dist/src/mcp/auto-setup.js +57 -32
  181. package/dist/src/mcp/client-pool.js +1 -1
  182. package/dist/src/mcp/ee-tools.js +1 -0
  183. package/dist/src/mcp/research-onboarding.js +8 -7
  184. package/dist/src/mcp/runtime.js +34 -2
  185. package/dist/src/models/catalog-client.d.ts +87 -0
  186. package/dist/src/models/catalog-client.js +105 -38
  187. package/dist/src/models/catalog.json +528 -265
  188. package/dist/src/models/registry.d.ts +22 -7
  189. package/dist/src/models/registry.js +73 -10
  190. package/dist/src/ops/doctor.js +1 -1
  191. package/dist/src/orchestrator/auto-commit.js +1 -1
  192. package/dist/src/orchestrator/batch-turn-runner.js +2 -2
  193. package/dist/src/orchestrator/cache-prefix.d.ts +67 -0
  194. package/dist/src/orchestrator/cache-prefix.js +83 -0
  195. package/dist/src/orchestrator/compact-request.d.ts +32 -0
  196. package/dist/src/orchestrator/compact-request.js +41 -0
  197. package/dist/src/orchestrator/compaction.d.ts +10 -0
  198. package/dist/src/orchestrator/compaction.js +27 -7
  199. package/dist/src/orchestrator/council-manager.d.ts +12 -3
  200. package/dist/src/orchestrator/council-manager.js +65 -24
  201. package/dist/src/orchestrator/cross-turn-dedup.d.ts +6 -0
  202. package/dist/src/orchestrator/cross-turn-dedup.js +22 -1
  203. package/dist/src/orchestrator/error-utils.d.ts +29 -0
  204. package/dist/src/orchestrator/error-utils.js +132 -24
  205. package/dist/src/orchestrator/grounding-check.js +39 -1
  206. package/dist/src/orchestrator/message-processor.js +242 -33
  207. package/dist/src/orchestrator/orchestrator.d.ts +39 -3
  208. package/dist/src/orchestrator/orchestrator.js +651 -102
  209. package/dist/src/orchestrator/preprocessor.js +1 -1
  210. package/dist/src/orchestrator/proactive-compact-detector.d.ts +26 -0
  211. package/dist/src/orchestrator/proactive-compact-detector.js +36 -0
  212. package/dist/src/orchestrator/prompts.js +17 -17
  213. package/dist/src/orchestrator/reactive-delegation.d.ts +39 -0
  214. package/dist/src/orchestrator/reactive-delegation.js +59 -0
  215. package/dist/src/orchestrator/retry-classifier.d.ts +2 -1
  216. package/dist/src/orchestrator/retry-classifier.js +46 -2
  217. package/dist/src/orchestrator/safety-intercept.d.ts +45 -0
  218. package/dist/src/orchestrator/safety-intercept.js +55 -0
  219. package/dist/src/orchestrator/scope-reminder.js +1 -1
  220. package/dist/src/orchestrator/session-experience.d.ts +2 -1
  221. package/dist/src/orchestrator/session-experience.js +2 -1
  222. package/dist/src/orchestrator/should-run-gate.d.ts +5 -0
  223. package/dist/src/orchestrator/should-run-gate.js +18 -0
  224. package/dist/src/orchestrator/stall-watchdog.d.ts +24 -3
  225. package/dist/src/orchestrator/stall-watchdog.js +47 -13
  226. package/dist/src/orchestrator/stream-runner.js +62 -29
  227. package/dist/src/orchestrator/sub-agent-cap.d.ts +13 -1
  228. package/dist/src/orchestrator/sub-agent-cap.js +16 -1
  229. package/dist/src/orchestrator/sub-agent-model-tier.d.ts +13 -1
  230. package/dist/src/orchestrator/sub-agent-model-tier.js +16 -2
  231. package/dist/src/orchestrator/subagent-compactor.d.ts +53 -1
  232. package/dist/src/orchestrator/subagent-compactor.js +126 -15
  233. package/dist/src/orchestrator/tool-engine.d.ts +22 -0
  234. package/dist/src/orchestrator/tool-engine.js +620 -56
  235. package/dist/src/orchestrator/tool-limit-auto-recover.d.ts +22 -0
  236. package/dist/src/orchestrator/tool-limit-auto-recover.js +30 -0
  237. package/dist/src/orchestrator/turn-runner-deps.d.ts +19 -0
  238. package/dist/src/orchestrator/turn-watchdog.d.ts +37 -0
  239. package/dist/src/orchestrator/turn-watchdog.js +55 -0
  240. package/dist/src/pil/agent-operating-contract.d.ts +1 -1
  241. package/dist/src/pil/agent-operating-contract.js +1 -1
  242. package/dist/src/pil/cheap-model-playbook.d.ts +1 -1
  243. package/dist/src/pil/cheap-model-playbook.js +5 -1
  244. package/dist/src/pil/cheap-model-workbooks.d.ts +1 -1
  245. package/dist/src/pil/cheap-model-workbooks.js +1 -1
  246. package/dist/src/pil/discovery-types.d.ts +1 -0
  247. package/dist/src/pil/discovery.js +16 -11
  248. package/dist/src/pil/layer1-intent.d.ts +18 -6
  249. package/dist/src/pil/layer1-intent.js +66 -757
  250. package/dist/src/pil/layer15-context-scan.js +15 -1
  251. package/dist/src/pil/layer3-ee-injection.js +23 -8
  252. package/dist/src/pil/layer4-gsd.js +69 -16
  253. package/dist/src/pil/layer5-context.js +7 -3
  254. package/dist/src/pil/layer6-output.d.ts +23 -0
  255. package/dist/src/pil/layer6-output.js +5 -1
  256. package/dist/src/pil/llm-classify.d.ts +33 -2
  257. package/dist/src/pil/llm-classify.js +123 -131
  258. package/dist/src/pil/native-capabilities-workbook.d.ts +1 -1
  259. package/dist/src/pil/native-capabilities-workbook.js +1 -0
  260. package/dist/src/pil/pipeline.js +34 -2
  261. package/dist/src/pil/response-tools.js +5 -3
  262. package/dist/src/pil/schema.d.ts +1 -0
  263. package/dist/src/pil/schema.js +2 -0
  264. package/dist/src/pil/types.d.ts +18 -0
  265. package/dist/src/playbook/directives.d.ts +4 -0
  266. package/dist/src/playbook/directives.js +17 -5
  267. package/dist/src/product-loop/backlog-builder.d.ts +14 -1
  268. package/dist/src/product-loop/backlog-builder.js +30 -6
  269. package/dist/src/product-loop/discovery-context-format.js +3 -1
  270. package/dist/src/product-loop/discovery-ecosystem.js +4 -1
  271. package/dist/src/product-loop/discovery-interview.js +32 -3
  272. package/dist/src/product-loop/discovery-schema.js +5 -1
  273. package/dist/src/product-loop/ideal-trace.d.ts +7 -0
  274. package/dist/src/product-loop/ideal-trace.js +64 -0
  275. package/dist/src/product-loop/index.d.ts +13 -1
  276. package/dist/src/product-loop/index.js +333 -52
  277. package/dist/src/product-loop/loop-driver.d.ts +7 -0
  278. package/dist/src/product-loop/loop-driver.js +310 -99
  279. package/dist/src/product-loop/phase-plan.d.ts +5 -0
  280. package/dist/src/product-loop/phase-plan.js +39 -2
  281. package/dist/src/product-loop/phase-runner.js +9 -1
  282. package/dist/src/product-loop/sprint-runner.d.ts +111 -0
  283. package/dist/src/product-loop/sprint-runner.js +559 -16
  284. package/dist/src/product-loop/types.d.ts +36 -5
  285. package/dist/src/providers/adapter.d.ts +1 -1
  286. package/dist/src/providers/adapter.js +3 -4
  287. package/dist/src/providers/auth/browser-flow.d.ts +1 -1
  288. package/dist/src/providers/auth/browser-flow.js +1 -1
  289. package/dist/src/providers/auth/openai-oauth.js +1 -1
  290. package/dist/src/providers/auth/registry.js +0 -34
  291. package/dist/src/providers/auth/token-store.js +4 -1
  292. package/dist/src/providers/auth/types.d.ts +1 -1
  293. package/dist/src/providers/auth/types.js +1 -1
  294. package/dist/src/providers/capabilities.d.ts +24 -5
  295. package/dist/src/providers/capabilities.js +42 -24
  296. package/dist/src/providers/endpoints.d.ts +2 -2
  297. package/dist/src/providers/endpoints.js +11 -10
  298. package/dist/src/providers/keychain.d.ts +1 -1
  299. package/dist/src/providers/keychain.js +7 -9
  300. package/dist/src/providers/mcp-vision-bridge.js +56 -146
  301. package/dist/src/providers/openai-compatible.js +8 -1
  302. package/dist/src/providers/pricing.d.ts +2 -2
  303. package/dist/src/providers/pricing.js +3 -13
  304. package/dist/src/providers/runtime.d.ts +27 -2
  305. package/dist/src/providers/runtime.js +78 -15
  306. package/dist/src/providers/strategies/base.strategy.d.ts +16 -0
  307. package/dist/src/providers/strategies/base.strategy.js +24 -1
  308. package/dist/src/providers/strategies/{siliconflow.strategy.d.ts → opencode-go.strategy.d.ts} +3 -3
  309. package/dist/src/providers/strategies/opencode-go.strategy.js +83 -0
  310. package/dist/src/providers/strategies/registry.js +4 -4
  311. package/dist/src/providers/strategies/thinking-mode.d.ts +109 -0
  312. package/dist/src/providers/strategies/thinking-mode.js +280 -1
  313. package/dist/src/providers/strategies/zai.strategy.d.ts +14 -0
  314. package/dist/src/providers/strategies/zai.strategy.js +44 -0
  315. package/dist/src/providers/types.d.ts +5 -6
  316. package/dist/src/providers/types.js +2 -2
  317. package/dist/src/providers/vision-backend.d.ts +47 -0
  318. package/dist/src/providers/vision-backend.js +258 -0
  319. package/dist/src/providers/vision-proxy.d.ts +22 -9
  320. package/dist/src/providers/vision-proxy.js +63 -132
  321. package/dist/src/providers/wire-debug.js +95 -0
  322. package/dist/src/router/decide.d.ts +13 -0
  323. package/dist/src/router/decide.js +138 -36
  324. package/dist/src/router/peak-hour.d.ts +38 -0
  325. package/dist/src/router/peak-hour.js +107 -0
  326. package/dist/src/router/step-router.js +3 -2
  327. package/dist/src/router/warm.js +4 -5
  328. package/dist/src/scaffold/continuation-prompt.d.ts +11 -0
  329. package/dist/src/scaffold/continuation-prompt.js +26 -0
  330. package/dist/src/scaffold/point-to-existing.d.ts +21 -0
  331. package/dist/src/scaffold/point-to-existing.js +25 -0
  332. package/dist/src/self-qa/agentic-loop.js +3 -3
  333. package/dist/src/{ui/state → state}/active-run.d.ts +19 -0
  334. package/dist/src/{ui/state → state}/active-run.js +21 -0
  335. package/dist/src/{ui/status-bar/store.d.ts → state/status-bar-store.d.ts} +4 -1
  336. package/dist/src/{ui/status-bar/store.js → state/status-bar-store.js} +12 -9
  337. package/dist/src/state/turn-trace.d.ts +43 -0
  338. package/dist/src/state/turn-trace.js +32 -0
  339. package/dist/src/storage/db.js +2 -1
  340. package/dist/src/storage/index.d.ts +1 -1
  341. package/dist/src/storage/index.js +1 -1
  342. package/dist/src/storage/interaction-log.d.ts +1 -1
  343. package/dist/src/storage/migrations.js +71 -1
  344. package/dist/src/storage/sessions.d.ts +28 -10
  345. package/dist/src/storage/sessions.js +78 -21
  346. package/dist/src/storage/transcript-view.js +1 -1
  347. package/dist/src/storage/transcript.d.ts +51 -0
  348. package/dist/src/storage/transcript.js +284 -13
  349. package/dist/src/tools/file.d.ts +15 -0
  350. package/dist/src/tools/file.js +32 -0
  351. package/dist/src/tools/native-tools.js +5 -0
  352. package/dist/src/tools/registry.d.ts +3 -0
  353. package/dist/src/tools/registry.js +460 -22
  354. package/dist/src/tools/research.d.ts +29 -0
  355. package/dist/src/tools/research.js +233 -0
  356. package/dist/src/types/index.d.ts +118 -3
  357. package/dist/src/ui/app.js +0 -0
  358. package/dist/src/ui/cards/product-status-card.js +1 -1
  359. package/dist/src/ui/components/bubble-body-guard.d.ts +27 -0
  360. package/dist/src/ui/components/bubble-body-guard.js +50 -0
  361. package/dist/src/ui/components/context-rail.d.ts +26 -0
  362. package/dist/src/ui/components/context-rail.js +33 -0
  363. package/dist/src/ui/components/council-conclusion-card.d.ts +73 -0
  364. package/dist/src/ui/components/council-conclusion-card.js +420 -0
  365. package/dist/src/ui/components/council-debate-pill.d.ts +36 -0
  366. package/dist/src/ui/components/council-debate-pill.js +34 -0
  367. package/dist/src/ui/components/council-info-card.js +2 -2
  368. package/dist/src/ui/components/council-leader-bubble.d.ts +10 -3
  369. package/dist/src/ui/components/council-leader-bubble.js +21 -11
  370. package/dist/src/ui/components/council-message-bubble.d.ts +15 -2
  371. package/dist/src/ui/components/council-message-bubble.js +16 -15
  372. package/dist/src/ui/components/council-phase-timeline.d.ts +9 -1
  373. package/dist/src/ui/components/council-phase-timeline.js +49 -15
  374. package/dist/src/ui/components/council-placeholder-bubble.d.ts +16 -9
  375. package/dist/src/ui/components/council-placeholder-bubble.js +32 -29
  376. package/dist/src/ui/components/council-question-card.js +12 -12
  377. package/dist/src/ui/components/council-rail-rounds.d.ts +26 -0
  378. package/dist/src/ui/components/council-rail-rounds.js +57 -0
  379. package/dist/src/ui/components/council-round-group.d.ts +38 -0
  380. package/dist/src/ui/components/council-round-group.js +88 -0
  381. package/dist/src/ui/components/council-status-list.d.ts +3 -1
  382. package/dist/src/ui/components/council-status-list.js +36 -24
  383. package/dist/src/ui/components/council-synthesis-banner.d.ts +7 -2
  384. package/dist/src/ui/components/council-synthesis-banner.js +20 -5
  385. package/dist/src/ui/components/halt-recovery-card.js +9 -5
  386. package/dist/src/ui/components/jump-to-latest-pill.d.ts +11 -0
  387. package/dist/src/ui/components/jump-to-latest-pill.js +14 -0
  388. package/dist/src/ui/components/prompt-box.js +18 -16
  389. package/dist/src/ui/components/session-tree-card.d.ts +14 -0
  390. package/dist/src/ui/components/session-tree-card.js +46 -0
  391. package/dist/src/ui/components/slash-inline-menu.d.ts +12 -0
  392. package/dist/src/ui/components/slash-inline-menu.js +26 -5
  393. package/dist/src/ui/components/task-list-panel.d.ts +14 -1
  394. package/dist/src/ui/components/task-list-panel.js +22 -2
  395. package/dist/src/ui/containers/modals-layer.d.ts +2 -1
  396. package/dist/src/ui/hooks/use-session-picker.d.ts +3 -3
  397. package/dist/src/ui/mcp-modal.js +2 -4
  398. package/dist/src/ui/modals/api-key-modal.js +1 -1
  399. package/dist/src/ui/modals/connect-modal.js +4 -3
  400. package/dist/src/ui/modals/session-picker-modal.d.ts +2 -2
  401. package/dist/src/ui/modals/session-picker-modal.js +3 -5
  402. package/dist/src/ui/picker-providers.d.ts +1 -1
  403. package/dist/src/ui/picker-providers.js +1 -1
  404. package/dist/src/ui/primitives/index.d.ts +1 -0
  405. package/dist/src/ui/primitives/index.js +2 -0
  406. package/dist/src/ui/primitives/semantic-primitives.d.ts +76 -0
  407. package/dist/src/ui/primitives/semantic-primitives.js +81 -0
  408. package/dist/src/ui/slash/compact.js +5 -7
  409. package/dist/src/ui/slash/cost.js +1 -1
  410. package/dist/src/ui/slash/council.js +19 -1
  411. package/dist/src/ui/slash/debug.d.ts +3 -31
  412. package/dist/src/ui/slash/debug.js +9 -20
  413. package/dist/src/ui/slash/ideal.d.ts +6 -2
  414. package/dist/src/ui/slash/ideal.js +97 -7
  415. package/dist/src/ui/slash/menu-items.d.ts +7 -0
  416. package/dist/src/ui/slash/menu-items.js +12 -18
  417. package/dist/src/ui/slash/registry.d.ts +2 -0
  418. package/dist/src/ui/slash/registry.js +4 -0
  419. package/dist/src/ui/status-bar/cache-hit.d.ts +6 -0
  420. package/dist/src/ui/status-bar/cache-hit.js +9 -0
  421. package/dist/src/ui/status-bar/index.d.ts +1 -1
  422. package/dist/src/ui/status-bar/index.js +7 -3
  423. package/dist/src/ui/status-bar/usd-meter.d.ts +5 -4
  424. package/dist/src/ui/status-bar/usd-meter.js +6 -4
  425. package/dist/src/ui/theme.d.ts +1 -0
  426. package/dist/src/ui/theme.js +2 -0
  427. package/dist/src/ui/types.d.ts +7 -0
  428. package/dist/src/ui/use-app-logic.js +0 -0
  429. package/dist/src/ui/utils/format.d.ts +14 -0
  430. package/dist/src/ui/utils/format.js +23 -3
  431. package/dist/src/usage/downgrade.js +2 -2
  432. package/dist/src/usage/product-ledger.js +2 -2
  433. package/dist/src/utils/install-manager.js +2 -1
  434. package/dist/src/utils/logger.js +2 -2
  435. package/dist/src/utils/permission-mode.js +5 -3
  436. package/dist/src/utils/redactor.js +1 -1
  437. package/dist/src/utils/settings.d.ts +153 -5
  438. package/dist/src/utils/settings.js +233 -29
  439. package/dist/src/utils/visible-retry.d.ts +11 -0
  440. package/dist/src/utils/visible-retry.js +10 -1
  441. package/dist/src/verify/entrypoint.d.ts +1 -1
  442. package/dist/src/verify/entrypoint.js +1 -1
  443. package/dist/src/verify/recipes.d.ts +13 -0
  444. package/dist/src/verify/recipes.js +15 -0
  445. package/package.json +135 -132
  446. package/dist/src/providers/auth/gcloud.d.ts +0 -28
  447. package/dist/src/providers/auth/gcloud.js +0 -102
  448. package/dist/src/providers/auth/gemini-oauth.d.ts +0 -82
  449. package/dist/src/providers/auth/gemini-oauth.js +0 -472
  450. package/dist/src/providers/gemini.d.ts +0 -11
  451. package/dist/src/providers/gemini.js +0 -45
  452. package/dist/src/providers/siliconflow-sse-repair.d.ts +0 -58
  453. package/dist/src/providers/siliconflow-sse-repair.js +0 -177
  454. package/dist/src/providers/strategies/google.strategy.d.ts +0 -22
  455. package/dist/src/providers/strategies/google.strategy.js +0 -174
  456. package/dist/src/providers/strategies/siliconflow.strategy.js +0 -29
  457. package/dist/src/ui/containers/chat-feed.d.ts +0 -40
  458. package/dist/src/ui/containers/chat-feed.js +0 -66
@@ -1,5 +1,5 @@
1
1
  import { pickCouncilTaskModel } from "./leader.js";
2
- import { tracedGenerate } from "./llm.js";
2
+ import { tracedGenerate, tracedGenerateWithFallback } from "./llm.js";
3
3
  import { phaseDone, phaseError, phaseStart } from "./phase-events.js";
4
4
  import { buildClarificationPrompt, buildReadinessJudgePrompt, buildSpecSynthesisPrompt } from "./prompts.js";
5
5
  /** P5: Hard cap on clarification rounds regardless of judge verdict. */
@@ -138,6 +138,45 @@ export function buildClarifyOptions(suggestions, recommended) {
138
138
  }
139
139
  return { options, defaultIndex };
140
140
  }
141
+ /**
142
+ * Build council options from the model's richer `{label, description,
143
+ * recommended}` objects, preserving each choice's explanation as `description`
144
+ * and pointing `defaultIndex` at the option the model flagged `recommended`
145
+ * (falling back to the first when none is flagged). The same "Type something" /
146
+ * "Chat about this" escape-hatches are appended as in `buildClarifyOptions`.
147
+ * Pure + exported for unit testing.
148
+ */
149
+ export function buildClarifyOptionsRich(specs) {
150
+ const cleaned = (specs ?? []).filter((s) => !!s && typeof s.label === "string" && s.label.trim().length > 0);
151
+ const choices = cleaned.map((s) => ({
152
+ label: s.label.trim(),
153
+ value: s.label.trim(),
154
+ kind: "choice",
155
+ ...(typeof s.description === "string" && s.description.trim().length > 0
156
+ ? { description: s.description.trim() }
157
+ : {}),
158
+ }));
159
+ const options = [
160
+ ...choices,
161
+ {
162
+ label: "Type something",
163
+ description: "Nhập câu trả lời tự do",
164
+ value: "",
165
+ kind: "freetext",
166
+ },
167
+ {
168
+ label: "Chat about this",
169
+ description: "Thảo luận thêm trước khi trả lời",
170
+ value: "",
171
+ kind: "chat",
172
+ },
173
+ ];
174
+ let defaultIndex;
175
+ const recIdx = cleaned.findIndex((s) => s.recommended === true);
176
+ if (recIdx !== -1)
177
+ defaultIndex = recIdx;
178
+ return { options, defaultIndex };
179
+ }
141
180
  export async function* runClarification(topic, leaderModelId, conversationContext, respondToQuestion, llm, signal, seedQuestions, maxRounds,
142
181
  /**
143
182
  * Optional pre-filled answers keyed by seed-question id. When the loop's
@@ -152,7 +191,13 @@ prefillAnswers,
152
191
  * a cheaper tier model on the same provider as the leader. Falls back to
153
192
  * the leader model if no cheaper model is cataloged. Default false.
154
193
  */
155
- costAware = false) {
194
+ costAware = false,
195
+ /**
196
+ * Healthy panel models to fall back to when the leader's cost-tier spec-synth
197
+ * model is on a failing proxy — keeps the spec's ≥3 observable criteria (and
198
+ * thus a meaningful leader eval) instead of degrading to one generic criterion.
199
+ */
200
+ fallbackModels = []) {
156
201
  // P5: use MAX_CLARIFY_ROUNDS (12) as the hard cap; respect explicit override
157
202
  // from callers that pass maxRounds (e.g. tests that want old 3-round behavior).
158
203
  const max = typeof maxRounds === "number" && maxRounds > 0 ? maxRounds : MAX_CLARIFY_ROUNDS;
@@ -284,7 +329,16 @@ costAware = false) {
284
329
  continue;
285
330
  }
286
331
  const questionId = crypto.randomUUID();
287
- const { options, defaultIndex } = buildClarifyOptions(q.suggestions, q.recommended);
332
+ // Prefer the model's richer {label, description, recommended} options so
333
+ // every choice shows its own explanation + the recommended default is
334
+ // pre-selected. Fall back to the legacy string `suggestions` shape (PIL
335
+ // gray-area seeds and any model still emitting the old format).
336
+ const { options, defaultIndex } = q.options && q.options.length > 0
337
+ ? buildClarifyOptionsRich(q.options)
338
+ : buildClarifyOptions(q.suggestions, q.recommended);
339
+ // Keep the deprecated `suggestions` mirror populated for consumers that
340
+ // still read it (audit/replay), deriving labels from rich options.
341
+ const suggestionLabels = q.suggestions ?? (q.options ? q.options.map((o) => o.label).filter((l) => typeof l === "string") : undefined);
288
342
  yield {
289
343
  type: "council_question",
290
344
  content: `**${q.question}**\n${q.why ? `> ${q.why}` : ""}`,
@@ -293,7 +347,7 @@ costAware = false) {
293
347
  phase: "clarify",
294
348
  question: q.question,
295
349
  context: q.why,
296
- suggestions: q.suggestions,
350
+ suggestions: suggestionLabels,
297
351
  options,
298
352
  isRequired: q.isRequired,
299
353
  defaultIndex,
@@ -329,7 +383,7 @@ costAware = false) {
329
383
  startedAt: phaseStartedAt,
330
384
  detail: allQA.length > 0 ? `${allQA.length} Q&A` : "no clarification needed",
331
385
  });
332
- const spec = yield* synthesizeSpec(topic, conversationContext, allQA, leaderModelId, llm, costAware);
386
+ const spec = yield* synthesizeSpec(topic, conversationContext, allQA, leaderModelId, llm, costAware, fallbackModels);
333
387
  // P5: attach ready-gate metadata to the returned spec
334
388
  spec.confidenceScore = gateConfidence;
335
389
  spec.remainingGaps = gateGaps;
@@ -355,13 +409,18 @@ costAware = false) {
355
409
  };
356
410
  return spec;
357
411
  }
358
- export function buildSpecFromTopic(topic, _conversationContext) {
412
+ export function buildSpecFromTopic(topic, conversationContext) {
359
413
  return {
360
414
  problemStatement: topic,
361
415
  constraints: [],
362
416
  successCriteria: [`Address the topic: ${topic.slice(0, 100)}`],
363
417
  scope: "Determined by conversation context",
364
418
  rawQA: [],
419
+ // Carry the session/task context onto the spec so every debate stage (not
420
+ // just the opening) can stay anchored to the parent task. Previously this
421
+ // argument was dropped (prefixed `_`), which is why auto-council debates saw
422
+ // only the isolated sub-task and drifted off-topic.
423
+ parentContext: conversationContext?.trim() || undefined,
365
424
  };
366
425
  }
367
426
  /**
@@ -374,7 +433,7 @@ export function buildSpecFromTopic(topic, _conversationContext) {
374
433
  * We LLM-extract criteria/constraints/scope from the topic when QA is empty.
375
434
  * Falls back to the original buildSpecFromTopic if the LLM call fails.
376
435
  */
377
- async function* inferSpecFromTopicOnly(topic, conversationContext, leaderModelId, llm, costAware) {
436
+ async function* inferSpecFromTopicOnly(topic, conversationContext, leaderModelId, llm, costAware, fallbackModels = []) {
378
437
  const system = `You are extracting an implicit specification from a short topic statement. ` +
379
438
  `The user did not answer clarification questions, but the topic itself often ` +
380
439
  `implies scope, criteria, and constraints. Tease them out.\n\n` +
@@ -396,16 +455,21 @@ async function* inferSpecFromTopicOnly(topic, conversationContext, leaderModelId
396
455
  `- Write all fields in the user's language (detected from the topic).`;
397
456
  const prompt = `## Topic\n${topic}\n\n${conversationContext ? `## Context\n${conversationContext}\n` : ""}`;
398
457
  try {
458
+ // Model-fallback: the cost-tier synth model can sit on a flaky proxy
459
+ // (Console Go glm/kimi → "Upstream request failed"). Falling back to
460
+ // buildSpecFromTopic yields a single generic criterion, which makes the
461
+ // leader eval read a vague "1/1 criteria met". Retry on healthy panel models
462
+ // first so the spec keeps its ≥3 observable criteria.
399
463
  const synthModel = pickCouncilTaskModel("spec_synthesis", leaderModelId, costAware);
400
- const raw = yield* tracedGenerate(llm, {
464
+ const raw = yield* tracedGenerateWithFallback(llm, {
401
465
  phase: "synthesis",
402
466
  label: "Inferring spec from topic (no clarification answers)",
403
- modelId: synthModel,
467
+ models: [synthModel, ...fallbackModels],
404
468
  system,
405
469
  prompt,
406
470
  maxTokens: 1024,
407
471
  });
408
- const match = raw.match(/\{[\s\S]*\}/);
472
+ const match = raw?.match(/\{[\s\S]*\}/);
409
473
  if (match) {
410
474
  const parsed = JSON.parse(match[0]);
411
475
  const criteria = Array.isArray(parsed.successCriteria) && parsed.successCriteria.length >= 1
@@ -429,12 +493,12 @@ async function* inferSpecFromTopicOnly(topic, conversationContext, leaderModelId
429
493
  }
430
494
  return buildSpecFromTopic(topic, conversationContext);
431
495
  }
432
- async function* synthesizeSpec(topic, conversationContext, qa, leaderModelId, llm, costAware = false) {
496
+ async function* synthesizeSpec(topic, conversationContext, qa, leaderModelId, llm, costAware = false, fallbackModels = []) {
433
497
  if (qa.length === 0) {
434
498
  // Clarifier asked 0 questions OR user skipped all of them. Use the LLM to
435
499
  // pull implicit criteria/constraints/scope from the topic alone instead of
436
500
  // returning the single-criterion fallback that makes leader-eval trivial.
437
- return yield* inferSpecFromTopicOnly(topic, conversationContext, leaderModelId, llm, costAware);
501
+ return yield* inferSpecFromTopicOnly(topic, conversationContext, leaderModelId, llm, costAware, fallbackModels);
438
502
  }
439
503
  const { system, prompt } = buildSpecSynthesisPrompt(topic, conversationContext, qa);
440
504
  const resolved = {};
@@ -444,16 +508,18 @@ async function* synthesizeSpec(topic, conversationContext, qa, leaderModelId, ll
444
508
  }
445
509
  }
446
510
  try {
511
+ // Model-fallback (see inferSpecFromTopicOnly): keep the ≥3-criteria spec even
512
+ // when the leader's cost-tier synth model is on a failing proxy.
447
513
  const synthModel = pickCouncilTaskModel("spec_synthesis", leaderModelId, costAware);
448
- const raw = yield* tracedGenerate(llm, {
514
+ const raw = yield* tracedGenerateWithFallback(llm, {
449
515
  phase: "synthesis",
450
516
  label: "Synthesizing clarified spec",
451
- modelId: synthModel,
517
+ models: [synthModel, ...fallbackModels],
452
518
  system,
453
519
  prompt,
454
520
  maxTokens: 2048,
455
521
  });
456
- const match = raw.match(/\{[\s\S]*\}/);
522
+ const match = raw?.match(/\{[\s\S]*\}/);
457
523
  if (match) {
458
524
  const parsed = JSON.parse(match[0]);
459
525
  return {
@@ -98,7 +98,7 @@ function getCompactionText(msg) {
98
98
  return null;
99
99
  return msg.content;
100
100
  }
101
- function extractUserText(content) {
101
+ function extractText(content) {
102
102
  if (typeof content === "string")
103
103
  return content;
104
104
  if (Array.isArray(content)) {
@@ -109,40 +109,74 @@ function extractUserText(content) {
109
109
  }
110
110
  return "";
111
111
  }
112
+ /** Filter assistant messages to exclude tool-call-only turns (tool_use + tool_result
113
+ * pairs that carry no analytical text). An assistant turn with ≥80 chars of text
114
+ * content is considered "analytical" (contains decisions, analysis, code, or plans). */
115
+ function isAnalyticalAssistant(content) {
116
+ const text = extractText(content);
117
+ return text.trim().length >= 80;
118
+ }
119
+ /** Maximum total characters for the built context string. Raised from 12K to 32K
120
+ * (~8K tokens) so council participants get meaningful session history rather than
121
+ * only 5 user-message snippets. */
122
+ const MAX_CONTEXT_CHARS = 32_000;
112
123
  export function buildCouncilContext(messages) {
113
124
  const parts = [];
125
+ // 1. Compaction summary (first message if it's a system compaction)
114
126
  if (messages.length > 0 && isCompactionSummary(messages[0])) {
115
127
  const summary = getCompactionText(messages[0]);
116
128
  if (summary) {
117
129
  parts.push(`## Session Context (from compaction summary)\n${summary}`);
118
130
  }
119
131
  }
120
- const userMessages = [];
121
- for (let i = messages.length - 1; i >= 0 && userMessages.length < 5; i--) {
132
+ // 2. Recent conversation turns — scan BOTH user + analytical assistant messages
133
+ // from the tail, capturing at most 8 meaningful entries.
134
+ // Previously only captured 5 user messages — lost ALL assistant analysis,
135
+ // code changes, and decisions made during the session.
136
+ const conversationTurns = [];
137
+ for (let i = messages.length - 1; i >= 0 && conversationTurns.length < 8; i--) {
122
138
  const msg = messages[i];
123
139
  if (msg.role === "user") {
124
- const text = extractUserText(msg.content);
140
+ const text = extractText(msg.content);
141
+ if (text.trim()) {
142
+ conversationTurns.unshift(`[User] ${text.slice(0, 3000).trim()}`);
143
+ }
144
+ }
145
+ else if (msg.role === "assistant" && isAnalyticalAssistant(msg.content)) {
146
+ const text = extractText(msg.content);
125
147
  if (text.trim()) {
126
- userMessages.unshift(`- ${text.slice(0, 2000).trim()}`);
148
+ // Cap assistant text to 1500 chars — enough to convey analysis/decisions
149
+ // without blowing the context budget on long code outputs.
150
+ conversationTurns.unshift(`[Assistant] ${text.slice(0, 1500).trim()}`);
127
151
  }
128
152
  }
129
153
  }
130
- if (userMessages.length > 0) {
131
- parts.push(`## Recent User Messages\n${userMessages.join("\n")}`);
154
+ if (conversationTurns.length > 0) {
155
+ parts.push(`## Recent Conversation\n${conversationTurns.join("\n\n")}`);
132
156
  }
133
- const councilMemories = [];
157
+ // 3. Key decisions from system messages (task outcomes, decisions, plans)
158
+ const decisionMsgs = [];
134
159
  for (const msg of messages) {
135
- if (msg.role === "system" && typeof msg.content === "string" && msg.content.includes("[Council Memory]")) {
136
- councilMemories.push(msg.content);
160
+ if (msg.role !== "system" || typeof msg.content !== "string")
161
+ continue;
162
+ const c = msg.content;
163
+ if (c.includes("[Council Memory]") ||
164
+ c.includes("[Council Decision]") ||
165
+ c.includes("[Council Action Items]") ||
166
+ c.includes("[Council Plan Update]") ||
167
+ c.includes("[Decision]") ||
168
+ c.includes("[Task Completed]") ||
169
+ c.includes("[NEEDS HUMAN REVIEW]")) {
170
+ decisionMsgs.push(c);
137
171
  }
138
172
  }
139
- if (councilMemories.length > 0) {
140
- const digests = councilMemories.slice(-2).map(formatCouncilMemoryDigest);
141
- parts.push(`## Previous Council Outcomes (cite by role/round)\n${digests.join("\n\n")}`);
173
+ if (decisionMsgs.length > 0) {
174
+ const digests = decisionMsgs.slice(-4).map(formatCouncilMemoryDigest);
175
+ parts.push(`## Key Decisions\n${digests.join("\n\n")}`);
142
176
  }
143
177
  const combined = parts.join("\n\n---\n\n");
144
- if (combined.length > 12000) {
145
- return `${combined.slice(0, 12000)}\n\n[... context truncated to fit token budget]`;
178
+ if (combined.length > MAX_CONTEXT_CHARS) {
179
+ return `${combined.slice(0, MAX_CONTEXT_CHARS)}\n\n[... context truncated to fit token budget]`;
146
180
  }
147
181
  return combined;
148
182
  }
@@ -0,0 +1,129 @@
1
+ /**
2
+ * src/council/debate-checkpoint.ts
3
+ *
4
+ * C (mid-debate checkpoint) — persist the CB-1 council debate's per-round state
5
+ * so a break mid-debate (provider 5xx on round 5, host crash, cap breach) can
6
+ * resume from the last completed round instead of re-running the whole debate.
7
+ *
8
+ * The debate accumulates all state in RAM (`exchangeLogs`, `active`,
9
+ * `runningSummary`, `archive`, convergence trackers). Without a checkpoint a
10
+ * break loses every round — a 5-round multi-provider debate must restart from
11
+ * round 1. This module owns the on-disk snapshot: `runDebate` writes it after
12
+ * each completed round and deletes it on normal completion; the resume path
13
+ * reads it and seeds `runDebate` so it skips research + openings + completed
14
+ * rounds.
15
+ *
16
+ * File: `<checkpointDir>/debate-checkpoint.json` (checkpointDir = the run dir,
17
+ * `.muonroi-flow/runs/<runId>`). Written atomically (tmp + rename) so a crash
18
+ * mid-write can never leave a half-JSON file.
19
+ */
20
+ import type { ClarifiedSpec, CouncilParticipant, DebateArchiveEntry } from "./types.js";
21
+ /** Bump when the checkpoint shape changes incompatibly so stale files are ignored. */
22
+ export declare const DEBATE_CHECKPOINT_VERSION: 1;
23
+ export declare const DEBATE_CHECKPOINT_FILE = "debate-checkpoint.json";
24
+ /**
25
+ * Serializable snapshot of an in-flight debate, written after each completed
26
+ * discussion round. `exchangeLogs` (a `Map`) is stored as entry tuples since
27
+ * JSON has no Map; everything else in the payload is already JSON-native.
28
+ */
29
+ export interface DebateCheckpoint {
30
+ version: typeof DEBATE_CHECKPOINT_VERSION;
31
+ /**
32
+ * Identity guard — the checkpoint is only restored when it matches the debate
33
+ * about to run. `problemStatement` pins the spec; `participantModels` pins the
34
+ * panel. A mismatch (spec changed, panel re-resolved differently) => ignore
35
+ * the stale checkpoint and run the debate fresh rather than splice wrong state.
36
+ */
37
+ problemStatement: string;
38
+ participantModels: string[];
39
+ /** Rounds fully completed (transcript + summary persisted). Resume starts at roundCount+1. */
40
+ roundCount: number;
41
+ /** Current round budget after any leader/user extensions. */
42
+ maxRounds: number;
43
+ /** Accumulated per-pair transcript (Map<pairKey, turns[]> serialized as entries). */
44
+ exchangeLogs: Array<[string, string[]]>;
45
+ /** Latest inter-round summary. */
46
+ runningSummary: string;
47
+ /** Research findings (so the research phase is not re-run on resume). */
48
+ researchFindings?: string;
49
+ /** Surviving participants with their latest position + stance (so openings are not re-run). */
50
+ active: CouncilParticipant[];
51
+ /** Citation archive accumulated so far. */
52
+ archive: DebateArchiveEntry[];
53
+ /** Convergence continuity: last round's per-criterion met flags. */
54
+ lastCriteriaMet: boolean[];
55
+ /** B4 auto-remedy high-water mark of pinned criteria met. */
56
+ bestCriteriaMetCount: number;
57
+ /** B4 consecutive evaluated rounds with no new met criterion. */
58
+ roundsSinceProgress: number;
59
+ /** Topic carried into the next round from the prior leader nextRoundFocus. */
60
+ nextTopic?: string;
61
+ /** Wall-clock the checkpoint was written (ISO). Diagnostics only. */
62
+ savedAt: string;
63
+ }
64
+ /** Input to {@link buildDebateCheckpoint} — the live round-loop locals. */
65
+ export interface DebateCheckpointInput {
66
+ problemStatement: string;
67
+ roundCount: number;
68
+ maxRounds: number;
69
+ exchangeLogs: Map<string, string[]>;
70
+ runningSummary: string;
71
+ researchFindings?: string;
72
+ active: CouncilParticipant[];
73
+ archive: DebateArchiveEntry[];
74
+ lastCriteriaMet: boolean[];
75
+ bestCriteriaMetCount: number;
76
+ roundsSinceProgress: number;
77
+ nextTopic?: string;
78
+ /** ISO timestamp; the caller supplies it (Date.now is unavailable in some sandboxes). */
79
+ savedAt: string;
80
+ }
81
+ /** Build the serializable checkpoint payload from live debate state. */
82
+ export declare function buildDebateCheckpoint(input: DebateCheckpointInput): DebateCheckpoint;
83
+ /** Rehydrate `exchangeLogs` from the serialized entry tuples. */
84
+ export declare function restoreExchangeLogs(cp: DebateCheckpoint): Map<string, string[]>;
85
+ /**
86
+ * Whether `cp` describes the debate that is about to run. Guards against
87
+ * splicing a stale checkpoint (different question, re-resolved panel) into a
88
+ * fresh debate. Participant order is not significant; the model SET must match.
89
+ */
90
+ export declare function checkpointMatches(cp: DebateCheckpoint, problemStatement: string, participantModels: string[]): boolean;
91
+ /**
92
+ * Atomically write the checkpoint to `<dir>/debate-checkpoint.json`.
93
+ * Non-fatal: a checkpoint write failure must never break the live debate — it
94
+ * only forfeits resumability for that round. Logs with context (No-Silent-Catch).
95
+ */
96
+ export declare function writeDebateCheckpoint(dir: string, cp: DebateCheckpoint): Promise<void>;
97
+ /**
98
+ * Read + parse the checkpoint. Returns null when absent or unparseable (a
99
+ * corrupt checkpoint must not crash resume — the debate just runs fresh).
100
+ */
101
+ export declare function readDebateCheckpoint(dir: string): Promise<DebateCheckpoint | null>;
102
+ /**
103
+ * Delete the checkpoint (debate completed normally, or the run was aborted).
104
+ * Non-fatal — a leftover checkpoint is only re-validated (and discarded on
105
+ * mismatch) on the next resume, never silently mis-applied.
106
+ */
107
+ export declare function deleteDebateCheckpoint(dir: string): Promise<void>;
108
+ export declare const DEBATE_INPUTS_FILE = "debate-inputs.json";
109
+ /**
110
+ * C-v2 — the minimal gather-stage outputs needed to re-enter the council debate
111
+ * on a fresh session (cross-process resume) WITHOUT re-running the interactive
112
+ * discovery + interview. Persisted once at debate start; the loop-driver resume
113
+ * entry restores `clarifiedSpec` + `conversationContext` and jumps straight to
114
+ * the research/debate stage (participants + leader are re-resolved from the
115
+ * session model deterministically, then matched against the checkpoint panel).
116
+ */
117
+ export interface DebateInputs {
118
+ version: typeof DEBATE_CHECKPOINT_VERSION;
119
+ problemStatement: string;
120
+ clarifiedSpec: ClarifiedSpec;
121
+ conversationContext: string;
122
+ savedAt: string;
123
+ }
124
+ /** Atomically persist the debate inputs. Non-fatal on failure (forfeits cross-session resume only). */
125
+ export declare function writeDebateInputs(dir: string, inputs: DebateInputs): Promise<void>;
126
+ /** Read the debate inputs. Returns null when absent/unparseable/version-mismatched. */
127
+ export declare function readDebateInputs(dir: string): Promise<DebateInputs | null>;
128
+ /** Delete the debate inputs (debate + scoping finished, or run aborted). Non-fatal. */
129
+ export declare function deleteDebateInputs(dir: string): Promise<void>;
@@ -0,0 +1,176 @@
1
+ /**
2
+ * src/council/debate-checkpoint.ts
3
+ *
4
+ * C (mid-debate checkpoint) — persist the CB-1 council debate's per-round state
5
+ * so a break mid-debate (provider 5xx on round 5, host crash, cap breach) can
6
+ * resume from the last completed round instead of re-running the whole debate.
7
+ *
8
+ * The debate accumulates all state in RAM (`exchangeLogs`, `active`,
9
+ * `runningSummary`, `archive`, convergence trackers). Without a checkpoint a
10
+ * break loses every round — a 5-round multi-provider debate must restart from
11
+ * round 1. This module owns the on-disk snapshot: `runDebate` writes it after
12
+ * each completed round and deletes it on normal completion; the resume path
13
+ * reads it and seeds `runDebate` so it skips research + openings + completed
14
+ * rounds.
15
+ *
16
+ * File: `<checkpointDir>/debate-checkpoint.json` (checkpointDir = the run dir,
17
+ * `.muonroi-flow/runs/<runId>`). Written atomically (tmp + rename) so a crash
18
+ * mid-write can never leave a half-JSON file.
19
+ */
20
+ import { promises as fs } from "node:fs";
21
+ import * as path from "node:path";
22
+ /** Bump when the checkpoint shape changes incompatibly so stale files are ignored. */
23
+ export const DEBATE_CHECKPOINT_VERSION = 1;
24
+ export const DEBATE_CHECKPOINT_FILE = "debate-checkpoint.json";
25
+ /** Build the serializable checkpoint payload from live debate state. */
26
+ export function buildDebateCheckpoint(input) {
27
+ return {
28
+ version: DEBATE_CHECKPOINT_VERSION,
29
+ problemStatement: input.problemStatement,
30
+ participantModels: input.active.map((p) => p.model),
31
+ roundCount: input.roundCount,
32
+ maxRounds: input.maxRounds,
33
+ exchangeLogs: [...input.exchangeLogs.entries()].map(([k, v]) => [k, [...v]]),
34
+ runningSummary: input.runningSummary,
35
+ researchFindings: input.researchFindings,
36
+ active: input.active,
37
+ archive: input.archive,
38
+ lastCriteriaMet: [...input.lastCriteriaMet],
39
+ bestCriteriaMetCount: input.bestCriteriaMetCount,
40
+ roundsSinceProgress: input.roundsSinceProgress,
41
+ nextTopic: input.nextTopic,
42
+ savedAt: input.savedAt,
43
+ };
44
+ }
45
+ /** Rehydrate `exchangeLogs` from the serialized entry tuples. */
46
+ export function restoreExchangeLogs(cp) {
47
+ return new Map(cp.exchangeLogs.map(([k, v]) => [k, [...v]]));
48
+ }
49
+ /**
50
+ * Whether `cp` describes the debate that is about to run. Guards against
51
+ * splicing a stale checkpoint (different question, re-resolved panel) into a
52
+ * fresh debate. Participant order is not significant; the model SET must match.
53
+ */
54
+ export function checkpointMatches(cp, problemStatement, participantModels) {
55
+ if (cp.version !== DEBATE_CHECKPOINT_VERSION)
56
+ return false;
57
+ if (cp.roundCount < 1)
58
+ return false; // nothing useful to resume from
59
+ if (cp.problemStatement !== problemStatement)
60
+ return false;
61
+ const a = [...cp.participantModels].sort();
62
+ const b = [...participantModels].sort();
63
+ if (a.length !== b.length)
64
+ return false;
65
+ return a.every((m, i) => m === b[i]);
66
+ }
67
+ /**
68
+ * Atomically write the checkpoint to `<dir>/debate-checkpoint.json`.
69
+ * Non-fatal: a checkpoint write failure must never break the live debate — it
70
+ * only forfeits resumability for that round. Logs with context (No-Silent-Catch).
71
+ */
72
+ export async function writeDebateCheckpoint(dir, cp) {
73
+ const finalPath = path.join(dir, DEBATE_CHECKPOINT_FILE);
74
+ const tmpPath = `${finalPath}.${process.pid}.tmp`;
75
+ try {
76
+ await fs.mkdir(dir, { recursive: true });
77
+ await fs.writeFile(tmpPath, JSON.stringify(cp), "utf8");
78
+ await fs.rename(tmpPath, finalPath);
79
+ }
80
+ catch (err) {
81
+ console.error(`[debate-checkpoint] write failed (dir=${dir}, round=${cp.roundCount}): ${err?.message}`);
82
+ // Best-effort cleanup of the temp file; ignore if it never got created.
83
+ await fs.rm(tmpPath, { force: true }).catch(() => { });
84
+ }
85
+ }
86
+ /**
87
+ * Read + parse the checkpoint. Returns null when absent or unparseable (a
88
+ * corrupt checkpoint must not crash resume — the debate just runs fresh).
89
+ */
90
+ export async function readDebateCheckpoint(dir) {
91
+ const finalPath = path.join(dir, DEBATE_CHECKPOINT_FILE);
92
+ let raw;
93
+ try {
94
+ raw = await fs.readFile(finalPath, "utf8");
95
+ }
96
+ catch (err) {
97
+ if (err?.code !== "ENOENT") {
98
+ console.error(`[debate-checkpoint] read failed (dir=${dir}): ${err?.message}`);
99
+ }
100
+ return null;
101
+ }
102
+ try {
103
+ const parsed = JSON.parse(raw);
104
+ if (parsed?.version !== DEBATE_CHECKPOINT_VERSION)
105
+ return null;
106
+ return parsed;
107
+ }
108
+ catch (err) {
109
+ console.error(`[debate-checkpoint] parse failed (dir=${dir}): ${err?.message}`);
110
+ return null;
111
+ }
112
+ }
113
+ /**
114
+ * Delete the checkpoint (debate completed normally, or the run was aborted).
115
+ * Non-fatal — a leftover checkpoint is only re-validated (and discarded on
116
+ * mismatch) on the next resume, never silently mis-applied.
117
+ */
118
+ export async function deleteDebateCheckpoint(dir) {
119
+ const finalPath = path.join(dir, DEBATE_CHECKPOINT_FILE);
120
+ try {
121
+ await fs.rm(finalPath, { force: true });
122
+ }
123
+ catch (err) {
124
+ console.error(`[debate-checkpoint] delete failed (dir=${dir}): ${err?.message}`);
125
+ }
126
+ }
127
+ // ── Debate inputs (C-v2 cross-session resume) ────────────────────────────────
128
+ export const DEBATE_INPUTS_FILE = "debate-inputs.json";
129
+ /** Atomically persist the debate inputs. Non-fatal on failure (forfeits cross-session resume only). */
130
+ export async function writeDebateInputs(dir, inputs) {
131
+ const finalPath = path.join(dir, DEBATE_INPUTS_FILE);
132
+ const tmpPath = `${finalPath}.${process.pid}.tmp`;
133
+ try {
134
+ await fs.mkdir(dir, { recursive: true });
135
+ await fs.writeFile(tmpPath, JSON.stringify(inputs), "utf8");
136
+ await fs.rename(tmpPath, finalPath);
137
+ }
138
+ catch (err) {
139
+ console.error(`[debate-checkpoint] inputs write failed (dir=${dir}): ${err?.message}`);
140
+ await fs.rm(tmpPath, { force: true }).catch(() => { });
141
+ }
142
+ }
143
+ /** Read the debate inputs. Returns null when absent/unparseable/version-mismatched. */
144
+ export async function readDebateInputs(dir) {
145
+ const finalPath = path.join(dir, DEBATE_INPUTS_FILE);
146
+ let raw;
147
+ try {
148
+ raw = await fs.readFile(finalPath, "utf8");
149
+ }
150
+ catch (err) {
151
+ if (err?.code !== "ENOENT") {
152
+ console.error(`[debate-checkpoint] inputs read failed (dir=${dir}): ${err?.message}`);
153
+ }
154
+ return null;
155
+ }
156
+ try {
157
+ const parsed = JSON.parse(raw);
158
+ if (parsed?.version !== DEBATE_CHECKPOINT_VERSION)
159
+ return null;
160
+ return parsed;
161
+ }
162
+ catch (err) {
163
+ console.error(`[debate-checkpoint] inputs parse failed (dir=${dir}): ${err?.message}`);
164
+ return null;
165
+ }
166
+ }
167
+ /** Delete the debate inputs (debate + scoping finished, or run aborted). Non-fatal. */
168
+ export async function deleteDebateInputs(dir) {
169
+ try {
170
+ await fs.rm(path.join(dir, DEBATE_INPUTS_FILE), { force: true });
171
+ }
172
+ catch (err) {
173
+ console.error(`[debate-checkpoint] inputs delete failed (dir=${dir}): ${err?.message}`);
174
+ }
175
+ }
176
+ //# sourceMappingURL=debate-checkpoint.js.map