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
@@ -148,8 +148,8 @@ export async function createLLMBrain(opts) {
148
148
  const retryOnEmpty = opts.retryOnEmpty !== false;
149
149
  // @ai-sdk/openai-compatible's chat schema exposes `strictJsonSchema: false`
150
150
  // 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
151
+ // DeepSeek only supports `response_format: {type: "json_object"}` — it
152
+ // rejects the full
153
153
  // schema response_format. Setting strictJsonSchema=false makes AI SDK fall
154
154
  // back to lenient json_object mode silently (no "responseFormat not
155
155
  // supported" warning) while still steering the model toward JSON output.
@@ -161,7 +161,7 @@ export async function createLLMBrain(opts) {
161
161
  const mergedProviderOptions = {
162
162
  ...(runtime.providerOptions ?? {}),
163
163
  };
164
- if (provider === "deepseek" || provider === "siliconflow") {
164
+ if (provider === "deepseek") {
165
165
  mergedProviderOptions[provider] = {
166
166
  ...(mergedProviderOptions[provider] ?? {}),
167
167
  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 (
@@ -1,4 +1,5 @@
1
- import type { AgentMode, SessionInfo, SessionStatus, WorkspaceInfo } from "../types/index.js";
1
+ import type { AgentMode, ResumeEntry, SessionInfo, SessionKind, SessionStatus, WorkspaceInfo } from "../types/index.js";
2
+ import { type SQLiteDatabase } from "./db.js";
2
3
  export declare class SessionStore {
3
4
  private readonly workspace;
4
5
  constructor(cwd: string);
@@ -6,18 +7,22 @@ export declare class SessionStore {
6
7
  openSession(selector: string | undefined, model: string, mode: AgentMode, cwd: string): SessionInfo;
7
8
  createSession(model: string, mode: AgentMode, cwd: string): SessionInfo;
8
9
  /**
9
- * List recent sessions in this workspace (most recently updated first).
10
- * Used by the `/sessions` slash command for resume picking.
11
- *
12
- * Only sessions that actually have a persisted message are returned. Every
13
- * CLI launch WITHOUT `--session` opens a fresh empty session, and test /
14
- * harness runs leave behind title-only stubs — without this filter the picker
15
- * fills with empty rows that resume to a blank screen, so a user picking one
16
- * thinks resume is broken.
10
+ * Link a freshly-created session as a child of `parentId`, tagging its `kind`
11
+ * and inheriting the parent's `root_session_id` so the whole tree shares one
12
+ * root. Replaces the ad-hoc `UPDATE sessions SET parent_session_id = ?` the
13
+ * orchestrator previously hand-wrote (which never set kind/root).
17
14
  */
18
- listRecentSessions(limit?: number): SessionInfo[];
15
+ linkChild(childId: string, parentId: string, kind: SessionKind): void;
16
+ listRecentSessions(limit?: number): ResumeEntry[];
19
17
  getLatestSession(): SessionInfo | null;
20
18
  getSessionById(id: string): SessionInfo | null;
19
+ /**
20
+ * Authoritative session kind ("conversation" | "rotation" | "subagent").
21
+ * SessionInfo omits `kind`, so callers that need to know whether a session is
22
+ * a forked child (e.g. the turn_tool_load instrument) read it here. Returns
23
+ * null when the session row is absent.
24
+ */
25
+ getSessionKind(id: string): SessionKind | null;
21
26
  getRequiredSession(id: string): SessionInfo;
22
27
  setStatus(id: string, status: SessionStatus): void;
23
28
  setTitle(id: string, title: string | null): void;
@@ -25,4 +30,17 @@ export declare class SessionStore {
25
30
  setMode(id: string, mode: AgentMode): void;
26
31
  touchSession(id: string, cwd: string): void;
27
32
  }
33
+ /**
34
+ * One row per conversation tree for the `/sessions` resume picker.
35
+ *
36
+ * - Groups by `root_session_id`; sub-agent sessions (`kind='subagent'`) are
37
+ * internal side-conversations and are excluded entirely.
38
+ * - Only trees with at least one persisted message surface (hides empty stub
39
+ * rows every keyless CLI launch creates).
40
+ * - The row's `title`/`created_at` come from the tree ROOT; `resume_id`,
41
+ * `model`, `updated_at`, `status` come from the LATEST leaf (the active tail
42
+ * of a rotation chain), so Enter resumes into the live session, not the
43
+ * compacted root. Ordered by tree activity, newest first.
44
+ */
45
+ export declare function queryResumeList(db: SQLiteDatabase, workspaceId: string, limit: number): ResumeEntry[];
28
46
  export { getSessionDir } from "./session-dir.js";
@@ -45,9 +45,9 @@ export class SessionStore {
45
45
  const db = getDatabase();
46
46
  db.prepare(`
47
47
  INSERT INTO sessions (
48
- id, workspace_id, title, model, mode, cwd_at_start, cwd_last, status, created_at, updated_at
48
+ id, workspace_id, title, model, mode, cwd_at_start, cwd_last, status, created_at, updated_at, kind, root_session_id
49
49
  ) VALUES (
50
- @id, @workspace_id, NULL, @model, @mode, @cwd_at_start, @cwd_last, 'active', @created_at, @updated_at
50
+ @id, @workspace_id, NULL, @model, @mode, @cwd_at_start, @cwd_last, 'active', @created_at, @updated_at, 'conversation', @id
51
51
  )
52
52
  `).run({
53
53
  id,
@@ -62,27 +62,19 @@ export class SessionStore {
62
62
  return this.getRequiredSession(id);
63
63
  }
64
64
  /**
65
- * List recent sessions in this workspace (most recently updated first).
66
- * Used by the `/sessions` slash command for resume picking.
67
- *
68
- * Only sessions that actually have a persisted message are returned. Every
69
- * CLI launch WITHOUT `--session` opens a fresh empty session, and test /
70
- * harness runs leave behind title-only stubs — without this filter the picker
71
- * fills with empty rows that resume to a blank screen, so a user picking one
72
- * thinks resume is broken.
65
+ * Link a freshly-created session as a child of `parentId`, tagging its `kind`
66
+ * and inheriting the parent's `root_session_id` so the whole tree shares one
67
+ * root. Replaces the ad-hoc `UPDATE sessions SET parent_session_id = ?` the
68
+ * orchestrator previously hand-wrote (which never set kind/root).
73
69
  */
70
+ linkChild(childId, parentId, kind) {
71
+ const db = getDatabase();
72
+ const parent = db.prepare("SELECT root_session_id FROM sessions WHERE id = ?").get(parentId);
73
+ const root = parent?.root_session_id ?? parentId;
74
+ db.prepare("UPDATE sessions SET parent_session_id = ?, kind = ?, root_session_id = ?, updated_at = ? WHERE id = ?").run(parentId, kind, root, new Date().toISOString(), childId);
75
+ }
74
76
  listRecentSessions(limit = 20) {
75
- const rows = getDatabase()
76
- .prepare(`
77
- SELECT s.id, s.workspace_id, s.title, s.model, s.mode, s.cwd_at_start, s.cwd_last, s.status, s.created_at, s.updated_at
78
- FROM sessions s
79
- WHERE s.workspace_id = ?
80
- AND EXISTS (SELECT 1 FROM messages m WHERE m.session_id = s.id)
81
- ORDER BY s.updated_at DESC
82
- LIMIT ?
83
- `)
84
- .all(this.workspace.id, limit);
85
- return rows.map(toSessionInfo);
77
+ return queryResumeList(getDatabase(), this.workspace.id, limit);
86
78
  }
87
79
  getLatestSession() {
88
80
  const row = getDatabase()
@@ -106,6 +98,16 @@ export class SessionStore {
106
98
  .get(id);
107
99
  return row ? toSessionInfo(row) : null;
108
100
  }
101
+ /**
102
+ * Authoritative session kind ("conversation" | "rotation" | "subagent").
103
+ * SessionInfo omits `kind`, so callers that need to know whether a session is
104
+ * a forked child (e.g. the turn_tool_load instrument) read it here. Returns
105
+ * null when the session row is absent.
106
+ */
107
+ getSessionKind(id) {
108
+ const row = getDatabase().prepare("SELECT kind FROM sessions WHERE id = ?").get(id);
109
+ return row?.kind ?? null;
110
+ }
109
111
  getRequiredSession(id) {
110
112
  const session = this.getSessionById(id);
111
113
  if (!session) {
@@ -180,6 +182,61 @@ function toSessionInfo(row) {
180
182
  updatedAt: new Date(row.updated_at),
181
183
  };
182
184
  }
185
+ /**
186
+ * One row per conversation tree for the `/sessions` resume picker.
187
+ *
188
+ * - Groups by `root_session_id`; sub-agent sessions (`kind='subagent'`) are
189
+ * internal side-conversations and are excluded entirely.
190
+ * - Only trees with at least one persisted message surface (hides empty stub
191
+ * rows every keyless CLI launch creates).
192
+ * - The row's `title`/`created_at` come from the tree ROOT; `resume_id`,
193
+ * `model`, `updated_at`, `status` come from the LATEST leaf (the active tail
194
+ * of a rotation chain), so Enter resumes into the live session, not the
195
+ * compacted root. Ordered by tree activity, newest first.
196
+ */
197
+ export function queryResumeList(db, workspaceId, limit) {
198
+ const rows = db
199
+ .prepare(`
200
+ WITH candidates AS (
201
+ SELECT s.id, s.workspace_id, s.title, s.model, s.mode, s.cwd_at_start,
202
+ s.cwd_last, s.status, s.created_at, s.updated_at, s.root_session_id
203
+ FROM sessions s
204
+ WHERE s.workspace_id = ?
205
+ AND s.kind != 'subagent'
206
+ AND EXISTS (SELECT 1 FROM messages m WHERE m.session_id = s.id)
207
+ ),
208
+ ranked AS (
209
+ SELECT c.*,
210
+ ROW_NUMBER() OVER (
211
+ PARTITION BY c.root_session_id
212
+ ORDER BY c.updated_at DESC, c.id DESC
213
+ ) AS rn
214
+ FROM candidates c
215
+ )
216
+ SELECT
217
+ r.root_session_id AS id,
218
+ r.id AS resume_id,
219
+ COALESCE(root.title, r.title) AS title,
220
+ r.model AS model,
221
+ r.mode AS mode,
222
+ COALESCE(root.cwd_at_start, r.cwd_at_start) AS cwd_at_start,
223
+ r.cwd_last AS cwd_last,
224
+ r.status AS status,
225
+ COALESCE(root.created_at, r.created_at) AS created_at,
226
+ r.updated_at AS updated_at,
227
+ r.workspace_id AS workspace_id
228
+ FROM ranked r
229
+ LEFT JOIN sessions root ON root.id = r.root_session_id
230
+ WHERE r.rn = 1
231
+ ORDER BY r.updated_at DESC
232
+ LIMIT ?
233
+ `)
234
+ .all(workspaceId, limit);
235
+ return rows.map(toResumeEntry);
236
+ }
237
+ function toResumeEntry(row) {
238
+ return { ...toSessionInfo(row), resumeId: row.resume_id };
239
+ }
183
240
  // ─────────────────────────────────────────────────────────────────────────────
184
241
  // B-3: Re-export getSessionDir from the isolated session-dir module.
185
242
  //
@@ -10,7 +10,7 @@ export function buildEffectiveTranscript(messages, seqs, timestamps, compaction)
10
10
  }
11
11
  const firstKeptIndex = seqs.findIndex((seq) => seq >= compaction.firstKeptSeq);
12
12
  let keptIndex = firstKeptIndex >= 0 ? firstKeptIndex : messages.length;
13
- // Anti-mù / Resilience: if firstKeptSeq landed on a `tool` message (e.g. due to seq array
13
+ // Anti-mù / Resilience: if firstKeptSeq landed on a `tool` message (e.g. due to seq array
14
14
  // misalignment or forced cut), we MUST step back to include its preceding `assistant` message.
15
15
  // Otherwise Vercel AI SDK throws: "Messages with role 'tool' must be a response to a preceding message with 'tool_calls'"
16
16
  if (keptIndex > 0 && messages[keptIndex]?.role === "tool") {
@@ -5,6 +5,45 @@ export declare function loadLatestCompaction(sessionId: string): PersistedCompac
5
5
  export declare function loadRawTranscript(sessionId: string): ModelMessage[];
6
6
  export declare function loadTranscriptState(sessionId: string): LoadedTranscriptState;
7
7
  export declare function loadTranscript(sessionId: string): ModelMessage[];
8
+ /**
9
+ * Resume a session together with every session in its conversation tree.
10
+ *
11
+ * When a session has spawned child sessions (silent rotations, sub-agents,
12
+ * sub-sessions), resuming only the parent loses the work that happened in the
13
+ * children. This loader walks up to the root and then down through all
14
+ * descendants, applies each session's own compaction, merges the messages
15
+ * chronologically, and returns them as one flat transcript for the model.
16
+ *
17
+ * Sequence numbers from sessions other than the one being resumed are replaced
18
+ * with `null` so that usage attribution and compaction still belong to the
19
+ * resumed session.
20
+ */
21
+ export declare function loadSessionChainTranscriptState(sessionId: string): LoadedTranscriptState;
22
+ /**
23
+ * One node of the session tree that a single resumed TUI hosts: the root
24
+ * conversation plus every rotation / sub-agent descendant. Powers the rail's
25
+ * "Sessions" block so the multi-session structure is visible after resume
26
+ * (the message content is already merged by loadSessionChainTranscriptState;
27
+ * this exposes WHICH sessions produced it).
28
+ */
29
+ export interface SessionChainNode {
30
+ id: string;
31
+ kind: "conversation" | "rotation" | "subagent" | null;
32
+ model: string | null;
33
+ status: string | null;
34
+ messageCount: number;
35
+ parentId: string | null;
36
+ /** Tree depth: 0 = root, 1 = direct child, … (for indented rendering). */
37
+ depth: number;
38
+ /** True for the node whose id was passed to resume (the "current" session). */
39
+ isCurrent: boolean;
40
+ }
41
+ /**
42
+ * Enumerate the full session chain (root + all descendants) with per-session
43
+ * metadata, ordered root-first then by creation time. Read-only; safe to call
44
+ * on every render. Returns a single-node array for a session with no children.
45
+ */
46
+ export declare function getSessionChainInfos(sessionId: string): SessionChainNode[];
8
47
  export declare function getNextMessageSequence(sessionId: string): number;
9
48
  export declare function appendMessages(sessionId: string, messages: ModelMessage[]): number[];
10
49
  export declare function appendSystemMessage(sessionId: string, content: string): number | null;
@@ -108,3 +147,15 @@ export declare function revertLatestCompaction(sessionId: string): void;
108
147
  export declare function getSessionChain(sessionId: string): string[];
109
148
  export declare function buildChatEntries(sessionId: string): ChatEntry[];
110
149
  export declare function getLastTodoWriteArgs(sessionId: string): string | null;
150
+ /**
151
+ * Persist the approved plan text for a session so that "tiếp tục"/"continue"
152
+ * turns (which are classified as chitchat/general by PIL Pass 0) can re-inject
153
+ * the APPROVED PLAN section into the system prompt instead of forgetting context.
154
+ *
155
+ * Stored as a synthetic tool_call row (tool_name='__approved_plan') so we reuse
156
+ * the existing schema and getLast* query pattern without a new table/migration.
157
+ * The planText is stored verbatim in args_json.
158
+ */
159
+ export declare function persistApprovedPlan(sessionId: string, planText: string): void;
160
+ /** Load the most recent persisted approved plan for this session (or parent chain). */
161
+ export declare function getLastApprovedPlan(sessionId: string): string | null;