muonroi-cli 1.8.4 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (562) hide show
  1. package/LICENSE +17 -5
  2. package/dist/packages/agent-harness-core/src/driver.d.ts +27 -1
  3. package/dist/packages/agent-harness-core/src/driver.js +46 -0
  4. package/dist/packages/agent-harness-core/src/event-filter.js +11 -0
  5. package/dist/packages/agent-harness-core/src/event-redact.js +7 -0
  6. package/dist/packages/agent-harness-core/src/event-tee.d.ts +64 -0
  7. package/dist/packages/agent-harness-core/src/event-tee.js +104 -0
  8. package/dist/packages/agent-harness-core/src/mcp-server.d.ts +25 -0
  9. package/dist/packages/agent-harness-core/src/mcp-server.js +169 -21
  10. package/dist/packages/agent-harness-core/src/predicate.d.ts +1 -1
  11. package/dist/packages/agent-harness-core/src/protocol.d.ts +90 -4
  12. package/dist/packages/agent-harness-core/src/protocol.js +15 -0
  13. package/dist/packages/agent-harness-core/src/visual-quality.d.ts +58 -0
  14. package/dist/packages/agent-harness-core/src/visual-quality.js +141 -0
  15. package/dist/packages/agent-harness-opentui/src/agent-mode.d.ts +6 -0
  16. package/dist/packages/agent-harness-opentui/src/agent-mode.js +14 -1
  17. package/dist/packages/agent-harness-opentui/src/input-bridge.d.ts +2 -10
  18. package/dist/packages/agent-harness-opentui/src/input-bridge.js +103 -16
  19. package/dist/packages/agent-harness-opentui/src/install.d.ts +8 -0
  20. package/dist/packages/agent-harness-opentui/src/install.js +10 -0
  21. package/dist/packages/agent-harness-opentui/src/semantic.js +12 -10
  22. package/dist/packages/agent-harness-opentui/src/visual-capture.d.ts +56 -0
  23. package/dist/packages/agent-harness-opentui/src/visual-capture.js +103 -0
  24. package/dist/src/agent-harness/mock-model.d.ts +38 -0
  25. package/dist/src/agent-harness/mock-model.js +69 -3
  26. package/dist/src/agent-harness/test-spawn.js +31 -0
  27. package/dist/src/chat/chat-keychain.d.ts +7 -12
  28. package/dist/src/chat/chat-keychain.js +19 -86
  29. package/dist/src/cli/config/screen-providers.js +1 -1
  30. package/dist/src/cli/cost-forensics.d.ts +10 -0
  31. package/dist/src/cli/cost-forensics.js +18 -3
  32. package/dist/src/cli/keys-bundle.d.ts +1 -1
  33. package/dist/src/cli/keys-bundle.js +1 -1
  34. package/dist/src/cli/keys.d.ts +10 -47
  35. package/dist/src/cli/keys.js +31 -399
  36. package/dist/src/council/clarifier.d.ts +31 -3
  37. package/dist/src/council/clarifier.js +220 -32
  38. package/dist/src/council/context.js +49 -15
  39. package/dist/src/council/debate-checkpoint.d.ts +129 -0
  40. package/dist/src/council/debate-checkpoint.js +176 -0
  41. package/dist/src/council/debate-planner.js +54 -5
  42. package/dist/src/council/debate-summary.d.ts +25 -0
  43. package/dist/src/council/debate-summary.js +85 -0
  44. package/dist/src/council/debate.d.ts +169 -2
  45. package/dist/src/council/debate.js +1265 -135
  46. package/dist/src/council/index.d.ts +108 -1
  47. package/dist/src/council/index.js +670 -197
  48. package/dist/src/council/leader.d.ts +26 -0
  49. package/dist/src/council/leader.js +150 -9
  50. package/dist/src/council/llm.d.ts +94 -0
  51. package/dist/src/council/llm.js +348 -55
  52. package/dist/src/council/panel-select.d.ts +30 -0
  53. package/dist/src/council/panel-select.js +82 -0
  54. package/dist/src/council/planner.js +40 -0
  55. package/dist/src/council/preflight.d.ts +17 -0
  56. package/dist/src/council/preflight.js +50 -2
  57. package/dist/src/council/prompts.d.ts +39 -4
  58. package/dist/src/council/prompts.js +256 -69
  59. package/dist/src/council/stance-recall.d.ts +42 -0
  60. package/dist/src/council/stance-recall.js +57 -0
  61. package/dist/src/council/strip-think.d.ts +17 -0
  62. package/dist/src/council/strip-think.js +33 -0
  63. package/dist/src/council/types.d.ts +138 -0
  64. package/dist/src/ee/artifact-cache.d.ts +16 -0
  65. package/dist/src/ee/artifact-cache.js +32 -0
  66. package/dist/src/ee/auth.d.ts +20 -0
  67. package/dist/src/ee/auth.js +54 -2
  68. package/dist/src/ee/bridge.d.ts +10 -0
  69. package/dist/src/ee/bridge.js +58 -0
  70. package/dist/src/ee/client.js +109 -21
  71. package/dist/src/ee/ee-onboarding.js +6 -26
  72. package/dist/src/ee/export-transcripts.d.ts +1 -0
  73. package/dist/src/ee/export-transcripts.js +8 -10
  74. package/dist/src/ee/extract-session.js +29 -0
  75. package/dist/src/ee/extract-style.d.ts +58 -0
  76. package/dist/src/ee/extract-style.js +270 -0
  77. package/dist/src/ee/recall-ledger.d.ts +9 -0
  78. package/dist/src/ee/recall-ledger.js +3 -0
  79. package/dist/src/ee/scope.d.ts +1 -0
  80. package/dist/src/ee/scope.js +26 -1
  81. package/dist/src/ee/search.d.ts +7 -0
  82. package/dist/src/ee/search.js +24 -0
  83. package/dist/src/ee/transcript-emit.js +2 -0
  84. package/dist/src/ee/types.d.ts +22 -0
  85. package/dist/src/ee/who-am-i-brain.d.ts +35 -0
  86. package/dist/src/ee/who-am-i-brain.js +220 -0
  87. package/dist/src/ee/who-am-i.d.ts +10 -3
  88. package/dist/src/ee/who-am-i.js +12 -0
  89. package/dist/src/ee/workflow-event.d.ts +48 -0
  90. package/dist/src/ee/workflow-event.js +81 -0
  91. package/dist/src/flow/compaction/compress.d.ts +3 -3
  92. package/dist/src/flow/compaction/compress.js +58 -8
  93. package/dist/src/flow/compaction/extract.d.ts +4 -7
  94. package/dist/src/flow/compaction/extract.js +50 -10
  95. package/dist/src/flow/compaction/index.d.ts +14 -1
  96. package/dist/src/flow/compaction/index.js +96 -3
  97. package/dist/src/flow/compaction/input-guard.d.ts +24 -0
  98. package/dist/src/flow/compaction/input-guard.js +43 -0
  99. package/dist/src/flow/compaction/progress.d.ts +35 -0
  100. package/dist/src/flow/compaction/progress.js +35 -0
  101. package/dist/src/flow/fold-planning.d.ts +36 -0
  102. package/dist/src/flow/fold-planning.js +83 -0
  103. package/dist/src/flow/hierarchy.d.ts +146 -0
  104. package/dist/src/flow/hierarchy.js +427 -0
  105. package/dist/src/flow/index.d.ts +1 -0
  106. package/dist/src/flow/index.js +2 -0
  107. package/dist/src/flow/run-artifacts.d.ts +102 -0
  108. package/dist/src/flow/run-artifacts.js +208 -0
  109. package/dist/src/generated/version.d.ts +1 -1
  110. package/dist/src/generated/version.js +1 -1
  111. package/dist/src/gsd/assessment-schema.d.ts +44 -0
  112. package/dist/src/gsd/assessment-schema.js +134 -0
  113. package/dist/src/gsd/capability-registry.d.ts +45 -0
  114. package/dist/src/gsd/capability-registry.js +337 -0
  115. package/dist/src/gsd/complexity-assessor.d.ts +39 -0
  116. package/dist/src/gsd/complexity-assessor.js +152 -0
  117. package/dist/src/gsd/config-bridge.d.ts +7 -0
  118. package/dist/src/gsd/config-bridge.js +114 -0
  119. package/dist/src/gsd/config-loader.d.ts +27 -0
  120. package/dist/src/gsd/config-loader.js +50 -0
  121. package/dist/src/gsd/council-context.d.ts +44 -0
  122. package/dist/src/gsd/council-context.js +114 -0
  123. package/dist/src/gsd/ee-closure.d.ts +28 -0
  124. package/dist/src/gsd/ee-closure.js +49 -0
  125. package/dist/src/gsd/flags.d.ts +66 -0
  126. package/dist/src/gsd/flags.js +102 -0
  127. package/dist/src/gsd/gsd-dispatch.d.ts +58 -0
  128. package/dist/src/gsd/gsd-dispatch.js +131 -0
  129. package/dist/src/gsd/gsd-runtime.d.ts +22 -0
  130. package/dist/src/gsd/gsd-runtime.js +37 -0
  131. package/dist/src/gsd/host-adapter.d.ts +11 -0
  132. package/dist/src/gsd/host-adapter.js +29 -0
  133. package/dist/src/gsd/index.d.ts +24 -1
  134. package/dist/src/gsd/index.js +27 -0
  135. package/dist/src/gsd/loop-host-contract.d.ts +21 -0
  136. package/dist/src/gsd/loop-host-contract.js +39 -0
  137. package/dist/src/gsd/loop-host.d.ts +69 -0
  138. package/dist/src/gsd/loop-host.js +245 -0
  139. package/dist/src/gsd/loop-resolver.d.ts +36 -0
  140. package/dist/src/gsd/loop-resolver.js +79 -0
  141. package/dist/src/gsd/model-tier.d.ts +13 -0
  142. package/dist/src/gsd/model-tier.js +45 -0
  143. package/dist/src/gsd/mutation-gate.d.ts +16 -0
  144. package/dist/src/gsd/mutation-gate.js +41 -0
  145. package/dist/src/gsd/native-roadmap.d.ts +89 -0
  146. package/dist/src/gsd/native-roadmap.js +343 -0
  147. package/dist/src/gsd/native-state.d.ts +47 -0
  148. package/dist/src/gsd/native-state.js +220 -0
  149. package/dist/src/gsd/paths.d.ts +23 -0
  150. package/dist/src/gsd/paths.js +66 -0
  151. package/dist/src/gsd/phase-dag.d.ts +12 -0
  152. package/dist/src/gsd/phase-dag.js +94 -0
  153. package/dist/src/gsd/phase-sync.d.ts +42 -0
  154. package/dist/src/gsd/phase-sync.js +321 -0
  155. package/dist/src/gsd/pil-gate-context.d.ts +13 -0
  156. package/dist/src/gsd/pil-gate-context.js +64 -0
  157. package/dist/src/gsd/pil-gate-critic.d.ts +19 -0
  158. package/dist/src/gsd/pil-gate-critic.js +74 -0
  159. package/dist/src/gsd/plan-council-prompts.d.ts +25 -0
  160. package/dist/src/gsd/plan-council-prompts.js +79 -0
  161. package/dist/src/gsd/plan-council.d.ts +44 -0
  162. package/dist/src/gsd/plan-council.js +283 -0
  163. package/dist/src/gsd/plan-gate-vocabulary.d.ts +40 -0
  164. package/dist/src/gsd/plan-gate-vocabulary.js +64 -0
  165. package/dist/src/gsd/product-workspace.d.ts +13 -0
  166. package/dist/src/gsd/product-workspace.js +124 -0
  167. package/dist/src/gsd/ship-bridge.d.ts +25 -0
  168. package/dist/src/gsd/ship-bridge.js +65 -0
  169. package/dist/src/gsd/state-document.d.ts +40 -0
  170. package/dist/src/gsd/state-document.js +163 -0
  171. package/dist/src/gsd/verdict-schema.d.ts +39 -0
  172. package/dist/src/gsd/verdict-schema.js +144 -0
  173. package/dist/src/gsd/verify-context.d.ts +22 -0
  174. package/dist/src/gsd/verify-context.js +27 -0
  175. package/dist/src/gsd/verify-council-prompts.d.ts +19 -0
  176. package/dist/src/gsd/verify-council-prompts.js +85 -0
  177. package/dist/src/gsd/verify-council.d.ts +25 -0
  178. package/dist/src/gsd/verify-council.js +119 -0
  179. package/dist/src/gsd/verify-gate-vocabulary.d.ts +25 -0
  180. package/dist/src/gsd/verify-gate-vocabulary.js +46 -0
  181. package/dist/src/gsd/workflow-engine.d.ts +60 -0
  182. package/dist/src/gsd/workflow-engine.js +207 -0
  183. package/dist/src/gsd/workflow-tools.d.ts +13 -0
  184. package/dist/src/gsd/workflow-tools.js +277 -0
  185. package/dist/src/headless/council-answers.js +4 -0
  186. package/dist/src/hooks/index.js +1 -1
  187. package/dist/src/index.js +172 -270
  188. package/dist/src/lsp/builtins.js +3 -1
  189. package/dist/src/lsp/manager.d.ts +5 -1
  190. package/dist/src/lsp/manager.js +249 -3
  191. package/dist/src/lsp/npm-cache.d.ts +11 -1
  192. package/dist/src/lsp/npm-cache.js +17 -1
  193. package/dist/src/lsp/runtime.d.ts +6 -1
  194. package/dist/src/lsp/runtime.js +17 -1
  195. package/dist/src/lsp/types.d.ts +83 -1
  196. package/dist/src/lsp/types.js +10 -0
  197. package/dist/src/maintain/pr-builder.js +23 -13
  198. package/dist/src/mcp/auto-setup.js +57 -32
  199. package/dist/src/mcp/client-pool.js +44 -16
  200. package/dist/src/mcp/lsp-tools.d.ts +5 -1
  201. package/dist/src/mcp/lsp-tools.js +93 -2
  202. package/dist/src/mcp/mcp-keychain.d.ts +3 -5
  203. package/dist/src/mcp/mcp-keychain.js +9 -49
  204. package/dist/src/mcp/research-onboarding.js +8 -7
  205. package/dist/src/mcp/runtime.js +34 -2
  206. package/dist/src/mcp/setup-guide-text.d.ts +1 -1
  207. package/dist/src/mcp/setup-guide-text.js +22 -2
  208. package/dist/src/mcp/tools-server.d.ts +10 -0
  209. package/dist/src/mcp/tools-server.js +10 -2
  210. package/dist/src/models/catalog-client.d.ts +87 -0
  211. package/dist/src/models/catalog-client.js +105 -38
  212. package/dist/src/models/catalog.json +528 -265
  213. package/dist/src/models/registry.d.ts +22 -7
  214. package/dist/src/models/registry.js +73 -10
  215. package/dist/src/ops/doctor.js +1 -1
  216. package/dist/src/orchestrator/ask-user.d.ts +61 -0
  217. package/dist/src/orchestrator/ask-user.js +65 -0
  218. package/dist/src/orchestrator/auto-commit.js +1 -1
  219. package/dist/src/orchestrator/batch-turn-runner.js +2 -2
  220. package/dist/src/orchestrator/cache-prefix.d.ts +67 -0
  221. package/dist/src/orchestrator/cache-prefix.js +83 -0
  222. package/dist/src/orchestrator/compact-request.d.ts +32 -0
  223. package/dist/src/orchestrator/compact-request.js +41 -0
  224. package/dist/src/orchestrator/compaction.d.ts +12 -3
  225. package/dist/src/orchestrator/compaction.js +35 -15
  226. package/dist/src/orchestrator/council-manager.d.ts +12 -3
  227. package/dist/src/orchestrator/council-manager.js +74 -32
  228. package/dist/src/orchestrator/council-request.d.ts +49 -0
  229. package/dist/src/orchestrator/council-request.js +62 -0
  230. package/dist/src/orchestrator/cross-turn-dedup.d.ts +6 -0
  231. package/dist/src/orchestrator/cross-turn-dedup.js +22 -1
  232. package/dist/src/orchestrator/error-utils.d.ts +29 -0
  233. package/dist/src/orchestrator/error-utils.js +132 -24
  234. package/dist/src/orchestrator/grounding-check.js +39 -1
  235. package/dist/src/orchestrator/interactive-pause.d.ts +26 -0
  236. package/dist/src/orchestrator/interactive-pause.js +36 -0
  237. package/dist/src/orchestrator/message-processor.d.ts +4 -0
  238. package/dist/src/orchestrator/message-processor.js +268 -41
  239. package/dist/src/orchestrator/orchestrator.d.ts +64 -3
  240. package/dist/src/orchestrator/orchestrator.js +823 -120
  241. package/dist/src/orchestrator/preprocessor.js +3 -3
  242. package/dist/src/orchestrator/proactive-compact-detector.d.ts +26 -0
  243. package/dist/src/orchestrator/proactive-compact-detector.js +36 -0
  244. package/dist/src/orchestrator/prompts.js +17 -17
  245. package/dist/src/orchestrator/reactive-delegation.d.ts +39 -0
  246. package/dist/src/orchestrator/reactive-delegation.js +59 -0
  247. package/dist/src/orchestrator/retry-classifier.d.ts +2 -1
  248. package/dist/src/orchestrator/retry-classifier.js +46 -2
  249. package/dist/src/orchestrator/safety-askcard.d.ts +1 -1
  250. package/dist/src/orchestrator/safety-askcard.js +5 -2
  251. package/dist/src/orchestrator/safety-intercept.d.ts +50 -0
  252. package/dist/src/orchestrator/safety-intercept.js +62 -0
  253. package/dist/src/orchestrator/scope-reminder.js +1 -1
  254. package/dist/src/orchestrator/session-experience.d.ts +2 -1
  255. package/dist/src/orchestrator/session-experience.js +2 -1
  256. package/dist/src/orchestrator/should-run-gate.d.ts +5 -0
  257. package/dist/src/orchestrator/should-run-gate.js +18 -0
  258. package/dist/src/orchestrator/stall-watchdog.d.ts +31 -3
  259. package/dist/src/orchestrator/stall-watchdog.js +65 -10
  260. package/dist/src/orchestrator/stream-runner.d.ts +13 -3
  261. package/dist/src/orchestrator/stream-runner.js +115 -49
  262. package/dist/src/orchestrator/sub-agent-cap.d.ts +13 -1
  263. package/dist/src/orchestrator/sub-agent-cap.js +16 -1
  264. package/dist/src/orchestrator/sub-agent-model-tier.d.ts +13 -1
  265. package/dist/src/orchestrator/sub-agent-model-tier.js +16 -2
  266. package/dist/src/orchestrator/subagent-compactor.d.ts +53 -1
  267. package/dist/src/orchestrator/subagent-compactor.js +126 -15
  268. package/dist/src/orchestrator/tool-engine.d.ts +41 -0
  269. package/dist/src/orchestrator/tool-engine.js +846 -66
  270. package/dist/src/orchestrator/tool-limit-auto-recover.d.ts +22 -0
  271. package/dist/src/orchestrator/tool-limit-auto-recover.js +30 -0
  272. package/dist/src/orchestrator/turn-runner-deps.d.ts +19 -0
  273. package/dist/src/orchestrator/turn-watchdog.d.ts +44 -0
  274. package/dist/src/orchestrator/turn-watchdog.js +84 -0
  275. package/dist/src/pil/agent-operating-contract.d.ts +1 -1
  276. package/dist/src/pil/agent-operating-contract.js +6 -4
  277. package/dist/src/pil/cheap-model-playbook.d.ts +1 -1
  278. package/dist/src/pil/cheap-model-playbook.js +5 -1
  279. package/dist/src/pil/cheap-model-workbooks.d.ts +1 -1
  280. package/dist/src/pil/cheap-model-workbooks.js +1 -1
  281. package/dist/src/pil/discovery-types.d.ts +1 -0
  282. package/dist/src/pil/discovery.d.ts +1 -1
  283. package/dist/src/pil/discovery.js +18 -13
  284. package/dist/src/pil/layer1-intent.d.ts +18 -6
  285. package/dist/src/pil/layer1-intent.js +66 -757
  286. package/dist/src/pil/layer15-context-scan.js +15 -1
  287. package/dist/src/pil/layer1_5-complexity-size.d.ts +7 -0
  288. package/dist/src/pil/layer1_5-complexity-size.js +31 -5
  289. package/dist/src/pil/layer3-ee-injection.js +23 -8
  290. package/dist/src/pil/layer4-gsd.js +69 -16
  291. package/dist/src/pil/layer5-context.js +7 -3
  292. package/dist/src/pil/layer6-output.d.ts +23 -0
  293. package/dist/src/pil/layer6-output.js +5 -1
  294. package/dist/src/pil/llm-classify.d.ts +111 -5
  295. package/dist/src/pil/llm-classify.js +421 -189
  296. package/dist/src/pil/native-capabilities-workbook.d.ts +1 -1
  297. package/dist/src/pil/native-capabilities-workbook.js +8 -0
  298. package/dist/src/pil/pipeline.js +36 -2
  299. package/dist/src/pil/repo-grounding-probe.d.ts +15 -0
  300. package/dist/src/pil/repo-grounding-probe.js +136 -0
  301. package/dist/src/pil/repo-structure-hints.d.ts +7 -0
  302. package/dist/src/pil/repo-structure-hints.js +45 -0
  303. package/dist/src/pil/response-tools.js +5 -3
  304. package/dist/src/pil/schema.d.ts +1 -0
  305. package/dist/src/pil/schema.js +2 -0
  306. package/dist/src/pil/types.d.ts +18 -0
  307. package/dist/src/playbook/directives.d.ts +4 -0
  308. package/dist/src/playbook/directives.js +17 -5
  309. package/dist/src/product-loop/artifact-io.js +4 -0
  310. package/dist/src/product-loop/backlog-builder.d.ts +14 -1
  311. package/dist/src/product-loop/backlog-builder.js +30 -6
  312. package/dist/src/product-loop/criteria-seed.d.ts +51 -0
  313. package/dist/src/product-loop/criteria-seed.js +200 -0
  314. package/dist/src/product-loop/discovery-context-format.js +3 -1
  315. package/dist/src/product-loop/discovery-ecosystem.js +4 -1
  316. package/dist/src/product-loop/discovery-interview.d.ts +9 -0
  317. package/dist/src/product-loop/discovery-interview.js +60 -12
  318. package/dist/src/product-loop/discovery-recommender.js +2 -1
  319. package/dist/src/product-loop/discovery-schema.js +19 -2
  320. package/dist/src/product-loop/discovery-triage.d.ts +23 -0
  321. package/dist/src/product-loop/discovery-triage.js +109 -0
  322. package/dist/src/product-loop/gather.js +150 -2
  323. package/dist/src/product-loop/ideal-trace.d.ts +7 -0
  324. package/dist/src/product-loop/ideal-trace.js +64 -0
  325. package/dist/src/product-loop/index.d.ts +13 -1
  326. package/dist/src/product-loop/index.js +340 -52
  327. package/dist/src/product-loop/loop-driver.d.ts +7 -0
  328. package/dist/src/product-loop/loop-driver.js +330 -106
  329. package/dist/src/product-loop/phase-plan.d.ts +21 -0
  330. package/dist/src/product-loop/phase-plan.js +81 -6
  331. package/dist/src/product-loop/phase-rituals.d.ts +3 -0
  332. package/dist/src/product-loop/phase-rituals.js +8 -3
  333. package/dist/src/product-loop/phase-runner.js +39 -12
  334. package/dist/src/product-loop/plan-adherence-review.d.ts +26 -0
  335. package/dist/src/product-loop/plan-adherence-review.js +144 -0
  336. package/dist/src/product-loop/sprint-runner.d.ts +173 -0
  337. package/dist/src/product-loop/sprint-runner.js +863 -19
  338. package/dist/src/product-loop/types.d.ts +61 -5
  339. package/dist/src/providers/adapter.d.ts +1 -1
  340. package/dist/src/providers/adapter.js +3 -4
  341. package/dist/src/providers/anthropic.d.ts +9 -8
  342. package/dist/src/providers/anthropic.js +13 -47
  343. package/dist/src/providers/auth/browser-flow.d.ts +1 -1
  344. package/dist/src/providers/auth/browser-flow.js +1 -1
  345. package/dist/src/providers/auth/grok-oauth.d.ts +1 -0
  346. package/dist/src/providers/auth/grok-oauth.js +30 -5
  347. package/dist/src/providers/auth/openai-oauth.d.ts +1 -0
  348. package/dist/src/providers/auth/openai-oauth.js +15 -1
  349. package/dist/src/providers/auth/registry.js +0 -34
  350. package/dist/src/providers/auth/token-store.d.ts +9 -9
  351. package/dist/src/providers/auth/token-store.js +8 -67
  352. package/dist/src/providers/auth/types.d.ts +9 -1
  353. package/dist/src/providers/auth/types.js +1 -1
  354. package/dist/src/providers/capabilities.d.ts +24 -5
  355. package/dist/src/providers/capabilities.js +42 -24
  356. package/dist/src/providers/endpoints.d.ts +2 -2
  357. package/dist/src/providers/endpoints.js +11 -10
  358. package/dist/src/providers/env-store.d.ts +17 -0
  359. package/dist/src/providers/env-store.js +228 -0
  360. package/dist/src/providers/keychain.d.ts +22 -18
  361. package/dist/src/providers/keychain.js +127 -140
  362. package/dist/src/providers/mcp-vision-bridge.js +56 -146
  363. package/dist/src/providers/openai-compatible.js +8 -1
  364. package/dist/src/providers/pricing.d.ts +2 -2
  365. package/dist/src/providers/pricing.js +3 -13
  366. package/dist/src/providers/runtime.d.ts +43 -3
  367. package/dist/src/providers/runtime.js +88 -14
  368. package/dist/src/providers/strategies/base.strategy.d.ts +16 -0
  369. package/dist/src/providers/strategies/base.strategy.js +24 -1
  370. package/dist/src/providers/strategies/{siliconflow.strategy.d.ts → opencode-go.strategy.d.ts} +3 -3
  371. package/dist/src/providers/strategies/opencode-go.strategy.js +83 -0
  372. package/dist/src/providers/strategies/registry.js +4 -4
  373. package/dist/src/providers/strategies/thinking-mode.d.ts +109 -0
  374. package/dist/src/providers/strategies/thinking-mode.js +288 -1
  375. package/dist/src/providers/strategies/xai.strategy.js +27 -0
  376. package/dist/src/providers/strategies/zai.strategy.d.ts +14 -0
  377. package/dist/src/providers/strategies/zai.strategy.js +44 -0
  378. package/dist/src/providers/types.d.ts +5 -6
  379. package/dist/src/providers/types.js +2 -2
  380. package/dist/src/providers/vision-backend.d.ts +47 -0
  381. package/dist/src/providers/vision-backend.js +258 -0
  382. package/dist/src/providers/vision-proxy.d.ts +22 -9
  383. package/dist/src/providers/vision-proxy.js +63 -132
  384. package/dist/src/providers/warm.d.ts +65 -0
  385. package/dist/src/providers/warm.js +145 -0
  386. package/dist/src/providers/wire-debug.js +95 -0
  387. package/dist/src/router/decide.d.ts +13 -0
  388. package/dist/src/router/decide.js +138 -36
  389. package/dist/src/router/peak-hour.d.ts +38 -0
  390. package/dist/src/router/peak-hour.js +107 -0
  391. package/dist/src/router/step-router.js +3 -2
  392. package/dist/src/router/warm.js +4 -5
  393. package/dist/src/scaffold/continuation-prompt.d.ts +11 -0
  394. package/dist/src/scaffold/continuation-prompt.js +26 -0
  395. package/dist/src/scaffold/point-to-existing.d.ts +21 -0
  396. package/dist/src/scaffold/point-to-existing.js +25 -0
  397. package/dist/src/self-qa/agentic-loop.js +6 -5
  398. package/dist/src/{ui/state → state}/active-run.d.ts +19 -0
  399. package/dist/src/{ui/state → state}/active-run.js +21 -0
  400. package/dist/src/{ui/status-bar/store.d.ts → state/status-bar-store.d.ts} +4 -1
  401. package/dist/src/{ui/status-bar/store.js → state/status-bar-store.js} +12 -9
  402. package/dist/src/state/turn-trace.d.ts +43 -0
  403. package/dist/src/state/turn-trace.js +32 -0
  404. package/dist/src/storage/db.js +2 -1
  405. package/dist/src/storage/index.d.ts +1 -1
  406. package/dist/src/storage/index.js +1 -1
  407. package/dist/src/storage/interaction-log.d.ts +1 -1
  408. package/dist/src/storage/migrations.js +71 -1
  409. package/dist/src/storage/sessions.d.ts +28 -10
  410. package/dist/src/storage/sessions.js +78 -21
  411. package/dist/src/storage/transcript-view.js +1 -1
  412. package/dist/src/storage/transcript.d.ts +51 -0
  413. package/dist/src/storage/transcript.js +340 -15
  414. package/dist/src/tools/file.d.ts +15 -0
  415. package/dist/src/tools/file.js +32 -0
  416. package/dist/src/tools/git-safety.d.ts +19 -0
  417. package/dist/src/tools/git-safety.js +168 -0
  418. package/dist/src/tools/native-tools.d.ts +1 -1
  419. package/dist/src/tools/native-tools.js +81 -1
  420. package/dist/src/tools/registry.d.ts +20 -0
  421. package/dist/src/tools/registry.js +576 -23
  422. package/dist/src/tools/research.d.ts +29 -0
  423. package/dist/src/tools/research.js +233 -0
  424. package/dist/src/types/index.d.ts +147 -4
  425. package/dist/src/ui/app.js +0 -0
  426. package/dist/src/ui/cards/product-status-card.js +1 -1
  427. package/dist/src/ui/components/agent-rail-activities.d.ts +26 -0
  428. package/dist/src/ui/components/agent-rail-activities.js +47 -0
  429. package/dist/src/ui/components/bubble-body-guard.d.ts +27 -0
  430. package/dist/src/ui/components/bubble-body-guard.js +50 -0
  431. package/dist/src/ui/components/compact-progress-card.d.ts +24 -0
  432. package/dist/src/ui/components/compact-progress-card.js +42 -0
  433. package/dist/src/ui/components/context-rail.d.ts +26 -0
  434. package/dist/src/ui/components/context-rail.js +33 -0
  435. package/dist/src/ui/components/council-conclusion-card.d.ts +73 -0
  436. package/dist/src/ui/components/council-conclusion-card.js +420 -0
  437. package/dist/src/ui/components/council-debate-pill.d.ts +36 -0
  438. package/dist/src/ui/components/council-debate-pill.js +34 -0
  439. package/dist/src/ui/components/council-info-card.js +2 -2
  440. package/dist/src/ui/components/council-leader-bubble.d.ts +10 -3
  441. package/dist/src/ui/components/council-leader-bubble.js +21 -11
  442. package/dist/src/ui/components/council-message-bubble.d.ts +15 -2
  443. package/dist/src/ui/components/council-message-bubble.js +16 -15
  444. package/dist/src/ui/components/council-phase-timeline.d.ts +9 -1
  445. package/dist/src/ui/components/council-phase-timeline.js +66 -17
  446. package/dist/src/ui/components/council-placeholder-bubble.d.ts +16 -9
  447. package/dist/src/ui/components/council-placeholder-bubble.js +32 -29
  448. package/dist/src/ui/components/council-question-card.js +13 -12
  449. package/dist/src/ui/components/council-rail-rounds.d.ts +26 -0
  450. package/dist/src/ui/components/council-rail-rounds.js +57 -0
  451. package/dist/src/ui/components/council-round-group.d.ts +38 -0
  452. package/dist/src/ui/components/council-round-group.js +88 -0
  453. package/dist/src/ui/components/council-status-list.d.ts +3 -1
  454. package/dist/src/ui/components/council-status-list.js +36 -24
  455. package/dist/src/ui/components/council-synthesis-banner.d.ts +7 -2
  456. package/dist/src/ui/components/council-synthesis-banner.js +20 -5
  457. package/dist/src/ui/components/halt-recovery-card.js +9 -5
  458. package/dist/src/ui/components/jump-to-latest-pill.d.ts +11 -0
  459. package/dist/src/ui/components/jump-to-latest-pill.js +14 -0
  460. package/dist/src/ui/components/message-view.d.ts +15 -0
  461. package/dist/src/ui/components/message-view.js +50 -1
  462. package/dist/src/ui/components/prompt-box.js +18 -16
  463. package/dist/src/ui/components/session-tree-card.d.ts +14 -0
  464. package/dist/src/ui/components/session-tree-card.js +46 -0
  465. package/dist/src/ui/components/slash-inline-menu.d.ts +12 -0
  466. package/dist/src/ui/components/slash-inline-menu.js +26 -5
  467. package/dist/src/ui/components/task-list-panel.d.ts +14 -1
  468. package/dist/src/ui/components/task-list-panel.js +22 -2
  469. package/dist/src/ui/components/tool-group.d.ts +15 -3
  470. package/dist/src/ui/components/tool-group.js +69 -11
  471. package/dist/src/ui/containers/modals-layer.d.ts +4 -2
  472. package/dist/src/ui/containers/modals-layer.js +2 -2
  473. package/dist/src/ui/council-harness-event.d.ts +57 -0
  474. package/dist/src/ui/council-harness-event.js +46 -0
  475. package/dist/src/ui/heartbeat-debug.d.ts +29 -0
  476. package/dist/src/ui/heartbeat-debug.js +45 -0
  477. package/dist/src/ui/hooks/use-session-picker.d.ts +3 -3
  478. package/dist/src/ui/mcp-modal.js +2 -4
  479. package/dist/src/ui/modals/api-key-modal.js +1 -1
  480. package/dist/src/ui/modals/connect-modal.js +4 -3
  481. package/dist/src/ui/modals/model-picker-modal.d.ts +8 -18
  482. package/dist/src/ui/modals/model-picker-modal.js +8 -10
  483. package/dist/src/ui/modals/session-picker-modal.d.ts +2 -2
  484. package/dist/src/ui/modals/session-picker-modal.js +3 -5
  485. package/dist/src/ui/picker-providers.d.ts +1 -1
  486. package/dist/src/ui/picker-providers.js +1 -1
  487. package/dist/src/ui/primitives/index.d.ts +1 -0
  488. package/dist/src/ui/primitives/index.js +2 -0
  489. package/dist/src/ui/primitives/semantic-primitives.d.ts +76 -0
  490. package/dist/src/ui/primitives/semantic-primitives.js +81 -0
  491. package/dist/src/ui/slash/compact.js +5 -7
  492. package/dist/src/ui/slash/cost.js +1 -1
  493. package/dist/src/ui/slash/council.js +19 -1
  494. package/dist/src/ui/slash/debug.d.ts +3 -31
  495. package/dist/src/ui/slash/debug.js +9 -20
  496. package/dist/src/ui/slash/ee.js +81 -0
  497. package/dist/src/ui/slash/ideal.d.ts +6 -2
  498. package/dist/src/ui/slash/ideal.js +97 -7
  499. package/dist/src/ui/slash/menu-items.d.ts +7 -0
  500. package/dist/src/ui/slash/menu-items.js +23 -20
  501. package/dist/src/ui/slash/registry.d.ts +2 -0
  502. package/dist/src/ui/slash/registry.js +4 -0
  503. package/dist/src/ui/status-bar/cache-hit.d.ts +6 -0
  504. package/dist/src/ui/status-bar/cache-hit.js +9 -0
  505. package/dist/src/ui/status-bar/index.d.ts +1 -1
  506. package/dist/src/ui/status-bar/index.js +7 -3
  507. package/dist/src/ui/status-bar/usd-meter.d.ts +5 -4
  508. package/dist/src/ui/status-bar/usd-meter.js +6 -4
  509. package/dist/src/ui/theme.d.ts +1 -0
  510. package/dist/src/ui/theme.js +2 -0
  511. package/dist/src/ui/types.d.ts +7 -0
  512. package/dist/src/ui/use-app-logic.js +0 -0
  513. package/dist/src/ui/utils/agent-activities.d.ts +39 -0
  514. package/dist/src/ui/utils/agent-activities.js +96 -0
  515. package/dist/src/ui/utils/format.d.ts +14 -0
  516. package/dist/src/ui/utils/format.js +23 -3
  517. package/dist/src/ui/utils/group-tool-entries.d.ts +26 -0
  518. package/dist/src/ui/utils/group-tool-entries.js +111 -0
  519. package/dist/src/ui/utils/tool-summary.d.ts +21 -0
  520. package/dist/src/ui/utils/tool-summary.js +91 -0
  521. package/dist/src/usage/downgrade.js +2 -2
  522. package/dist/src/usage/product-ledger.js +2 -2
  523. package/dist/src/utils/event-loop-monitor.d.ts +85 -0
  524. package/dist/src/utils/event-loop-monitor.js +107 -0
  525. package/dist/src/utils/install-manager.js +2 -1
  526. package/dist/src/utils/llm-deadline.d.ts +14 -0
  527. package/dist/src/utils/llm-deadline.js +19 -0
  528. package/dist/src/utils/logger.js +2 -2
  529. package/dist/src/utils/loop-profiler.d.ts +102 -0
  530. package/dist/src/utils/loop-profiler.js +202 -0
  531. package/dist/src/utils/permission-mode.js +5 -3
  532. package/dist/src/utils/redactor.js +1 -1
  533. package/dist/src/utils/settings.d.ts +180 -5
  534. package/dist/src/utils/settings.js +271 -31
  535. package/dist/src/utils/side-question.d.ts +1 -2
  536. package/dist/src/utils/side-question.js +2 -2
  537. package/dist/src/utils/visible-retry.d.ts +11 -0
  538. package/dist/src/utils/visible-retry.js +10 -1
  539. package/dist/src/verify/entrypoint.d.ts +1 -1
  540. package/dist/src/verify/entrypoint.js +52 -17
  541. package/dist/src/verify/orchestrator.d.ts +1 -1
  542. package/dist/src/verify/orchestrator.js +20 -3
  543. package/dist/src/verify/recipes.d.ts +13 -0
  544. package/dist/src/verify/recipes.js +15 -0
  545. package/package.json +134 -132
  546. package/dist/src/cli/bw-vault.d.ts +0 -55
  547. package/dist/src/cli/bw-vault.js +0 -133
  548. package/dist/src/mcp/ee-tools.d.ts +0 -46
  549. package/dist/src/mcp/ee-tools.js +0 -193
  550. package/dist/src/providers/auth/gcloud.d.ts +0 -28
  551. package/dist/src/providers/auth/gcloud.js +0 -102
  552. package/dist/src/providers/auth/gemini-oauth.d.ts +0 -82
  553. package/dist/src/providers/auth/gemini-oauth.js +0 -472
  554. package/dist/src/providers/gemini.d.ts +0 -11
  555. package/dist/src/providers/gemini.js +0 -45
  556. package/dist/src/providers/siliconflow-sse-repair.d.ts +0 -58
  557. package/dist/src/providers/siliconflow-sse-repair.js +0 -177
  558. package/dist/src/providers/strategies/google.strategy.d.ts +0 -22
  559. package/dist/src/providers/strategies/google.strategy.js +0 -174
  560. package/dist/src/providers/strategies/siliconflow.strategy.js +0 -29
  561. package/dist/src/ui/containers/chat-feed.d.ts +0 -40
  562. package/dist/src/ui/containers/chat-feed.js +0 -66
@@ -3,8 +3,16 @@ import type { Theme } from "../theme.js";
3
3
  export interface CouncilPhaseTimelineProps {
4
4
  phases: CouncilPhaseEvent[];
5
5
  theme: Theme;
6
+ /**
7
+ * When false (default), completed phases fold into a single "N steps done"
8
+ * summary line and only the live (active/error) phase is shown — the
9
+ * aggressive-collapse UX. When true (Ctrl+O), the full done-trail is shown.
10
+ * Shares the council transcript's expand state so one toggle controls all
11
+ * council detail.
12
+ */
13
+ expanded?: boolean;
6
14
  }
7
- export declare function CouncilPhaseTimeline({ phases, theme: t }: CouncilPhaseTimelineProps): import("react").ReactNode;
15
+ export declare function CouncilPhaseTimeline({ phases, theme: t, expanded }: CouncilPhaseTimelineProps): import("react").ReactNode;
8
16
  /**
9
17
  * Reducer-friendly upsert: returns the next phase array given an incoming event.
10
18
  * Matches by phaseId; preserves insertion order for new phases. The first event
@@ -1,10 +1,14 @@
1
1
  import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "@opentui/react/jsx-runtime";
2
2
  import { useEffect, useState } from "react";
3
+ import { heartbeatDebug } from "../heartbeat-debug.js";
3
4
  const SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
4
5
  function Spinner() {
5
6
  const [frame, setFrame] = useState(0);
6
7
  useEffect(() => {
7
- const id = setInterval(() => setFrame((n) => (n + 1) % SPINNER_FRAMES.length), 100);
8
+ const id = setInterval(() => {
9
+ heartbeatDebug("phase-timeline-spinner", "timer");
10
+ setFrame((n) => (n + 1) % SPINNER_FRAMES.length);
11
+ }, 100);
8
12
  return () => clearInterval(id);
9
13
  }, []);
10
14
  return _jsx(_Fragment, { children: SPINNER_FRAMES[frame] });
@@ -31,28 +35,73 @@ function formatElapsed(ms) {
31
35
  function useHeartbeat(tickMs = 1000) {
32
36
  const [, force] = useState(0);
33
37
  useEffect(() => {
34
- const id = setInterval(() => force((n) => n + 1), tickMs);
38
+ const id = setInterval(() => {
39
+ heartbeatDebug("phase-timeline-heartbeat", "timer");
40
+ force((n) => n + 1);
41
+ }, tickMs);
35
42
  return () => clearInterval(id);
36
43
  }, [tickMs]);
37
44
  return Date.now();
38
45
  }
39
- export function CouncilPhaseTimeline({ phases, theme: t }) {
46
+ export function CouncilPhaseTimeline({ phases, theme: t, expanded = false }) {
40
47
  const now = useHeartbeat(1000);
41
- if (phases.length === 0)
48
+ // Collapse per-round brackets: the "Clarification round N" / "Round N" phases
49
+ // duplicate the live status line ("Generating clarification questions (round
50
+ // N)"), stacking 3-4 near-identical spinner rows at council start. Keep only
51
+ // the coarse lifecycle phases here; round detail already rides on the status
52
+ // line label. Errors are always shown so a failed round stays visible.
53
+ const visible = phases.filter((p) => !((p.kind === "clarification_round" || p.kind === "round") && p.state !== "error"));
54
+ if (visible.length === 0)
42
55
  return null;
43
- return (_jsx("box", { flexDirection: "column", paddingLeft: 2, paddingTop: 0, flexShrink: 0, children: phases.map((p) => {
44
- const isError = p.state === "error";
45
- const isDone = p.state === "done";
46
- const marker = isError ? "✗" : isDone ? "✓" : _jsx(Spinner, {});
47
- const markerColor = isError ? t.diffRemovedFg : isDone ? t.planOptionCheck : t.accent;
48
- const labelColor = isError ? t.diffRemovedFg : isDone ? t.textMuted : t.text;
49
- // For active phases with a startedAt, derive elapsed from wall-clock
50
- // so the displayed time keeps counting up between emitter updates.
51
- const liveElapsedMs = p.state === "active" && typeof p.startedAt === "number" ? Math.max(0, now - p.startedAt) : p.elapsedMs;
52
- const elapsed = formatElapsed(liveElapsedMs);
53
- const meta = elapsed ? ` (${elapsed})` : isDone ? "" : " …";
54
- return (_jsxs("box", { flexDirection: "column", children: [_jsxs("box", { children: [_jsx("text", { fg: markerColor, children: marker }), _jsx("text", { fg: labelColor, children: ` ${p.label}` }), meta && _jsx("text", { fg: t.textDim, children: meta })] }), p.detail && (_jsx("box", { paddingLeft: 2, children: _jsx("text", { fg: t.textMuted, children: `└ ${truncate(p.detail, 100)}` }) })), p.errorMessage && (_jsx("box", { paddingLeft: 2, children: _jsx("text", { fg: t.diffRemovedFg, children: `└ ${truncate(p.errorMessage, 100)}` }) }))] }, p.phaseId));
55
- }) }));
56
+ // Aggressive collapse (default): show only the live (active/error) phases as
57
+ // full rows; fold every completed phase into one "N steps done" summary line.
58
+ // Ctrl+O (expanded=true) restores the full done-trail. Errors are never
59
+ // folded a failed phase always stays visible.
60
+ const live = visible.filter((p) => p.state === "active" || p.state === "error");
61
+ const doneCount = visible.length - live.length;
62
+ const rows = expanded ? visible : live;
63
+ // Split off error phases so identical provider failures can be folded into one
64
+ // row instead of N repeated blocks. A flaky proxy (Console Go glm/kimi) makes
65
+ // clarify + spec-infer + eval all fail with the SAME message — rendered raw
66
+ // that spammed the (narrow) context rail. Non-error rows keep their order.
67
+ const nonErrorRows = rows.filter((p) => p.state !== "error");
68
+ const errorRows = rows.filter((p) => p.state === "error");
69
+ const errorGroups = [];
70
+ for (const p of errorRows) {
71
+ const message = p.errorMessage ?? "(failed)";
72
+ const existing = errorGroups.find((g) => g.message === message);
73
+ if (existing)
74
+ existing.phases.push(p);
75
+ else
76
+ errorGroups.push({ message, phases: [p] });
77
+ }
78
+ const renderRow = (p) => {
79
+ const isDone = p.state === "done";
80
+ const marker = isDone ? "✓" : _jsx(Spinner, {});
81
+ const markerColor = isDone ? t.planOptionCheck : t.accent;
82
+ const labelColor = isDone ? t.textMuted : t.text;
83
+ const liveElapsedMs = p.state === "active" && typeof p.startedAt === "number" ? Math.max(0, now - p.startedAt) : p.elapsedMs;
84
+ if (p.state === "active") {
85
+ heartbeatDebug("phase-timeline", "render", {
86
+ label: p.label,
87
+ liveElapsedMs,
88
+ startedAt: p.startedAt,
89
+ now,
90
+ });
91
+ }
92
+ const elapsed = formatElapsed(liveElapsedMs);
93
+ const meta = elapsed ? ` (${elapsed})` : isDone ? "" : " …";
94
+ return (_jsxs("box", { flexDirection: "column", children: [_jsxs("box", { children: [_jsx("text", { fg: markerColor, children: marker }), _jsx("text", { fg: labelColor, children: ` ${p.label}` }), meta && _jsx("text", { fg: t.textDim, children: meta })] }), p.detail && (_jsx("box", { paddingLeft: 2, children: _jsx("text", { fg: t.textMuted, children: `└ ${truncate(p.detail, 100)}` }) }))] }, p.phaseId));
95
+ };
96
+ return (_jsxs("box", { flexDirection: "column", paddingLeft: 2, paddingTop: 0, flexShrink: 0, children: [!expanded && doneCount > 0 && (_jsxs("box", { children: [_jsx("text", { fg: t.planOptionCheck, children: "\u2713" }), _jsx("text", { fg: t.textDim, children: ` ${doneCount} step${doneCount === 1 ? "" : "s"} done` }), _jsx("text", { fg: t.textMuted, children: " · Ctrl+O to expand" })] })), nonErrorRows.map(renderRow), errorGroups.map((g) => {
97
+ // One header per unique error; when >1 phase shares it, name the count
98
+ // and list the affected steps compactly instead of repeating the message.
99
+ const first = g.phases[0];
100
+ const label = g.phases.length === 1
101
+ ? first.label
102
+ : `${g.phases.length} steps failed: ${g.phases.map((p) => p.label).join(", ")}`;
103
+ return (_jsxs("box", { flexDirection: "column", children: [_jsxs("box", { children: [_jsx("text", { fg: t.diffRemovedFg, children: "\u2717" }), _jsx("text", { fg: t.diffRemovedFg, children: ` ${truncate(label, 100)}` })] }), _jsx("box", { paddingLeft: 2, children: _jsx("text", { fg: t.diffRemovedFg, children: `└ ${truncate(g.message, 100)}` }) })] }, `err-${first.phaseId}`));
104
+ })] }));
56
105
  }
57
106
  function truncate(s, max) {
58
107
  if (s.length <= max)
@@ -2,20 +2,27 @@ import type { Theme } from "../theme.js";
2
2
  export type PlaceholderVariant = "participant" | "leader";
3
3
  export interface CouncilPlaceholderBubbleProps {
4
4
  role: string;
5
+ /** Legacy pair-side hint — ignored in the linear group-chat layout. */
5
6
  side: "left" | "right";
6
7
  terminalCols: number;
7
8
  color: string;
8
9
  theme: Theme;
9
10
  variant?: PlaceholderVariant;
11
+ /**
12
+ * Optional per-speaker context shown as muted sub-lines while composing, so the
13
+ * main console is not a bare spinner during the 20–60s atomic generateText
14
+ * window. Newline-separated "Name — lens" entries emitted by the engine in the
15
+ * opening/exchange `council_status.detail`. Empty/undefined → label-only.
16
+ */
17
+ detail?: string;
10
18
  }
11
- export declare function buildPlaceholderLabel(role: string): string;
19
+ export declare function buildPlaceholderLabel(role: string, elapsedSec?: number): string;
12
20
  /**
13
- * Thin placeholder bubble shown while the producer is generating a turn.
14
- * Rendered at turn-start (when council_status{state:"start"} arrives for this speaker).
15
- * Swapped for the real CouncilMessageBubble when council_message arrives.
16
- *
17
- * Two variants:
18
- * - "participant" (default): left/right aligned per pair-side map, palette color border
19
- * - "leader": centered, leader-gray border, 40% width — matches the real leader bubble
21
+ * "Typing…" indicator for a speaker whose turn is in flight — a spinner + a
22
+ * role-colored line (`⠋ Role · composing… · 12s`) in the same linear stream as
23
+ * the real messages, WhatsApp-style. Swapped for the real CouncilMessageBubble
24
+ * when the council_message arrives. The spinner + ticking elapsed distinguish
25
+ * a live provider from a hung one (a static line looked identical at 2s and
26
+ * 2min live-verified 2026-07-06).
20
27
  */
21
- export declare function CouncilPlaceholderBubble({ role, side, terminalCols, color, theme, variant, }: CouncilPlaceholderBubbleProps): import("react").ReactNode;
28
+ export declare function CouncilPlaceholderBubble({ role, color, detail, theme: t }: CouncilPlaceholderBubbleProps): import("react").ReactNode;
@@ -1,37 +1,40 @@
1
- import { jsx as _jsx } from "@opentui/react/jsx-runtime";
2
- import { computeBubbleLayout } from "./bubble-layout.js";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "@opentui/react/jsx-runtime";
2
+ import { useEffect, useState } from "react";
3
3
  const FALLBACK_ROLE = "Speaker";
4
- const PLACEHOLDER_MAX_COLS = 40;
5
- export function buildPlaceholderLabel(role) {
4
+ const SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
5
+ export function buildPlaceholderLabel(role, elapsedSec) {
6
6
  const trimmed = role.trim();
7
7
  const display = trimmed.length > 0 ? trimmed : FALLBACK_ROLE;
8
- return `${display} · composing…`;
8
+ const elapsed = elapsedSec !== undefined && elapsedSec >= 1 ? ` · ${elapsedSec}s` : "";
9
+ return `${display} · composing…${elapsed}`;
9
10
  }
10
11
  /**
11
- * Thin placeholder bubble shown while the producer is generating a turn.
12
- * Rendered at turn-start (when council_status{state:"start"} arrives for this speaker).
13
- * Swapped for the real CouncilMessageBubble when council_message arrives.
14
- *
15
- * Two variants:
16
- * - "participant" (default): left/right aligned per pair-side map, palette color border
17
- * - "leader": centered, leader-gray border, 40% width — matches the real leader bubble
12
+ * "Typing…" indicator for a speaker whose turn is in flight — a spinner + a
13
+ * role-colored line (`⠋ Role · composing… · 12s`) in the same linear stream as
14
+ * the real messages, WhatsApp-style. Swapped for the real CouncilMessageBubble
15
+ * when the council_message arrives. The spinner + ticking elapsed distinguish
16
+ * a live provider from a hung one (a static line looked identical at 2s and
17
+ * 2min live-verified 2026-07-06).
18
18
  */
19
- export function CouncilPlaceholderBubble({ role, side, terminalCols, color, theme, variant = "participant", }) {
20
- const layout = computeBubbleLayout(terminalCols);
21
- const isLeader = variant === "leader";
22
- const width = layout.fallback
23
- ? terminalCols
24
- : isLeader
25
- ? layout.leaderCols
26
- : Math.min(layout.bubbleCols, PLACEHOLDER_MAX_COLS);
27
- const indent = layout.fallback
28
- ? 0
29
- : isLeader
30
- ? Math.max(0, Math.floor((terminalCols - width) / 2))
31
- : side === "right"
32
- ? layout.rightIndent
33
- : 0;
34
- const borderColor = isLeader ? theme.councilLeaderBorder : color;
35
- return (_jsx("box", { marginLeft: indent, marginBottom: 1, children: _jsx("box", { width: width, borderStyle: "single", borderColor: borderColor, paddingLeft: 1, paddingRight: 1, children: _jsx("text", { fg: theme.textMuted, children: buildPlaceholderLabel(role) }) }) }));
19
+ export function CouncilPlaceholderBubble({ role, color, detail, theme: t }) {
20
+ const [startedAt] = useState(() => Date.now());
21
+ const [frame, setFrame] = useState(0);
22
+ useEffect(() => {
23
+ // The 100ms spinner interval doubles as the elapsed re-render tick — no
24
+ // second timer needed. Braille frames match the timeline/pill spinner
25
+ // vocabulary rather than adding a third glyph set.
26
+ const id = setInterval(() => setFrame((n) => n + 1), 100);
27
+ return () => clearInterval(id);
28
+ }, []);
29
+ const elapsedSec = Math.floor((Date.now() - startedAt) / 1000);
30
+ // Split the engine-emitted roster ("Name — lens" per line) into muted preview
31
+ // rows. Trimmed + non-empty only so a trailing newline never renders a blank.
32
+ const detailLines = detail
33
+ ? detail
34
+ .split("\n")
35
+ .map((l) => l.trim())
36
+ .filter((l) => l.length > 0)
37
+ : [];
38
+ return (_jsxs("box", { marginBottom: 1, paddingLeft: 2, flexDirection: "column", children: [_jsxs("box", { children: [_jsx("text", { fg: t.accent, children: `${SPINNER_FRAMES[frame % SPINNER_FRAMES.length]} ` }), _jsx("text", { fg: color, children: buildPlaceholderLabel(role, elapsedSec) })] }), detailLines.map((line, i) => (_jsx("text", { fg: t.textMuted, children: ` ↳ ${line}` }, i)))] }));
36
39
  }
37
40
  //# sourceMappingURL=council-placeholder-bubble.js.map
@@ -1,14 +1,15 @@
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 PHASE_LABEL = {
4
- clarify: "Clarify",
5
- preflight: "Pre-flight",
6
- "plan-confirm": "Plan",
7
- "post-debate": "Post-Debate",
8
- "pil-interview": "Understanding",
9
- "pil-acceptance": "Confirm Intent",
10
- "tool-loop-cap": "Tool-loop guard",
11
- "safety-override": "Safety override",
4
+ clarify: "Quick question",
5
+ preflight: "Pre-flight check",
6
+ "plan-confirm": "Plan review",
7
+ "post-debate": "Debate wrap-up",
8
+ "pil-interview": "Understanding your idea",
9
+ "pil-acceptance": "Confirm the goal",
10
+ "tool-loop-cap": "Tool-loop checkpoint",
11
+ "safety-override": "Safety check",
12
+ "ask-user": "The agent is asking",
12
13
  };
13
14
  /**
14
15
  * AskUserQuestion-style card. Controlled component — parent owns the state
@@ -32,8 +33,8 @@ export function CouncilQuestionCard({ question, theme: t, state, freetextInputWi
32
33
  const cursor = selected ? "›" : " ";
33
34
  const numberColor = selected ? t.accent : t.textMuted;
34
35
  const labelColor = selected ? t.accent : t.text;
35
- return (_jsx(Semantic, { id: `askcard-option-${opt.value ?? i}`, role: "button", name: opt.label, selected: selected || undefined, children: _jsxs("box", { flexDirection: "column", children: [_jsxs("box", { flexDirection: "row", children: [_jsx("text", { fg: numberColor, children: `${cursor} ${i + 1}. ` }), _jsx("text", { fg: labelColor, children: opt.label }), isRecommended && _jsx("text", { fg: t.planOptionCheck, children: " (Recommended)" })] }), opt.description && (_jsx("box", { paddingLeft: 5, children: _jsx("text", { fg: t.textMuted, children: opt.description }) }))] }) }, i));
36
- }), freetext !== null && (_jsxs("box", { flexDirection: "column", paddingTop: 1, children: [_jsx("text", { fg: t.textMuted, children: "Type your answer · Enter to submit · Esc to go back" }), _jsx(Semantic, { id: "askcard-input", role: "textbox", value: freetext, children: _jsx("box", { paddingLeft: 1, paddingRight: 1, width: freetextInputWidth, children: _jsx("text", { fg: t.planInputText, bg: t.planInputBg, children: freetext.length > 0 ? freetext : " " }) }) })] })), _jsx("box", { paddingTop: 1, children: _jsx("text", { fg: t.textDim, children: "Enter to select · ↑/↓ to navigate · Esc to cancel" }) })] }) }));
36
+ return (_jsx(Semantic, { id: `askcard-option-${opt.value ?? i}`, role: "button", name: opt.label, selected: selected || undefined, children: _jsxs("box", { flexDirection: "column", children: [_jsxs("box", { flexDirection: "row", children: [_jsx("text", { fg: numberColor, children: `${cursor} ${i + 1}. ` }), _jsx("text", { fg: labelColor, children: opt.label }), isRecommended && _jsx("text", { fg: t.planOptionCheck, children: " (recommended)" })] }), opt.description && (_jsx("box", { paddingLeft: 5, children: _jsx("text", { fg: t.textMuted, children: opt.description }) }))] }) }, i));
37
+ }), freetext !== null && (_jsxs("box", { flexDirection: "column", paddingTop: 1, children: [_jsx("text", { fg: t.textMuted, children: "Type your answer · Enter to send · Esc to go back" }), _jsx(Semantic, { id: "askcard-input", role: "textbox", value: freetext, children: _jsx("box", { paddingLeft: 1, paddingRight: 1, width: freetextInputWidth, children: _jsx("text", { fg: t.planInputText, bg: t.planInputBg, children: freetext.length > 0 ? freetext : " " }) }) })] })), _jsx("box", { paddingTop: 1, children: _jsx("text", { fg: t.textDim, children: "↑/↓ to browse · Enter to select · Esc to cancel" }) })] }) }));
37
38
  }
38
39
  export function clampIndex(i, len) {
39
40
  if (len <= 0)
@@ -50,7 +51,7 @@ function legacyFallback(q) {
50
51
  .filter((s) => typeof s === "string" && s.trim().length > 0)
51
52
  .map((s) => ({ label: s.trim(), value: s.trim(), kind: "choice" }));
52
53
  if (choices.length === 0) {
53
- choices.push({ label: "Answer", value: "", kind: "freetext", description: "Nhập câu trả lời" });
54
+ choices.push({ label: "Answer", value: "", kind: "freetext", description: "Type your answer here" });
54
55
  }
55
56
  return choices;
56
57
  }
@@ -116,7 +117,7 @@ export function reduceCardKey(question, state, key) {
116
117
  // Sentinel-ish text: caller may special-case this prefix to pause
117
118
  // the council loop. For now we ship a clear default that records a
118
119
  // meaningful answer so the run keeps moving.
119
- text: "Let me discuss this further before answering.",
120
+ text: "I'd like to talk this through a bit more before answering.",
120
121
  kind: "chat",
121
122
  },
122
123
  },
@@ -0,0 +1,26 @@
1
+ import type { CouncilRoundRecord } from "../../types/index.js";
2
+ import type { Theme } from "../theme.js";
3
+ /**
4
+ * Selectable round list in the context rail (P2 / feature D). The rail keeps its
5
+ * GLOBAL overview; this list lets the user scope the MAIN transcript pane to a
6
+ * single round's debate turns without hiding the overview — reconciling the
7
+ * documented "never re-scope the rail" constraint (context-rail.tsx) with the
8
+ * "click a round → see its debate" request.
9
+ *
10
+ * Selection is driven by keyboard (Ctrl+←/→ when the composer is empty) and by
11
+ * mouse (OpenTUI onMouseDown per row). `null` = global view (all/live rounds).
12
+ */
13
+ /**
14
+ * Pure selection-cycle used by the keyboard handler. Stepping before the first
15
+ * round returns to the global view (null); stepping past the last clamps.
16
+ */
17
+ export declare function cycleRoundSelection(roundNumbers: number[], current: number | null, dir: 1 | -1): number | null;
18
+ export interface CouncilRailRoundsProps {
19
+ rounds: CouncilRoundRecord[];
20
+ /** Selected round number, or null for the global (all-rounds) view. */
21
+ selected: number | null;
22
+ onSelect: (round: number | null) => void;
23
+ width: number;
24
+ theme: Theme;
25
+ }
26
+ export declare function CouncilRailRounds({ rounds, selected, onSelect, width, theme }: CouncilRailRoundsProps): import("react").ReactNode;
@@ -0,0 +1,57 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "@opentui/react/jsx-runtime";
2
+ import { Semantic } from "@muonroi/agent-harness-opentui";
3
+ /**
4
+ * Selectable round list in the context rail (P2 / feature D). The rail keeps its
5
+ * GLOBAL overview; this list lets the user scope the MAIN transcript pane to a
6
+ * single round's debate turns without hiding the overview — reconciling the
7
+ * documented "never re-scope the rail" constraint (context-rail.tsx) with the
8
+ * "click a round → see its debate" request.
9
+ *
10
+ * Selection is driven by keyboard (Ctrl+←/→ when the composer is empty) and by
11
+ * mouse (OpenTUI onMouseDown per row). `null` = global view (all/live rounds).
12
+ */
13
+ /**
14
+ * Pure selection-cycle used by the keyboard handler. Stepping before the first
15
+ * round returns to the global view (null); stepping past the last clamps.
16
+ */
17
+ export function cycleRoundSelection(roundNumbers, current, dir) {
18
+ if (roundNumbers.length === 0)
19
+ return null;
20
+ if (current === null)
21
+ return dir === 1 ? roundNumbers[0] : null;
22
+ const i = roundNumbers.indexOf(current);
23
+ if (i === -1)
24
+ return dir === 1 ? roundNumbers[0] : null;
25
+ const j = i + dir;
26
+ if (j < 0)
27
+ return null; // step before first → global
28
+ if (j >= roundNumbers.length)
29
+ return roundNumbers[roundNumbers.length - 1]; // clamp at last
30
+ return roundNumbers[j];
31
+ }
32
+ function roundLabel(rec, maxWidth) {
33
+ const base = `Round ${rec.round}`;
34
+ if (!rec.topic)
35
+ return base;
36
+ const budget = Math.max(6, maxWidth - base.length - 3);
37
+ const topic = rec.topic.length > budget ? `${rec.topic.slice(0, budget - 1)}…` : rec.topic;
38
+ return `${base}: ${topic}`;
39
+ }
40
+ export function CouncilRailRounds({ rounds, selected, onSelect, width, theme }) {
41
+ if (rounds.length === 0)
42
+ return null;
43
+ const rows = [
44
+ { key: "all", label: "All rounds", round: null, running: false },
45
+ ...rounds.map((rec) => ({
46
+ key: `round-${rec.round}`,
47
+ label: roundLabel(rec, width),
48
+ round: rec.round,
49
+ running: rec.state === "running",
50
+ })),
51
+ ];
52
+ return (_jsx(Semantic, { id: "rail-rounds", role: "listbox", name: "Debate rounds", props: { selected: selected ?? -1, count: rounds.length }, children: _jsxs("box", { flexDirection: "column", flexShrink: 0, marginTop: 1, children: [_jsx("text", { fg: theme.textMuted, attributes: 1, children: "Rounds" }), _jsx("text", { fg: theme.textDim, children: "ctrl+\u2190/\u2192 scope" }), rows.map((row) => {
53
+ const isSel = row.round === selected || (row.round === null && selected === null);
54
+ return (_jsx(Semantic, { id: row.round === null ? "rail-round-all" : `rail-round-${row.round}`, role: "listitem", name: row.label, selected: isSel || undefined, children: _jsxs("box", { flexDirection: "row", onMouseDown: () => onSelect(row.round), children: [_jsx("text", { fg: isSel ? theme.accent : theme.textMuted, children: isSel ? "› " : " " }), _jsx("text", { fg: isSel ? theme.accent : theme.text, attributes: isSel ? 1 : 0, children: row.label }), row.running ? _jsx("text", { fg: theme.accent, children: " ·live" }) : null] }) }, row.key));
55
+ })] }) }));
56
+ }
57
+ //# sourceMappingURL=council-rail-rounds.js.map
@@ -0,0 +1,38 @@
1
+ import type { ReactNode } from "react";
2
+ import type { CouncilRoundRecord } from "../../types/index.js";
3
+ import type { Theme } from "../theme.js";
4
+ export interface CouncilRoundGroupProps {
5
+ record: CouncilRoundRecord;
6
+ /**
7
+ * Turn nodes for this round. Rendered while the round is running (live stream)
8
+ * OR when the round is selected in the rail (inspect a finished round's debate).
9
+ */
10
+ children?: ReactNode;
11
+ /** True when this round is the one selected in the rail — highlight + expand. */
12
+ selected?: boolean;
13
+ theme: Theme;
14
+ }
15
+ /**
16
+ * F2 — header that frames a DONE round's persisted directive as its *opening*
17
+ * brief. Without it the directive's "Unmet (n/m)" line reads as current state and
18
+ * contradicts the "✓ Outcome: m/m met" line below on a converged round. When the
19
+ * round met everything, say so ("resolved below") so brief + outcome cohere.
20
+ */
21
+ export declare function openingBriefHeader(allMet: boolean): string;
22
+ /**
23
+ * One debate round in the round-grouped transcript (P6). The running round auto-
24
+ * opens and streams its turns live; a done round collapses to an expanded-inline
25
+ * summary — input (topic + members), outcome (criteria met/total), and the
26
+ * leader's decision — so a finished round always shows what it achieved instead
27
+ * of a bare "done". When `selected` (P2/D — the round chosen in the rail), a
28
+ * finished round also expands its debate turns and highlights.
29
+ */
30
+ export declare function CouncilRoundGroup({ record, children, selected, theme }: CouncilRoundGroupProps): ReactNode;
31
+ export interface CouncilRoundsOverviewProps {
32
+ rounds: CouncilRoundRecord[];
33
+ theme: Theme;
34
+ /** Optional session id appended to the overview so it stays visible above the debate transcript. */
35
+ sessionId?: string;
36
+ }
37
+ /** One-line overview above the round groups: totals + emergent count + session id. */
38
+ export declare function CouncilRoundsOverview({ rounds, theme, sessionId }: CouncilRoundsOverviewProps): ReactNode;
@@ -0,0 +1,88 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "@opentui/react/jsx-runtime";
2
+ import { Semantic } from "@muonroi/agent-harness-opentui";
3
+ const DECISION_LABEL = {
4
+ continue: "continue",
5
+ stop: "sufficient — stop",
6
+ extend: "extend (emergent round)",
7
+ aborted: "aborted",
8
+ "circuit-break": "circuit breaker",
9
+ "eval-unavailable": "evaluation unavailable",
10
+ };
11
+ /**
12
+ * F2 — header that frames a DONE round's persisted directive as its *opening*
13
+ * brief. Without it the directive's "Unmet (n/m)" line reads as current state and
14
+ * contradicts the "✓ Outcome: m/m met" line below on a converged round. When the
15
+ * round met everything, say so ("resolved below") so brief + outcome cohere.
16
+ */
17
+ export function openingBriefHeader(allMet) {
18
+ return `Opening brief${allMet ? " — resolved below" : ""}:`;
19
+ }
20
+ /** Color the leader's decision by whether the round landed well. */
21
+ function decisionColor(decision, theme) {
22
+ switch (decision) {
23
+ case "stop":
24
+ return theme.diffAddedFg; // sufficient — a clean landing
25
+ case "aborted":
26
+ case "circuit-break":
27
+ case "eval-unavailable":
28
+ return theme.diffRemovedFg; // ended abnormally
29
+ default:
30
+ return theme.accent; // continue / extend — still in progress
31
+ }
32
+ }
33
+ /**
34
+ * One debate round in the round-grouped transcript (P6). The running round auto-
35
+ * opens and streams its turns live; a done round collapses to an expanded-inline
36
+ * summary — input (topic + members), outcome (criteria met/total), and the
37
+ * leader's decision — so a finished round always shows what it achieved instead
38
+ * of a bare "done". When `selected` (P2/D — the round chosen in the rail), a
39
+ * finished round also expands its debate turns and highlights.
40
+ */
41
+ export function CouncilRoundGroup({ record, children, selected = false, theme }) {
42
+ const running = record.state === "running";
43
+ const headParts = [`Round ${record.round}`];
44
+ if (record.topic)
45
+ headParts.push(record.topic);
46
+ if (record.emergent)
47
+ headParts.push("(emergent)");
48
+ // Outcome verdict: how many acceptance criteria the round met. Colored + marked
49
+ // so a scan of the transcript shows at a glance which rounds landed cleanly.
50
+ const total = record.criteriaTotal ?? -1;
51
+ const met = record.criteriaMet ?? 0;
52
+ const hasCriteria = total > 0;
53
+ const allMet = hasCriteria && met >= total;
54
+ const noneMet = hasCriteria && met === 0;
55
+ const outcomeColor = allMet ? theme.diffAddedFg : noneMet ? theme.diffRemovedFg : theme.mdItalic;
56
+ const outcomeMark = allMet ? "✓" : noneMet ? "✗" : "◐";
57
+ return (_jsx(Semantic, { id: `council-round-${record.round}`, role: "region", name: `Round ${record.round}`, props: {
58
+ state: record.state,
59
+ emergent: record.emergent,
60
+ selected,
61
+ criteriaMet: record.criteriaMet ?? -1,
62
+ criteriaTotal: record.criteriaTotal ?? -1,
63
+ decision: record.leaderDecision ?? "",
64
+ directive: record.directive ?? "",
65
+ }, children: _jsxs("box", { flexDirection: "column", marginBottom: 1, border: ["left"], borderColor: running || selected ? theme.accent : theme.councilLeaderBorder, paddingLeft: 2, children: [_jsx("text", { fg: running || selected ? theme.accent : theme.textMuted, attributes: 1, children: `${running ? "> " : selected ? "› " : "✓ "}${headParts.join(" · ")}` }), record.participants.length > 0 && (_jsx("text", { fg: theme.textMuted, children: `${record.participants.length} members: ${record.participants.join(", ")}` })), running ? (
66
+ // Running round streams its turns live.
67
+ _jsx("box", { flexDirection: "column", marginTop: 1, children: children })) : (
68
+ // Done round: expanded debate turns (when selected in the rail) followed
69
+ // by the outcome verdict + leader decision summary.
70
+ _jsxs("box", { flexDirection: "column", children: [children ? (_jsx("box", { flexDirection: "column", marginTop: 1, marginBottom: 1, children: children })) : null, record.directive && _jsx("text", { fg: theme.textMuted, children: openingBriefHeader(allMet) }), record.directive &&
71
+ record.directive.split("\n").map((line, i) => (_jsx("text", { fg: theme.accent, attributes: i === 0 ? 1 : 0, children: i === 0 ? `▶ ${line}` : ` ${line}` }, `dir-${i}`))), hasCriteria && (_jsx("text", { fg: outcomeColor, attributes: 1, children: `${outcomeMark} Outcome: ${met}/${total} criteria met` })), record.leaderDecision && (_jsx("text", { fg: decisionColor(record.leaderDecision, theme), attributes: 1, children: `Decision: ${DECISION_LABEL[record.leaderDecision]}` })), record.leaderReason && _jsx("text", { fg: theme.textMuted, children: record.leaderReason }), record.nextRoundFocus && _jsx("text", { fg: theme.textMuted, children: `Next focus: ${record.nextRoundFocus}` })] }))] }) }));
72
+ }
73
+ /** One-line overview above the round groups: totals + emergent count + session id. */
74
+ export function CouncilRoundsOverview({ rounds, theme, sessionId }) {
75
+ const total = rounds.length;
76
+ const emergent = rounds.filter((r) => r.emergent).length;
77
+ const members = new Set();
78
+ for (const r of rounds)
79
+ for (const p of r.participants)
80
+ members.add(p);
81
+ const parts = [`Debate: ${total} round${total === 1 ? "" : "s"}`, `${members.size} members`];
82
+ if (emergent > 0)
83
+ parts.push(`${emergent} emergent`);
84
+ if (sessionId)
85
+ parts.push(`session ${sessionId}`);
86
+ return (_jsx(Semantic, { id: "council-rounds-overview", role: "region", props: { total, emergent, members: members.size, sessionId }, children: _jsx("text", { fg: theme.textMuted, attributes: 1, children: parts.join(" · ") }) }));
87
+ }
88
+ //# sourceMappingURL=council-round-group.js.map
@@ -3,8 +3,10 @@ import type { Theme } from "../theme.js";
3
3
  export interface CouncilStatusListProps {
4
4
  statuses: CouncilStatusData[];
5
5
  theme: Theme;
6
+ /** Optional session id rendered as a pinned header so the user can correlate a council run with its session. */
7
+ sessionId?: string;
6
8
  }
7
- export declare function CouncilStatusList({ statuses, theme: t }: CouncilStatusListProps): import("react").ReactNode;
9
+ export declare function CouncilStatusList({ statuses, theme: t, sessionId }: CouncilStatusListProps): import("react").ReactNode;
8
10
  /**
9
11
  * Reducer-friendly upsert: returns the next status array given an incoming
10
12
  * status chunk. Done statuses are kept until DONE_HOLD_MS expires (caller
@@ -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. */