muonroi-cli 1.8.3 → 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 (477) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +133 -122
  3. package/dist/packages/agent-harness-core/src/driver.d.ts +27 -1
  4. package/dist/packages/agent-harness-core/src/driver.js +46 -0
  5. package/dist/packages/agent-harness-core/src/event-tee.d.ts +48 -0
  6. package/dist/packages/agent-harness-core/src/event-tee.js +77 -0
  7. package/dist/packages/agent-harness-core/src/mcp-server.d.ts +11 -0
  8. package/dist/packages/agent-harness-core/src/mcp-server.js +87 -15
  9. package/dist/packages/agent-harness-core/src/protocol.d.ts +66 -2
  10. package/dist/packages/agent-harness-core/src/protocol.js +15 -0
  11. package/dist/packages/agent-harness-core/src/visual-quality.d.ts +58 -0
  12. package/dist/packages/agent-harness-core/src/visual-quality.js +141 -0
  13. package/dist/packages/agent-harness-opentui/src/agent-mode.d.ts +6 -0
  14. package/dist/packages/agent-harness-opentui/src/agent-mode.js +14 -1
  15. package/dist/packages/agent-harness-opentui/src/input-bridge.d.ts +2 -10
  16. package/dist/packages/agent-harness-opentui/src/input-bridge.js +103 -16
  17. package/dist/packages/agent-harness-opentui/src/install.d.ts +8 -0
  18. package/dist/packages/agent-harness-opentui/src/install.js +10 -0
  19. package/dist/packages/agent-harness-opentui/src/semantic.js +12 -10
  20. package/dist/packages/agent-harness-opentui/src/visual-capture.d.ts +56 -0
  21. package/dist/packages/agent-harness-opentui/src/visual-capture.js +103 -0
  22. package/dist/src/agent-harness/mock-model.d.ts +28 -0
  23. package/dist/src/agent-harness/mock-model.js +63 -1
  24. package/dist/src/agent-harness/test-spawn.js +31 -0
  25. package/dist/src/cli/config/screen-providers.js +1 -1
  26. package/dist/src/cli/cost-forensics.d.ts +10 -0
  27. package/dist/src/cli/cost-forensics.js +30 -15
  28. package/dist/src/cli/keys-bundle.d.ts +1 -1
  29. package/dist/src/cli/keys-bundle.js +1 -1
  30. package/dist/src/cli/keys.d.ts +2 -2
  31. package/dist/src/cli/keys.js +19 -81
  32. package/dist/src/council/clarifier.d.ts +28 -2
  33. package/dist/src/council/clarifier.js +81 -15
  34. package/dist/src/council/context.js +49 -15
  35. package/dist/src/council/debate-checkpoint.d.ts +129 -0
  36. package/dist/src/council/debate-checkpoint.js +176 -0
  37. package/dist/src/council/debate-planner.js +51 -3
  38. package/dist/src/council/debate-summary.d.ts +25 -0
  39. package/dist/src/council/debate-summary.js +85 -0
  40. package/dist/src/council/debate.d.ts +169 -2
  41. package/dist/src/council/debate.js +1210 -134
  42. package/dist/src/council/index.d.ts +85 -1
  43. package/dist/src/council/index.js +634 -196
  44. package/dist/src/council/leader.d.ts +26 -0
  45. package/dist/src/council/leader.js +150 -9
  46. package/dist/src/council/llm.d.ts +32 -0
  47. package/dist/src/council/llm.js +231 -38
  48. package/dist/src/council/panel-select.d.ts +30 -0
  49. package/dist/src/council/panel-select.js +72 -0
  50. package/dist/src/council/planner.js +23 -0
  51. package/dist/src/council/preflight.d.ts +7 -0
  52. package/dist/src/council/preflight.js +14 -2
  53. package/dist/src/council/prompts.d.ts +30 -3
  54. package/dist/src/council/prompts.js +254 -84
  55. package/dist/src/council/stance-recall.d.ts +42 -0
  56. package/dist/src/council/stance-recall.js +57 -0
  57. package/dist/src/council/strip-think.d.ts +17 -0
  58. package/dist/src/council/strip-think.js +33 -0
  59. package/dist/src/council/types.d.ts +128 -0
  60. package/dist/src/ee/artifact-cache.d.ts +16 -0
  61. package/dist/src/ee/artifact-cache.js +32 -0
  62. package/dist/src/ee/auth.d.ts +1 -0
  63. package/dist/src/ee/auth.js +15 -2
  64. package/dist/src/ee/bridge.d.ts +10 -0
  65. package/dist/src/ee/bridge.js +58 -0
  66. package/dist/src/ee/client.js +81 -18
  67. package/dist/src/ee/export-transcripts.d.ts +1 -0
  68. package/dist/src/ee/export-transcripts.js +8 -10
  69. package/dist/src/ee/extract-session.js +29 -0
  70. package/dist/src/ee/extract-style.d.ts +58 -0
  71. package/dist/src/ee/extract-style.js +270 -0
  72. package/dist/src/ee/recall-ledger.d.ts +9 -0
  73. package/dist/src/ee/recall-ledger.js +3 -0
  74. package/dist/src/ee/scope.d.ts +1 -0
  75. package/dist/src/ee/scope.js +26 -1
  76. package/dist/src/ee/search.d.ts +7 -0
  77. package/dist/src/ee/search.js +24 -0
  78. package/dist/src/ee/transcript-emit.js +2 -0
  79. package/dist/src/ee/types.d.ts +22 -0
  80. package/dist/src/ee/who-am-i-brain.d.ts +35 -0
  81. package/dist/src/ee/who-am-i-brain.js +220 -0
  82. package/dist/src/ee/who-am-i.d.ts +10 -3
  83. package/dist/src/ee/who-am-i.js +12 -0
  84. package/dist/src/ee/workflow-event.d.ts +48 -0
  85. package/dist/src/ee/workflow-event.js +81 -0
  86. package/dist/src/flow/compaction/compress.d.ts +3 -3
  87. package/dist/src/flow/compaction/compress.js +45 -8
  88. package/dist/src/flow/compaction/extract.d.ts +4 -7
  89. package/dist/src/flow/compaction/extract.js +50 -10
  90. package/dist/src/flow/compaction/index.d.ts +13 -1
  91. package/dist/src/flow/compaction/index.js +70 -3
  92. package/dist/src/flow/compaction/input-guard.d.ts +24 -0
  93. package/dist/src/flow/compaction/input-guard.js +43 -0
  94. package/dist/src/flow/fold-planning.d.ts +36 -0
  95. package/dist/src/flow/fold-planning.js +83 -0
  96. package/dist/src/flow/hierarchy.d.ts +146 -0
  97. package/dist/src/flow/hierarchy.js +427 -0
  98. package/dist/src/flow/index.d.ts +1 -0
  99. package/dist/src/flow/index.js +2 -0
  100. package/dist/src/flow/run-artifacts.d.ts +102 -0
  101. package/dist/src/flow/run-artifacts.js +208 -0
  102. package/dist/src/generated/version.d.ts +1 -1
  103. package/dist/src/generated/version.js +1 -1
  104. package/dist/src/gsd/assessment-schema.d.ts +44 -0
  105. package/dist/src/gsd/assessment-schema.js +134 -0
  106. package/dist/src/gsd/capability-registry.d.ts +45 -0
  107. package/dist/src/gsd/capability-registry.js +337 -0
  108. package/dist/src/gsd/complexity-assessor.d.ts +39 -0
  109. package/dist/src/gsd/complexity-assessor.js +152 -0
  110. package/dist/src/gsd/config-bridge.d.ts +7 -0
  111. package/dist/src/gsd/config-bridge.js +114 -0
  112. package/dist/src/gsd/config-loader.d.ts +27 -0
  113. package/dist/src/gsd/config-loader.js +50 -0
  114. package/dist/src/gsd/council-context.d.ts +44 -0
  115. package/dist/src/gsd/council-context.js +114 -0
  116. package/dist/src/gsd/ee-closure.d.ts +28 -0
  117. package/dist/src/gsd/ee-closure.js +49 -0
  118. package/dist/src/gsd/flags.d.ts +55 -0
  119. package/dist/src/gsd/flags.js +83 -0
  120. package/dist/src/gsd/gsd-dispatch.d.ts +58 -0
  121. package/dist/src/gsd/gsd-dispatch.js +131 -0
  122. package/dist/src/gsd/gsd-runtime.d.ts +22 -0
  123. package/dist/src/gsd/gsd-runtime.js +37 -0
  124. package/dist/src/gsd/host-adapter.d.ts +11 -0
  125. package/dist/src/gsd/host-adapter.js +29 -0
  126. package/dist/src/gsd/index.d.ts +24 -1
  127. package/dist/src/gsd/index.js +27 -0
  128. package/dist/src/gsd/loop-host-contract.d.ts +21 -0
  129. package/dist/src/gsd/loop-host-contract.js +39 -0
  130. package/dist/src/gsd/loop-host.d.ts +69 -0
  131. package/dist/src/gsd/loop-host.js +245 -0
  132. package/dist/src/gsd/loop-resolver.d.ts +36 -0
  133. package/dist/src/gsd/loop-resolver.js +79 -0
  134. package/dist/src/gsd/model-tier.d.ts +13 -0
  135. package/dist/src/gsd/model-tier.js +45 -0
  136. package/dist/src/gsd/mutation-gate.d.ts +16 -0
  137. package/dist/src/gsd/mutation-gate.js +41 -0
  138. package/dist/src/gsd/native-roadmap.d.ts +89 -0
  139. package/dist/src/gsd/native-roadmap.js +343 -0
  140. package/dist/src/gsd/native-state.d.ts +47 -0
  141. package/dist/src/gsd/native-state.js +220 -0
  142. package/dist/src/gsd/paths.d.ts +23 -0
  143. package/dist/src/gsd/paths.js +66 -0
  144. package/dist/src/gsd/phase-dag.d.ts +12 -0
  145. package/dist/src/gsd/phase-dag.js +94 -0
  146. package/dist/src/gsd/phase-sync.d.ts +42 -0
  147. package/dist/src/gsd/phase-sync.js +321 -0
  148. package/dist/src/gsd/pil-gate-context.d.ts +13 -0
  149. package/dist/src/gsd/pil-gate-context.js +64 -0
  150. package/dist/src/gsd/pil-gate-critic.d.ts +19 -0
  151. package/dist/src/gsd/pil-gate-critic.js +74 -0
  152. package/dist/src/gsd/plan-council-prompts.d.ts +25 -0
  153. package/dist/src/gsd/plan-council-prompts.js +79 -0
  154. package/dist/src/gsd/plan-council.d.ts +44 -0
  155. package/dist/src/gsd/plan-council.js +251 -0
  156. package/dist/src/gsd/plan-gate-vocabulary.d.ts +40 -0
  157. package/dist/src/gsd/plan-gate-vocabulary.js +64 -0
  158. package/dist/src/gsd/product-workspace.d.ts +13 -0
  159. package/dist/src/gsd/product-workspace.js +124 -0
  160. package/dist/src/gsd/ship-bridge.d.ts +25 -0
  161. package/dist/src/gsd/ship-bridge.js +65 -0
  162. package/dist/src/gsd/state-document.d.ts +40 -0
  163. package/dist/src/gsd/state-document.js +163 -0
  164. package/dist/src/gsd/verdict-schema.d.ts +39 -0
  165. package/dist/src/gsd/verdict-schema.js +144 -0
  166. package/dist/src/gsd/verify-context.d.ts +22 -0
  167. package/dist/src/gsd/verify-context.js +27 -0
  168. package/dist/src/gsd/verify-council-prompts.d.ts +19 -0
  169. package/dist/src/gsd/verify-council-prompts.js +85 -0
  170. package/dist/src/gsd/verify-council.d.ts +25 -0
  171. package/dist/src/gsd/verify-council.js +119 -0
  172. package/dist/src/gsd/verify-gate-vocabulary.d.ts +25 -0
  173. package/dist/src/gsd/verify-gate-vocabulary.js +46 -0
  174. package/dist/src/gsd/workflow-engine.d.ts +60 -0
  175. package/dist/src/gsd/workflow-engine.js +207 -0
  176. package/dist/src/gsd/workflow-tools.d.ts +13 -0
  177. package/dist/src/gsd/workflow-tools.js +277 -0
  178. package/dist/src/hooks/index.js +1 -1
  179. package/dist/src/index.js +44 -11
  180. package/dist/src/maintain/pr-builder.js +23 -13
  181. package/dist/src/mcp/auto-setup.js +57 -32
  182. package/dist/src/mcp/client-pool.js +1 -1
  183. package/dist/src/mcp/ee-tools.js +1 -0
  184. package/dist/src/mcp/oauth-callback.js +2 -2
  185. package/dist/src/mcp/research-onboarding.js +8 -7
  186. package/dist/src/mcp/runtime.js +34 -2
  187. package/dist/src/mcp/setup-guide-text.d.ts +1 -1
  188. package/dist/src/mcp/setup-guide-text.js +77 -76
  189. package/dist/src/models/catalog-client.d.ts +87 -0
  190. package/dist/src/models/catalog-client.js +105 -38
  191. package/dist/src/models/catalog.json +528 -265
  192. package/dist/src/models/registry.d.ts +22 -7
  193. package/dist/src/models/registry.js +73 -10
  194. package/dist/src/ops/doctor.js +8 -8
  195. package/dist/src/orchestrator/auto-commit.js +1 -1
  196. package/dist/src/orchestrator/batch-turn-runner.js +2 -2
  197. package/dist/src/orchestrator/cache-prefix.d.ts +67 -0
  198. package/dist/src/orchestrator/cache-prefix.js +83 -0
  199. package/dist/src/orchestrator/compact-request.d.ts +32 -0
  200. package/dist/src/orchestrator/compact-request.js +41 -0
  201. package/dist/src/orchestrator/compaction.d.ts +10 -0
  202. package/dist/src/orchestrator/compaction.js +27 -7
  203. package/dist/src/orchestrator/council-manager.d.ts +12 -3
  204. package/dist/src/orchestrator/council-manager.js +65 -24
  205. package/dist/src/orchestrator/cross-turn-dedup.d.ts +6 -0
  206. package/dist/src/orchestrator/cross-turn-dedup.js +22 -1
  207. package/dist/src/orchestrator/error-utils.d.ts +29 -0
  208. package/dist/src/orchestrator/error-utils.js +132 -24
  209. package/dist/src/orchestrator/grounding-check.js +39 -1
  210. package/dist/src/orchestrator/message-processor.js +242 -33
  211. package/dist/src/orchestrator/orchestrator.d.ts +39 -3
  212. package/dist/src/orchestrator/orchestrator.js +651 -102
  213. package/dist/src/orchestrator/preprocessor.js +1 -1
  214. package/dist/src/orchestrator/proactive-compact-detector.d.ts +26 -0
  215. package/dist/src/orchestrator/proactive-compact-detector.js +36 -0
  216. package/dist/src/orchestrator/prompts.js +159 -159
  217. package/dist/src/orchestrator/reactive-delegation.d.ts +39 -0
  218. package/dist/src/orchestrator/reactive-delegation.js +59 -0
  219. package/dist/src/orchestrator/retry-classifier.d.ts +2 -1
  220. package/dist/src/orchestrator/retry-classifier.js +46 -2
  221. package/dist/src/orchestrator/safety-intercept.d.ts +45 -0
  222. package/dist/src/orchestrator/safety-intercept.js +55 -0
  223. package/dist/src/orchestrator/scope-reminder.js +1 -1
  224. package/dist/src/orchestrator/session-experience.d.ts +2 -1
  225. package/dist/src/orchestrator/session-experience.js +2 -1
  226. package/dist/src/orchestrator/should-run-gate.d.ts +5 -0
  227. package/dist/src/orchestrator/should-run-gate.js +18 -0
  228. package/dist/src/orchestrator/stall-watchdog.d.ts +24 -3
  229. package/dist/src/orchestrator/stall-watchdog.js +47 -13
  230. package/dist/src/orchestrator/stream-runner.js +62 -29
  231. package/dist/src/orchestrator/sub-agent-cap.d.ts +13 -1
  232. package/dist/src/orchestrator/sub-agent-cap.js +16 -1
  233. package/dist/src/orchestrator/sub-agent-model-tier.d.ts +13 -1
  234. package/dist/src/orchestrator/sub-agent-model-tier.js +16 -2
  235. package/dist/src/orchestrator/subagent-compactor.d.ts +53 -1
  236. package/dist/src/orchestrator/subagent-compactor.js +126 -15
  237. package/dist/src/orchestrator/tool-engine.d.ts +26 -0
  238. package/dist/src/orchestrator/tool-engine.js +669 -56
  239. package/dist/src/orchestrator/tool-limit-auto-recover.d.ts +22 -0
  240. package/dist/src/orchestrator/tool-limit-auto-recover.js +30 -0
  241. package/dist/src/orchestrator/turn-runner-deps.d.ts +19 -0
  242. package/dist/src/orchestrator/turn-watchdog.d.ts +37 -0
  243. package/dist/src/orchestrator/turn-watchdog.js +55 -0
  244. package/dist/src/pil/agent-operating-contract.d.ts +1 -1
  245. package/dist/src/pil/agent-operating-contract.js +1 -1
  246. package/dist/src/pil/cheap-model-playbook.d.ts +1 -1
  247. package/dist/src/pil/cheap-model-playbook.js +5 -1
  248. package/dist/src/pil/cheap-model-workbooks.d.ts +1 -1
  249. package/dist/src/pil/cheap-model-workbooks.js +1 -1
  250. package/dist/src/pil/discovery-types.d.ts +1 -0
  251. package/dist/src/pil/discovery.js +16 -11
  252. package/dist/src/pil/layer1-intent.d.ts +18 -6
  253. package/dist/src/pil/layer1-intent.js +66 -757
  254. package/dist/src/pil/layer15-context-scan.js +15 -1
  255. package/dist/src/pil/layer2_5-ponytail.js +8 -8
  256. package/dist/src/pil/layer3-ee-injection.js +23 -8
  257. package/dist/src/pil/layer4-gsd.js +69 -16
  258. package/dist/src/pil/layer5-context.js +7 -3
  259. package/dist/src/pil/layer6-output.d.ts +23 -0
  260. package/dist/src/pil/layer6-output.js +5 -1
  261. package/dist/src/pil/llm-classify.d.ts +33 -2
  262. package/dist/src/pil/llm-classify.js +123 -131
  263. package/dist/src/pil/native-capabilities-workbook.d.ts +1 -1
  264. package/dist/src/pil/native-capabilities-workbook.js +1 -0
  265. package/dist/src/pil/pipeline.js +34 -2
  266. package/dist/src/pil/response-tools.js +5 -3
  267. package/dist/src/pil/schema.d.ts +1 -0
  268. package/dist/src/pil/schema.js +2 -0
  269. package/dist/src/pil/types.d.ts +18 -0
  270. package/dist/src/playbook/directives.d.ts +4 -0
  271. package/dist/src/playbook/directives.js +17 -5
  272. package/dist/src/product-loop/backlog-builder.d.ts +14 -1
  273. package/dist/src/product-loop/backlog-builder.js +30 -6
  274. package/dist/src/product-loop/discovery-context-format.js +3 -1
  275. package/dist/src/product-loop/discovery-ecosystem.js +4 -1
  276. package/dist/src/product-loop/discovery-interview.js +32 -3
  277. package/dist/src/product-loop/discovery-schema.js +5 -1
  278. package/dist/src/product-loop/done-gate.js +3 -3
  279. package/dist/src/product-loop/ideal-trace.d.ts +7 -0
  280. package/dist/src/product-loop/ideal-trace.js +64 -0
  281. package/dist/src/product-loop/index.d.ts +13 -1
  282. package/dist/src/product-loop/index.js +333 -52
  283. package/dist/src/product-loop/loop-driver.d.ts +7 -0
  284. package/dist/src/product-loop/loop-driver.js +327 -116
  285. package/dist/src/product-loop/phase-plan.d.ts +5 -0
  286. package/dist/src/product-loop/phase-plan.js +39 -2
  287. package/dist/src/product-loop/phase-runner.js +9 -1
  288. package/dist/src/product-loop/progress-snapshot.js +4 -4
  289. package/dist/src/product-loop/sprint-runner.d.ts +111 -0
  290. package/dist/src/product-loop/sprint-runner.js +559 -16
  291. package/dist/src/product-loop/types.d.ts +36 -5
  292. package/dist/src/providers/adapter.d.ts +1 -1
  293. package/dist/src/providers/adapter.js +3 -4
  294. package/dist/src/providers/auth/browser-flow.d.ts +1 -1
  295. package/dist/src/providers/auth/browser-flow.js +1 -1
  296. package/dist/src/providers/auth/openai-oauth.js +1 -1
  297. package/dist/src/providers/auth/registry.js +0 -34
  298. package/dist/src/providers/auth/token-store.js +4 -1
  299. package/dist/src/providers/auth/types.d.ts +1 -1
  300. package/dist/src/providers/auth/types.js +1 -1
  301. package/dist/src/providers/capabilities.d.ts +24 -5
  302. package/dist/src/providers/capabilities.js +42 -24
  303. package/dist/src/providers/endpoints.d.ts +2 -2
  304. package/dist/src/providers/endpoints.js +11 -10
  305. package/dist/src/providers/keychain.d.ts +1 -1
  306. package/dist/src/providers/keychain.js +7 -9
  307. package/dist/src/providers/mcp-vision-bridge.js +82 -172
  308. package/dist/src/providers/openai-compatible.js +8 -1
  309. package/dist/src/providers/pricing.d.ts +2 -2
  310. package/dist/src/providers/pricing.js +3 -13
  311. package/dist/src/providers/runtime.d.ts +27 -2
  312. package/dist/src/providers/runtime.js +78 -15
  313. package/dist/src/providers/strategies/base.strategy.d.ts +16 -0
  314. package/dist/src/providers/strategies/base.strategy.js +24 -1
  315. package/dist/src/providers/strategies/{siliconflow.strategy.d.ts → opencode-go.strategy.d.ts} +3 -3
  316. package/dist/src/providers/strategies/opencode-go.strategy.js +83 -0
  317. package/dist/src/providers/strategies/registry.js +4 -4
  318. package/dist/src/providers/strategies/thinking-mode.d.ts +109 -0
  319. package/dist/src/providers/strategies/thinking-mode.js +280 -1
  320. package/dist/src/providers/strategies/zai.strategy.d.ts +14 -0
  321. package/dist/src/providers/strategies/zai.strategy.js +44 -0
  322. package/dist/src/providers/types.d.ts +5 -6
  323. package/dist/src/providers/types.js +2 -2
  324. package/dist/src/providers/vision-backend.d.ts +47 -0
  325. package/dist/src/providers/vision-backend.js +258 -0
  326. package/dist/src/providers/vision-proxy.d.ts +22 -9
  327. package/dist/src/providers/vision-proxy.js +63 -132
  328. package/dist/src/providers/wire-debug.js +95 -0
  329. package/dist/src/reporter/index.js +1 -1
  330. package/dist/src/router/decide.d.ts +13 -0
  331. package/dist/src/router/decide.js +138 -36
  332. package/dist/src/router/peak-hour.d.ts +38 -0
  333. package/dist/src/router/peak-hour.js +107 -0
  334. package/dist/src/router/step-router.js +3 -2
  335. package/dist/src/router/warm.js +4 -5
  336. package/dist/src/scaffold/bb-ecosystem-apply.js +47 -47
  337. package/dist/src/scaffold/bb-quality-gate.js +5 -5
  338. package/dist/src/scaffold/continuation-prompt.d.ts +11 -0
  339. package/dist/src/scaffold/continuation-prompt.js +86 -60
  340. package/dist/src/scaffold/init-new.js +453 -453
  341. package/dist/src/scaffold/point-to-existing.d.ts +21 -0
  342. package/dist/src/scaffold/point-to-existing.js +25 -0
  343. package/dist/src/self-qa/agentic-loop.js +22 -22
  344. package/dist/src/{ui/state → state}/active-run.d.ts +19 -0
  345. package/dist/src/{ui/state → state}/active-run.js +21 -0
  346. package/dist/src/{ui/status-bar/store.d.ts → state/status-bar-store.d.ts} +4 -1
  347. package/dist/src/{ui/status-bar/store.js → state/status-bar-store.js} +12 -9
  348. package/dist/src/state/turn-trace.d.ts +43 -0
  349. package/dist/src/state/turn-trace.js +32 -0
  350. package/dist/src/storage/db.js +2 -1
  351. package/dist/src/storage/index.d.ts +1 -1
  352. package/dist/src/storage/index.js +1 -1
  353. package/dist/src/storage/interaction-log.d.ts +1 -1
  354. package/dist/src/storage/interaction-log.js +5 -5
  355. package/dist/src/storage/migrations.js +196 -126
  356. package/dist/src/storage/session-experience-store.js +4 -4
  357. package/dist/src/storage/sessions.d.ts +28 -10
  358. package/dist/src/storage/sessions.js +112 -55
  359. package/dist/src/storage/transcript-view.js +1 -1
  360. package/dist/src/storage/transcript.d.ts +51 -0
  361. package/dist/src/storage/transcript.js +383 -112
  362. package/dist/src/storage/usage.js +14 -14
  363. package/dist/src/storage/workspaces.js +12 -12
  364. package/dist/src/tools/file.d.ts +15 -0
  365. package/dist/src/tools/file.js +32 -0
  366. package/dist/src/tools/native-tools.js +5 -0
  367. package/dist/src/tools/registry.d.ts +3 -0
  368. package/dist/src/tools/registry.js +460 -22
  369. package/dist/src/tools/research.d.ts +29 -0
  370. package/dist/src/tools/research.js +233 -0
  371. package/dist/src/types/index.d.ts +118 -3
  372. package/dist/src/ui/app.js +0 -0
  373. package/dist/src/ui/cards/product-status-card.js +1 -1
  374. package/dist/src/ui/components/bubble-body-guard.d.ts +27 -0
  375. package/dist/src/ui/components/bubble-body-guard.js +50 -0
  376. package/dist/src/ui/components/context-rail.d.ts +26 -0
  377. package/dist/src/ui/components/context-rail.js +33 -0
  378. package/dist/src/ui/components/council-conclusion-card.d.ts +73 -0
  379. package/dist/src/ui/components/council-conclusion-card.js +420 -0
  380. package/dist/src/ui/components/council-debate-pill.d.ts +36 -0
  381. package/dist/src/ui/components/council-debate-pill.js +34 -0
  382. package/dist/src/ui/components/council-info-card.js +2 -2
  383. package/dist/src/ui/components/council-leader-bubble.d.ts +10 -3
  384. package/dist/src/ui/components/council-leader-bubble.js +21 -11
  385. package/dist/src/ui/components/council-message-bubble.d.ts +15 -2
  386. package/dist/src/ui/components/council-message-bubble.js +16 -15
  387. package/dist/src/ui/components/council-phase-timeline.d.ts +9 -1
  388. package/dist/src/ui/components/council-phase-timeline.js +49 -15
  389. package/dist/src/ui/components/council-placeholder-bubble.d.ts +16 -9
  390. package/dist/src/ui/components/council-placeholder-bubble.js +32 -29
  391. package/dist/src/ui/components/council-question-card.js +12 -12
  392. package/dist/src/ui/components/council-rail-rounds.d.ts +26 -0
  393. package/dist/src/ui/components/council-rail-rounds.js +57 -0
  394. package/dist/src/ui/components/council-round-group.d.ts +38 -0
  395. package/dist/src/ui/components/council-round-group.js +88 -0
  396. package/dist/src/ui/components/council-status-list.d.ts +3 -1
  397. package/dist/src/ui/components/council-status-list.js +36 -24
  398. package/dist/src/ui/components/council-synthesis-banner.d.ts +7 -2
  399. package/dist/src/ui/components/council-synthesis-banner.js +20 -5
  400. package/dist/src/ui/components/halt-recovery-card.js +9 -5
  401. package/dist/src/ui/components/jump-to-latest-pill.d.ts +11 -0
  402. package/dist/src/ui/components/jump-to-latest-pill.js +14 -0
  403. package/dist/src/ui/components/prompt-box.js +18 -16
  404. package/dist/src/ui/components/session-tree-card.d.ts +14 -0
  405. package/dist/src/ui/components/session-tree-card.js +46 -0
  406. package/dist/src/ui/components/slash-inline-menu.d.ts +12 -0
  407. package/dist/src/ui/components/slash-inline-menu.js +26 -5
  408. package/dist/src/ui/components/task-list-panel.d.ts +14 -1
  409. package/dist/src/ui/components/task-list-panel.js +22 -2
  410. package/dist/src/ui/containers/modals-layer.d.ts +2 -1
  411. package/dist/src/ui/hooks/use-session-picker.d.ts +3 -3
  412. package/dist/src/ui/mcp-modal.js +2 -4
  413. package/dist/src/ui/modals/api-key-modal.js +1 -1
  414. package/dist/src/ui/modals/connect-modal.js +4 -3
  415. package/dist/src/ui/modals/session-picker-modal.d.ts +2 -2
  416. package/dist/src/ui/modals/session-picker-modal.js +3 -5
  417. package/dist/src/ui/picker-providers.d.ts +1 -1
  418. package/dist/src/ui/picker-providers.js +1 -1
  419. package/dist/src/ui/primitives/index.d.ts +1 -0
  420. package/dist/src/ui/primitives/index.js +2 -0
  421. package/dist/src/ui/primitives/semantic-primitives.d.ts +76 -0
  422. package/dist/src/ui/primitives/semantic-primitives.js +81 -0
  423. package/dist/src/ui/slash/compact.js +5 -7
  424. package/dist/src/ui/slash/cost.js +1 -1
  425. package/dist/src/ui/slash/council-inspect.js +4 -4
  426. package/dist/src/ui/slash/council.js +19 -1
  427. package/dist/src/ui/slash/debug.d.ts +3 -31
  428. package/dist/src/ui/slash/debug.js +9 -20
  429. package/dist/src/ui/slash/ideal.d.ts +6 -2
  430. package/dist/src/ui/slash/ideal.js +97 -7
  431. package/dist/src/ui/slash/menu-items.d.ts +7 -0
  432. package/dist/src/ui/slash/menu-items.js +12 -18
  433. package/dist/src/ui/slash/registry.d.ts +2 -0
  434. package/dist/src/ui/slash/registry.js +4 -0
  435. package/dist/src/ui/status-bar/cache-hit.d.ts +6 -0
  436. package/dist/src/ui/status-bar/cache-hit.js +9 -0
  437. package/dist/src/ui/status-bar/index.d.ts +1 -1
  438. package/dist/src/ui/status-bar/index.js +7 -3
  439. package/dist/src/ui/status-bar/usd-meter.d.ts +5 -4
  440. package/dist/src/ui/status-bar/usd-meter.js +6 -4
  441. package/dist/src/ui/theme.d.ts +1 -0
  442. package/dist/src/ui/theme.js +2 -0
  443. package/dist/src/ui/types.d.ts +7 -0
  444. package/dist/src/ui/use-app-logic.js +0 -0
  445. package/dist/src/ui/utils/format.d.ts +14 -0
  446. package/dist/src/ui/utils/format.js +23 -3
  447. package/dist/src/usage/downgrade.js +2 -2
  448. package/dist/src/usage/product-ledger.js +2 -2
  449. package/dist/src/utils/clipboard-image.js +23 -23
  450. package/dist/src/utils/install-manager.js +14 -11
  451. package/dist/src/utils/logger.js +2 -2
  452. package/dist/src/utils/permission-mode.js +5 -3
  453. package/dist/src/utils/redactor.js +1 -1
  454. package/dist/src/utils/settings.d.ts +153 -5
  455. package/dist/src/utils/settings.js +233 -29
  456. package/dist/src/utils/side-question.js +2 -2
  457. package/dist/src/utils/skills.js +3 -3
  458. package/dist/src/utils/visible-retry.d.ts +11 -0
  459. package/dist/src/utils/visible-retry.js +10 -1
  460. package/dist/src/verify/entrypoint.d.ts +1 -1
  461. package/dist/src/verify/entrypoint.js +1 -1
  462. package/dist/src/verify/recipes.d.ts +13 -0
  463. package/dist/src/verify/recipes.js +15 -0
  464. package/package.json +135 -132
  465. package/dist/src/providers/auth/gcloud.d.ts +0 -28
  466. package/dist/src/providers/auth/gcloud.js +0 -102
  467. package/dist/src/providers/auth/gemini-oauth.d.ts +0 -82
  468. package/dist/src/providers/auth/gemini-oauth.js +0 -472
  469. package/dist/src/providers/gemini.d.ts +0 -11
  470. package/dist/src/providers/gemini.js +0 -45
  471. package/dist/src/providers/siliconflow-sse-repair.d.ts +0 -58
  472. package/dist/src/providers/siliconflow-sse-repair.js +0 -177
  473. package/dist/src/providers/strategies/google.strategy.d.ts +0 -22
  474. package/dist/src/providers/strategies/google.strategy.js +0 -174
  475. package/dist/src/providers/strategies/siliconflow.strategy.js +0 -29
  476. package/dist/src/ui/containers/chat-feed.d.ts +0 -40
  477. package/dist/src/ui/containers/chat-feed.js +0 -66
@@ -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)
@@ -13,9 +13,9 @@ export function applyMigrations(db) {
13
13
  db.pragma("user_version = 2");
14
14
  }
15
15
  if (version < 3) {
16
- db.exec(`
17
- ALTER TABLE usage_events ADD COLUMN pil_active INTEGER NOT NULL DEFAULT 0;
18
- ALTER TABLE usage_events ADD COLUMN enrichment_delta INTEGER NOT NULL DEFAULT 0;
16
+ db.exec(`
17
+ ALTER TABLE usage_events ADD COLUMN pil_active INTEGER NOT NULL DEFAULT 0;
18
+ ALTER TABLE usage_events ADD COLUMN enrichment_delta INTEGER NOT NULL DEFAULT 0;
19
19
  `);
20
20
  db.pragma("user_version = 3");
21
21
  }
@@ -34,24 +34,24 @@ export function applyMigrations(db) {
34
34
  db.pragma("user_version = 4");
35
35
  }
36
36
  if (version < 5) {
37
- db.exec(`
38
- CREATE TABLE IF NOT EXISTS interaction_logs (
39
- id INTEGER PRIMARY KEY AUTOINCREMENT,
40
- session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
41
- event_type TEXT NOT NULL,
42
- event_subtype TEXT,
43
- model TEXT,
44
- duration_ms INTEGER,
45
- input_tokens INTEGER,
46
- output_tokens INTEGER,
47
- metadata_json TEXT,
48
- created_at TEXT NOT NULL
49
- ) STRICT;
50
-
51
- CREATE INDEX IF NOT EXISTS idx_interaction_logs_session
52
- ON interaction_logs(session_id, created_at DESC);
53
- CREATE INDEX IF NOT EXISTS idx_interaction_logs_event_type
54
- ON interaction_logs(event_type, created_at DESC);
37
+ db.exec(`
38
+ CREATE TABLE IF NOT EXISTS interaction_logs (
39
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
40
+ session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
41
+ event_type TEXT NOT NULL,
42
+ event_subtype TEXT,
43
+ model TEXT,
44
+ duration_ms INTEGER,
45
+ input_tokens INTEGER,
46
+ output_tokens INTEGER,
47
+ metadata_json TEXT,
48
+ created_at TEXT NOT NULL
49
+ ) STRICT;
50
+
51
+ CREATE INDEX IF NOT EXISTS idx_interaction_logs_session
52
+ ON interaction_logs(session_id, created_at DESC);
53
+ CREATE INDEX IF NOT EXISTS idx_interaction_logs_event_type
54
+ ON interaction_logs(event_type, created_at DESC);
55
55
  `);
56
56
  db.pragma("user_version = 5");
57
57
  }
@@ -96,117 +96,187 @@ 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
- db.exec(`
104
- CREATE TABLE IF NOT EXISTS workspaces (
105
- id TEXT PRIMARY KEY,
106
- scope_key TEXT NOT NULL UNIQUE,
107
- canonical_path TEXT NOT NULL,
108
- git_root TEXT,
109
- display_name TEXT NOT NULL,
110
- last_seen_at TEXT NOT NULL
111
- ) STRICT;
112
-
113
- CREATE TABLE IF NOT EXISTS sessions (
114
- id TEXT PRIMARY KEY,
115
- workspace_id TEXT NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
116
- parent_session_id TEXT REFERENCES sessions(id) ON DELETE SET NULL,
117
- title TEXT,
118
- model TEXT NOT NULL,
119
- mode TEXT NOT NULL,
120
- cwd_at_start TEXT NOT NULL,
121
- cwd_last TEXT NOT NULL,
122
- status TEXT NOT NULL,
123
- created_at TEXT NOT NULL,
124
- updated_at TEXT NOT NULL
125
- ) STRICT;
126
-
127
- CREATE TABLE IF NOT EXISTS messages (
128
- session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
129
- seq INTEGER NOT NULL,
130
- role TEXT NOT NULL,
131
- message_json TEXT NOT NULL,
132
- created_at TEXT NOT NULL,
133
- status TEXT,
134
- PRIMARY KEY (session_id, seq)
135
- ) STRICT;
136
-
137
- CREATE TABLE IF NOT EXISTS tool_calls (
138
- id INTEGER PRIMARY KEY AUTOINCREMENT,
139
- session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
140
- message_seq INTEGER NOT NULL,
141
- tool_call_id TEXT NOT NULL,
142
- tool_name TEXT NOT NULL,
143
- args_json TEXT NOT NULL,
144
- status TEXT NOT NULL,
145
- started_at TEXT NOT NULL,
146
- completed_at TEXT,
147
- UNIQUE(session_id, tool_call_id)
148
- ) STRICT;
149
-
150
- CREATE TABLE IF NOT EXISTS tool_results (
151
- id INTEGER PRIMARY KEY AUTOINCREMENT,
152
- tool_call_row_id INTEGER NOT NULL REFERENCES tool_calls(id) ON DELETE CASCADE,
153
- output_kind TEXT NOT NULL,
154
- output_json TEXT NOT NULL,
155
- success INTEGER NOT NULL,
156
- created_at TEXT NOT NULL
157
- ) STRICT;
158
-
159
- CREATE TABLE IF NOT EXISTS usage_events (
160
- id INTEGER PRIMARY KEY AUTOINCREMENT,
161
- session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
162
- message_seq INTEGER,
163
- source TEXT NOT NULL,
164
- model TEXT NOT NULL,
165
- input_tokens INTEGER NOT NULL DEFAULT 0,
166
- output_tokens INTEGER NOT NULL DEFAULT 0,
167
- total_tokens INTEGER NOT NULL DEFAULT 0,
168
- cost_micros INTEGER NOT NULL DEFAULT 0,
169
- created_at TEXT NOT NULL,
170
- cache_read_tokens INTEGER NOT NULL DEFAULT 0,
171
- cache_creation_tokens INTEGER NOT NULL DEFAULT 0
172
- ) STRICT;
173
-
174
- CREATE TABLE IF NOT EXISTS compactions (
175
- id INTEGER PRIMARY KEY AUTOINCREMENT,
176
- session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
177
- first_kept_seq INTEGER NOT NULL,
178
- summary TEXT NOT NULL,
179
- tokens_before INTEGER NOT NULL DEFAULT 0,
180
- created_at TEXT NOT NULL
181
- ) STRICT;
182
-
183
- CREATE INDEX IF NOT EXISTS idx_sessions_workspace_updated
184
- ON sessions(workspace_id, updated_at DESC);
185
- CREATE INDEX IF NOT EXISTS idx_sessions_parent_id
186
- ON sessions(parent_session_id);
187
- CREATE INDEX IF NOT EXISTS idx_messages_session_seq
188
- ON messages(session_id, seq);
189
- CREATE INDEX IF NOT EXISTS idx_tool_calls_session_seq
190
- ON tool_calls(session_id, message_seq);
191
- CREATE INDEX IF NOT EXISTS idx_usage_events_session_created
192
- ON usage_events(session_id, created_at DESC);
193
- CREATE INDEX IF NOT EXISTS idx_compactions_session_created
194
- ON compactions(session_id, created_at DESC);
173
+ db.exec(`
174
+ CREATE TABLE IF NOT EXISTS workspaces (
175
+ id TEXT PRIMARY KEY,
176
+ scope_key TEXT NOT NULL UNIQUE,
177
+ canonical_path TEXT NOT NULL,
178
+ git_root TEXT,
179
+ display_name TEXT NOT NULL,
180
+ last_seen_at TEXT NOT NULL
181
+ ) STRICT;
182
+
183
+ CREATE TABLE IF NOT EXISTS sessions (
184
+ id TEXT PRIMARY KEY,
185
+ workspace_id TEXT NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
186
+ parent_session_id TEXT REFERENCES sessions(id) ON DELETE SET NULL,
187
+ title TEXT,
188
+ model TEXT NOT NULL,
189
+ mode TEXT NOT NULL,
190
+ cwd_at_start TEXT NOT NULL,
191
+ cwd_last TEXT NOT NULL,
192
+ status TEXT NOT NULL,
193
+ created_at TEXT NOT NULL,
194
+ updated_at TEXT NOT NULL
195
+ ) STRICT;
196
+
197
+ CREATE TABLE IF NOT EXISTS messages (
198
+ session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
199
+ seq INTEGER NOT NULL,
200
+ role TEXT NOT NULL,
201
+ message_json TEXT NOT NULL,
202
+ created_at TEXT NOT NULL,
203
+ status TEXT,
204
+ PRIMARY KEY (session_id, seq)
205
+ ) STRICT;
206
+
207
+ CREATE TABLE IF NOT EXISTS tool_calls (
208
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
209
+ session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
210
+ message_seq INTEGER NOT NULL,
211
+ tool_call_id TEXT NOT NULL,
212
+ tool_name TEXT NOT NULL,
213
+ args_json TEXT NOT NULL,
214
+ status TEXT NOT NULL,
215
+ started_at TEXT NOT NULL,
216
+ completed_at TEXT,
217
+ UNIQUE(session_id, tool_call_id)
218
+ ) STRICT;
219
+
220
+ CREATE TABLE IF NOT EXISTS tool_results (
221
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
222
+ tool_call_row_id INTEGER NOT NULL REFERENCES tool_calls(id) ON DELETE CASCADE,
223
+ output_kind TEXT NOT NULL,
224
+ output_json TEXT NOT NULL,
225
+ success INTEGER NOT NULL,
226
+ created_at TEXT NOT NULL
227
+ ) STRICT;
228
+
229
+ CREATE TABLE IF NOT EXISTS usage_events (
230
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
231
+ session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
232
+ message_seq INTEGER,
233
+ source TEXT NOT NULL,
234
+ model TEXT NOT NULL,
235
+ input_tokens INTEGER NOT NULL DEFAULT 0,
236
+ output_tokens INTEGER NOT NULL DEFAULT 0,
237
+ total_tokens INTEGER NOT NULL DEFAULT 0,
238
+ cost_micros INTEGER NOT NULL DEFAULT 0,
239
+ created_at TEXT NOT NULL,
240
+ cache_read_tokens INTEGER NOT NULL DEFAULT 0,
241
+ cache_creation_tokens INTEGER NOT NULL DEFAULT 0
242
+ ) STRICT;
243
+
244
+ CREATE TABLE IF NOT EXISTS compactions (
245
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
246
+ session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
247
+ first_kept_seq INTEGER NOT NULL,
248
+ summary TEXT NOT NULL,
249
+ tokens_before INTEGER NOT NULL DEFAULT 0,
250
+ created_at TEXT NOT NULL
251
+ ) STRICT;
252
+
253
+ CREATE INDEX IF NOT EXISTS idx_sessions_workspace_updated
254
+ ON sessions(workspace_id, updated_at DESC);
255
+ CREATE INDEX IF NOT EXISTS idx_sessions_parent_id
256
+ ON sessions(parent_session_id);
257
+ CREATE INDEX IF NOT EXISTS idx_messages_session_seq
258
+ ON messages(session_id, seq);
259
+ CREATE INDEX IF NOT EXISTS idx_tool_calls_session_seq
260
+ ON tool_calls(session_id, message_seq);
261
+ CREATE INDEX IF NOT EXISTS idx_usage_events_session_created
262
+ ON usage_events(session_id, created_at DESC);
263
+ CREATE INDEX IF NOT EXISTS idx_compactions_session_created
264
+ ON compactions(session_id, created_at DESC);
195
265
  `);
196
266
  }
197
267
  function createCompactionSchema(db) {
198
- db.exec(`
199
- CREATE TABLE IF NOT EXISTS compactions (
200
- id INTEGER PRIMARY KEY AUTOINCREMENT,
201
- session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
202
- first_kept_seq INTEGER NOT NULL,
203
- summary TEXT NOT NULL,
204
- tokens_before INTEGER NOT NULL DEFAULT 0,
205
- created_at TEXT NOT NULL
206
- ) STRICT;
207
-
208
- CREATE INDEX IF NOT EXISTS idx_compactions_session_created
209
- ON compactions(session_id, created_at DESC);
268
+ db.exec(`
269
+ CREATE TABLE IF NOT EXISTS compactions (
270
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
271
+ session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
272
+ first_kept_seq INTEGER NOT NULL,
273
+ summary TEXT NOT NULL,
274
+ tokens_before INTEGER NOT NULL DEFAULT 0,
275
+ created_at TEXT NOT NULL
276
+ ) STRICT;
277
+
278
+ CREATE INDEX IF NOT EXISTS idx_compactions_session_created
279
+ ON compactions(session_id, created_at DESC);
210
280
  `);
211
281
  }
212
282
  //# sourceMappingURL=migrations.js.map
@@ -70,8 +70,8 @@ function parseCounts(json) {
70
70
  export function selectSessionExperience(sessionId) {
71
71
  try {
72
72
  const row = getDatabase()
73
- .prepare(`SELECT metadata_json FROM interaction_logs
74
- WHERE session_id = ? AND event_type = ?
73
+ .prepare(`SELECT metadata_json FROM interaction_logs
74
+ WHERE session_id = ? AND event_type = ?
75
75
  ORDER BY created_at DESC, id DESC LIMIT 1`)
76
76
  .get(sessionId, EVENT_TYPE);
77
77
  return parseCounts(row?.metadata_json ?? null);
@@ -150,8 +150,8 @@ export function computeExperienceAggregate(rows, limit = 100) {
150
150
  export function aggregateSessionExperience(limit = 100) {
151
151
  try {
152
152
  const rows = getDatabase()
153
- .prepare(`SELECT session_id, metadata_json, created_at FROM interaction_logs
154
- WHERE event_type = ?
153
+ .prepare(`SELECT session_id, metadata_json, created_at FROM interaction_logs
154
+ WHERE event_type = ?
155
155
  ORDER BY created_at DESC, id DESC`)
156
156
  .all(EVENT_TYPE);
157
157
  return computeExperienceAggregate(rows, limit);
@@ -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";
@@ -43,12 +43,12 @@ export class SessionStore {
43
43
  const now = new Date().toISOString();
44
44
  const id = createSessionId();
45
45
  const db = getDatabase();
46
- db.prepare(`
47
- INSERT INTO sessions (
48
- id, workspace_id, title, model, mode, cwd_at_start, cwd_last, status, created_at, updated_at
49
- ) VALUES (
50
- @id, @workspace_id, NULL, @model, @mode, @cwd_at_start, @cwd_last, 'active', @created_at, @updated_at
51
- )
46
+ db.prepare(`
47
+ INSERT INTO sessions (
48
+ id, workspace_id, title, model, mode, cwd_at_start, cwd_last, status, created_at, updated_at, kind, root_session_id
49
+ ) VALUES (
50
+ @id, @workspace_id, NULL, @model, @mode, @cwd_at_start, @cwd_last, 'active', @created_at, @updated_at, 'conversation', @id
51
+ )
52
52
  `).run({
53
53
  id,
54
54
  workspace_id: this.workspace.id,
@@ -62,50 +62,52 @@ 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()
89
- .prepare(`
90
- SELECT id, workspace_id, title, model, mode, cwd_at_start, cwd_last, status, created_at, updated_at
91
- FROM sessions
92
- WHERE workspace_id = ?
93
- ORDER BY updated_at DESC
94
- LIMIT 1
81
+ .prepare(`
82
+ SELECT id, workspace_id, title, model, mode, cwd_at_start, cwd_last, status, created_at, updated_at
83
+ FROM sessions
84
+ WHERE workspace_id = ?
85
+ ORDER BY updated_at DESC
86
+ LIMIT 1
95
87
  `)
96
88
  .get(this.workspace.id);
97
89
  return row ? toSessionInfo(row) : null;
98
90
  }
99
91
  getSessionById(id) {
100
92
  const row = getDatabase()
101
- .prepare(`
102
- SELECT id, workspace_id, title, model, mode, cwd_at_start, cwd_last, status, created_at, updated_at
103
- FROM sessions
104
- WHERE id = ?
93
+ .prepare(`
94
+ SELECT id, workspace_id, title, model, mode, cwd_at_start, cwd_last, status, created_at, updated_at
95
+ FROM sessions
96
+ WHERE id = ?
105
97
  `)
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) {
@@ -116,49 +118,49 @@ export class SessionStore {
116
118
  setStatus(id, status) {
117
119
  const now = new Date().toISOString();
118
120
  getDatabase()
119
- .prepare(`
120
- UPDATE sessions
121
- SET status = ?, updated_at = ?
122
- WHERE id = ?
121
+ .prepare(`
122
+ UPDATE sessions
123
+ SET status = ?, updated_at = ?
124
+ WHERE id = ?
123
125
  `)
124
126
  .run(status, now, id);
125
127
  }
126
128
  setTitle(id, title) {
127
129
  const now = new Date().toISOString();
128
130
  getDatabase()
129
- .prepare(`
130
- UPDATE sessions
131
- SET title = ?, updated_at = ?
132
- WHERE id = ?
131
+ .prepare(`
132
+ UPDATE sessions
133
+ SET title = ?, updated_at = ?
134
+ WHERE id = ?
133
135
  `)
134
136
  .run(title, now, id);
135
137
  }
136
138
  setModel(id, model) {
137
139
  const now = new Date().toISOString();
138
140
  getDatabase()
139
- .prepare(`
140
- UPDATE sessions
141
- SET model = ?, updated_at = ?
142
- WHERE id = ?
141
+ .prepare(`
142
+ UPDATE sessions
143
+ SET model = ?, updated_at = ?
144
+ WHERE id = ?
143
145
  `)
144
146
  .run(model, now, id);
145
147
  }
146
148
  setMode(id, mode) {
147
149
  const now = new Date().toISOString();
148
150
  getDatabase()
149
- .prepare(`
150
- UPDATE sessions
151
- SET mode = ?, updated_at = ?
152
- WHERE id = ?
151
+ .prepare(`
152
+ UPDATE sessions
153
+ SET mode = ?, updated_at = ?
154
+ WHERE id = ?
153
155
  `)
154
156
  .run(mode, now, id);
155
157
  }
156
158
  touchSession(id, cwd) {
157
159
  getDatabase()
158
- .prepare(`
159
- UPDATE sessions
160
- SET cwd_last = ?, updated_at = ?
161
- WHERE id = ?
160
+ .prepare(`
161
+ UPDATE sessions
162
+ SET cwd_last = ?, updated_at = ?
163
+ WHERE id = ?
162
164
  `)
163
165
  .run(cwd, new Date().toISOString(), id);
164
166
  }
@@ -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") {