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
@@ -0,0 +1,107 @@
1
+ import { getModelByTier, getModelInfo, getProviderPeakHourRule, SWITCH_PROVIDER_ORDER } from "../models/registry.js";
2
+ import { detectProviderForModel } from "../providers/runtime.js";
3
+ import { getPeakHourPolicy, isProviderDisabled } from "../utils/settings.js";
4
+ export function hourInTimezone(now, timezone) {
5
+ const parts = new Intl.DateTimeFormat("en-US", {
6
+ timeZone: timezone,
7
+ hour: "numeric",
8
+ hour12: false,
9
+ }).formatToParts(now);
10
+ const hour = parts.find((p) => p.type === "hour")?.value;
11
+ return hour ? Number(hour) : 0;
12
+ }
13
+ /** @deprecated Use hourInTimezone — kept for tests referencing UTC+8. */
14
+ export function hourUtc8(now) {
15
+ return hourInTimezone(now, "Asia/Shanghai");
16
+ }
17
+ export function peakHourWindows(rule) {
18
+ if (rule.windows?.length)
19
+ return rule.windows;
20
+ if (rule.start_hour != null && rule.end_hour != null) {
21
+ return [{ start_hour: rule.start_hour, end_hour: rule.end_hour }];
22
+ }
23
+ return [];
24
+ }
25
+ export function isPeakHourForProvider(provider, now, userPolicy = getPeakHourPolicy()) {
26
+ if (userPolicy.enabled === false)
27
+ return false;
28
+ const rule = getProviderPeakHourRule(provider);
29
+ if (!rule)
30
+ return false;
31
+ const h = hourInTimezone(now, rule.timezone);
32
+ return peakHourWindows(rule).some((w) => h >= w.start_hour && h < w.end_hour);
33
+ }
34
+ /** @deprecated Use isPeakHourForProvider(provider, ...) — window is per-provider from catalog. */
35
+ export function isPeakHourUtc8(now, userPolicy) {
36
+ return isPeakHourForProvider("zai", now, userPolicy) || isPeakHourForProvider("deepseek", now, userPolicy);
37
+ }
38
+ function sameProviderDowngrade(modelId, provider) {
39
+ const rule = getProviderPeakHourRule(provider);
40
+ if (!rule)
41
+ return null;
42
+ if (!rule.sensitive_model_ids.includes(modelId))
43
+ return null;
44
+ return rule.fallback_model_id;
45
+ }
46
+ function pickSwitchFallback(excludeProvider) {
47
+ const rule = getProviderPeakHourRule(excludeProvider);
48
+ const order = rule?.switch_fallback_providers ?? SWITCH_PROVIDER_ORDER;
49
+ for (const p of order) {
50
+ if (p === excludeProvider)
51
+ continue;
52
+ if (isProviderDisabled(p))
53
+ continue;
54
+ const m = getModelByTier("fast", p) ?? getModelByTier("balanced", p);
55
+ if (m && m.provider === p)
56
+ return m;
57
+ }
58
+ return undefined;
59
+ }
60
+ /**
61
+ * Adjust a concrete model id for peak-hour policy. No-op outside the provider's
62
+ * catalog-defined window or when user policy is disabled.
63
+ */
64
+ export function adjustPeakHourModel(modelId, opts) {
65
+ const userPolicy = opts?.policy ?? getPeakHourPolicy();
66
+ const now = opts?.now ?? new Date();
67
+ const provider = detectProviderForModel(modelId);
68
+ if (!isPeakHourForProvider(provider, now, userPolicy)) {
69
+ return { modelId, provider, adjusted: false };
70
+ }
71
+ const downgraded = sameProviderDowngrade(modelId, provider);
72
+ if (downgraded) {
73
+ if (userPolicy.mode === "switch") {
74
+ const alt = pickSwitchFallback(provider);
75
+ if (alt && alt.id !== modelId) {
76
+ return {
77
+ modelId: alt.id,
78
+ provider: alt.provider,
79
+ adjusted: true,
80
+ reason: `peak-hour(${provider}→${alt.provider}:${alt.id})`,
81
+ };
82
+ }
83
+ }
84
+ const info = getModelInfo(downgraded);
85
+ if (info?.provider === provider) {
86
+ return {
87
+ modelId: downgraded,
88
+ provider,
89
+ adjusted: true,
90
+ reason: `peak-hour(${modelId}→${downgraded})`,
91
+ };
92
+ }
93
+ }
94
+ return { modelId, provider, adjusted: false };
95
+ }
96
+ /** Tier lookup with peak-hour adjustment applied to the resolved model. */
97
+ export function getRoutedModelByTier(tier, preferProvider, opts) {
98
+ const base = getModelByTier(tier, preferProvider);
99
+ if (!base)
100
+ return undefined;
101
+ const adj = adjustPeakHourModel(base.id, opts);
102
+ if (!adj.adjusted)
103
+ return base;
104
+ const info = getModelInfo(adj.modelId);
105
+ return info ?? base;
106
+ }
107
+ //# sourceMappingURL=peak-hour.js.map
@@ -28,6 +28,7 @@ import { getModelByTier } from "../models/registry.js";
28
28
  import { detectProviderForModel } from "../providers/runtime.js";
29
29
  import { readTimeoutEnv } from "../utils/ee-logger.js";
30
30
  import { isModelDisabled, isProviderDisabled, loadUserSettings } from "../utils/settings.js";
31
+ import { getRoutedModelByTier } from "./peak-hour.js";
31
32
  // ─── EE-guided SAMR override ─────────────────────────────────────────────────
32
33
  // EE_SAMR_TIMEOUT: per-call budget for consulting the EE brain about SAMR.
33
34
  // Default 2s, range 500ms..5s. Falls back to heuristics on timeout/error.
@@ -187,7 +188,7 @@ export function decideStepRouting(phase1ModelId, defaultProvider, config) {
187
188
  // ─── Internal helpers ────────────────────────────────────────────────────────
188
189
  function resolveExecutionModel(provider, tier, excludeModelId) {
189
190
  // Try same provider first
190
- const sameProvider = getModelByTier(tier, provider);
191
+ const sameProvider = getRoutedModelByTier(tier, provider);
191
192
  if (sameProvider && sameProvider.id !== excludeModelId) {
192
193
  // Guard: check provider-disabled and model-disabled (mirrors cross-provider branch)
193
194
  if (!isProviderDisabled(sameProvider.provider) && !isModelDisabled(sameProvider.id)) {
@@ -195,7 +196,7 @@ function resolveExecutionModel(provider, tier, excludeModelId) {
195
196
  }
196
197
  }
197
198
  // If no same-provider model in this tier, try any provider
198
- const anyProvider = getModelByTier(tier);
199
+ const anyProvider = getRoutedModelByTier(tier);
199
200
  if (anyProvider && anyProvider.id !== excludeModelId) {
200
201
  // Only use cross-provider if the provider and model aren't disabled
201
202
  if (!isProviderDisabled(anyProvider.provider) && !isModelDisabled(anyProvider.id)) {
@@ -6,13 +6,13 @@
6
6
  */
7
7
  import { routeModel as bridgeRouteModel } from "../ee/bridge.js";
8
8
  import { getDefaultEEClient } from "../ee/intercept.js";
9
- import { getModelByTier } from "../models/registry.js";
9
+ import { getRoutedModelByTier } from "./peak-hour.js";
10
10
  import { PROVIDER_INHERIT } from "./provider-sentinel.js";
11
11
  /**
12
12
  * Map the session's provider to the EE runtime whose tier ladder the EE serves.
13
13
  * EE keys its `getModelTiers()` ladders by agent runtime, not provider, and only
14
- * the catalog-backed runtimes (claude/codex/gemini) resolve to a non-null model.
15
- * Providers without an EE runtime (deepseek/siliconflow/xai) map to "" — EE then
14
+ * the catalog-backed runtimes (claude/codex) resolve to a non-null model.
15
+ * Providers without an EE runtime (deepseek/xai) map to "" — EE then
16
16
  * returns the routing TIER with a null model and we resolve the concrete model
17
17
  * from the shared catalog for the session provider. This removes the old
18
18
  * hardcoded `runtime:"claude"`, which leaked EE's stale `claude-*` ids into the
@@ -21,7 +21,6 @@ import { PROVIDER_INHERIT } from "./provider-sentinel.js";
21
21
  const PROVIDER_TO_EE_RUNTIME = Object.freeze({
22
22
  anthropic: "claude",
23
23
  openai: "codex",
24
- google: "gemini",
25
24
  });
26
25
  function eeRuntimeForProvider(provider) {
27
26
  return (provider && PROVIDER_TO_EE_RUNTIME[provider]) || "";
@@ -36,7 +35,7 @@ export async function callWarmRoute(prompt, opts) {
36
35
  // EE returns a model only for catalog-backed runtimes; otherwise resolve the
37
36
  // concrete model from the catalog for the session provider + decided tier.
38
37
  const catalogTier = TIER_TO_CATALOG_TIER[bridgeResult.tier] ?? "balanced";
39
- const model = bridgeResult.model || getModelByTier(catalogTier, opts.defaultProvider)?.id;
38
+ const model = bridgeResult.model || getRoutedModelByTier(catalogTier, opts.defaultProvider)?.id;
40
39
  if (model) {
41
40
  return {
42
41
  tier: bridgeResult.tier === "fast" ? "hot" : bridgeResult.tier === "premium" ? "cold" : "warm",
@@ -10,3 +10,14 @@ export interface ContinuationPromptInput {
10
10
  installedPackages?: readonly string[];
11
11
  }
12
12
  export declare function buildIdealContinuationPrompt(input: ContinuationPromptInput): string;
13
+ export interface AdoptExistingPromptInput {
14
+ originalPrompt: string;
15
+ projectDir: string;
16
+ }
17
+ /**
18
+ * Build the continuation prompt fed to the LLM after the user adopts an EXISTING
19
+ * project via the CB-3 "Point to existing" recovery option. Unlike the init_new
20
+ * variant this makes no template/BB assumptions — the target is an arbitrary
21
+ * repo the user already has, with a verify recipe already detected there.
22
+ */
23
+ export declare function buildAdoptExistingContinuationPrompt(input: AdoptExistingPromptInput): string;
@@ -63,4 +63,30 @@ Constraints:
63
63
  - Don't ask "do you want me to continue?" — proceed end-to-end.
64
64
  - If you hit a blocker that genuinely needs the user, state EXACTLY which decision is blocking and stop.`;
65
65
  }
66
+ /**
67
+ * Build the continuation prompt fed to the LLM after the user adopts an EXISTING
68
+ * project via the CB-3 "Point to existing" recovery option. Unlike the init_new
69
+ * variant this makes no template/BB assumptions — the target is an arbitrary
70
+ * repo the user already has, with a verify recipe already detected there.
71
+ */
72
+ export function buildAdoptExistingContinuationPrompt(input) {
73
+ const { originalPrompt, projectDir } = input;
74
+ return `You are continuing a /ideal product loop in an EXISTING project the user pointed to at ${projectDir}. A verify recipe was detected there, so the project already has a runnable test/build setup — adopt it, do NOT re-scaffold.
75
+
76
+ User's original request:
77
+ "${originalPrompt}"
78
+
79
+ Implement this now against the existing codebase. Do NOT ask the user to restate the request.
80
+
81
+ 1. Discover structure (1-2 tool calls max): list the project directory, then read the manifest that exists (package.json / pyproject.toml / go.mod / Cargo.toml / *.csproj) and any README/AGENTS.md to learn the stack, entry point, and conventions. Do NOT assume files — skip whatever is absent.
82
+ 2. Design briefly (5-10 lines): the entities/modules/endpoints to add, matching the conventions you observed (naming, error-handling, DI, folder layout).
83
+ 3. Implement: create/edit files in the EXISTING layout; mirror the surrounding patterns. Never hardcode secrets or URLs — read config from the project's existing mechanism.
84
+ 4. Verify: run the project's OWN detected test/build command; it must pass. Report the exact command + an output excerpt as evidence.
85
+ 5. Summarize in 3-5 bullets: what was added, where, and how to run/test it.
86
+
87
+ Constraints:
88
+ - Your working directory is already ${projectDir}; do NOT cd into it and do NOT re-scaffold.
89
+ - Don't ask "do you want me to continue?" — proceed end-to-end.
90
+ - If a genuine blocker needs the user (missing API key, ambiguous requirement), state EXACTLY which decision is blocking and stop.`;
91
+ }
66
92
  //# sourceMappingURL=continuation-prompt.js.map
@@ -5,6 +5,7 @@
5
5
  * Designed for testability: callers inject detectVerifyRecipe so unit tests
6
6
  * never hit the real orchestrator.
7
7
  */
8
+ import type { VerifyRecipe } from "../types/index.js";
8
9
  import { detectBBFramework } from "./init-new.js";
9
10
  export interface PointToExistingOptions {
10
11
  /** Raw path string entered by the user. May be relative or absolute. */
@@ -26,6 +27,26 @@ export interface PointToExistingResult {
26
27
  targetFramework?: "muonroi-building-block" | string;
27
28
  }
28
29
  export { detectBBFramework };
30
+ /**
31
+ * Deterministic, filesystem-only verify-recipe detection for an existing project
32
+ * the user pointed to via the CB-3 recovery card.
33
+ *
34
+ * WHY NOT the orchestrator's detectVerifyRecipe: that path runs an LLM
35
+ * verify-detect sub-agent against the orchestrator's OWN (session) cwd via
36
+ * `this.bash.getCwd()` — it cannot inspect an arbitrary pointed-to directory, so
37
+ * wiring it here was deferred and the handler stubbed to `null` (point-to-existing
38
+ * therefore ALWAYS reported "no recipe", making the recovery option inert).
39
+ * `inferVerifyProjectProfile` reads the target dir's manifest/build files
40
+ * directly (package.json test/check/lint, Makefile, pyproject, go.mod, cargo,
41
+ * maven/gradle, *.csproj) — the correct detector for adopting an existing repo.
42
+ *
43
+ * Returns the recipe only when it is actually RUNNABLE (has a test/build/start
44
+ * command). The bare "unknown" fallback that `inferVerifyProjectProfile` emits
45
+ * for a directory with no recognizable project files carries empty commands →
46
+ * we return null so point-to-existing correctly reports `no_recipe` instead of
47
+ * falsely adopting an empty directory.
48
+ */
49
+ export declare function detectExistingProjectRecipe(cwd: string): VerifyRecipe | null;
29
50
  /**
30
51
  * Resolve and validate a user-provided path for sprint re-entry.
31
52
  *
@@ -7,8 +7,33 @@
7
7
  */
8
8
  import { realpathSync, statSync } from "node:fs";
9
9
  import * as path from "node:path";
10
+ import { inferVerifyProjectProfile } from "../verify/recipes.js";
10
11
  import { detectBBFramework } from "./init-new.js";
11
12
  export { detectBBFramework };
13
+ /**
14
+ * Deterministic, filesystem-only verify-recipe detection for an existing project
15
+ * the user pointed to via the CB-3 recovery card.
16
+ *
17
+ * WHY NOT the orchestrator's detectVerifyRecipe: that path runs an LLM
18
+ * verify-detect sub-agent against the orchestrator's OWN (session) cwd via
19
+ * `this.bash.getCwd()` — it cannot inspect an arbitrary pointed-to directory, so
20
+ * wiring it here was deferred and the handler stubbed to `null` (point-to-existing
21
+ * therefore ALWAYS reported "no recipe", making the recovery option inert).
22
+ * `inferVerifyProjectProfile` reads the target dir's manifest/build files
23
+ * directly (package.json test/check/lint, Makefile, pyproject, go.mod, cargo,
24
+ * maven/gradle, *.csproj) — the correct detector for adopting an existing repo.
25
+ *
26
+ * Returns the recipe only when it is actually RUNNABLE (has a test/build/start
27
+ * command). The bare "unknown" fallback that `inferVerifyProjectProfile` emits
28
+ * for a directory with no recognizable project files carries empty commands →
29
+ * we return null so point-to-existing correctly reports `no_recipe` instead of
30
+ * falsely adopting an empty directory.
31
+ */
32
+ export function detectExistingProjectRecipe(cwd) {
33
+ const { recipe } = inferVerifyProjectProfile(cwd);
34
+ const runnable = recipe.testCommands.length > 0 || recipe.buildCommands.length > 0 || Boolean(recipe.startCommand);
35
+ return runnable ? recipe : null;
36
+ }
12
37
  // ---------------------------------------------------------------------------
13
38
  // Implementation
14
39
  // ---------------------------------------------------------------------------
@@ -141,15 +141,16 @@ export async function createLLMBrain(opts) {
141
141
  const apiKey = await loadKeyForProvider(provider);
142
142
  if (!apiKey)
143
143
  throw new Error(`No API key found for provider '${provider}'`);
144
- const { factory } = createProviderFactory(provider, { apiKey });
145
- const runtime = resolveModelRuntime(factory, opts.modelId);
144
+ // Registers the factory so resolveModelRuntime can derive it from the model.
145
+ createProviderFactory(provider, { apiKey });
146
+ const runtime = resolveModelRuntime(opts.modelId);
146
147
  const systemPrompt = opts.systemPrompt ?? DEFAULT_SYSTEM_PROMPT;
147
148
  const maxTokens = opts.maxTokens ?? 1024;
148
149
  const retryOnEmpty = opts.retryOnEmpty !== false;
149
150
  // @ai-sdk/openai-compatible's chat schema exposes `strictJsonSchema: false`
150
151
  // for providers that don't honour OpenAI's strict JSON schema mode.
151
- // DeepSeek (and DeepSeek-compatible providers like SiliconFlow) only
152
- // support `response_format: {type: "json_object"}` — they reject the full
152
+ // DeepSeek only supports `response_format: {type: "json_object"}` — it
153
+ // rejects the full
153
154
  // schema response_format. Setting strictJsonSchema=false makes AI SDK fall
154
155
  // back to lenient json_object mode silently (no "responseFormat not
155
156
  // supported" warning) while still steering the model toward JSON output.
@@ -161,7 +162,7 @@ export async function createLLMBrain(opts) {
161
162
  const mergedProviderOptions = {
162
163
  ...(runtime.providerOptions ?? {}),
163
164
  };
164
- if (provider === "deepseek" || provider === "siliconflow") {
165
+ if (provider === "deepseek") {
165
166
  mergedProviderOptions[provider] = {
166
167
  ...(mergedProviderOptions[provider] ?? {}),
167
168
  strictJsonSchema: false,
@@ -21,6 +21,25 @@ export declare const activeRunStore: {
21
21
  clearActiveRun: () => void;
22
22
  subscribe: (fn: Listener) => (() => void);
23
23
  };
24
+ /**
25
+ * Single canonical writer for "which run is the workspace focused on".
26
+ * Writes the durable pointer (`state.md` `Active Run`) AND the in-memory cache
27
+ * atomically, so disk and StatusBar/reporter never drift (F8 follow-up).
28
+ *
29
+ * The disk write is idempotent with `ensureRunScoped`'s earlier write for the
30
+ * same runId — routing the focus moment through here just guarantees they agree.
31
+ */
32
+ export declare function setWorkspaceFocus(flowDir: string, focus: {
33
+ runId: string;
34
+ productSlug: string;
35
+ reason?: string;
36
+ }): Promise<void>;
37
+ /**
38
+ * Clear the workspace focus cache when a run ends (shipped / halted).
39
+ * Cache-only by design: the durable `Active Run` pointer is left intact as a
40
+ * historical marker so `/ideal phases` still resolves the last run post-ship.
41
+ */
42
+ export declare function clearWorkspaceFocus(): void;
24
43
  /** Test helper: reset to initial state. */
25
44
  export declare function __resetActiveRunStoreForTests(): void;
26
45
  export {};
@@ -37,6 +37,27 @@ function makeActiveRunStore() {
37
37
  };
38
38
  }
39
39
  export const activeRunStore = makeActiveRunStore();
40
+ /**
41
+ * Single canonical writer for "which run is the workspace focused on".
42
+ * Writes the durable pointer (`state.md` `Active Run`) AND the in-memory cache
43
+ * atomically, so disk and StatusBar/reporter never drift (F8 follow-up).
44
+ *
45
+ * The disk write is idempotent with `ensureRunScoped`'s earlier write for the
46
+ * same runId — routing the focus moment through here just guarantees they agree.
47
+ */
48
+ export async function setWorkspaceFocus(flowDir, focus) {
49
+ const { setActiveRunId } = await import("../flow/run-manager.js");
50
+ await setActiveRunId(flowDir, focus.runId);
51
+ activeRunStore.setActiveRun(focus.runId, flowDir, focus.productSlug);
52
+ }
53
+ /**
54
+ * Clear the workspace focus cache when a run ends (shipped / halted).
55
+ * Cache-only by design: the durable `Active Run` pointer is left intact as a
56
+ * historical marker so `/ideal phases` still resolves the last run post-ship.
57
+ */
58
+ export function clearWorkspaceFocus() {
59
+ activeRunStore.clearActiveRun();
60
+ }
40
61
  /** Test helper: reset to initial state. */
41
62
  export function __resetActiveRunStoreForTests() {
42
63
  activeRunStore.clearActiveRun();
@@ -1,9 +1,12 @@
1
1
  /**
2
- * src/ui/status-bar/store.ts
2
+ * src/state/status-bar-store.ts
3
3
  *
4
4
  * Status bar store: subscribable atom holding model/provider/tier/tokens/USD/degraded.
5
5
  * wireStatusBar() connects to routerStore (Plan 03), subscribeThresholds (Plan 04),
6
6
  * and subscribeDowngrade (Plan 05).
7
+ *
8
+ * Presentation-agnostic state — lives outside src/ui so the headless core
9
+ * (orchestrator/tool-engine/stream-runner) can read it without importing ui/.
7
10
  */
8
11
  export interface SprintProgressSegment {
9
12
  /** Active sprint index (1-based). */
@@ -1,15 +1,18 @@
1
1
  /**
2
- * src/ui/status-bar/store.ts
2
+ * src/state/status-bar-store.ts
3
3
  *
4
4
  * Status bar store: subscribable atom holding model/provider/tier/tokens/USD/degraded.
5
5
  * wireStatusBar() connects to routerStore (Plan 03), subscribeThresholds (Plan 04),
6
6
  * and subscribeDowngrade (Plan 05).
7
+ *
8
+ * Presentation-agnostic state — lives outside src/ui so the headless core
9
+ * (orchestrator/tool-engine/stream-runner) can read it without importing ui/.
7
10
  */
8
- import { getCircuitState } from "../../ee/client.js";
9
- import { routerStore } from "../../router/store.js";
10
- import { subscribeDowngrade } from "../../usage/downgrade.js";
11
- import { subscribeThresholds } from "../../usage/thresholds.js";
12
- import { activeRunStore } from "../state/active-run.js";
11
+ import { getCircuitState } from "../ee/client.js";
12
+ import { routerStore } from "../router/store.js";
13
+ import { subscribeDowngrade } from "../usage/downgrade.js";
14
+ import { subscribeThresholds } from "../usage/thresholds.js";
15
+ import { activeRunStore } from "./active-run.js";
13
16
  function makeStore() {
14
17
  let state = {
15
18
  provider: "",
@@ -141,8 +144,8 @@ export function wireStatusBar() {
141
144
  }
142
145
  try {
143
146
  // Lazy import to avoid circular dependency at module load time.
144
- const { readBacklog } = await import("../../product-loop/backlog-store.js");
145
- const { readSprintPlan } = await import("../../product-loop/sprint-store.js");
147
+ const { readBacklog } = await import("../product-loop/backlog-store.js");
148
+ const { readSprintPlan } = await import("../product-loop/sprint-store.js");
146
149
  const [backlog, sprintPlan] = await Promise.all([
147
150
  readBacklog(run.flowDir, run.runId).catch(() => null),
148
151
  readSprintPlan(run.flowDir, run.runId).catch(() => null),
@@ -276,4 +279,4 @@ export function __resetStatusBarStoreForTests() {
276
279
  });
277
280
  wired = false;
278
281
  }
279
- //# sourceMappingURL=store.js.map
282
+ //# sourceMappingURL=status-bar-store.js.map
@@ -0,0 +1,43 @@
1
+ /**
2
+ * src/state/turn-trace.ts
3
+ *
4
+ * Pipeline debug-trace state — the toggle flag + recorded per-turn traces.
5
+ *
6
+ * Presentation-agnostic: the headless core (tool-engine, message-processor)
7
+ * records traces here without importing src/ui. The /debug slash command
8
+ * (src/ui/slash/debug.ts) owns the human formatting and toggling on top of
9
+ * this state.
10
+ */
11
+ export interface PipelineStep {
12
+ name: string;
13
+ duration_ms: number;
14
+ input_summary: string;
15
+ output_summary: string;
16
+ tokens_saved?: number;
17
+ }
18
+ export interface TurnTrace {
19
+ turn_id: number;
20
+ timestamp: number;
21
+ raw_prompt: string;
22
+ steps: PipelineStep[];
23
+ model_requested: string;
24
+ model_used: string;
25
+ routed: boolean;
26
+ input_tokens: number;
27
+ output_tokens: number;
28
+ cache_read_tokens: number;
29
+ cost_usd: number;
30
+ estimated_savings: SavingsEstimate;
31
+ }
32
+ export interface SavingsEstimate {
33
+ pil_tokens_saved: number;
34
+ cache_tokens_saved: number;
35
+ router_cost_saved_usd: number;
36
+ total_tokens_saved: number;
37
+ total_cost_saved_usd: number;
38
+ }
39
+ export declare function isDebugEnabled(): boolean;
40
+ export declare function setDebugEnabled(value: boolean): void;
41
+ export declare function recordTurnTrace(trace: TurnTrace): void;
42
+ export declare function getLastTrace(): TurnTrace | null;
43
+ export declare function getAllTraces(): readonly TurnTrace[];
@@ -0,0 +1,32 @@
1
+ /**
2
+ * src/state/turn-trace.ts
3
+ *
4
+ * Pipeline debug-trace state — the toggle flag + recorded per-turn traces.
5
+ *
6
+ * Presentation-agnostic: the headless core (tool-engine, message-processor)
7
+ * records traces here without importing src/ui. The /debug slash command
8
+ * (src/ui/slash/debug.ts) owns the human formatting and toggling on top of
9
+ * this state.
10
+ */
11
+ let debugEnabled = false;
12
+ const turnTraces = [];
13
+ export function isDebugEnabled() {
14
+ return debugEnabled;
15
+ }
16
+ export function setDebugEnabled(value) {
17
+ debugEnabled = value;
18
+ }
19
+ export function recordTurnTrace(trace) {
20
+ if (!debugEnabled)
21
+ return;
22
+ turnTraces.push(trace);
23
+ if (turnTraces.length > 50)
24
+ turnTraces.shift();
25
+ }
26
+ export function getLastTrace() {
27
+ return turnTraces[turnTraces.length - 1] ?? null;
28
+ }
29
+ export function getAllTraces() {
30
+ return turnTraces;
31
+ }
32
+ //# sourceMappingURL=turn-trace.js.map
@@ -9,7 +9,8 @@ let db = null;
9
9
  // async would be a much larger refactor.
10
10
  const requireSync = createRequire(import.meta.url);
11
11
  export function getDatabasePath() {
12
- const dir = path.join(os.homedir(), ".muonroi-cli");
12
+ const home = process.env.HOME ?? process.env.USERPROFILE ?? os.homedir();
13
+ const dir = path.join(home, ".muonroi-cli");
13
14
  fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
14
15
  return path.join(dir, "muonroi.db");
15
16
  }
@@ -3,7 +3,7 @@ export { loadConfig, type MuonroiConfig } from "./config.js";
3
3
  export { getDatabasePath } from "./db.js";
4
4
  export { type InteractionEventType, logInteraction } from "./interaction-log.js";
5
5
  export { SessionStore } from "./sessions.js";
6
- export { appendCompaction, appendMessages, appendSystemMessage, buildChatEntries, getLastTodoWriteArgs, getNextMessageSequence, getSessionChain, loadLatestCompaction, loadRawTranscript, loadTranscript, loadTranscriptState, markMessageCompleted, markMessageErrored, markToolCallErrored, persistMessageWriteAhead, persistToolCallWriteAhead, revertLatestCompaction, } from "./transcript.js";
6
+ export { appendCompaction, appendMessages, appendSystemMessage, buildChatEntries, getLastApprovedPlan, getLastTodoWriteArgs, getNextMessageSequence, getSessionChain, getSessionChainInfos, loadLatestCompaction, loadRawTranscript, loadSessionChainTranscriptState, loadTranscript, loadTranscriptState, markMessageCompleted, markMessageErrored, markToolCallErrored, persistApprovedPlan, persistMessageWriteAhead, persistToolCallWriteAhead, revertLatestCompaction, type SessionChainNode, } from "./transcript.js";
7
7
  export { buildEffectiveTranscript, type LoadedTranscriptState, type PersistedCompaction } from "./transcript-view.js";
8
8
  export { logUIInteraction, type UIInteractionSubtype } from "./ui-interaction-log.js";
9
9
  export { getSessionTotalTokens, listSessionUsage, recordUsageEvent, type TokenUsageLike } from "./usage.js";
@@ -4,7 +4,7 @@ export { loadConfig } from "./config.js";
4
4
  export { getDatabasePath } from "./db.js";
5
5
  export { logInteraction } from "./interaction-log.js";
6
6
  export { SessionStore } from "./sessions.js";
7
- export { appendCompaction, appendMessages, appendSystemMessage, buildChatEntries, getLastTodoWriteArgs, getNextMessageSequence, getSessionChain, loadLatestCompaction, loadRawTranscript, loadTranscript, loadTranscriptState, markMessageCompleted, markMessageErrored, markToolCallErrored, persistMessageWriteAhead, persistToolCallWriteAhead, revertLatestCompaction, } from "./transcript.js";
7
+ export { appendCompaction, appendMessages, appendSystemMessage, buildChatEntries, getLastApprovedPlan, getLastTodoWriteArgs, getNextMessageSequence, getSessionChain, getSessionChainInfos, loadLatestCompaction, loadRawTranscript, loadSessionChainTranscriptState, loadTranscript, loadTranscriptState, markMessageCompleted, markMessageErrored, markToolCallErrored, persistApprovedPlan, persistMessageWriteAhead, persistToolCallWriteAhead, revertLatestCompaction, } from "./transcript.js";
8
8
  export { buildEffectiveTranscript } from "./transcript-view.js";
9
9
  export { logUIInteraction } from "./ui-interaction-log.js";
10
10
  export { getSessionTotalTokens, listSessionUsage, recordUsageEvent } from "./usage.js";
@@ -4,7 +4,7 @@
4
4
  * Fire-and-forget interaction logging for detailed user-agent event tracking.
5
5
  * All calls are fail-open — logging never breaks the main flow.
6
6
  */
7
- export type InteractionEventType = "user_message" | "agent_response" | "tool_call" | "tool_result" | "compaction" | "routing" | "pil" | "error" | "model_switch" | "council" | "ee_intercept" | "ee_judge" | "ee_injection" | "ui_interaction" | "stream_retry" | "f6_synthesis" | "grounding_flag" | "stall_rescue" | "stream_start" | "text_tool_resteer" | "session_experience";
7
+ export type InteractionEventType = "user_message" | "agent_response" | "tool_call" | "tool_result" | "compaction" | "routing" | "pil" | "error" | "model_switch" | "council" | "ee_intercept" | "ee_judge" | "ee_injection" | "ui_interaction" | "stream_retry" | "f6_synthesis" | "grounding_flag" | "stall_rescue" | "stream_start" | "text_tool_resteer" | "turn_tool_load" | "session_experience";
8
8
  export interface EEInjectionRow {
9
9
  session_id: string;
10
10
  event_subtype: string | null;
@@ -1,4 +1,4 @@
1
- const LATEST_DB_VERSION = 8;
1
+ const LATEST_DB_VERSION = 10;
2
2
  export function applyMigrations(db) {
3
3
  const version = Number(db.pragma("user_version", { simple: true })) || 0;
4
4
  if (version >= LATEST_DB_VERSION)
@@ -96,9 +96,79 @@ export function applyMigrations(db) {
96
96
  db.exec("CREATE INDEX IF NOT EXISTS idx_sessions_parent_id ON sessions(parent_session_id)");
97
97
  db.pragma("user_version = 8");
98
98
  }
99
+ if (version < 9) {
100
+ db.exec(`
101
+ CREATE VIRTUAL TABLE IF NOT EXISTS session_history_fts USING fts5(
102
+ session_id UNINDEXED,
103
+ seq UNINDEXED,
104
+ role UNINDEXED,
105
+ tool_name UNINDEXED,
106
+ content,
107
+ tool_args,
108
+ tool_output
109
+ );
110
+
111
+ CREATE TRIGGER IF NOT EXISTS t_sessions_delete_fts
112
+ AFTER DELETE ON sessions
113
+ BEGIN
114
+ DELETE FROM session_history_fts WHERE session_id = old.id;
115
+ END;
116
+ `);
117
+ db.pragma("user_version = 9");
118
+ }
119
+ if (version < 10) {
120
+ // Thread-aware resume. The resume picker groups sessions by conversation
121
+ // tree; `kind` distinguishes the two kinds of child (both previously only
122
+ // set parent_session_id, indistinguishable) and `root_session_id`
123
+ // denormalizes the tree root so the picker groups without a recursive CTE.
124
+ const cols = db.prepare("PRAGMA table_info(sessions)").all();
125
+ const colNames = new Set(cols.map((c) => c.name));
126
+ if (!colNames.has("kind")) {
127
+ db.exec("ALTER TABLE sessions ADD COLUMN kind TEXT NOT NULL DEFAULT 'conversation'");
128
+ }
129
+ if (!colNames.has("root_session_id")) {
130
+ db.exec("ALTER TABLE sessions ADD COLUMN root_session_id TEXT");
131
+ }
132
+ db.exec("CREATE INDEX IF NOT EXISTS idx_sessions_root ON sessions(workspace_id, root_session_id)");
133
+ backfillSessionTrees(db);
134
+ db.pragma("user_version = 10");
135
+ }
99
136
  });
100
137
  migrate();
101
138
  }
139
+ /**
140
+ * Backfill kind + root_session_id for pre-v10 rows.
141
+ *
142
+ * - Rows with no parent become their own root (kind 'conversation').
143
+ * - Rows WITH a parent are marked 'rotation' — historical data cannot tell
144
+ * rotation apart from sub-agent spawns, and marking them 'rotation' collapses
145
+ * them under their root rather than risking hiding a real conversation.
146
+ * - root_session_id propagates down the parent chain via a bounded fixpoint
147
+ * loop (chains are shallow; the bound also guards against FK cycles).
148
+ */
149
+ function backfillSessionTrees(db) {
150
+ // Roots: own id, keep default kind 'conversation'.
151
+ db.exec("UPDATE sessions SET root_session_id = id WHERE parent_session_id IS NULL AND root_session_id IS NULL");
152
+ // Children of a real parent → rotation (default kind was 'conversation').
153
+ db.exec("UPDATE sessions SET kind = 'rotation' WHERE parent_session_id IS NOT NULL");
154
+ // Propagate root down the chain until no row changes (bounded to 100 passes).
155
+ for (let i = 0; i < 100; i++) {
156
+ db.exec(`
157
+ UPDATE sessions
158
+ SET root_session_id = (SELECT p.root_session_id FROM sessions p WHERE p.id = sessions.parent_session_id)
159
+ WHERE root_session_id IS NULL
160
+ AND parent_session_id IS NOT NULL
161
+ AND (SELECT p.root_session_id FROM sessions p WHERE p.id = sessions.parent_session_id) IS NOT NULL
162
+ `);
163
+ const remaining = db
164
+ .prepare("SELECT COUNT(*) AS c FROM sessions WHERE root_session_id IS NULL AND parent_session_id IS NOT NULL")
165
+ .get();
166
+ if (!remaining || remaining.c === 0)
167
+ break;
168
+ }
169
+ // Any leftover (orphaned/cyclic parent chains) → treat as its own root.
170
+ db.exec("UPDATE sessions SET root_session_id = id WHERE root_session_id IS NULL");
171
+ }
102
172
  function createInitialSchema(db) {
103
173
  db.exec(`
104
174
  CREATE TABLE IF NOT EXISTS workspaces (