muonroi-cli 1.7.1 → 1.8.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 (328) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +122 -122
  3. package/dist/packages/agent-harness-core/src/event-filter.js +1 -0
  4. package/dist/packages/agent-harness-core/src/event-redact.js +8 -0
  5. package/dist/packages/agent-harness-core/src/mcp-server.d.ts +16 -0
  6. package/dist/packages/agent-harness-core/src/mcp-server.js +88 -10
  7. package/dist/packages/agent-harness-core/src/protocol.d.ts +15 -0
  8. package/dist/packages/agent-harness-core/src/selector.js +2 -2
  9. package/dist/packages/agent-harness-core/src/transports/sidechannel.js +1 -1
  10. package/dist/packages/agent-harness-opentui/src/agent-mode.js +1 -1
  11. package/dist/packages/agent-harness-opentui/src/install.js +1 -1
  12. package/dist/src/__test-stubs__/vitest-setup.js +4 -0
  13. package/dist/src/agent-harness/__tests__/mock-model.spec.js +54 -0
  14. package/dist/src/agent-harness/mock-model.d.ts +1 -1
  15. package/dist/src/agent-harness/mock-model.js +32 -3
  16. package/dist/src/agent-harness/test-spawn.js +3 -1
  17. package/dist/src/chat/chat-keychain.js +9 -1
  18. package/dist/src/cli/cost-forensics.js +12 -12
  19. package/dist/src/cli/keys.d.ts +2 -2
  20. package/dist/src/cli/keys.js +107 -28
  21. package/dist/src/cli/usage-report.js +2 -2
  22. package/dist/src/council/__tests__/abort-threading.test.d.ts +1 -0
  23. package/dist/src/council/__tests__/abort-threading.test.js +193 -0
  24. package/dist/src/council/__tests__/clarification-prompt.test.js +21 -1
  25. package/dist/src/council/__tests__/council-turn-length-emit.test.d.ts +1 -0
  26. package/dist/src/council/__tests__/council-turn-length-emit.test.js +130 -0
  27. package/dist/src/council/__tests__/debate-round-budget.test.d.ts +1 -0
  28. package/dist/src/council/__tests__/debate-round-budget.test.js +45 -0
  29. package/dist/src/council/__tests__/evaluator-metrics.test.js +3 -0
  30. package/dist/src/council/__tests__/post-debate-recommendation.test.d.ts +1 -0
  31. package/dist/src/council/__tests__/post-debate-recommendation.test.js +58 -0
  32. package/dist/src/council/__tests__/research-tools.test.js +4 -0
  33. package/dist/src/council/__tests__/round-tools.test.js +6 -0
  34. package/dist/src/council/clarifier-question-cap.test.d.ts +12 -0
  35. package/dist/src/council/clarifier-question-cap.test.js +73 -0
  36. package/dist/src/council/clarifier.d.ts +29 -1
  37. package/dist/src/council/clarifier.js +64 -1
  38. package/dist/src/council/debate-planner.d.ts +2 -1
  39. package/dist/src/council/debate-planner.js +5 -3
  40. package/dist/src/council/debate.d.ts +9 -0
  41. package/dist/src/council/debate.js +88 -8
  42. package/dist/src/council/index.d.ts +46 -1
  43. package/dist/src/council/index.js +125 -19
  44. package/dist/src/council/llm.js +41 -13
  45. package/dist/src/council/prompts.js +56 -21
  46. package/dist/src/council/types.d.ts +14 -1
  47. package/dist/src/ee/__tests__/export-transcripts.test.js +1 -1
  48. package/dist/src/ee/bridge.d.ts +3 -1
  49. package/dist/src/ee/bridge.js +9 -3
  50. package/dist/src/ee/bridge.test.js +1 -2
  51. package/dist/src/ee/client.js +15 -2
  52. package/dist/src/ee/recall-ledger.d.ts +20 -0
  53. package/dist/src/ee/recall-ledger.js +40 -1
  54. package/dist/src/ee/search.d.ts +25 -0
  55. package/dist/src/ee/search.js +73 -0
  56. package/dist/src/ee/types.d.ts +15 -1
  57. package/dist/src/ee/who-am-i.d.ts +41 -0
  58. package/dist/src/ee/who-am-i.js +143 -0
  59. package/dist/src/ee/who-am-i.test.d.ts +1 -0
  60. package/dist/src/ee/who-am-i.test.js +101 -0
  61. package/dist/src/generated/version.d.ts +1 -1
  62. package/dist/src/generated/version.js +1 -1
  63. package/dist/src/headless/council-answers.js +1 -0
  64. package/dist/src/headless/output.d.ts +30 -1
  65. package/dist/src/headless/output.js +180 -0
  66. package/dist/src/headless/output.test.js +61 -1
  67. package/dist/src/hooks/index.js +48 -2
  68. package/dist/src/index.d.ts +2 -0
  69. package/dist/src/index.js +143 -104
  70. package/dist/src/lsp/client-normalize.test.d.ts +12 -0
  71. package/dist/src/lsp/client-normalize.test.js +34 -0
  72. package/dist/src/lsp/client.d.ts +1 -0
  73. package/dist/src/lsp/client.js +10 -2
  74. package/dist/src/lsp/manager.d.ts +1 -1
  75. package/dist/src/lsp/manager.js +2 -2
  76. package/dist/src/lsp/manager.test.js +2 -2
  77. package/dist/src/lsp/runtime.d.ts +1 -1
  78. package/dist/src/lsp/runtime.js +2 -2
  79. package/dist/src/maintain/__tests__/gh-create-pr.test.js +7 -3
  80. package/dist/src/maintain/__tests__/pr-builder.test.js +7 -3
  81. package/dist/src/mcp/__tests__/client-pool.spec.js +3 -3
  82. package/dist/src/mcp/__tests__/ee-tools.test.js +42 -14
  83. package/dist/src/mcp/__tests__/forensics-tools.test.js +3 -3
  84. package/dist/src/mcp/__tests__/lsp-tools.test.js +4 -4
  85. package/dist/src/mcp/client-pool.d.ts +1 -1
  86. package/dist/src/mcp/client-pool.js +25 -9
  87. package/dist/src/mcp/ee-tools.d.ts +10 -0
  88. package/dist/src/mcp/ee-tools.js +34 -0
  89. package/dist/src/mcp/oauth-callback.js +2 -2
  90. package/dist/src/mcp/parse-headers.test.js +14 -14
  91. package/dist/src/mcp/self-verify-jobs.js +1 -3
  92. package/dist/src/mcp/setup-guide-text.js +74 -74
  93. package/dist/src/mcp/smoke.test.js +43 -43
  94. package/dist/src/models/catalog-gemini.test.js +12 -10
  95. package/dist/src/models/catalog.json +106 -38
  96. package/dist/src/ops/doctor.js +7 -7
  97. package/dist/src/orchestrator/__tests__/auto-commit.test.d.ts +1 -0
  98. package/dist/src/orchestrator/__tests__/auto-commit.test.js +142 -0
  99. package/dist/src/orchestrator/__tests__/batch-turn-runner.test.js +2 -0
  100. package/dist/src/orchestrator/__tests__/delegations.test.d.ts +1 -0
  101. package/dist/src/orchestrator/__tests__/delegations.test.js +96 -0
  102. package/dist/src/orchestrator/__tests__/message-processor.test.js +1 -0
  103. package/dist/src/orchestrator/__tests__/stream-runner.test.js +29 -0
  104. package/dist/src/orchestrator/agent.test.js +5 -2
  105. package/dist/src/orchestrator/auto-commit.d.ts +110 -0
  106. package/dist/src/orchestrator/auto-commit.js +364 -0
  107. package/dist/src/orchestrator/batch-turn-runner.d.ts +3 -2
  108. package/dist/src/orchestrator/batch-turn-runner.js +2 -1
  109. package/dist/src/orchestrator/compaction-proposer-prompt.d.ts +8 -0
  110. package/dist/src/orchestrator/compaction-proposer-prompt.js +43 -0
  111. package/dist/src/orchestrator/compaction.d.ts +22 -0
  112. package/dist/src/orchestrator/compaction.js +42 -0
  113. package/dist/src/orchestrator/delegations.d.ts +1 -0
  114. package/dist/src/orchestrator/delegations.js +41 -0
  115. package/dist/src/orchestrator/error-utils.js +27 -2
  116. package/dist/src/orchestrator/message-processor.d.ts +21 -5
  117. package/dist/src/orchestrator/message-processor.js +219 -2573
  118. package/dist/src/orchestrator/orchestrator.d.ts +8 -2
  119. package/dist/src/orchestrator/orchestrator.js +187 -34
  120. package/dist/src/orchestrator/preprocessor.d.ts +13 -0
  121. package/dist/src/orchestrator/preprocessor.js +145 -0
  122. package/dist/src/orchestrator/prompts.d.ts +8 -0
  123. package/dist/src/orchestrator/prompts.js +76 -60
  124. package/dist/src/orchestrator/safety-askcard.d.ts +66 -0
  125. package/dist/src/orchestrator/safety-askcard.js +74 -0
  126. package/dist/src/orchestrator/sandbox.test.js +8 -5
  127. package/dist/src/orchestrator/scope-ceiling.js +1 -1
  128. package/dist/src/orchestrator/stall-watchdog.d.ts +47 -0
  129. package/dist/src/orchestrator/stall-watchdog.js +23 -0
  130. package/dist/src/orchestrator/stall-watchdog.test.js +36 -1
  131. package/dist/src/orchestrator/stream-runner.js +52 -30
  132. package/dist/src/orchestrator/sub-agent-model-tier.js +1 -1
  133. package/dist/src/orchestrator/subagent-compactor.d.ts +14 -0
  134. package/dist/src/orchestrator/subagent-compactor.js +83 -10
  135. package/dist/src/orchestrator/subagent-compactor.spec.js +132 -0
  136. package/dist/src/orchestrator/text-tool-call-detector.d.ts +9 -5
  137. package/dist/src/orchestrator/text-tool-call-detector.js +16 -12
  138. package/dist/src/orchestrator/text-tool-call-detector.test.js +66 -8
  139. package/dist/src/orchestrator/tool-args-repair.js +1 -1
  140. package/dist/src/orchestrator/tool-engine.d.ts +151 -0
  141. package/dist/src/orchestrator/tool-engine.js +3079 -0
  142. package/dist/src/orchestrator/turn-runner-deps.d.ts +1 -0
  143. package/dist/src/pil/__tests__/discovery-types.test.js +15 -4
  144. package/dist/src/pil/__tests__/discovery.test.js +89 -145
  145. package/dist/src/pil/__tests__/dual-run.test.js +2 -0
  146. package/dist/src/pil/__tests__/layer1-intent-trace.test.js +3 -0
  147. package/dist/src/pil/__tests__/layer1-intent.test.js +3 -0
  148. package/dist/src/pil/__tests__/layer16-clarity.test.js +35 -32
  149. package/dist/src/pil/__tests__/layer18-acceptance.test.js +1 -78
  150. package/dist/src/pil/__tests__/layer2_5-ponytail.test.d.ts +1 -0
  151. package/dist/src/pil/__tests__/layer2_5-ponytail.test.js +53 -0
  152. package/dist/src/pil/__tests__/layer3-ee-injection.test.js +67 -4
  153. package/dist/src/pil/__tests__/layer3-injected-chunk.test.js +14 -2
  154. package/dist/src/pil/__tests__/layer4-gsd.test.js +30 -0
  155. package/dist/src/pil/__tests__/layer5-context.test.js +39 -1
  156. package/dist/src/pil/__tests__/layer6-output.test.js +30 -2
  157. package/dist/src/pil/__tests__/orchestrator-integration.test.js +3 -1
  158. package/dist/src/pil/__tests__/pipeline.test.js +11 -8
  159. package/dist/src/pil/__tests__/response-tools.test.js +52 -0
  160. package/dist/src/pil/agent-operating-contract.d.ts +1 -1
  161. package/dist/src/pil/agent-operating-contract.js +5 -3
  162. package/dist/src/pil/agent-operating-contract.test.js +2 -2
  163. package/dist/src/pil/cheap-model-playbook.d.ts +1 -1
  164. package/dist/src/pil/cheap-model-playbook.js +1 -1
  165. package/dist/src/pil/cheap-model-workbooks.d.ts +1 -1
  166. package/dist/src/pil/cheap-model-workbooks.js +1 -1
  167. package/dist/src/pil/config.d.ts +20 -0
  168. package/dist/src/pil/config.js +35 -0
  169. package/dist/src/pil/discovery-types.d.ts +32 -9
  170. package/dist/src/pil/discovery.d.ts +3 -3
  171. package/dist/src/pil/discovery.js +138 -244
  172. package/dist/src/pil/index.d.ts +1 -1
  173. package/dist/src/pil/index.js +1 -1
  174. package/dist/src/pil/layer1-intent.d.ts +9 -1
  175. package/dist/src/pil/layer1-intent.js +99 -44
  176. package/dist/src/pil/layer1-intent.test.js +112 -2
  177. package/dist/src/pil/layer15-context-scan.js +1 -1
  178. package/dist/src/pil/layer16-clarity.d.ts +21 -25
  179. package/dist/src/pil/layer16-clarity.js +52 -88
  180. package/dist/src/pil/layer18-acceptance.d.ts +13 -3
  181. package/dist/src/pil/layer18-acceptance.js +1 -75
  182. package/dist/src/pil/layer2-personality.js +7 -8
  183. package/dist/src/pil/layer2_5-ponytail.d.ts +2 -0
  184. package/dist/src/pil/layer2_5-ponytail.js +41 -0
  185. package/dist/src/pil/layer3-ee-injection.js +180 -14
  186. package/dist/src/pil/layer4-gsd.js +22 -8
  187. package/dist/src/pil/layer5-context.d.ts +1 -0
  188. package/dist/src/pil/layer5-context.js +26 -11
  189. package/dist/src/pil/layer6-output.js +23 -5
  190. package/dist/src/pil/llm-classify.js +3 -3
  191. package/dist/src/pil/native-capabilities-workbook.d.ts +1 -1
  192. package/dist/src/pil/native-capabilities-workbook.js +4 -3
  193. package/dist/src/pil/pipeline.js +15 -1
  194. package/dist/src/pil/response-tools.d.ts +16 -0
  195. package/dist/src/pil/response-tools.js +41 -0
  196. package/dist/src/pil/schema.d.ts +8 -0
  197. package/dist/src/pil/schema.js +24 -3
  198. package/dist/src/pil/task-tier-map.js +7 -7
  199. package/dist/src/pil/types.d.ts +9 -3
  200. package/dist/src/product-loop/__tests__/discovery-interview.test.js +103 -1
  201. package/dist/src/product-loop/__tests__/gather-selectable-alts.test.js +34 -0
  202. package/dist/src/product-loop/__tests__/sprint-self-verify.test.js +12 -12
  203. package/dist/src/product-loop/discovery-interview.d.ts +20 -0
  204. package/dist/src/product-loop/discovery-interview.js +121 -7
  205. package/dist/src/product-loop/done-gate.js +3 -3
  206. package/dist/src/product-loop/gather.js +19 -5
  207. package/dist/src/product-loop/loop-driver.js +20 -20
  208. package/dist/src/product-loop/progress-snapshot.js +4 -4
  209. package/dist/src/product-loop/sprint-self-verify.js +2 -2
  210. package/dist/src/providers/__tests__/reasoning-roundtrip.test.js +12 -0
  211. package/dist/src/providers/auth/__tests__/gemini-oauth.test.d.ts +1 -5
  212. package/dist/src/providers/auth/__tests__/gemini-oauth.test.js +1 -5
  213. package/dist/src/providers/auth/gcloud.d.ts +28 -0
  214. package/dist/src/providers/auth/gcloud.js +102 -0
  215. package/dist/src/providers/auth/gemini-oauth.d.ts +46 -19
  216. package/dist/src/providers/auth/gemini-oauth.js +292 -105
  217. package/dist/src/providers/auth/grok-oauth.d.ts +1 -1
  218. package/dist/src/providers/auth/grok-oauth.js +1 -1
  219. package/dist/src/providers/auth/registry.js +28 -6
  220. package/dist/src/providers/gemini.d.ts +1 -1
  221. package/dist/src/providers/gemini.js +10 -3
  222. package/dist/src/providers/keychain.d.ts +2 -2
  223. package/dist/src/providers/keychain.js +20 -2
  224. package/dist/src/providers/mcp-vision-bridge.js +48 -48
  225. package/dist/src/providers/openai.js +5 -1
  226. package/dist/src/providers/runtime.d.ts +1 -1
  227. package/dist/src/providers/runtime.js +11 -2
  228. package/dist/src/providers/siliconflow-sse-repair.js +1 -1
  229. package/dist/src/providers/strategies/google.strategy.d.ts +9 -2
  230. package/dist/src/providers/strategies/google.strategy.js +159 -9
  231. package/dist/src/providers/strategies/thinking-mode.js +19 -6
  232. package/dist/src/providers/types.d.ts +1 -1
  233. package/dist/src/reporter/index.js +1 -1
  234. package/dist/src/router/decide.test.js +40 -8
  235. package/dist/src/router/step-router.d.ts +20 -0
  236. package/dist/src/router/step-router.js +73 -0
  237. package/dist/src/scaffold/bb-ecosystem-apply.js +47 -47
  238. package/dist/src/scaffold/bb-quality-gate.js +5 -5
  239. package/dist/src/scaffold/continuation-prompt.js +60 -60
  240. package/dist/src/scaffold/init-new.js +453 -453
  241. package/dist/src/self-qa/__tests__/scenario-planner.test.js +3 -3
  242. package/dist/src/self-qa/agentic-loop.js +19 -19
  243. package/dist/src/self-qa/orchestrator.js +5 -5
  244. package/dist/src/self-qa/scenario-planner.js +1 -1
  245. package/dist/src/self-qa/spec-emitter.js +4 -4
  246. package/dist/src/storage/__tests__/migrations.test.js +2 -2
  247. package/dist/src/storage/index.d.ts +1 -1
  248. package/dist/src/storage/index.js +1 -1
  249. package/dist/src/storage/interaction-log.js +5 -5
  250. package/dist/src/storage/migrations.js +122 -122
  251. package/dist/src/storage/session-experience-store.js +4 -4
  252. package/dist/src/storage/sessions.d.ts +6 -0
  253. package/dist/src/storage/sessions.js +49 -42
  254. package/dist/src/storage/transcript-view.js +7 -1
  255. package/dist/src/storage/transcript.d.ts +1 -0
  256. package/dist/src/storage/transcript.js +55 -0
  257. package/dist/src/storage/ui-interaction-log.d.ts +17 -0
  258. package/dist/src/storage/usage.js +14 -14
  259. package/dist/src/storage/workspaces.js +12 -12
  260. package/dist/src/tools/__tests__/native-tools.test.js +6 -0
  261. package/dist/src/tools/bash.d.ts +36 -23
  262. package/dist/src/tools/bash.js +59 -301
  263. package/dist/src/tools/bash.test.js +2 -257
  264. package/dist/src/tools/file-tracker.d.ts +5 -0
  265. package/dist/src/tools/file-tracker.js +14 -0
  266. package/dist/src/tools/file.js +2 -0
  267. package/dist/src/tools/git-safety.d.ts +38 -1
  268. package/dist/src/tools/git-safety.js +94 -4
  269. package/dist/src/tools/git-safety.test.js +45 -1
  270. package/dist/src/tools/native-tools.d.ts +1 -1
  271. package/dist/src/tools/native-tools.js +56 -0
  272. package/dist/src/tools/registry-bash-empty-command.test.js +4 -3
  273. package/dist/src/tools/registry-git-safety.test.js +17 -6
  274. package/dist/src/tools/registry.d.ts +5 -0
  275. package/dist/src/tools/registry.js +185 -11
  276. package/dist/src/types/index.d.ts +7 -1
  277. package/dist/src/ui/app.d.ts +2 -1
  278. package/dist/src/ui/app.js +0 -0
  279. package/dist/src/ui/components/council-question-card.js +1 -0
  280. package/dist/src/ui/components/message-view.js +37 -3
  281. package/dist/src/ui/components/structured-response-view.js +7 -0
  282. package/dist/src/ui/components/task-list-panel.js +3 -12
  283. package/dist/src/ui/containers/chat-feed.d.ts +40 -0
  284. package/dist/src/ui/containers/chat-feed.js +66 -0
  285. package/dist/src/ui/containers/modals-layer.d.ts +87 -0
  286. package/dist/src/ui/containers/modals-layer.js +18 -0
  287. package/dist/src/ui/modals/session-picker-modal.js +14 -9
  288. package/dist/src/ui/modals/update-modal.js +2 -1
  289. package/dist/src/ui/slash/__tests__/menu-parity.test.d.ts +1 -0
  290. package/dist/src/ui/slash/__tests__/menu-parity.test.js +1 -0
  291. package/dist/src/ui/slash/__tests__/ponytail.test.d.ts +1 -0
  292. package/dist/src/ui/slash/__tests__/ponytail.test.js +37 -0
  293. package/dist/src/ui/slash/council-inspect.js +4 -4
  294. package/dist/src/ui/slash/export.js +16 -1
  295. package/dist/src/ui/slash/menu-items.js +1 -0
  296. package/dist/src/ui/slash/ponytail.d.ts +2 -0
  297. package/dist/src/ui/slash/ponytail.js +19 -0
  298. package/dist/src/ui/status-bar/store.js +0 -1
  299. package/dist/src/ui/status-bar/store.test.js +2 -2
  300. package/dist/src/ui/types.d.ts +7 -0
  301. package/dist/src/ui/use-app-logic.d.ts +26 -0
  302. package/dist/src/ui/use-app-logic.js +0 -0
  303. package/dist/src/ui/utils/relaunch.d.ts +17 -0
  304. package/dist/src/ui/utils/relaunch.js +28 -4
  305. package/dist/src/ui/utils/relaunch.test.js +50 -0
  306. package/dist/src/ui/utils/text.d.ts +11 -0
  307. package/dist/src/ui/utils/text.js +21 -1
  308. package/dist/src/ui/utils/text.test.js +25 -1
  309. package/dist/src/ui/utils/tools.js +3 -1
  310. package/dist/src/usage/cost-log.js +2 -2
  311. package/dist/src/usage/estimator.d.ts +14 -0
  312. package/dist/src/usage/estimator.js +22 -0
  313. package/dist/src/utils/__tests__/compaction-caps.test.d.ts +21 -0
  314. package/dist/src/utils/__tests__/compaction-caps.test.js +187 -0
  315. package/dist/src/utils/__tests__/llm-deadline-abort.test.d.ts +1 -0
  316. package/dist/src/utils/__tests__/llm-deadline-abort.test.js +33 -0
  317. package/dist/src/utils/clipboard-image.js +23 -23
  318. package/dist/src/utils/llm-deadline.d.ts +9 -1
  319. package/dist/src/utils/llm-deadline.js +31 -2
  320. package/dist/src/utils/permission-mode.d.ts +33 -0
  321. package/dist/src/utils/permission-mode.js +164 -3
  322. package/dist/src/utils/permission-mode.test.js +92 -1
  323. package/dist/src/utils/settings.d.ts +27 -31
  324. package/dist/src/utils/settings.js +100 -109
  325. package/dist/src/utils/side-question.js +2 -2
  326. package/dist/src/utils/skills.js +3 -3
  327. package/dist/src/verify/__tests__/coverage-parsers.test.js +30 -30
  328. package/package.json +1 -1
@@ -36,32 +36,23 @@ function ItemRow({ item, t }) {
36
36
  let glyph;
37
37
  let glyphColor;
38
38
  let textColor;
39
- let label;
39
+ const label = item.status === "in_progress" ? (item.activeForm ?? item.subject) : item.subject;
40
40
  if (item.status === "completed") {
41
41
  glyph = "✓";
42
42
  glyphColor = t.diffAddedFg;
43
43
  textColor = t.textMuted;
44
- // Strikethrough via U+0336 combining mark. OpenTUI doesn't expose CSS
45
- // text-decoration so we synthesise it character-by-character — matches the
46
- // visual goal in the spec.
47
- label = item.subject
48
- .split("")
49
- .map((c) => `${c}̶`)
50
- .join("");
51
44
  }
52
45
  else if (item.status === "in_progress") {
53
46
  glyph = "◉";
54
47
  glyphColor = t.accent;
55
48
  textColor = t.text;
56
- label = item.activeForm ?? item.subject;
57
49
  }
58
50
  else {
59
51
  glyph = "○";
60
52
  glyphColor = t.textMuted;
61
53
  textColor = t.text;
62
- label = item.subject;
63
54
  }
64
- return (_jsx(Semantic, { id: `task-${item.id}`, role: "listitem", name: label, state: item.status, children: _jsxs("text", { children: [_jsx("span", { style: { fg: glyphColor }, children: `${glyph} ` }), _jsx("span", { style: { fg: textColor }, children: trunc(label, 80) })] }) }));
55
+ return (_jsx(Semantic, { id: `task-${item.id}`, role: "listitem", name: label, state: item.status, children: _jsx("box", { width: "100%", children: _jsxs("text", { fg: textColor, children: [_jsx("span", { style: { fg: glyphColor }, children: `${glyph} ` }), trunc(label, 70)] }) }) }));
65
56
  }
66
57
  export function TaskListPanel({ snapshot, t, expanded }) {
67
58
  if (snapshot.items.length === 0)
@@ -78,7 +69,7 @@ export function TaskListPanel({ snapshot, t, expanded }) {
78
69
  if (pending > 0)
79
70
  footerParts.push(`${pending} queued`);
80
71
  const footer = footerParts.length > 0 ? footerParts.join(" · ") : "0 todos";
81
- return (_jsx(Semantic, { id: "task-list", role: "listbox", name: "Todos", children: _jsxs("box", { border: true, borderColor: t.border, paddingLeft: 2, paddingRight: 2, paddingTop: 0, paddingBottom: 0, marginBottom: 1, flexDirection: "column", children: [_jsx("text", { children: _jsx("span", { style: { fg: t.textMuted }, children: "Todos" }) }), visible.map((it) => (_jsx(ItemRow, { item: it, t: t }, it.id))), overflow > 0 && _jsx("text", { fg: t.textMuted, children: `… +${overflow} more (ctrl+e to expand)` }), _jsx("text", { fg: t.textDim, children: footer })] }) }));
72
+ return (_jsx(Semantic, { id: "task-list", role: "listbox", name: "Todos", children: _jsxs("box", { border: true, borderColor: t.border, paddingLeft: 2, paddingRight: 2, paddingTop: 0, paddingBottom: 0, marginBottom: 1, flexDirection: "column", children: [_jsx("box", { paddingBottom: 1, children: _jsx("text", { fg: t.textMuted, children: _jsx("b", { children: "Todos" }) }) }), _jsx("box", { flexDirection: "column", children: visible.map((it) => (_jsx(ItemRow, { item: it, t: t }, it.id))) }), overflow > 0 && (_jsx("box", { marginTop: 1, children: _jsx("text", { fg: t.textMuted, children: `… +${overflow} more (ctrl+e to expand)` }) })), _jsx("box", { marginTop: 1, paddingTop: 1, border: ["top"], borderColor: t.border, children: _jsx("text", { fg: t.textDim, children: footer }) })] }) }));
82
73
  }
83
74
  export const __TEST_ONLY__ = { sortItems, MAX_VISIBLE };
84
75
  //# sourceMappingURL=task-list-panel.js.map
@@ -0,0 +1,40 @@
1
+ import React from "react";
2
+ export interface ChatFeedProps {
3
+ getSide: any;
4
+ getPartnerLast: any;
5
+ resolveStyle: any;
6
+ scrollRef: any;
7
+ messages: any;
8
+ expandedMessages: any;
9
+ liveTurnSourceLabel: any;
10
+ activeToolCalls: any;
11
+ streamContent: any;
12
+ isProcessing: any;
13
+ activeSubagent: any;
14
+ councilPhases: any;
15
+ productStatus: any;
16
+ councilStatuses: any;
17
+ councilInfoCards: any;
18
+ councilMessages: any;
19
+ councilPlaceholders: any;
20
+ reasoningActive: any;
21
+ lastReasoningElapsedMs: any;
22
+ streamReasoning: any;
23
+ showPlanPanel: any;
24
+ planQuestions: any;
25
+ pqs: any;
26
+ pendingPaymentApproval: any;
27
+ activeHaltCard: any;
28
+ haltSelectedIndex: any;
29
+ initNewForm: any;
30
+ pointToExistingForm: any;
31
+ councilProgress: any;
32
+ pendingCouncilQuestion: any;
33
+ councilCardState: any;
34
+ pendingCouncilPreflight: any;
35
+ preflightCardState: any;
36
+ t: any;
37
+ width: any;
38
+ modeInfo: any;
39
+ }
40
+ export declare function ChatFeed(props: ChatFeedProps): React.ReactNode;
@@ -0,0 +1,66 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "@opentui/react/jsx-runtime";
2
+ import { Semantic } from "@muonroi/agent-harness-opentui";
3
+ import { ProductStatusCard } from "../cards/product-status-card.js";
4
+ import { makePairKey } from "../components/bubble-layout.js";
5
+ import { CouncilInfoCardView } from "../components/council-info-card.js";
6
+ import { CouncilLeaderBubble } from "../components/council-leader-bubble.js";
7
+ import { CouncilMessageBubble } from "../components/council-message-bubble.js";
8
+ import { CouncilPhaseTimeline } from "../components/council-phase-timeline.js";
9
+ import { CouncilPlaceholderBubble } from "../components/council-placeholder-bubble.js";
10
+ import { CouncilQuestionCard } from "../components/council-question-card.js";
11
+ import { CouncilStatusList } from "../components/council-status-list.js";
12
+ import { CouncilSynthesisBanner } from "../components/council-synthesis-banner.js";
13
+ import { HaltRecoveryCard } from "../components/halt-recovery-card.js";
14
+ import { InitNewFormCard } from "../components/init-new-form-card.js";
15
+ import { computeMcpRunInfo, MessageView } from "../components/message-view.js";
16
+ import { PointToExistingFormCard } from "../components/point-to-existing-form-card.js";
17
+ import { DelegationTaskLine, InlineTool, ShimmerText, SubagentActivity, SubagentTaskLine, } from "../components/tool-result-views.js";
18
+ import { Markdown } from "../markdown.js";
19
+ import { PaymentApprovalPanel } from "../modals/wallet-picker-modal.js";
20
+ import { PlanQuestionsPanel } from "../plan.js";
21
+ import { buildPreflightQuestion } from "../utils/format.js";
22
+ import { toolArgs, toolLabel, tryParseArg } from "../utils/tools.js";
23
+ export function ChatFeed(props) {
24
+ const { modeInfo, getSide, getPartnerLast, resolveStyle, scrollRef, messages, expandedMessages, liveTurnSourceLabel, activeToolCalls, streamContent, isProcessing, activeSubagent, councilPhases, productStatus, councilStatuses, councilInfoCards, councilMessages, councilPlaceholders, reasoningActive, lastReasoningElapsedMs, streamReasoning, showPlanPanel, planQuestions, pqs, pendingPaymentApproval, activeHaltCard, haltSelectedIndex, initNewForm, pointToExistingForm, councilProgress, pendingCouncilQuestion, councilCardState, pendingCouncilPreflight, preflightCardState, t, width, } = props;
25
+ return (_jsx(Semantic, { id: "log", role: "log", props: { scrollTop: scrollRef.current?.scrollTop ?? 0 }, children: _jsxs("scrollbox", { ref: scrollRef, flexGrow: 1, stickyScroll: true, stickyStart: "bottom", children: [(() => {
26
+ const mcpRuns = computeMcpRunInfo(messages);
27
+ // Phase 5 F7 — index of the last assistant message so
28
+ // MessageView can skip auto-collapse on it (final answer
29
+ // should always be fully visible, not hidden behind
30
+ // "ctrl+e expand").
31
+ let _lastAssistantIdx = -1;
32
+ for (let _i = messages.length - 1; _i >= 0; _i--) {
33
+ if (messages[_i]?.type === "assistant") {
34
+ _lastAssistantIdx = _i;
35
+ break;
36
+ }
37
+ }
38
+ return messages.map((msg, i) => (
39
+ // biome-ignore lint/suspicious/noArrayIndexKey: append-only message log; index is part of the stable semantic id
40
+ _jsx(Semantic, { id: `msg-${i}`, role: "listitem", name: `${msg.type ?? "msg"}:${String(msg.content ?? "").slice(0, 40)}`, children: _jsx(MessageView, { entry: msg, index: i, t: t, modeColor: modeInfo.color, expandedMessages: expandedMessages, mcpRun: mcpRuns[i], isFinalAssistant: i === _lastAssistantIdx }, `${msg.timestamp?.getTime?.() ?? i}-${msg.type}-${msg.remoteKey ?? ""}-${String(msg.content ?? "").slice(0, 24)}`) }, `sem-${msg.timestamp?.getTime?.() ?? i}-${i}`)));
41
+ })(), liveTurnSourceLabel && (activeToolCalls.length > 0 || streamContent || isProcessing) && (_jsx("box", { paddingLeft: 3, marginTop: 1, flexShrink: 0, children: _jsx("text", { fg: t.textMuted, children: liveTurnSourceLabel }) })), activeToolCalls.map((tc) => tc.function.name === "task" ? (_jsx(SubagentTaskLine, { t: t, agent: tryParseArg(tc, "agent") || "sub-agent", label: toolArgs(tc) || "Working", pending: true }, tc.id)) : tc.function.name === "delegate" ? (_jsx(DelegationTaskLine, { t: t, label: toolArgs(tc) || "Background research", pending: true, id: undefined }, tc.id)) : (_jsx(InlineTool, { t: t, pending: true, children: toolLabel(tc) }, tc.id))), activeSubagent && _jsx(SubagentActivity, { t: t, status: activeSubagent }), councilPhases.length > 0 && (_jsx(Semantic, { id: "council-phases", role: "listbox", name: "Council Phases", children: _jsx(CouncilPhaseTimeline, { phases: councilPhases, theme: t }) })), productStatus && _jsx(ProductStatusCard, { data: productStatus, theme: t }), councilStatuses.length > 0 && (_jsx(Semantic, { id: "council-status", role: "listbox", name: "Council Status", children: _jsx(CouncilStatusList, { statuses: councilStatuses, theme: t }) })), councilInfoCards.map((card, idx) => (_jsx(Semantic, { id: `council-card-${idx}`, role: "listitem", name: card.title || `Council card ${idx}`, children: _jsx(CouncilInfoCardView, { card: card, terminalCols: width, theme: t }, `info-card-${idx}-${card.title}`) }, `sem-info-${idx}-${card.title}`))), councilMessages.map((cm, idx) => {
42
+ const side = cm.kind === "debate" && cm.partner
43
+ ? getSide(makePairKey(cm.speaker.role, cm.partner.role), cm.speaker.role)
44
+ : "left";
45
+ const semName = `${cm.kind}:${cm.speaker?.role ?? "?"}`;
46
+ if (cm.kind === "leader") {
47
+ return (_jsx(Semantic, { id: `council-msg-${idx}`, role: "listitem", name: semName, value: cm.text, children: _jsx(CouncilLeaderBubble, { msg: cm, terminalCols: width }, idx) }, `sem-cm-${idx}`));
48
+ }
49
+ if (cm.kind === "synthesis") {
50
+ return (_jsx(Semantic, { id: `council-msg-${idx}`, role: "listitem", name: semName, value: cm.text, children: _jsx(CouncilSynthesisBanner, { msg: cm }, idx) }, `sem-cm-${idx}`));
51
+ }
52
+ const pairKey = cm.partner ? makePairKey(cm.speaker.role, cm.partner.role) : `solo::${cm.speaker.role}`;
53
+ const partnerLastText = cm.partner ? getPartnerLast(pairKey, cm.partner.role) : undefined;
54
+ return (_jsx(Semantic, { id: `council-msg-${idx}`, role: "listitem", name: semName, value: cm.text, children: _jsx(CouncilMessageBubble, { msg: cm, terminalCols: width, side: side, resolveStyle: resolveStyle, partnerLastText: partnerLastText, partnerRole: cm.partner?.role, theme: t }, idx) }, `sem-cm-${idx}`));
55
+ }), Array.from(councilPlaceholders.entries()).map(([id, p]) => (_jsx(CouncilPlaceholderBubble, { role: p.role, side: p.side, terminalCols: width, color: p.color, theme: t, variant: p.variant }, id))), (reasoningActive || lastReasoningElapsedMs > 0) && (_jsxs("box", { paddingLeft: 3, marginTop: 1, flexShrink: 0, flexDirection: "column", children: [_jsx("text", { fg: t.textMuted, children: reasoningActive ? "[Thought] Thinking..." : `[Thought] Thought for ${(lastReasoningElapsedMs / 1000).toFixed(1)}s` }), streamReasoning ? (_jsx("box", { border: ["left"], borderColor: t.textMuted, paddingLeft: 2, marginTop: 1, flexDirection: "column", children: reasoningActive ? (
56
+ // While actively streaming, render only the last 3
57
+ // non-empty lines as plain text to avoid Markdown
58
+ // re-parse overhead every 150ms.
59
+ _jsx("text", { fg: t.textMuted, children: streamReasoning
60
+ .split("\n")
61
+ .filter((l) => l.trim().length > 0)
62
+ .slice(-3)
63
+ .join(" · ") })) : (_jsx(Markdown, { content: streamReasoning, t: t })) })) : null] })), streamContent && (_jsx("box", { paddingLeft: 3, marginTop: 1, flexShrink: 0, children: _jsx(Markdown, { content: streamContent, t: t }) })), isProcessing && !streamContent && activeToolCalls.length === 0 && (_jsx(ShimmerText, { t: t, text: "Planning next moves" })), showPlanPanel && _jsx(PlanQuestionsPanel, { t: t, questions: planQuestions, state: pqs }), pendingPaymentApproval && _jsx(PaymentApprovalPanel, { t: t, payment: pendingPaymentApproval }), activeHaltCard && (_jsx(HaltRecoveryCard, { halt: activeHaltCard, selectedIndex: haltSelectedIndex, terminalCols: width, theme: t })), initNewForm && _jsx(InitNewFormCard, { state: initNewForm, terminalCols: width, theme: t }), pointToExistingForm && _jsx(PointToExistingFormCard, { state: pointToExistingForm, terminalCols: width, theme: t }), councilProgress && (_jsx(Semantic, { id: "continue-as-council-progress", role: "log", name: "Council brainstorm", children: _jsx("box", { flexDirection: "column", borderStyle: "single", borderColor: councilProgress.status === "error" ? t.initFormError : t.text, padding: 1, marginTop: 1, children: _jsxs("text", { fg: t.text, children: [councilProgress.status === "running" && "Council brainstorming — writing spec.md...", councilProgress.status === "done" &&
64
+ `Council brainstorm complete: ${councilProgress.specPath}${councilProgress.hasContent ? "" : " (no content — production council wiring deferred)"}`, councilProgress.status === "error" && `Council brainstorm failed: ${councilProgress.error}`] }) }) })), pendingCouncilQuestion && councilCardState && (_jsx(CouncilQuestionCard, { question: pendingCouncilQuestion, theme: t, state: councilCardState })), pendingCouncilPreflight && preflightCardState && (_jsx(CouncilQuestionCard, { question: buildPreflightQuestion(pendingCouncilPreflight), theme: t, state: preflightCardState }))] }) }));
65
+ }
66
+ //# sourceMappingURL=chat-feed.js.map
@@ -0,0 +1,87 @@
1
+ export interface ModalsLayerProps {
2
+ agentRows: any;
3
+ agentsEditorDraft: any;
4
+ agentsEditorError: any;
5
+ agentsEditorField: any;
6
+ agentsEditorModelIndex: any;
7
+ agentsEditorSyncKey: any;
8
+ agentsModalIndex: any;
9
+ agentsSearchQuery: any;
10
+ apiKeyError: any;
11
+ apiKeyInputRef: any;
12
+ apiKeyPrompt: any;
13
+ bwSync: any;
14
+ configuredProviders: any;
15
+ connectModalIndex: any;
16
+ defaultProvider: any;
17
+ disabledModels: any;
18
+ disabledProviders: any;
19
+ editingMcpId: any;
20
+ editingSubagent: any;
21
+ filteredModels: any;
22
+ height: any;
23
+ mcpArgsRef: any;
24
+ mcpCommandRef: any;
25
+ mcpCwdRef: any;
26
+ mcpEditorDraft: any;
27
+ mcpEditorError: any;
28
+ mcpEditorField: any;
29
+ mcpEditorSyncKey: any;
30
+ mcpEnvRef: any;
31
+ mcpHeadersRef: any;
32
+ mcpLabelRef: any;
33
+ mcpModalIndex: any;
34
+ mcpRows: any;
35
+ mcpSearchQuery: any;
36
+ mcpUrlRef: any;
37
+ model: any;
38
+ modelPickerFocus: any;
39
+ modelPickerIndex: any;
40
+ modelSearchQuery: any;
41
+ providerChipIndex: any;
42
+ providersWithKey: any;
43
+ reasoningEffortByModel: any;
44
+ sandboxMode: any;
45
+ sandboxSettings: any;
46
+ sandboxSettingsEditBuffer: any;
47
+ sandboxSettingsEditing: any;
48
+ sandboxSettingsFocusIndex: any;
49
+ scheduleModalIndex: any;
50
+ scheduleRows: any;
51
+ scheduleSearchQuery: any;
52
+ sessionPickerIndex: any;
53
+ sessionPickerList: any;
54
+ showAgentsEditor: any;
55
+ showAgentsModal: any;
56
+ showApiKeyModal: any;
57
+ showConnectModal: any;
58
+ showMcpEditor: any;
59
+ showMcpModal: any;
60
+ showModelPicker: any;
61
+ showSandboxPicker: any;
62
+ showScheduleModal: any;
63
+ showSessionPicker: any;
64
+ showTelegramPairModal: any;
65
+ showTelegramTokenModal: any;
66
+ showUpdateModal: any;
67
+ showWalletPicker: any;
68
+ startupConfig: any;
69
+ subagentInstructionRef: any;
70
+ subagentNameRef: any;
71
+ submitApiKey: any;
72
+ submitMcpEditor: any;
73
+ submitSubagentEditor: any;
74
+ submitTelegramPair: any;
75
+ submitTelegramToken: any;
76
+ t: any;
77
+ telegramPairError: any;
78
+ telegramPairInputRef: any;
79
+ telegramTokenError: any;
80
+ telegramTokenInputRef: any;
81
+ updateInfo: any;
82
+ walletDisplayInfo: any;
83
+ walletFocusIndex: any;
84
+ walletSettings: any;
85
+ width: any;
86
+ }
87
+ export declare function ModalsLayer(props: ModalsLayerProps): import("react").ReactNode;
@@ -0,0 +1,18 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "@opentui/react/jsx-runtime";
2
+ import { formatSubagentName } from "../../utils/subagent-display.js";
3
+ import { SubagentEditorModal, SubagentsBrowserModal } from "../agents-modal.js";
4
+ import { CONNECT_CHANNELS } from "../constants.js";
5
+ import { McpBrowserModal, McpEditorModal } from "../mcp-modal.js";
6
+ import { ApiKeyModal } from "../modals/api-key-modal.js";
7
+ import { ConnectModal, TelegramPairModal, TelegramTokenModal } from "../modals/connect-modal.js";
8
+ import { ModelPickerModal } from "../modals/model-picker-modal.js";
9
+ import { SandboxPickerModal } from "../modals/sandbox-picker-modal.js";
10
+ import { SessionPickerModal } from "../modals/session-picker-modal.js";
11
+ import { UpdateModal } from "../modals/update-modal.js";
12
+ import { WalletPickerModal } from "../modals/wallet-picker-modal.js";
13
+ import { ScheduleBrowserModal } from "../schedule-modal.js";
14
+ export function ModalsLayer(props) {
15
+ const { agentRows, agentsEditorDraft, agentsEditorError, agentsEditorField, agentsEditorModelIndex, agentsEditorSyncKey, agentsModalIndex, agentsSearchQuery, apiKeyError, apiKeyInputRef, apiKeyPrompt, bwSync, configuredProviders, connectModalIndex, defaultProvider, disabledModels, disabledProviders, editingMcpId, editingSubagent, filteredModels, height, mcpArgsRef, mcpCommandRef, mcpCwdRef, mcpEditorDraft, mcpEditorError, mcpEditorField, mcpEditorSyncKey, mcpEnvRef, mcpHeadersRef, mcpLabelRef, mcpModalIndex, mcpRows, mcpSearchQuery, mcpUrlRef, model, modelPickerFocus, modelPickerIndex, modelSearchQuery, providerChipIndex, providersWithKey, reasoningEffortByModel, sandboxMode, sandboxSettings, sandboxSettingsEditBuffer, sandboxSettingsEditing, sandboxSettingsFocusIndex, scheduleModalIndex, scheduleRows, scheduleSearchQuery, sessionPickerIndex, sessionPickerList, showAgentsEditor, showAgentsModal, showApiKeyModal, showConnectModal, showMcpEditor, showMcpModal, showModelPicker, showSandboxPicker, showScheduleModal, showSessionPicker, showTelegramPairModal, showTelegramTokenModal, showUpdateModal, showWalletPicker, startupConfig, subagentInstructionRef, subagentNameRef, submitApiKey, submitMcpEditor, submitSubagentEditor, submitTelegramPair, submitTelegramToken, t, telegramPairError, telegramPairInputRef, telegramTokenError, telegramTokenInputRef, updateInfo, walletDisplayInfo, walletFocusIndex, walletSettings, width, } = props;
16
+ return (_jsxs(_Fragment, { children: [showApiKeyModal && (_jsx(ApiKeyModal, { t: t, width: width, height: height, inputRef: apiKeyInputRef, error: apiKeyError, onSubmit: submitApiKey })), showUpdateModal && updateInfo && (_jsx(UpdateModal, { t: t, width: width, height: height, currentVersion: startupConfig.version, latestVersion: updateInfo.latestVersion })), showMcpModal && !showMcpEditor && (_jsx(McpBrowserModal, { t: t, width: width, height: height, selectedIndex: mcpModalIndex, searchQuery: mcpSearchQuery, rows: mcpRows })), showMcpEditor && (_jsx(McpEditorModal, { t: t, width: width, height: height, draft: mcpEditorDraft, focusedField: mcpEditorField, syncKey: mcpEditorSyncKey, error: mcpEditorError, title: editingMcpId ? "Edit MCP Server" : "Add MCP Server", labelRef: mcpLabelRef, urlRef: mcpUrlRef, headersRef: mcpHeadersRef, commandRef: mcpCommandRef, argsRef: mcpArgsRef, cwdRef: mcpCwdRef, envRef: mcpEnvRef, onSubmit: submitMcpEditor })), showScheduleModal && (_jsx(ScheduleBrowserModal, { t: t, width: width, height: height, selectedIndex: scheduleModalIndex, searchQuery: scheduleSearchQuery, rows: scheduleRows })), showAgentsModal && !showAgentsEditor && (_jsx(SubagentsBrowserModal, { t: t, width: width, height: height, selectedIndex: agentsModalIndex, searchQuery: agentsSearchQuery, rows: agentRows })), showAgentsEditor && (_jsx(SubagentEditorModal, { t: t, width: width, height: height, draft: agentsEditorDraft, focusedField: agentsEditorField, modelIndex: agentsEditorModelIndex, error: agentsEditorError, title: editingSubagent ? `Edit sub-agent: ${formatSubagentName(editingSubagent.name)}` : "Add sub-agent", nameRef: subagentNameRef, instructionRef: subagentInstructionRef, onSubmit: submitSubagentEditor, showRemoveHint: !!editingSubagent }, `subagent-editor-${agentsEditorSyncKey}`)), showModelPicker && (_jsx(ModelPickerModal, { t: t, currentModel: model, selectedIndex: modelPickerIndex, width: width, height: height, searchQuery: modelSearchQuery, filteredModels: filteredModels, reasoningEffortByModel: reasoningEffortByModel, configuredProviders: configuredProviders, disabledProviders: disabledProviders, disabledModels: disabledModels, defaultProvider: defaultProvider, focus: modelPickerFocus, providerChipIndex: providerChipIndex, providersWithKey: providersWithKey, apiKeyPrompt: apiKeyPrompt, bwSync: bwSync })), showSessionPicker && (_jsx(SessionPickerModal, { t: t, sessions: sessionPickerList, focusIndex: sessionPickerIndex, width: width, height: height })), showWalletPicker && (_jsx(WalletPickerModal, { t: t, settings: walletSettings, walletInfo: walletDisplayInfo, focusIndex: walletFocusIndex, width: width, height: height })), showSandboxPicker && (_jsx(SandboxPickerModal, { t: t, currentMode: sandboxMode, settings: sandboxSettings, focusIndex: sandboxSettingsFocusIndex, editing: sandboxSettingsEditing, editBuffer: sandboxSettingsEditBuffer, width: width, height: height })), showConnectModal && (_jsx(ConnectModal, { t: t, width: width, height: height, selectedIndex: connectModalIndex, channels: CONNECT_CHANNELS })), showTelegramTokenModal && (_jsx(TelegramTokenModal, { t: t, width: width, height: height, inputRef: telegramTokenInputRef, error: telegramTokenError, onSubmit: submitTelegramToken })), showTelegramPairModal && (_jsx(TelegramPairModal, { t: t, width: width, height: height, inputRef: telegramPairInputRef, error: telegramPairError, onSubmit: () => void submitTelegramPair() }))] }));
17
+ }
18
+ //# sourceMappingURL=modals-layer.js.map
@@ -1,4 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "@opentui/react/jsx-runtime";
2
+ import { Semantic } from "@muonroi/agent-harness-opentui";
2
3
  import { bottomAlignedModalTop } from "../utils/modal.js";
3
4
  /**
4
5
  * Recent-sessions picker. Opened by `/sessions` or `/session`. Selecting a
@@ -14,15 +15,19 @@ export function SessionPickerModal({ t, sessions, focusIndex, width, height, })
14
15
  const panelHeight = Math.min(contentHeight, maxH);
15
16
  const top = bottomAlignedModalTop(height, panelHeight);
16
17
  const overlayBg = "#000000cc";
17
- return (_jsx("box", { position: "absolute", left: 0, top: 0, width: width, height: height, alignItems: "center", paddingTop: top, backgroundColor: overlayBg, children: _jsxs("box", { width: panelWidth, height: panelHeight, backgroundColor: t.backgroundPanel, paddingTop: 1, paddingBottom: 1, flexDirection: "column", children: [_jsxs("box", { flexShrink: 0, flexDirection: "row", justifyContent: "space-between", paddingLeft: 2, paddingRight: 2, children: [_jsx("text", { fg: t.primary, children: _jsx("b", { children: "Resume session" }) }), _jsx("text", { fg: t.textMuted, children: "esc" })] }), _jsx("scrollbox", { flexGrow: 1, minHeight: 0, children: sessions.length === 0 ? (_jsx("box", { paddingLeft: 2, paddingRight: 2, paddingTop: 1, children: _jsx("text", { fg: t.textMuted, children: "No prior sessions in this workspace." }) })) : (sessions.map((s, idx) => {
18
- const focused = idx === focusIndex;
19
- const ts = formatTimestamp(s.updatedAt);
20
- const titleRaw = s.title?.trim() || "(untitled)";
21
- const titleMax = Math.max(8, panelWidth - 38);
22
- const title = titleRaw.length > titleMax ? `${titleRaw.slice(0, titleMax - 1)}…` : titleRaw;
23
- const idShort = s.id.slice(-8);
24
- return (_jsxs("box", { backgroundColor: focused ? t.selectedBg : undefined, paddingLeft: 2, paddingRight: 2, width: "100%", flexDirection: "row", justifyContent: "space-between", children: [_jsx("text", { fg: focused ? t.selected : t.text, children: `${ts} ${title}` }), _jsx("text", { fg: focused ? t.primary : t.textMuted, children: `${s.model} ${idShort}` })] }, s.id));
25
- })) }), _jsx("box", { flexShrink: 0, paddingLeft: 2, paddingRight: 2, paddingTop: 1, children: _jsx("text", { fg: t.textMuted, children: "↑↓ navigate · enter resume (restarts CLI) · esc cancel" }) })] }) }));
18
+ return (_jsx(Semantic, { id: "session-picker", role: "dialog", name: "Resume session", isModal: true, children: _jsx("box", { position: "absolute", left: 0, top: 0, width: width, height: height, alignItems: "center", paddingTop: top, backgroundColor: overlayBg, children: _jsxs("box", { width: panelWidth, height: panelHeight, backgroundColor: t.backgroundPanel, paddingTop: 1, paddingBottom: 1, flexDirection: "column", children: [_jsxs("box", { flexShrink: 0, flexDirection: "row", justifyContent: "space-between", paddingLeft: 2, paddingRight: 2, children: [_jsx("text", { fg: t.primary, children: _jsx("b", { children: "Resume session" }) }), _jsx("text", { fg: t.textMuted, children: "esc" })] }), _jsx("scrollbox", { flexGrow: 1, minHeight: 0, children: sessions.length === 0 ? (_jsx("box", { paddingLeft: 2, paddingRight: 2, paddingTop: 1, children: _jsx("text", { fg: t.textMuted, children: "No prior sessions in this workspace." }) })) : (sessions.map((s, idx) => {
19
+ const focused = idx === focusIndex;
20
+ const ts = formatTimestamp(s.updatedAt);
21
+ const titleRaw = s.title?.trim() || "(untitled)";
22
+ const titleMax = Math.max(8, panelWidth - 38);
23
+ const title = titleRaw.length > titleMax ? `${titleRaw.slice(0, titleMax - 1)}…` : titleRaw;
24
+ // Show the FULL session id (not slice(-8)). The resumed-session
25
+ // header displays the full id, so a truncated id here made users
26
+ // think they had resumed a "different" session when it was the
27
+ // same one shown in two formats.
28
+ const idLabel = s.id;
29
+ return (_jsx(Semantic, { id: `session-item-${idx}`, role: "listitem", name: `${title} ${idLabel}`, value: s.id, selected: focused || undefined, children: _jsxs("box", { backgroundColor: focused ? t.selectedBg : undefined, paddingLeft: 2, paddingRight: 2, width: "100%", flexDirection: "row", justifyContent: "space-between", children: [_jsx("text", { fg: focused ? t.selected : t.text, children: `${ts} ${title}` }), _jsx("text", { fg: focused ? t.primary : t.textMuted, children: `${s.model} ${idLabel}` })] }) }, s.id));
30
+ })) }), _jsx("box", { flexShrink: 0, paddingLeft: 2, paddingRight: 2, paddingTop: 1, children: _jsx("text", { fg: t.textMuted, children: "↑↓ navigate · enter resume (restarts CLI) · esc cancel" }) })] }) }) }));
26
31
  }
27
32
  /**
28
33
  * Compact MM-DD HH:MM timestamp for the picker rows. Trades the year for
@@ -1,10 +1,11 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "@opentui/react/jsx-runtime";
2
+ import { Semantic } from "@muonroi/agent-harness-opentui";
2
3
  import { bottomAlignedModalTop } from "../utils/modal.js";
3
4
  export function UpdateModal({ t, width, height, currentVersion, latestVersion, }) {
4
5
  const overlayBg = "#000000cc";
5
6
  const panelWidth = Math.min(60, width - 6);
6
7
  const panelHeight = 9;
7
8
  const top = bottomAlignedModalTop(height, panelHeight);
8
- return (_jsx("box", { position: "absolute", left: 0, top: 0, width: width, height: height, alignItems: "center", paddingTop: top, backgroundColor: overlayBg, children: _jsxs("box", { width: panelWidth, height: panelHeight, backgroundColor: t.backgroundPanel, paddingTop: 1, paddingBottom: 1, flexDirection: "column", children: [_jsxs("box", { flexShrink: 0, flexDirection: "row", justifyContent: "space-between", paddingLeft: 2, paddingRight: 2, children: [_jsx("text", { fg: "#f59e0b", children: _jsx("b", { children: "Update Available" }) }), _jsx("text", { fg: t.textMuted, children: "esc to dismiss" })] }), _jsx("box", { flexShrink: 0, paddingLeft: 2, paddingRight: 2, paddingTop: 1, children: _jsxs("text", { fg: t.text, children: ["A new version of muonroi-cli is available: ", _jsxs("span", { style: { fg: t.textMuted }, children: ["v", currentVersion] }), " → ", _jsxs("span", { style: { fg: "#22c55e" }, children: ["v", latestVersion] })] }) }), _jsx("box", { flexShrink: 0, paddingLeft: 2, paddingRight: 2, paddingTop: 1, children: _jsx("text", { fg: t.textMuted, children: "Press enter to update now, or esc to dismiss" }) })] }) }));
9
+ return (_jsx(Semantic, { id: "update-modal", role: "dialog", name: "Update Available", isModal: true, children: _jsx("box", { position: "absolute", left: 0, top: 0, width: width, height: height, alignItems: "center", paddingTop: top, backgroundColor: overlayBg, children: _jsxs("box", { width: panelWidth, height: panelHeight, backgroundColor: t.backgroundPanel, paddingTop: 1, paddingBottom: 1, flexDirection: "column", children: [_jsxs("box", { flexShrink: 0, flexDirection: "row", justifyContent: "space-between", paddingLeft: 2, paddingRight: 2, children: [_jsx("text", { fg: "#f59e0b", children: _jsx("b", { children: "Update Available" }) }), _jsx("text", { fg: t.textMuted, children: "esc to dismiss" })] }), _jsx("box", { flexShrink: 0, paddingLeft: 2, paddingRight: 2, paddingTop: 1, children: _jsxs("text", { fg: t.text, children: ["A new version of muonroi-cli is available: ", _jsxs("span", { style: { fg: t.textMuted }, children: ["v", currentVersion] }), " → ", _jsxs("span", { style: { fg: "#22c55e" }, children: ["v", latestVersion] })] }) }), _jsx("box", { flexShrink: 0, paddingLeft: 2, paddingRight: 2, paddingTop: 1, children: _jsx("text", { fg: t.textMuted, children: "Press enter to update now, or esc to dismiss" }) })] }) }) }));
9
10
  }
10
11
  //# sourceMappingURL=update-modal.js.map
@@ -14,3 +14,4 @@ import "../council-inspect.js";
14
14
  import "../ideal.js";
15
15
  import "../route.js";
16
16
  import "../status.js";
17
+ import "../ponytail.js";
@@ -26,6 +26,7 @@ import "../council-inspect.js";
26
26
  import "../ideal.js";
27
27
  import "../route.js";
28
28
  import "../status.js";
29
+ import "../ponytail.js";
29
30
  describe("slash menu / registry parity", () => {
30
31
  it("every registered slash command has a menu entry", () => {
31
32
  const menuIds = new Set(SLASH_MENU_ITEMS.map((m) => m.id));
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,37 @@
1
+ import { afterEach, describe, expect, it } from "vitest";
2
+ import { handlePonytailSlash } from "../ponytail.js";
3
+ describe("handlePonytailSlash", () => {
4
+ const originalEnv = process.env.MUONROI_PONYTAIL_DISABLE;
5
+ const dummyCtx = {
6
+ cwd: "/",
7
+ tenantId: "test",
8
+ defaultProvider: "openai",
9
+ defaultModel: "gpt-4",
10
+ };
11
+ afterEach(() => {
12
+ if (originalEnv === undefined) {
13
+ delete process.env.MUONROI_PONYTAIL_DISABLE;
14
+ }
15
+ else {
16
+ process.env.MUONROI_PONYTAIL_DISABLE = originalEnv;
17
+ }
18
+ });
19
+ it("should enable ponytail mode and modify process.env", async () => {
20
+ process.env.MUONROI_PONYTAIL_DISABLE = "1";
21
+ const response = await handlePonytailSlash(["on"], dummyCtx);
22
+ expect(process.env.MUONROI_PONYTAIL_DISABLE).toBe("0");
23
+ expect(response).toContain("Ponytail Mode enabled");
24
+ });
25
+ it("should disable ponytail mode and modify process.env", async () => {
26
+ process.env.MUONROI_PONYTAIL_DISABLE = "0";
27
+ const response = await handlePonytailSlash(["off"], dummyCtx);
28
+ expect(process.env.MUONROI_PONYTAIL_DISABLE).toBe("1");
29
+ expect(response).toContain("Ponytail Mode disabled");
30
+ });
31
+ it("should return status when no args are provided", async () => {
32
+ process.env.MUONROI_PONYTAIL_DISABLE = "0";
33
+ const response = await handlePonytailSlash([], dummyCtx);
34
+ expect(response).toContain("ON (enabled)");
35
+ });
36
+ });
37
+ //# sourceMappingURL=ponytail.test.js.map
@@ -37,10 +37,10 @@ export const handleCouncilInspectSlash = async (args) => {
37
37
  }
38
38
  // Load all system messages for this session — parameterized query prevents SQL injection (T-17-04)
39
39
  const rows = db
40
- .prepare(`SELECT role, message_json, seq, created_at
41
- FROM messages
42
- WHERE session_id = ?
43
- AND role = 'system'
40
+ .prepare(`SELECT role, message_json, seq, created_at
41
+ FROM messages
42
+ WHERE session_id = ?
43
+ AND role = 'system'
44
44
  ORDER BY seq ASC`)
45
45
  .all(sessionId);
46
46
  if (rows.length === 0) {
@@ -18,6 +18,7 @@
18
18
  */
19
19
  import * as fs from "node:fs";
20
20
  import * as path from "node:path";
21
+ import { appendCrashLog } from "../../index.js";
21
22
  import { getDatabase } from "../../storage/db.js";
22
23
  import { buildChatEntries } from "../../storage/transcript.js";
23
24
  import { registerSlash } from "./registry.js";
@@ -251,7 +252,21 @@ export const handleExportSlash = async (_args, ctx) => {
251
252
  if (!sessionId) {
252
253
  return "No active session. Start a conversation first.";
253
254
  }
254
- const dbEntries = buildChatEntries(sessionId);
255
+ let dbEntries = [];
256
+ try {
257
+ dbEntries = buildChatEntries(sessionId);
258
+ }
259
+ catch (err) {
260
+ // A DB read failure here (SQLite busy/locked right after a council turn,
261
+ // schema drift, a corrupt page) used to throw out of this async handler.
262
+ // With no .catch on the dispatchSlash call site that became an
263
+ // unhandledRejection → process.exit(1) → the user kicked out of the TUI.
264
+ // Degrade instead: export the live TUI scrollback + interaction timeline.
265
+ // Logged to crash.log, NOT console (console.error corrupts the OpenTUI
266
+ // framebuffer while the renderer owns the terminal).
267
+ appendCrashLog("EXPORT", `buildChatEntries failed for session ${sessionId}: ${err instanceof Error ? err.stack || err.message : String(err)}`);
268
+ dbEntries = [];
269
+ }
255
270
  const liveEntries = ctx.getLiveEntries?.() ?? [];
256
271
  const timeline = selectInteractionTimeline(sessionId);
257
272
  if (dbEntries.length === 0 && liveEntries.length === 0 && timeline.length === 0) {
@@ -87,6 +87,7 @@ export const SLASH_MENU_ITEMS = [
87
87
  { id: "ee-context-status", label: "ee-context status", description: "Show current BB-context setting", hidden: true },
88
88
  { id: "export", label: "export", description: "Export entire conversation to a .txt file", hidden: true },
89
89
  { id: "status", label: "status", description: "Show Agile sprint progress dashboard", hidden: true },
90
+ { id: "ponytail", label: "ponytail", description: "Toggle Lazy Senior AI Mode (on/off/status)", hidden: false },
90
91
  ];
91
92
  /** Items shown in the splash autocomplete + /help listing. */
92
93
  export const VISIBLE_SLASH_MENU_ITEMS = SLASH_MENU_ITEMS.filter((m) => !m.hidden);
@@ -0,0 +1,2 @@
1
+ import { type SlashHandler } from "./registry.js";
2
+ export declare const handlePonytailSlash: SlashHandler;
@@ -0,0 +1,19 @@
1
+ import { isPonytailModeEnabled } from "../../pil/config.js";
2
+ import { registerSlash } from "./registry.js";
3
+ export const handlePonytailSlash = async (args, _ctx) => {
4
+ const arg = args[0]?.toLowerCase();
5
+ if (arg === "on") {
6
+ process.env.MUONROI_PONYTAIL_DISABLE = "0";
7
+ return "✅ Ponytail Mode enabled (Lazy Senior mode). The agent will enforce YAGNI and prioritize standard libraries.";
8
+ }
9
+ if (arg === "off") {
10
+ process.env.MUONROI_PONYTAIL_DISABLE = "1";
11
+ return "❌ Ponytail Mode disabled. The agent is free to propose complex architectures and dependencies.";
12
+ }
13
+ // Status check
14
+ const status = isPonytailModeEnabled() ? "ON (enabled)" : "OFF (disabled)";
15
+ return `Ponytail Mode is currently: **${status}**\nUsage: \`/ponytail on\` or \`/ponytail off\``;
16
+ };
17
+ // Self-register on module import
18
+ registerSlash("ponytail", handlePonytailSlash);
19
+ //# sourceMappingURL=ponytail.js.map
@@ -65,7 +65,6 @@ export function wireStatusBar() {
65
65
  });
66
66
  const offThresholds = subscribeThresholds((ev) => {
67
67
  statusBarStore.setState({
68
- session_usd: ev.current_usd,
69
68
  month_usd: ev.current_usd,
70
69
  cap_usd: ev.cap_usd,
71
70
  current_pct: ev.current_pct,
@@ -87,7 +87,7 @@ describe("wireStatusBar", () => {
87
87
  expect(s.model).toBe("claude-3-5-sonnet-latest");
88
88
  off();
89
89
  });
90
- it("threshold event updates session_usd and month_usd", () => {
90
+ it("threshold event updates month_usd (not session_usd)", () => {
91
91
  const off = wireStatusBar();
92
92
  thresholdCallback({
93
93
  level: 80,
@@ -97,8 +97,8 @@ describe("wireStatusBar", () => {
97
97
  atMs: 1,
98
98
  });
99
99
  const s = statusBarStore.getState();
100
- expect(s.session_usd).toBe(12.5);
101
100
  expect(s.month_usd).toBe(12.5);
101
+ expect(s.session_usd).toBe(0); // orchestrator owns session_usd, not thresholds
102
102
  off();
103
103
  });
104
104
  it("downgrade event updates model", () => {
@@ -69,6 +69,13 @@ export interface AppProps {
69
69
  startupConfig: AppStartupConfig;
70
70
  initialMessage?: string;
71
71
  onExit?: () => void;
72
+ /**
73
+ * Restart the CLI bound to a different session id (used by the /sessions
74
+ * picker). Routes through the same terminal teardown as onExit and supervises
75
+ * the child process, so resuming never strands the user at a corrupted shell
76
+ * prompt.
77
+ */
78
+ onRelaunch?: (sessionId: string) => void;
72
79
  }
73
80
  export interface ActiveTurnState {
74
81
  kind: "local" | "telegram";
@@ -0,0 +1,26 @@
1
+ import "./slash/route.js";
2
+ import "./slash/optimize.js";
3
+ import "./slash/discuss.js";
4
+ import "./slash/plan.js";
5
+ import "./slash/execute.js";
6
+ import "./slash/compact.js";
7
+ import "./slash/expand.js";
8
+ import "./slash/clear.js";
9
+ import "./slash/pin.js";
10
+ import "./slash/cost.js";
11
+ import "./slash/ee.js";
12
+ import "./slash/debug.js";
13
+ import "./slash/council.js";
14
+ import "./slash/ideal.js";
15
+ import "./slash/export.js";
16
+ import "./slash/status.js";
17
+ import "./slash/ponytail.js";
18
+ export type { AppStartupConfig } from "./types.js";
19
+ export interface AppLogicProps {
20
+ agent: any;
21
+ startupConfig: any;
22
+ initialMessage?: any;
23
+ onExit: any;
24
+ onRelaunch: any;
25
+ }
26
+ export declare function useAppLogic(props: AppLogicProps): any;
Binary file
@@ -27,6 +27,23 @@ export interface RelaunchOptions {
27
27
  onExit?: (code: number) => void;
28
28
  /** Injected spawn for tests. Defaults to the real node:child_process spawn. */
29
29
  spawnFn?: typeof spawn;
30
+ /**
31
+ * Run immediately before the spawn — used to tear down the current TUI
32
+ * (renderer.destroy, restore raw mode / mouse tracking / alt-screen) so the
33
+ * child inherits a CLEAN terminal. Without this the child (and the shell, if
34
+ * the parent exits) inherit a terminal still in mouse-tracking/alt-screen
35
+ * mode, and stray escape bytes get parsed by the shell as a command
36
+ * ("'…' is not recognized as an internal or external command").
37
+ */
38
+ beforeSpawn?: () => void;
39
+ /**
40
+ * When true, the parent stays alive and supervises the child: it exits with
41
+ * the child's exit code instead of exiting the instant the child spawns.
42
+ * This keeps any intermediate launcher (a `bun run` wrapper, the bun bin
43
+ * shim) alive so the shell does NOT regain the terminal foreground while the
44
+ * child is running. Required for a correct in-terminal restart on Windows.
45
+ */
46
+ supervise?: boolean;
30
47
  }
31
48
  /**
32
49
  * Spawn a fresh CLI process bound to {sessionId} and exit the current one.