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
@@ -4,7 +4,11 @@ import type { RoleStyle } from "./role-palette.js";
4
4
  export interface CouncilMessageBubbleProps {
5
5
  msg: CouncilMessage;
6
6
  terminalCols: number;
7
- /** "left" | "right" — computed by usePairSideMap in container, NOT on the message */
7
+ /**
8
+ * Legacy left/right pairing hint. The debate now renders as a single linear
9
+ * group-chat stream (WhatsApp-style), so this is ignored — kept only for
10
+ * call-site/back-compat. Do not reintroduce side-based alignment.
11
+ */
8
12
  side: "left" | "right";
9
13
  /** Stable role → style resolver from useRolePalette */
10
14
  resolveStyle: (role: string) => RoleStyle;
@@ -17,4 +21,13 @@ export interface CouncilMessageBubbleProps {
17
21
  export declare function buildFooter(msg: CouncilMessage): string;
18
22
  export declare function buildHeader(msg: CouncilMessage, style: RoleStyle): string;
19
23
  export declare function buildQuoteLine(partnerLastText: string, partnerRole: string): string;
20
- export declare function CouncilMessageBubble({ msg, terminalCols, side, resolveStyle, partnerLastText, partnerRole, theme, }: CouncilMessageBubbleProps): import("react").ReactNode;
24
+ /**
25
+ * Linear group-chat message row. Every speaker renders the same way — a
26
+ * role-colored left bar + a bold role-colored header (`● Role · model`), the
27
+ * body, and a muted footer — appended in chronological order like a WhatsApp
28
+ * group chat. No left/right/bubble alignment: the reader follows one downward
29
+ * stream instead of a two-column ping-pong. The role color (from the shared
30
+ * palette) is the sole visual identity per speaker, so a long debate stays
31
+ * easy to track.
32
+ */
33
+ export declare function CouncilMessageBubble({ msg, terminalCols, resolveStyle, partnerLastText, partnerRole, theme, }: CouncilMessageBubbleProps): import("react").ReactNode;
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "@opentui/react/jsx-runtime";
2
- import { computeBubbleLayout } from "./bubble-layout.js";
2
+ import { capBubbleBody } from "./bubble-body-guard.js";
3
3
  import { truncateCodeBlocks } from "./code-block-truncate.js";
4
4
  const MAX_QUOTE_CHARS = 80;
5
5
  export function buildFooter(msg) {
@@ -25,23 +25,24 @@ export function buildQuoteLine(partnerLastText, partnerRole) {
25
25
  const ellipsis = flat.length > MAX_QUOTE_CHARS ? "…" : "";
26
26
  return `↪ ${partnerRole}: "${excerpt}${ellipsis}"`;
27
27
  }
28
- function FlatDebateBubble({ msg, style, theme }) {
29
- const header = buildHeader(msg, style);
30
- const footer = buildFooter(msg);
31
- return (_jsxs("box", { flexDirection: "column", marginBottom: 1, children: [_jsx("text", { fg: style.color, attributes: 1, children: header }), _jsx("text", { fg: theme.text, children: msg.text.trim() }), _jsx("text", { fg: theme.textMuted, children: footer })] }));
32
- }
33
- export function CouncilMessageBubble({ msg, terminalCols, side, resolveStyle, partnerLastText, partnerRole, theme, }) {
34
- const layout = computeBubbleLayout(terminalCols);
28
+ /**
29
+ * Linear group-chat message row. Every speaker renders the same way — a
30
+ * role-colored left bar + a bold role-colored header (`● Role · model`), the
31
+ * body, and a muted footer appended in chronological order like a WhatsApp
32
+ * group chat. No left/right/bubble alignment: the reader follows one downward
33
+ * stream instead of a two-column ping-pong. The role color (from the shared
34
+ * palette) is the sole visual identity per speaker, so a long debate stays
35
+ * easy to track.
36
+ */
37
+ export function CouncilMessageBubble({ msg, terminalCols, resolveStyle, partnerLastText, partnerRole, theme, }) {
35
38
  const style = resolveStyle(msg.speaker.role);
36
- if (layout.fallback) {
37
- return _jsx(FlatDebateBubble, { msg: msg, style: style, theme: theme });
38
- }
39
- const isRight = side === "right";
40
- const indent = isRight ? layout.rightIndent : 0;
41
39
  const header = buildHeader(msg, style);
42
40
  const footer = buildFooter(msg);
43
- const bodyText = truncateCodeBlocks(msg.text.trim());
41
+ // Guard the terminal wrapper against a pathological body (mega single-line /
42
+ // 100KB+ blob) that hard-freezes the shared render+loop thread. See
43
+ // bubble-body-guard.ts for the live root-cause trace.
44
+ const bodyText = capBubbleBody(truncateCodeBlocks(msg.text.trim()), terminalCols);
44
45
  const roundLabel = msg.round !== undefined ? `Round ${msg.round} · ` : "";
45
- return (_jsxs("box", { flexDirection: "column", marginBottom: 1, children: [partnerLastText && partnerRole && (_jsx("box", { marginLeft: indent, children: _jsx("text", { fg: theme.textMuted, children: buildQuoteLine(partnerLastText, partnerRole) }) })), _jsxs("box", { marginLeft: indent, width: layout.bubbleCols, borderStyle: "single", borderColor: style.color, flexDirection: "column", paddingLeft: 1, paddingRight: 1, children: [_jsx("text", { fg: style.color, attributes: 1, children: header }), _jsx("text", { fg: theme.text, children: bodyText }), _jsx("text", { fg: theme.textMuted, children: `${roundLabel}${footer}` })] })] }));
46
+ return (_jsxs("box", { flexDirection: "column", marginBottom: 1, border: ["left"], borderColor: style.color, paddingLeft: 2, children: [partnerLastText && partnerRole && (_jsx("text", { fg: theme.textMuted, children: buildQuoteLine(partnerLastText, partnerRole) })), _jsx("text", { fg: style.color, attributes: 1, children: header }), _jsx("text", { fg: theme.text, children: bodyText }), _jsx("text", { fg: theme.textMuted, children: `${roundLabel}${footer}` })] }));
46
47
  }
47
48
  //# sourceMappingURL=council-message-bubble.js.map
@@ -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
@@ -36,23 +36,57 @@ function useHeartbeat(tickMs = 1000) {
36
36
  }, [tickMs]);
37
37
  return Date.now();
38
38
  }
39
- export function CouncilPhaseTimeline({ phases, theme: t }) {
39
+ export function CouncilPhaseTimeline({ phases, theme: t, expanded = false }) {
40
40
  const now = useHeartbeat(1000);
41
- if (phases.length === 0)
41
+ // Collapse per-round brackets: the "Clarification round N" / "Round N" phases
42
+ // duplicate the live status line ("Generating clarification questions (round
43
+ // N)"), stacking 3-4 near-identical spinner rows at council start. Keep only
44
+ // the coarse lifecycle phases here; round detail already rides on the status
45
+ // line label. Errors are always shown so a failed round stays visible.
46
+ const visible = phases.filter((p) => !((p.kind === "clarification_round" || p.kind === "round") && p.state !== "error"));
47
+ if (visible.length === 0)
42
48
  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
- }) }));
49
+ // Aggressive collapse (default): show only the live (active/error) phases as
50
+ // full rows; fold every completed phase into one "N steps done" summary line.
51
+ // Ctrl+O (expanded=true) restores the full done-trail. Errors are never
52
+ // folded a failed phase always stays visible.
53
+ const live = visible.filter((p) => p.state === "active" || p.state === "error");
54
+ const doneCount = visible.length - live.length;
55
+ const rows = expanded ? visible : live;
56
+ // Split off error phases so identical provider failures can be folded into one
57
+ // row instead of N repeated blocks. A flaky proxy (Console Go glm/kimi) makes
58
+ // clarify + spec-infer + eval all fail with the SAME message — rendered raw
59
+ // that spammed the (narrow) context rail. Non-error rows keep their order.
60
+ const nonErrorRows = rows.filter((p) => p.state !== "error");
61
+ const errorRows = rows.filter((p) => p.state === "error");
62
+ const errorGroups = [];
63
+ for (const p of errorRows) {
64
+ const message = p.errorMessage ?? "(failed)";
65
+ const existing = errorGroups.find((g) => g.message === message);
66
+ if (existing)
67
+ existing.phases.push(p);
68
+ else
69
+ errorGroups.push({ message, phases: [p] });
70
+ }
71
+ const renderRow = (p) => {
72
+ const isDone = p.state === "done";
73
+ const marker = isDone ? "✓" : _jsx(Spinner, {});
74
+ const markerColor = isDone ? t.planOptionCheck : t.accent;
75
+ const labelColor = isDone ? t.textMuted : t.text;
76
+ const liveElapsedMs = p.state === "active" && typeof p.startedAt === "number" ? Math.max(0, now - p.startedAt) : p.elapsedMs;
77
+ const elapsed = formatElapsed(liveElapsedMs);
78
+ const meta = elapsed ? ` (${elapsed})` : isDone ? "" : " …";
79
+ 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));
80
+ };
81
+ 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) => {
82
+ // One header per unique error; when >1 phase shares it, name the count
83
+ // and list the affected steps compactly instead of repeating the message.
84
+ const first = g.phases[0];
85
+ const label = g.phases.length === 1
86
+ ? first.label
87
+ : `${g.phases.length} steps failed: ${g.phases.map((p) => p.label).join(", ")}`;
88
+ 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}`));
89
+ })] }));
56
90
  }
57
91
  function truncate(s, max) {
58
92
  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,14 @@
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
12
  };
13
13
  /**
14
14
  * AskUserQuestion-style card. Controlled component — parent owns the state
@@ -32,8 +32,8 @@ export function CouncilQuestionCard({ question, theme: t, state, freetextInputWi
32
32
  const cursor = selected ? "›" : " ";
33
33
  const numberColor = selected ? t.accent : t.textMuted;
34
34
  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" }) })] }) }));
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 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
37
  }
38
38
  export function clampIndex(i, len) {
39
39
  if (len <= 0)
@@ -50,7 +50,7 @@ function legacyFallback(q) {
50
50
  .filter((s) => typeof s === "string" && s.trim().length > 0)
51
51
  .map((s) => ({ label: s.trim(), value: s.trim(), kind: "choice" }));
52
52
  if (choices.length === 0) {
53
- choices.push({ label: "Answer", value: "", kind: "freetext", description: "Nhập câu trả lời" });
53
+ choices.push({ label: "Answer", value: "", kind: "freetext", description: "Type your answer here" });
54
54
  }
55
55
  return choices;
56
56
  }
@@ -116,7 +116,7 @@ export function reduceCardKey(question, state, key) {
116
116
  // Sentinel-ish text: caller may special-case this prefix to pause
117
117
  // the council loop. For now we ship a clear default that records a
118
118
  // meaningful answer so the run keeps moving.
119
- text: "Let me discuss this further before answering.",
119
+ text: "I'd like to talk this through a bit more before answering.",
120
120
  kind: "chat",
121
121
  },
122
122
  },
@@ -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