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
@@ -2,16 +2,6 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "@opentui/reac
2
2
  import { useEffect, useState } from "react";
3
3
  const SPINNER_FRAMES = ["⬒", "⬔", "⬓", "⬕"];
4
4
  const DONE_HOLD_MS = 1500;
5
- const PHASE_LABEL = {
6
- clarify: "clarify",
7
- plan_debate: "plan",
8
- research: "research",
9
- opening: "opening",
10
- exchange: "exchange",
11
- evaluate: "evaluate",
12
- synthesis: "synthesis",
13
- summary: "summary",
14
- };
15
5
  function Spinner() {
16
6
  const [frame, setFrame] = useState(0);
17
7
  useEffect(() => {
@@ -20,6 +10,20 @@ function Spinner() {
20
10
  }, []);
21
11
  return _jsx(_Fragment, { children: SPINNER_FRAMES[frame] });
22
12
  }
13
+ /**
14
+ * Re-render every `tickMs` so active rows show a live-ticking elapsed counter.
15
+ * Without this the row freezes at the last emitter-sent elapsedMs (emitters
16
+ * only send on state transitions) — the same "im lìm" symptom the phase
17
+ * timeline fixed with its own heartbeat (council-phase-timeline.tsx).
18
+ */
19
+ function useHeartbeat(tickMs = 1000) {
20
+ const [, force] = useState(0);
21
+ useEffect(() => {
22
+ const id = setInterval(() => force((n) => n + 1), tickMs);
23
+ return () => clearInterval(id);
24
+ }, [tickMs]);
25
+ return Date.now();
26
+ }
23
27
  function formatElapsed(ms) {
24
28
  if (!ms || ms < 0)
25
29
  return "0s";
@@ -38,18 +42,22 @@ function formatTokens(s) {
38
42
  parts.push(`↓ ${s.tokensOut}`);
39
43
  return parts.length > 0 ? ` · ${parts.join(" · ")} tokens` : "";
40
44
  }
41
- export function CouncilStatusList({ statuses, theme: t }) {
42
- if (statuses.length === 0)
45
+ export function CouncilStatusList({ statuses, theme: t, sessionId }) {
46
+ // Hook must run unconditionally — before the empty-list early return.
47
+ const now = useHeartbeat(1000);
48
+ if (statuses.length === 0 && !sessionId)
43
49
  return null;
44
- return (_jsx("box", { flexDirection: "column", paddingLeft: 2, paddingTop: 0, flexShrink: 0, children: statuses.map((s) => {
45
- const isError = s.state === "error";
46
- const isDone = s.state === "done";
47
- const marker = isError ? "✗" : isDone ? "✓" : _jsx(Spinner, {});
48
- const markerColor = isError ? t.diffRemovedFg : isDone ? t.planOptionCheck : t.accent;
49
- const labelColor = isError ? t.diffRemovedFg : t.text;
50
- const meta = `(${formatElapsed(s.elapsedMs)}${formatTokens(s)})`;
51
- return (_jsxs("box", { flexDirection: "column", children: [_jsxs("box", { children: [_jsx("text", { fg: markerColor, children: marker }), _jsx("text", { fg: labelColor, children: ` ${s.label}` }), _jsx("text", { fg: t.textMuted, children: ` ${meta}` }), _jsx("text", { fg: t.textDim, children: ` [${PHASE_LABEL[s.phase]}]` })] }), s.detail && (_jsx("box", { paddingLeft: 2, children: _jsx("text", { fg: t.textMuted, children: `└ ${truncate(s.detail, 90)}` }) })), s.errorMessage && (_jsx("box", { paddingLeft: 2, children: _jsx("text", { fg: t.diffRemovedFg, children: `└ ${truncate(s.errorMessage, 90)}` }) }))] }, s.statusId));
52
- }) }));
50
+ return (_jsxs("box", { flexDirection: "column", paddingLeft: 2, paddingTop: 0, flexShrink: 0, children: [sessionId && (_jsx("box", { marginBottom: 1, children: _jsx("text", { fg: t.textDim, children: `session ${sessionId}` }) })), statuses.map((s) => {
51
+ const isError = s.state === "error";
52
+ const isDone = s.state === "done";
53
+ const marker = isError ? "✗" : isDone ? "✓" : _jsx(Spinner, {});
54
+ const markerColor = isError ? t.diffRemovedFg : isDone ? t.planOptionCheck : t.accent;
55
+ const labelColor = isError ? t.diffRemovedFg : t.text;
56
+ const isLive = s.state === "start" || s.state === "tick";
57
+ const liveElapsedMs = isLive && typeof s.startedAt === "number" ? Math.max(0, now - s.startedAt) : s.elapsedMs;
58
+ const meta = `(${formatElapsed(liveElapsedMs)}${formatTokens(s)})`;
59
+ return (_jsxs("box", { flexDirection: "column", children: [_jsxs("box", { children: [_jsx("text", { fg: markerColor, children: marker }), _jsx("text", { fg: labelColor, children: ` ${s.label}` }), _jsx("text", { fg: t.textMuted, children: ` ${meta}` })] }), s.detail && (_jsx("box", { paddingLeft: 2, children: _jsx("text", { fg: t.textMuted, children: `└ ${truncate(s.detail, 140)}` }) })), s.errorMessage && (_jsx("box", { paddingLeft: 2, children: _jsx("text", { fg: t.diffRemovedFg, children: `└ ${truncate(s.errorMessage, 140)}` }) }))] }, s.statusId));
60
+ })] }));
53
61
  }
54
62
  function truncate(s, max) {
55
63
  if (s.length <= max)
@@ -63,10 +71,14 @@ function truncate(s, max) {
63
71
  */
64
72
  export function upsertStatus(prev, next) {
65
73
  const idx = prev.findIndex((s) => s.statusId === next.statusId);
66
- if (idx === -1)
67
- return [...prev, next];
74
+ if (idx === -1) {
75
+ // Stamp a client-side start anchor so the row can live-tick between emitter
76
+ // updates (emitters only send elapsedMs on state transitions).
77
+ const startedAt = next.startedAt ?? Date.now() - (next.elapsedMs ?? 0);
78
+ return [...prev, { ...next, startedAt }];
79
+ }
68
80
  const out = prev.slice();
69
- out[idx] = next;
81
+ out[idx] = { ...next, startedAt: next.startedAt ?? out[idx].startedAt };
70
82
  return out;
71
83
  }
72
84
  /** Remove `done` rows that finished more than DONE_HOLD_MS ago. */
@@ -1,9 +1,14 @@
1
1
  import type { CouncilMessage } from "../../types/index.js";
2
+ import type { Theme } from "../theme.js";
2
3
  export interface CouncilSynthesisBannerProps {
3
4
  msg: CouncilMessage;
5
+ theme: Theme;
4
6
  }
5
7
  export declare function buildSynthesisTitle(round: number | undefined): string;
6
8
  /**
7
- * Full-width double-border pinned banner for round and final synthesis.
9
+ * Round / final synthesis, rendered as a linear group-chat row that closes the
10
+ * thread. A synthesis-colored left bar + bold title distinguishes the leader's
11
+ * verdict from the debate turns while staying in the same downward stream (no
12
+ * centered/full-width banner that broke the chat flow).
8
13
  */
9
- export declare function CouncilSynthesisBanner({ msg }: CouncilSynthesisBannerProps): import("react").ReactNode;
14
+ export declare function CouncilSynthesisBanner({ msg, theme: t }: CouncilSynthesisBannerProps): import("react").ReactNode;
@@ -1,15 +1,30 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "@opentui/react/jsx-runtime";
2
- import { dark } from "../theme.js";
3
2
  import { truncateCodeBlocks } from "./code-block-truncate.js";
3
+ import { CouncilConclusionCard, parseConclusion } from "./council-conclusion-card.js";
4
4
  export function buildSynthesisTitle(round) {
5
5
  return round === undefined ? "Final Synthesis" : `Round ${round} Synthesis`;
6
6
  }
7
7
  /**
8
- * Full-width double-border pinned banner for round and final synthesis.
8
+ * Round / final synthesis, rendered as a linear group-chat row that closes the
9
+ * thread. A synthesis-colored left bar + bold title distinguishes the leader's
10
+ * verdict from the debate turns while staying in the same downward stream (no
11
+ * centered/full-width banner that broke the chat flow).
9
12
  */
10
- export function CouncilSynthesisBanner({ msg }) {
11
- const bodyText = truncateCodeBlocks(msg.text.trim());
13
+ export function CouncilSynthesisBanner({ msg, theme: t }) {
14
+ // When the synthesis is a structured evaluation/decision JSON, render it as a
15
+ // scannable conclusion card instead of dumping raw JSON as freetext. Prose
16
+ // syntheses (no parseable JSON object) fall through to the plain-text path.
17
+ const conclusion = parseConclusion(msg.text);
18
+ if (conclusion) {
19
+ return _jsx(CouncilConclusionCard, { conclusion: conclusion, round: msg.round, theme: t });
20
+ }
21
+ // A `---READABLE---` marker means the synthesizer already produced a human
22
+ // prose tail — show ONLY that tail, not the raw JSON above it. Empty tail
23
+ // (marker at the very end) falls back to the full text.
24
+ const raw = msg.text.trim();
25
+ const readableIdx = raw.indexOf("---READABLE---");
26
+ const bodyText = truncateCodeBlocks(readableIdx !== -1 ? raw.slice(readableIdx + "---READABLE---".length).trim() || raw : raw);
12
27
  const title = buildSynthesisTitle(msg.round);
13
- return (_jsx("box", { flexDirection: "column", marginBottom: 1, children: _jsxs("box", { borderStyle: "double", borderColor: dark.councilSynthesisBorder, flexDirection: "column", paddingLeft: 1, paddingRight: 1, children: [_jsx("text", { fg: dark.councilSynthesisBorder, attributes: 1, children: title }), _jsx("text", { fg: dark.text, children: bodyText })] }) }));
28
+ return (_jsxs("box", { flexDirection: "column", marginBottom: 1, border: ["left"], borderColor: t.councilSynthesisBorder, paddingLeft: 2, children: [_jsx("text", { fg: t.councilSynthesisBorder, attributes: 1, children: title }), _jsx("text", { fg: t.text, children: bodyText })] }));
14
29
  }
15
30
  //# sourceMappingURL=council-synthesis-banner.js.map
@@ -14,14 +14,18 @@ import { Semantic } from "@muonroi/agent-harness-opentui";
14
14
  const MAX_CARD_COLS = 100;
15
15
  const FALLBACK_THRESHOLD = 70;
16
16
  const REASON_LABELS = {
17
- no_recipe: "No verify recipe detected",
18
- zero_coverage: "Zero test coverage detected",
19
- budget_exhausted: "Token budget exhausted",
17
+ no_recipe: "No verification recipe found",
18
+ zero_coverage: "No test coverage found",
19
+ budget_exhausted: "Token budget used up",
20
+ sprint_failed: "Sprint failed",
20
21
  };
21
22
  export function HaltRecoveryCard({ halt, selectedIndex, terminalCols, theme }) {
22
23
  const fallback = terminalCols < FALLBACK_THRESHOLD;
23
24
  const width = fallback ? terminalCols : Math.min(terminalCols - 2, MAX_CARD_COLS);
24
- const title = `Halted ${REASON_LABELS[halt.reason] ?? halt.reason}`;
25
- return (_jsx(Semantic, { id: "ideal-halt-card", role: "dialog", name: "Recovery options", isModal: true, children: _jsx("box", { flexDirection: "column", marginBottom: 1, children: _jsxs("box", { width: width, borderStyle: "single", borderColor: theme.haltCardBorder, flexDirection: "column", paddingLeft: 1, paddingRight: 1, children: [_jsx("text", { fg: theme.haltCardTitle, attributes: 1, children: title }), halt.detail && (_jsx("text", { fg: theme.haltCardDetail, marginTop: 1, children: halt.detail })), _jsx("box", { flexDirection: "column", marginTop: 1, children: halt.recovery_options.map((opt, i) => (_jsxs(Semantic, { id: `halt-option-${opt.id}`, role: "listitem", name: opt.label, selected: i === selectedIndex || undefined, children: [_jsx("box", { flexDirection: "row", marginBottom: 0, children: _jsxs("text", { fg: i === selectedIndex ? theme.haltCardOptionSelected : theme.haltCardOptionDefault, children: [i === selectedIndex ? "▶ " : " ", opt.label] }) }), _jsx("box", { flexDirection: "row", marginLeft: 2, children: _jsx("text", { fg: theme.haltCardOptionDesc, children: opt.description }) })] }, opt.id))) }), _jsx("text", { fg: theme.haltCardHint, marginTop: 1, children: "\u2191\u2193 navigate \u00B7 Enter select \u00B7 Esc dismiss" })] }) }) }));
25
+ // A a sprint break titles with the failing sprint number ("Sprint 3 failed").
26
+ const title = halt.reason === "sprint_failed" && typeof halt.sprintN === "number"
27
+ ? `Halted — Sprint ${halt.sprintN} failed`
28
+ : `Halted — ${REASON_LABELS[halt.reason] ?? halt.reason}`;
29
+ return (_jsx(Semantic, { id: "ideal-halt-card", role: "dialog", name: "Recovery options", isModal: true, children: _jsx("box", { flexDirection: "column", marginBottom: 1, children: _jsxs("box", { width: width, borderStyle: "single", borderColor: theme.haltCardBorder, flexDirection: "column", paddingLeft: 1, paddingRight: 1, children: [_jsx("text", { fg: theme.haltCardTitle, attributes: 1, children: title }), halt.detail && (_jsx("text", { fg: theme.haltCardDetail, marginTop: 1, children: halt.detail })), _jsx("box", { flexDirection: "column", marginTop: 1, children: halt.recovery_options.map((opt, i) => (_jsxs(Semantic, { id: `halt-option-${opt.id}`, role: "listitem", name: opt.label, selected: i === selectedIndex || undefined, children: [_jsx("box", { flexDirection: "row", marginBottom: 0, children: _jsxs("text", { fg: i === selectedIndex ? theme.haltCardOptionSelected : theme.haltCardOptionDefault, children: [i === selectedIndex ? "▶ " : " ", opt.label] }) }), _jsx("box", { flexDirection: "row", marginLeft: 2, children: _jsx("text", { fg: theme.haltCardOptionDesc, children: opt.description }) })] }, opt.id))) }), _jsx("text", { fg: theme.haltCardHint, marginTop: 1, children: "\u2191/\u2193 to browse \u00B7 Enter to select \u00B7 Esc to dismiss" })] }) }) }));
26
30
  }
27
31
  //# sourceMappingURL=halt-recovery-card.js.map
@@ -0,0 +1,11 @@
1
+ export interface JumpToLatestPillProps {
2
+ /** Number of new appends suppressed since the user scrolled up. */
3
+ newSinceLock: number;
4
+ }
5
+ /**
6
+ * Scroll-lock affordance (MUONROI_SCROLL_LOCK): shown pinned to the bottom of
7
+ * the transcript when the user has scrolled up while new content is arriving.
8
+ * Pressing End re-pins to the live tail. Glyph-free (ASCII only) so it renders
9
+ * on every terminal — the debate pill work proved non-`● ✓` glyphs are unsafe.
10
+ */
11
+ export declare function JumpToLatestPill({ newSinceLock }: JumpToLatestPillProps): import("react").ReactNode;
@@ -0,0 +1,14 @@
1
+ import { jsx as _jsx } from "@opentui/react/jsx-runtime";
2
+ import { Semantic } from "@muonroi/agent-harness-opentui";
3
+ import { dark } from "../theme.js";
4
+ /**
5
+ * Scroll-lock affordance (MUONROI_SCROLL_LOCK): shown pinned to the bottom of
6
+ * the transcript when the user has scrolled up while new content is arriving.
7
+ * Pressing End re-pins to the live tail. Glyph-free (ASCII only) so it renders
8
+ * on every terminal — the debate pill work proved non-`● ✓` glyphs are unsafe.
9
+ */
10
+ export function JumpToLatestPill({ newSinceLock }) {
11
+ const label = newSinceLock > 0 ? `${newSinceLock} new below - press End to jump` : "press End to jump to latest";
12
+ return (_jsx(Semantic, { id: "jump-to-latest", role: "region", props: { newSinceLock }, children: _jsx("box", { flexDirection: "row", alignSelf: "center", paddingLeft: 1, paddingRight: 1, backgroundColor: dark.accent, children: _jsx("text", { fg: "#ffffff", attributes: 1, children: label }) }) }));
13
+ }
14
+ //# sourceMappingURL=jump-to-latest-pill.js.map
@@ -1,7 +1,7 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "@opentui/react/jsx-runtime";
2
- import { Semantic } from "@muonroi/agent-harness-opentui";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "@opentui/react/jsx-runtime";
3
2
  import { useEffect, useState } from "react";
4
3
  import { PROMPT_LOADING_FRAMES } from "../constants.js";
4
+ import { Menu, TextBox } from "../primitives/index.js";
5
5
  import { withAlpha } from "../utils/color.js";
6
6
  import { SuggestionOverlay } from "./SuggestionOverlay.js";
7
7
  import { ContextMeter } from "./session-header.js";
@@ -40,27 +40,29 @@ export function PromptModeLabel({ t, modeInfo, isProcessing, }) {
40
40
  export function PromptBox({ t, inputRef, isProcessing, showModelPicker, showSandboxPicker, showWalletPicker, showSlashMenu, showPlanQuestions, showApiKeyModal, blockPrompt, onSubmit, onPaste, pasteBlocks: _pasteBlocks, modeInfo, model, modelInfo, contextStats, placeholder, queuedCount, queuedMessages, typeahead, slashItems, slashSelectedIndex, slashInputIsMatched, composerValue, }) {
41
41
  const hasQueue = (queuedMessages?.length ?? 0) > 0;
42
42
  const showSuggestions = typeahead?.visible ?? false;
43
+ // Single source of truth for composer focus — mirrored to BOTH the OpenTUI
44
+ // textarea (`focused`) and the semantic node (via TextBox `focused`). Keeping
45
+ // one expression avoids the two drifting apart (the harness would otherwise
46
+ // report a focus state that disagrees with what the textarea actually has).
47
+ const composerFocused = !showModelPicker &&
48
+ !showSandboxPicker &&
49
+ !showWalletPicker &&
50
+ !showPlanQuestions &&
51
+ !showApiKeyModal &&
52
+ !blockPrompt;
43
53
  return (_jsxs("box", { backgroundColor: t.backgroundPanel, children: [_jsxs("box", { children: [hasQueue && (_jsxs("box", { paddingLeft: 2, paddingRight: 2, paddingTop: 1, paddingBottom: 1, backgroundColor: t.queueBg, flexShrink: 0, children: [queuedMessages.map((msg, i) => (
44
54
  // biome-ignore lint/suspicious/noArrayIndexKey: append-only queue of plain strings
45
- _jsxs("text", { fg: t.text, children: ["→ ", msg] }, i))), _jsx("box", { height: 1 }), _jsxs("text", { children: [_jsx("span", { style: { fg: t.primary }, children: "enter " }), _jsx("span", { style: { fg: t.textMuted }, children: "send now" }), _jsx("span", { style: { fg: t.textDim }, children: " · " }), _jsx("span", { style: { fg: t.primary }, children: "↑ " }), _jsx("span", { style: { fg: t.textMuted }, children: "edit" }), _jsx("span", { style: { fg: t.textDim }, children: " · " }), _jsx("span", { style: { fg: t.primary }, children: "esc " }), _jsx("span", { style: { fg: t.textMuted }, children: "cancel" })] })] })), showSlashMenu && slashItems && (_jsx(Semantic, { id: "slash-menu", role: "menu", isModal: true, name: "Slash commands", children: _jsx(SlashInlineMenu, { t: t, items: slashItems, selectedIndex: slashSelectedIndex ?? 0 }) })), showSuggestions && typeahead && (_jsx(SuggestionOverlay, { t: t, suggestions: typeahead.suggestions, selectedIndex: typeahead.selectedIndex })), _jsxs("box", { paddingLeft: 2, paddingRight: 2, paddingTop: 1, paddingBottom: 1, backgroundColor: t.backgroundElement, flexDirection: "row", gap: 2, alignItems: "flex-start", flexShrink: 0, children: [_jsx(PromptModeLabel, { t: t, modeInfo: modeInfo, isProcessing: isProcessing }), _jsx("box", { flexGrow: 1, children: _jsx(Semantic, { id: "composer", role: "textbox",
55
+ _jsxs("text", { fg: t.text, children: ["→ ", msg] }, i))), _jsx("box", { height: 1 }), _jsxs("text", { children: [_jsx("span", { style: { fg: t.primary }, children: "enter " }), _jsx("span", { style: { fg: t.textMuted }, children: "send now" }), _jsx("span", { style: { fg: t.textDim }, children: " · " }), _jsx("span", { style: { fg: t.primary }, children: "↑ " }), _jsx("span", { style: { fg: t.textMuted }, children: "edit" }), _jsx("span", { style: { fg: t.textDim }, children: " · " }), _jsx("span", { style: { fg: t.primary }, children: "esc " }), _jsx("span", { style: { fg: t.textMuted }, children: "cancel" })] })] })), showSlashMenu && slashItems && (_jsx(Menu, { id: "slash-menu", name: "Slash commands", children: _jsx(SlashInlineMenu, { t: t, items: slashItems, selectedIndex: slashSelectedIndex ?? 0 }) })), showSuggestions && typeahead && (_jsx(SuggestionOverlay, { t: t, suggestions: typeahead.suggestions, selectedIndex: typeahead.selectedIndex })), _jsxs("box", { paddingLeft: 2, paddingRight: 2, paddingTop: 1, paddingBottom: 1, backgroundColor: t.backgroundElement, flexDirection: "row", gap: 2, alignItems: "flex-start", flexShrink: 0, children: [_jsx(PromptModeLabel, { t: t, modeInfo: modeInfo, isProcessing: isProcessing }), _jsx("box", { flexGrow: 1, children: _jsx(TextBox, { id: "composer",
46
56
  // Mirror current composer text so external harness drivers
47
57
  // can read back what the user typed. Prefer the textarea's
48
58
  // actual plainText (ground truth) over composerValue (derived
49
59
  // from slashSearchQuery). This makes post-Tab state visible
50
60
  // even while the slash menu is technically still open — was
51
61
  // hiding the trailing space inserted by Tab autocomplete.
52
- value: inputRef.current?.plainText ?? composerValue ?? "", focus: !showModelPicker &&
53
- !showSandboxPicker &&
54
- !showWalletPicker &&
55
- !showPlanQuestions &&
56
- !showApiKeyModal &&
57
- !blockPrompt
58
- ? true
59
- : undefined, children: _jsx("textarea", { ref: inputRef, focused: !showModelPicker &&
60
- !showSandboxPicker &&
61
- !showWalletPicker &&
62
- !showPlanQuestions &&
63
- !showApiKeyModal &&
64
- !blockPrompt, placeholder: isProcessing ? "Queue a follow-up... (esc to interrupt)" : placeholder || "Message muonroi-cli...", textColor: slashInputIsMatched ? "#3b82f6" : t.text, backgroundColor: t.backgroundElement, placeholderColor: t.textMuted, minHeight: 1, maxHeight: 10, wrapMode: "word", keyBindings: TEXTAREA_KEYBINDINGS, onSubmit: onSubmit, onPaste: onPaste }) }) })] })] }), _jsxs("box", { flexDirection: "row", justifyContent: "space-between", alignItems: "center", paddingLeft: 2, paddingRight: 2, height: 1, flexShrink: 0, children: [_jsxs("box", { flexDirection: "row", gap: 1, alignItems: "center", height: 1, children: [_jsx("text", { fg: t.text, children: modelInfo?.name || model }), contextStats ? _jsx(ContextMeter, { t: t, stats: contextStats }) : null] }), _jsx("box", { flexDirection: "row", gap: 1, alignItems: "center", height: 1, children: isProcessing ? (_jsxs("box", { flexDirection: "row", gap: 1, children: [_jsxs("text", { fg: t.text, children: ["enter ", _jsx("span", { style: { fg: t.textMuted }, children: "queue" })] }), _jsxs("text", { fg: t.text, children: ["esc ", _jsx("span", { style: { fg: t.textMuted }, children: (queuedCount ?? 0) > 0 ? "clear queue" : "interrupt" })] })] })) : showSlashMenu ? (_jsxs("box", { flexDirection: "row", gap: 1, children: [_jsxs("text", { fg: t.text, children: ["↑↓ ", _jsx("span", { style: { fg: t.textMuted }, children: "navigate" })] }), _jsxs("text", { fg: t.text, children: ["enter ", _jsx("span", { style: { fg: t.textMuted }, children: "select" })] }), _jsxs("text", { fg: t.text, children: ["esc ", _jsx("span", { style: { fg: t.textMuted }, children: "dismiss" })] })] })) : showSuggestions ? (_jsxs("box", { flexDirection: "row", gap: 1, children: [_jsxs("text", { fg: t.text, children: ["tab ", _jsx("span", { style: { fg: t.textMuted }, children: "accept" })] }), _jsxs("text", { fg: t.text, children: ["↑↓ ", _jsx("span", { style: { fg: t.textMuted }, children: "navigate" })] }), _jsxs("text", { fg: t.text, children: ["esc ", _jsx("span", { style: { fg: t.textMuted }, children: "dismiss" })] })] })) : (_jsxs(_Fragment, { children: [_jsxs("text", { fg: t.text, children: ["@ ", _jsx("span", { style: { fg: t.textMuted }, children: "files" })] }), _jsxs("text", { fg: t.text, children: ["shift+enter ", _jsx("span", { style: { fg: t.textMuted }, children: "new line" })] }), _jsxs("text", { fg: t.text, children: ["tab ", _jsx("span", { style: { fg: t.textMuted }, children: "modes" })] })] })) })] })] }));
62
+ value: inputRef.current?.plainText ?? composerValue ?? "", focused: composerFocused, children: _jsx("textarea", { ref: inputRef, focused: composerFocused, placeholder: isProcessing ? "Queue a follow-up... (esc to interrupt)" : placeholder || "Message muonroi-cli...", textColor: slashInputIsMatched ? "#3b82f6" : t.text, backgroundColor: t.backgroundElement, placeholderColor: t.textMuted, minHeight: 1, maxHeight: 10, wrapMode: "word", keyBindings: TEXTAREA_KEYBINDINGS, onSubmit: onSubmit, onPaste: onPaste }) }) })] })] }), _jsxs("box", { flexDirection: "row", justifyContent: "space-between", alignItems: "center", paddingLeft: 2, paddingRight: 2, height: 1, flexShrink: 0, children: [_jsxs("box", { flexDirection: "row", gap: 1, alignItems: "center", height: 1, children: [_jsx("text", { fg: t.text, children: modelInfo?.name || model }), contextStats ? _jsx(ContextMeter, { t: t, stats: contextStats }) : null] }), _jsx("box", { flexDirection: "row", gap: 1, alignItems: "center", height: 1, children: isProcessing ? (_jsxs("box", { flexDirection: "row", gap: 1, children: [_jsxs("text", { fg: t.text, children: ["enter ", _jsx("span", { style: { fg: t.textMuted }, children: "queue" })] }), _jsxs("text", { fg: t.text, children: ["esc ", _jsx("span", { style: { fg: t.textMuted }, children: (queuedCount ?? 0) > 0 ? "clear queue" : "interrupt" })] })] })) : showSlashMenu ? (_jsxs("box", { flexDirection: "row", gap: 1, children: [_jsxs("text", { fg: t.text, children: ["↑↓ ", _jsx("span", { style: { fg: t.textMuted }, children: "navigate" })] }), _jsxs("text", { fg: t.text, children: ["enter ", _jsx("span", { style: { fg: t.textMuted }, children: "select" })] }), _jsxs("text", { fg: t.text, children: ["esc ", _jsx("span", { style: { fg: t.textMuted }, children: "dismiss" })] })] })) : showSuggestions ? (_jsxs("box", { flexDirection: "row", gap: 1, children: [_jsxs("text", { fg: t.text, children: ["tab ", _jsx("span", { style: { fg: t.textMuted }, children: "accept" })] }), _jsxs("text", { fg: t.text, children: ["↑↓ ", _jsx("span", { style: { fg: t.textMuted }, children: "navigate" })] }), _jsxs("text", { fg: t.text, children: ["esc ", _jsx("span", { style: { fg: t.textMuted }, children: "dismiss" })] })] })) : (
63
+ // Wrap in a gap'd row like the other three hint branches — a bare
64
+ // fragment let the three hints run together ("@ filesshift+enter new
65
+ // linetab modes"); the inner box restores the 1-col separators.
66
+ _jsxs("box", { flexDirection: "row", gap: 1, children: [_jsxs("text", { fg: t.text, children: ["@ ", _jsx("span", { style: { fg: t.textMuted }, children: "files" })] }), _jsxs("text", { fg: t.text, children: ["shift+enter ", _jsx("span", { style: { fg: t.textMuted }, children: "new line" })] }), _jsxs("text", { fg: t.text, children: ["tab ", _jsx("span", { style: { fg: t.textMuted }, children: "modes" })] })] })) })] })] }));
65
67
  }
66
68
  //# sourceMappingURL=prompt-box.js.map
@@ -0,0 +1,14 @@
1
+ import type { SessionChainNode } from "../../storage/transcript.js";
2
+ /**
3
+ * Rail "Sessions" block: the session tree this TUI hosts — the root
4
+ * conversation plus every rotation / sub-agent descendant. The children's
5
+ * message content is already merged into the transcript on resume; this makes
6
+ * the multi-session structure VISIBLE (which session produced what, how many
7
+ * messages, whether it is the current one) instead of a flat anonymous timeline.
8
+ *
9
+ * Renders nothing extra when the tree is a single conversation (no sub-sessions)
10
+ * — keeps a lean rail for the common single-session case.
11
+ */
12
+ export declare function SessionTreeCard({ nodes }: {
13
+ nodes: SessionChainNode[];
14
+ }): import("react").ReactNode;
@@ -0,0 +1,46 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "@opentui/react/jsx-runtime";
2
+ import { Semantic } from "@muonroi/agent-harness-opentui";
3
+ import { dark } from "../theme.js";
4
+ /** Short kind badge for the rail — keeps rows narrow in a ~34-col panel. */
5
+ function kindBadge(kind) {
6
+ switch (kind) {
7
+ case "subagent":
8
+ return "sub";
9
+ case "rotation":
10
+ return "rot";
11
+ case "conversation":
12
+ return "conv";
13
+ default:
14
+ return "·";
15
+ }
16
+ }
17
+ /** Trim a model id to its last path segment so "deepseek/deepseek-v4-flash"
18
+ * reads as "deepseek-v4-flash" in the narrow rail. */
19
+ function shortModel(model) {
20
+ if (!model)
21
+ return "—";
22
+ const seg = model.includes("/") ? model.slice(model.lastIndexOf("/") + 1) : model;
23
+ return seg.length > 16 ? `${seg.slice(0, 15)}…` : seg;
24
+ }
25
+ /**
26
+ * Rail "Sessions" block: the session tree this TUI hosts — the root
27
+ * conversation plus every rotation / sub-agent descendant. The children's
28
+ * message content is already merged into the transcript on resume; this makes
29
+ * the multi-session structure VISIBLE (which session produced what, how many
30
+ * messages, whether it is the current one) instead of a flat anonymous timeline.
31
+ *
32
+ * Renders nothing extra when the tree is a single conversation (no sub-sessions)
33
+ * — keeps a lean rail for the common single-session case.
34
+ */
35
+ export function SessionTreeCard({ nodes }) {
36
+ if (nodes.length <= 1)
37
+ return null;
38
+ return (_jsx(Semantic, { id: "session-tree", role: "listbox", name: "Sessions", props: { count: nodes.length, ids: nodes.map((n) => n.id.slice(0, 8)).join(",") }, children: _jsxs("box", { flexDirection: "column", flexShrink: 0, paddingTop: 1, children: [_jsx("text", { fg: dark.textMuted, attributes: 1, children: `Sessions (${nodes.length})` }), nodes.map((n) => {
39
+ const indent = " ".repeat(Math.min(n.depth, 3));
40
+ const marker = n.isCurrent ? "●" : n.depth > 0 ? "↳" : "○";
41
+ const badge = kindBadge(n.kind);
42
+ const done = n.status && n.status !== "active";
43
+ return (_jsx(Semantic, { id: `session-node-${n.id}`, role: "listitem", name: `${badge} ${n.id.slice(0, 8)}`, value: `${n.messageCount} msg`, selected: n.isCurrent ? true : undefined, props: { kind: n.kind ?? "", status: n.status ?? "", messageCount: n.messageCount, depth: n.depth }, children: _jsx("box", { flexDirection: "column", children: _jsxs("text", { children: [_jsx("span", { style: { fg: n.isCurrent ? dark.selected : dark.textMuted }, children: `${indent}${marker} ` }), _jsx("span", { style: { fg: n.isCurrent ? dark.selected : dark.text }, children: `${badge} ` }), _jsx("span", { style: { fg: dark.textMuted }, children: `${n.id.slice(0, 8)} ${shortModel(n.model)} ` }), _jsx("span", { style: { fg: dark.text }, children: `${n.messageCount} msg` }), done ? _jsx("span", { style: { fg: dark.textDim }, children: ` ${n.status}` }) : null] }) }) }, n.id));
44
+ })] }) }));
45
+ }
46
+ //# sourceMappingURL=session-tree-card.js.map
@@ -1,5 +1,17 @@
1
1
  import type { SlashMenuItem } from "../slash/menu-items.js";
2
2
  import type { Theme } from "../theme.js";
3
+ /**
4
+ * Compute the scrolling viewport for the slash dropdown so the highlighted row
5
+ * stays visible when the filtered list exceeds the window. Pure + exported so
6
+ * the scroll behavior is unit-testable without a live TUI (the api-key modal
7
+ * blocks a clean greenfield harness drive of the menu).
8
+ */
9
+ export declare function computeSlashMenuWindow(total: number, selectedIndex: number, maxVisible?: number): {
10
+ start: number;
11
+ end: number;
12
+ hiddenAbove: number;
13
+ hiddenBelow: number;
14
+ };
3
15
  export declare function SlashInlineMenu({ t, items, selectedIndex, }: {
4
16
  t: Theme;
5
17
  items: SlashMenuItem[];
@@ -1,14 +1,35 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "@opentui/react/jsx-runtime";
2
2
  import { Semantic } from "@muonroi/agent-harness-opentui";
3
3
  const SLASH_MENU_MAX_VISIBLE = 8;
4
+ /**
5
+ * Compute the scrolling viewport for the slash dropdown so the highlighted row
6
+ * stays visible when the filtered list exceeds the window. Pure + exported so
7
+ * the scroll behavior is unit-testable without a live TUI (the api-key modal
8
+ * blocks a clean greenfield harness drive of the menu).
9
+ */
10
+ export function computeSlashMenuWindow(total, selectedIndex, maxVisible = SLASH_MENU_MAX_VISIBLE) {
11
+ if (total <= 0)
12
+ return { start: 0, end: 0, hiddenAbove: 0, hiddenBelow: 0 };
13
+ const clamped = Math.min(Math.max(0, selectedIndex), total - 1);
14
+ const start = total <= maxVisible ? 0 : Math.min(Math.max(0, clamped - maxVisible + 1), total - maxVisible);
15
+ const end = Math.min(start + maxVisible, total);
16
+ return { start, end, hiddenAbove: start, hiddenBelow: total - end };
17
+ }
4
18
  export function SlashInlineMenu({ t, items, selectedIndex, }) {
5
- const visible = items.slice(0, SLASH_MENU_MAX_VISIBLE);
6
- if (visible.length === 0) {
19
+ if (items.length === 0) {
7
20
  return (_jsx("box", { paddingLeft: 2, paddingRight: 2, paddingTop: 1, flexShrink: 0, flexDirection: "column", children: _jsx("box", { height: 1, paddingLeft: 1, children: _jsx("text", { fg: t.textMuted, children: "No commands match" }) }) }));
8
21
  }
9
- return (_jsxs("box", { paddingLeft: 2, paddingRight: 2, paddingTop: 1, flexShrink: 0, flexDirection: "column", children: [visible.map((item, i) => {
10
- const isSelected = i === selectedIndex;
22
+ // Scrolling viewport: keep the highlighted row in view when the filtered list
23
+ // exceeds the visible window. Without this the window was pinned to
24
+ // items.slice(0,8) and pressing Down past index 7 moved the selection
25
+ // off-screen — the list "wouldn't continue past the initial suggestions".
26
+ const clampedSelected = Math.min(Math.max(0, selectedIndex), items.length - 1);
27
+ const { start: windowStart, hiddenAbove, hiddenBelow } = computeSlashMenuWindow(items.length, clampedSelected);
28
+ const visible = items.slice(windowStart, windowStart + SLASH_MENU_MAX_VISIBLE);
29
+ return (_jsxs("box", { paddingLeft: 2, paddingRight: 2, paddingTop: 1, flexShrink: 0, flexDirection: "column", children: [hiddenAbove > 0 && _jsx("text", { fg: t.textDim, children: ` ▲ ${hiddenAbove} more` }), visible.map((item, i) => {
30
+ const absoluteIndex = windowStart + i;
31
+ const isSelected = absoluteIndex === clampedSelected;
11
32
  return (_jsx(Semantic, { id: `slash-item-${item.id}`, role: "listitem", name: `/${item.label}`, value: item.description, selected: isSelected ? true : undefined, children: _jsxs("box", { height: 1, backgroundColor: isSelected ? t.selectedBg : undefined, flexDirection: "row", justifyContent: "space-between", paddingLeft: 1, paddingRight: 1, children: [_jsxs("text", { fg: isSelected ? t.selected : t.text, children: ["/", item.label] }), _jsx("text", { fg: t.textMuted, children: item.description })] }) }, item.id));
12
- }), items.length > SLASH_MENU_MAX_VISIBLE && (_jsx("text", { fg: t.textDim, children: ` +${items.length - SLASH_MENU_MAX_VISIBLE} more` }))] }));
33
+ }), hiddenBelow > 0 && _jsx("text", { fg: t.textDim, children: ` ▼ ${hiddenBelow} more` })] }));
13
34
  }
14
35
  //# sourceMappingURL=slash-inline-menu.js.map
@@ -19,14 +19,27 @@
19
19
  import type { TaskListItem, TaskListSnapshot } from "../../types/index.js";
20
20
  import type { Theme } from "../theme.js";
21
21
  declare function sortItems(items: ReadonlyArray<TaskListItem>): TaskListItem[];
22
+ /**
23
+ * One-line summary used by the collapsed panel. Kept pure + exported so it can be
24
+ * unit-tested without rendering OpenTUI. Mirrors the expanded footer's bucket
25
+ * wording so the two views read consistently.
26
+ */
27
+ declare function buildCollapsedLine(snapshot: TaskListSnapshot): string;
22
28
  export interface TaskListPanelProps {
23
29
  snapshot: TaskListSnapshot;
24
30
  t: Theme;
25
31
  expanded: boolean;
32
+ /**
33
+ * Render a single summary line instead of the full panel. Set while a council
34
+ * debate is active so the todo band stops eating the debate scrollbox's height
35
+ * (~11 rows → 1). `ctrl+e` flips it back to the full panel for a peek.
36
+ */
37
+ collapsed?: boolean;
26
38
  }
27
- export declare function TaskListPanel({ snapshot, t, expanded }: TaskListPanelProps): import("react").ReactNode;
39
+ export declare function TaskListPanel({ snapshot, t, expanded, collapsed }: TaskListPanelProps): import("react").ReactNode;
28
40
  export declare const __TEST_ONLY__: {
29
41
  sortItems: typeof sortItems;
30
42
  MAX_VISIBLE: number;
43
+ buildCollapsedLine: typeof buildCollapsedLine;
31
44
  };
32
45
  export {};
@@ -54,9 +54,29 @@ function ItemRow({ item, t }) {
54
54
  }
55
55
  return (_jsx(Semantic, { id: `task-${item.id}`, role: "listitem", name: label, state: item.status, children: _jsx("box", { width: "100%", children: _jsxs("text", { fg: textColor, children: [_jsx("span", { style: { fg: glyphColor }, children: `${glyph} ` }), trunc(label, 70)] }) }) }));
56
56
  }
57
- export function TaskListPanel({ snapshot, t, expanded }) {
57
+ /**
58
+ * One-line summary used by the collapsed panel. Kept pure + exported so it can be
59
+ * unit-tested without rendering OpenTUI. Mirrors the expanded footer's bucket
60
+ * wording so the two views read consistently.
61
+ */
62
+ function buildCollapsedLine(snapshot) {
63
+ const { completed, inProgress, pending } = snapshot.counts;
64
+ const parts = [];
65
+ if (completed > 0)
66
+ parts.push(`${completed} completed`);
67
+ if (inProgress > 0)
68
+ parts.push(`${inProgress} in progress`);
69
+ if (pending > 0)
70
+ parts.push(`${pending} queued`);
71
+ const body = parts.length > 0 ? parts.join(" · ") : "0 todos";
72
+ return `▸ Todos · ${body} (ctrl+e)`;
73
+ }
74
+ export function TaskListPanel({ snapshot, t, expanded, collapsed = false }) {
58
75
  if (snapshot.items.length === 0)
59
76
  return null;
77
+ if (collapsed) {
78
+ return (_jsx(Semantic, { id: "task-list", role: "listbox", name: "Todos", state: "collapsed", children: _jsx("box", { paddingLeft: 2, paddingRight: 2, marginBottom: 1, children: _jsx("text", { fg: t.textMuted, children: buildCollapsedLine(snapshot) }) }) }));
79
+ }
60
80
  const sorted = sortItems(snapshot.items);
61
81
  const overflow = !expanded && sorted.length > MAX_VISIBLE ? sorted.length - MAX_VISIBLE : 0;
62
82
  const visible = overflow > 0 ? sorted.slice(0, MAX_VISIBLE) : sorted;
@@ -71,5 +91,5 @@ export function TaskListPanel({ snapshot, t, expanded }) {
71
91
  const footer = footerParts.length > 0 ? footerParts.join(" · ") : "0 todos";
72
92
  return (_jsx(Semantic, { id: "task-list", role: "listbox", name: "Todos", children: _jsxs("box", { border: true, borderColor: t.border, paddingLeft: 2, paddingRight: 2, paddingTop: 0, paddingBottom: 0, marginBottom: 1, flexDirection: "column", children: [_jsx("box", { paddingBottom: 1, children: _jsx("text", { fg: t.textMuted, children: _jsx("b", { children: "Todos" }) }) }), _jsx("box", { flexDirection: "column", children: visible.map((it) => (_jsx(ItemRow, { item: it, t: t }, it.id))) }), overflow > 0 && (_jsx("box", { marginTop: 1, children: _jsx("text", { fg: t.textMuted, children: `… +${overflow} more (ctrl+e to expand)` }) })), _jsx("box", { marginTop: 1, paddingTop: 1, border: ["top"], borderColor: t.border, children: _jsx("text", { fg: t.textDim, children: footer }) })] }) }));
73
93
  }
74
- export const __TEST_ONLY__ = { sortItems, MAX_VISIBLE };
94
+ export const __TEST_ONLY__ = { sortItems, MAX_VISIBLE, buildCollapsedLine };
75
95
  //# sourceMappingURL=task-list-panel.js.map
@@ -1,3 +1,4 @@
1
+ import type { ResumeEntry } from "../../types/index.js";
1
2
  export interface ModalsLayerProps {
2
3
  agentRows: any;
3
4
  agentsEditorDraft: any;
@@ -50,7 +51,7 @@ export interface ModalsLayerProps {
50
51
  scheduleRows: any;
51
52
  scheduleSearchQuery: any;
52
53
  sessionPickerIndex: any;
53
- sessionPickerList: any;
54
+ sessionPickerList: ResumeEntry[];
54
55
  showAgentsEditor: any;
55
56
  showAgentsModal: any;
56
57
  showApiKeyModal: any;
@@ -1,4 +1,4 @@
1
- import type { SessionInfo } from "../../types/index.js";
1
+ import type { ResumeEntry } from "../../types/index.js";
2
2
  /**
3
3
  * State for the /sessions picker modal. Sessions are loaded lazily when the
4
4
  * picker is opened (the SQLite query is cheap — ORDER BY updated_at LIMIT 20
@@ -9,6 +9,6 @@ export declare function useSessionPicker(): {
9
9
  setShowSessionPicker: import("react").Dispatch<import("react").SetStateAction<boolean>>;
10
10
  sessionPickerIndex: number;
11
11
  setSessionPickerIndex: import("react").Dispatch<import("react").SetStateAction<number>>;
12
- sessions: SessionInfo[];
13
- setSessions: import("react").Dispatch<import("react").SetStateAction<SessionInfo[]>>;
12
+ sessions: ResumeEntry[];
13
+ setSessions: import("react").Dispatch<import("react").SetStateAction<ResumeEntry[]>>;
14
14
  };
@@ -53,8 +53,7 @@ export function McpBrowserModal({ t, width, height, selectedIndex, searchQuery,
53
53
  const contentHeight = itemCount + 7;
54
54
  const maxHeight = Math.floor(height * 0.68);
55
55
  const panelHeight = Math.min(contentHeight, maxHeight);
56
- const overlayBg = "#000000cc";
57
- return (_jsx(Semantic, { id: "mcp-modal", role: "dialog", name: "MCP Servers", isModal: true, children: _jsx("box", { position: "absolute", left: 0, top: 0, width: width, height: height, alignItems: "center", paddingTop: bottomAlignedModalTop(height, panelHeight), backgroundColor: overlayBg, children: _jsxs("box", { width: Math.min(96, width - 4), height: panelHeight, backgroundColor: t.backgroundPanel, paddingTop: 1, paddingBottom: 1, flexDirection: "column", children: [_jsxs("box", { flexShrink: 0, flexDirection: "row", justifyContent: "space-between", paddingLeft: 2, paddingRight: 2, children: [_jsx("text", { fg: t.primary, children: _jsx("b", { children: "MCP Servers" }) }), _jsx("text", { fg: t.textMuted, children: "esc" })] }), _jsx("box", { flexShrink: 0, paddingLeft: 2, paddingRight: 2, paddingTop: 1, paddingBottom: 1, children: _jsx("text", { fg: t.text, children: searchQuery || _jsx("span", { style: { fg: t.textMuted }, children: "Search servers..." }) }) }), _jsx(Semantic, { id: "mcp-list", role: "listbox", children: _jsx("scrollbox", { ref: listRef, flexGrow: 1, minHeight: 0, children: rows.map((row, idx) => {
56
+ return (_jsx(Semantic, { id: "mcp-modal", role: "dialog", name: "MCP Servers", isModal: true, children: _jsx("box", { position: "absolute", left: 0, top: 0, width: width, height: height, alignItems: "center", paddingTop: bottomAlignedModalTop(height, panelHeight), backgroundColor: t.overlay, children: _jsxs("box", { width: Math.min(96, width - 4), height: panelHeight, backgroundColor: t.backgroundPanel, paddingTop: 1, paddingBottom: 1, flexDirection: "column", children: [_jsxs("box", { flexShrink: 0, flexDirection: "row", justifyContent: "space-between", paddingLeft: 2, paddingRight: 2, children: [_jsx("text", { fg: t.primary, children: _jsx("b", { children: "MCP Servers" }) }), _jsx("text", { fg: t.textMuted, children: "esc" })] }), _jsx("box", { flexShrink: 0, paddingLeft: 2, paddingRight: 2, paddingTop: 1, paddingBottom: 1, children: _jsx("text", { fg: t.text, children: searchQuery || _jsx("span", { style: { fg: t.textMuted }, children: "Search servers..." }) }) }), _jsx(Semantic, { id: "mcp-list", role: "listbox", children: _jsx("scrollbox", { ref: listRef, flexGrow: 1, minHeight: 0, children: rows.map((row, idx) => {
58
57
  const selected = idx === selectedIndex;
59
58
  if (row.kind === "server") {
60
59
  const enabledColor = row.server.enabled ? t.diffAddedFg : selected ? t.selected : t.text;
@@ -74,7 +73,6 @@ function syncRef(ref, value) {
74
73
  }
75
74
  export function McpEditorModal({ t, width, height, draft, focusedField, syncKey, error, title, labelRef, urlRef, headersRef, commandRef, argsRef, cwdRef, envRef, onSubmit, }) {
76
75
  const panelHeight = Math.min(30, Math.floor(height * 0.82));
77
- const overlayBg = "#000000cc";
78
76
  const isRemote = draft.transport === "http" || draft.transport === "sse";
79
77
  // biome-ignore lint/correctness/useExhaustiveDependencies: syncKey is an intentional cache-bust prop
80
78
  useEffect(() => {
@@ -86,7 +84,7 @@ export function McpEditorModal({ t, width, height, draft, focusedField, syncKey,
86
84
  syncRef(cwdRef, draft.cwd);
87
85
  syncRef(envRef, draft.envText);
88
86
  }, [draft, syncKey, labelRef, urlRef, headersRef, commandRef, argsRef, cwdRef, envRef]);
89
- return (_jsx(Semantic, { id: "mcp-editor", role: "dialog", name: title, isModal: true, children: _jsx("box", { position: "absolute", left: 0, top: 0, width: width, height: height, alignItems: "center", paddingTop: bottomAlignedModalTop(height, panelHeight), backgroundColor: overlayBg, children: _jsxs("box", { width: Math.min(86, width - 6), height: panelHeight, backgroundColor: t.backgroundPanel, paddingTop: 1, paddingBottom: 1, flexDirection: "column", children: [_jsxs("box", { flexShrink: 0, flexDirection: "row", justifyContent: "space-between", paddingLeft: 2, paddingRight: 2, children: [_jsx("text", { fg: t.primary, children: _jsx("b", { children: title }) }), _jsx("text", { fg: t.textMuted, children: "esc" })] }), _jsx("box", { flexShrink: 0, paddingLeft: 2, paddingRight: 2, paddingTop: 1, children: _jsxs("box", { flexDirection: "row", gap: 1, alignItems: "center", children: [_jsx("text", { fg: focusedField === "transport" ? t.primary : t.textMuted, children: "Transport" }), ["stdio", "http", "sse"].map((option) => {
87
+ return (_jsx(Semantic, { id: "mcp-editor", role: "dialog", name: title, isModal: true, children: _jsx("box", { position: "absolute", left: 0, top: 0, width: width, height: height, alignItems: "center", paddingTop: bottomAlignedModalTop(height, panelHeight), backgroundColor: t.overlay, children: _jsxs("box", { width: Math.min(86, width - 6), height: panelHeight, backgroundColor: t.backgroundPanel, paddingTop: 1, paddingBottom: 1, flexDirection: "column", children: [_jsxs("box", { flexShrink: 0, flexDirection: "row", justifyContent: "space-between", paddingLeft: 2, paddingRight: 2, children: [_jsx("text", { fg: t.primary, children: _jsx("b", { children: title }) }), _jsx("text", { fg: t.textMuted, children: "esc" })] }), _jsx("box", { flexShrink: 0, paddingLeft: 2, paddingRight: 2, paddingTop: 1, children: _jsxs("box", { flexDirection: "row", gap: 1, alignItems: "center", children: [_jsx("text", { fg: focusedField === "transport" ? t.primary : t.textMuted, children: "Transport" }), ["stdio", "http", "sse"].map((option) => {
90
88
  const active = draft.transport === option;
91
89
  const focused = focusedField === "transport";
92
90
  return (_jsx("box", { backgroundColor: active ? (focused ? t.selectedBg : t.backgroundElement) : undefined, paddingLeft: 1, paddingRight: 1, children: _jsx("text", { fg: active ? (focused ? t.primary : t.text) : t.textMuted, children: option }) }, option));
@@ -10,6 +10,6 @@ export function ApiKeyModal({ t, width, height, inputRef, error, onSubmit, }) {
10
10
  const panelWidth = Math.min(68, width - 6);
11
11
  const panelHeight = 13;
12
12
  const top = bottomAlignedModalTop(height, panelHeight);
13
- return (_jsx(Semantic, { id: "api-key-modal", role: "dialog", name: "API Key", isModal: true, children: _jsx("box", { position: "absolute", left: 0, top: 0, width: width, height: height, alignItems: "center", paddingTop: top, backgroundColor: overlayBg, children: _jsxs("box", { width: panelWidth, height: panelHeight, backgroundColor: t.backgroundPanel, paddingTop: 1, paddingBottom: 1, flexDirection: "column", children: [_jsxs("box", { flexShrink: 0, flexDirection: "row", justifyContent: "space-between", paddingLeft: 2, paddingRight: 2, children: [_jsx("text", { fg: t.primary, children: _jsx("b", { children: "Add API key" }) }), _jsx("text", { fg: t.textMuted, children: "esc" })] }), _jsx("box", { paddingLeft: 2, paddingRight: 2, paddingTop: 1, children: _jsx("text", { fg: t.text, children: "Paste your DeepSeek or SiliconFlow API key to unlock chat. Esc hides this prompt." }) }), _jsx("box", { paddingLeft: 2, paddingRight: 2, paddingTop: 1, children: _jsx("box", { backgroundColor: t.backgroundElement, paddingLeft: 1, paddingRight: 1, width: "100%", children: _jsx(Semantic, { id: "api-key-input", role: "textbox", children: _jsx("textarea", { ref: inputRef, focused: true, placeholder: "sk-...", textColor: t.text, backgroundColor: t.backgroundElement, placeholderColor: t.textMuted, minHeight: 1, maxHeight: 3, wrapMode: "word", keyBindings: TEXTAREA_KEYBINDINGS, onSubmit: onSubmit }) }) }) }), _jsx("box", { flexGrow: 1, minHeight: 0 }), _jsx("box", { paddingLeft: 2, paddingRight: 2, paddingTop: 2, paddingBottom: 1, children: error ? (_jsx("text", { fg: t.diffRemovedFg, children: error })) : (_jsxs("text", { children: [_jsx("span", { style: { fg: t.primary }, children: "enter " }), _jsx("span", { style: { fg: t.textMuted }, children: "save key · " }), _jsx("span", { style: { fg: t.primary }, children: "esc " }), _jsx("span", { style: { fg: t.textMuted }, children: "hide" })] })) })] }) }) }));
13
+ return (_jsx(Semantic, { id: "api-key-modal", role: "dialog", name: "API Key", isModal: true, children: _jsx("box", { position: "absolute", left: 0, top: 0, width: width, height: height, alignItems: "center", paddingTop: top, backgroundColor: overlayBg, children: _jsxs("box", { width: panelWidth, height: panelHeight, backgroundColor: t.backgroundPanel, paddingTop: 1, paddingBottom: 1, flexDirection: "column", children: [_jsxs("box", { flexShrink: 0, flexDirection: "row", justifyContent: "space-between", paddingLeft: 2, paddingRight: 2, children: [_jsx("text", { fg: t.primary, children: _jsx("b", { children: "Add API key" }) }), _jsx("text", { fg: t.textMuted, children: "esc" })] }), _jsx("box", { paddingLeft: 2, paddingRight: 2, paddingTop: 1, children: _jsx("text", { fg: t.text, children: "Paste your DeepSeek API key to unlock chat. Esc hides this prompt." }) }), _jsx("box", { paddingLeft: 2, paddingRight: 2, paddingTop: 1, children: _jsx("box", { backgroundColor: t.backgroundElement, paddingLeft: 1, paddingRight: 1, width: "100%", children: _jsx(Semantic, { id: "api-key-input", role: "textbox", children: _jsx("textarea", { ref: inputRef, focused: true, placeholder: "sk-...", textColor: t.text, backgroundColor: t.backgroundElement, placeholderColor: t.textMuted, minHeight: 1, maxHeight: 3, wrapMode: "word", keyBindings: TEXTAREA_KEYBINDINGS, onSubmit: onSubmit }) }) }) }), _jsx("box", { flexGrow: 1, minHeight: 0 }), _jsx("box", { paddingLeft: 2, paddingRight: 2, paddingTop: 2, paddingBottom: 1, children: error ? (_jsx("text", { fg: t.diffRemovedFg, children: error })) : (_jsxs("text", { children: [_jsx("span", { style: { fg: t.primary }, children: "enter " }), _jsx("span", { style: { fg: t.textMuted }, children: "save key · " }), _jsx("span", { style: { fg: t.primary }, children: "esc " }), _jsx("span", { style: { fg: t.textMuted }, children: "hide" })] })) })] }) }) }));
14
14
  }
15
15
  //# sourceMappingURL=api-key-modal.js.map