muonroi-cli 1.8.4 → 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 (458) hide show
  1. package/LICENSE +17 -5
  2. package/dist/packages/agent-harness-core/src/driver.d.ts +27 -1
  3. package/dist/packages/agent-harness-core/src/driver.js +46 -0
  4. package/dist/packages/agent-harness-core/src/event-tee.d.ts +48 -0
  5. package/dist/packages/agent-harness-core/src/event-tee.js +77 -0
  6. package/dist/packages/agent-harness-core/src/mcp-server.d.ts +11 -0
  7. package/dist/packages/agent-harness-core/src/mcp-server.js +87 -15
  8. package/dist/packages/agent-harness-core/src/protocol.d.ts +66 -2
  9. package/dist/packages/agent-harness-core/src/protocol.js +15 -0
  10. package/dist/packages/agent-harness-core/src/visual-quality.d.ts +58 -0
  11. package/dist/packages/agent-harness-core/src/visual-quality.js +141 -0
  12. package/dist/packages/agent-harness-opentui/src/agent-mode.d.ts +6 -0
  13. package/dist/packages/agent-harness-opentui/src/agent-mode.js +14 -1
  14. package/dist/packages/agent-harness-opentui/src/input-bridge.d.ts +2 -10
  15. package/dist/packages/agent-harness-opentui/src/input-bridge.js +103 -16
  16. package/dist/packages/agent-harness-opentui/src/install.d.ts +8 -0
  17. package/dist/packages/agent-harness-opentui/src/install.js +10 -0
  18. package/dist/packages/agent-harness-opentui/src/semantic.js +12 -10
  19. package/dist/packages/agent-harness-opentui/src/visual-capture.d.ts +56 -0
  20. package/dist/packages/agent-harness-opentui/src/visual-capture.js +103 -0
  21. package/dist/src/agent-harness/mock-model.d.ts +28 -0
  22. package/dist/src/agent-harness/mock-model.js +63 -1
  23. package/dist/src/agent-harness/test-spawn.js +31 -0
  24. package/dist/src/cli/config/screen-providers.js +1 -1
  25. package/dist/src/cli/cost-forensics.d.ts +10 -0
  26. package/dist/src/cli/cost-forensics.js +18 -3
  27. package/dist/src/cli/keys-bundle.d.ts +1 -1
  28. package/dist/src/cli/keys-bundle.js +1 -1
  29. package/dist/src/cli/keys.d.ts +2 -2
  30. package/dist/src/cli/keys.js +19 -81
  31. package/dist/src/council/clarifier.d.ts +28 -2
  32. package/dist/src/council/clarifier.js +81 -15
  33. package/dist/src/council/context.js +49 -15
  34. package/dist/src/council/debate-checkpoint.d.ts +129 -0
  35. package/dist/src/council/debate-checkpoint.js +176 -0
  36. package/dist/src/council/debate-planner.js +51 -3
  37. package/dist/src/council/debate-summary.d.ts +25 -0
  38. package/dist/src/council/debate-summary.js +85 -0
  39. package/dist/src/council/debate.d.ts +169 -2
  40. package/dist/src/council/debate.js +1210 -134
  41. package/dist/src/council/index.d.ts +85 -1
  42. package/dist/src/council/index.js +634 -196
  43. package/dist/src/council/leader.d.ts +26 -0
  44. package/dist/src/council/leader.js +150 -9
  45. package/dist/src/council/llm.d.ts +32 -0
  46. package/dist/src/council/llm.js +231 -38
  47. package/dist/src/council/panel-select.d.ts +30 -0
  48. package/dist/src/council/panel-select.js +72 -0
  49. package/dist/src/council/planner.js +23 -0
  50. package/dist/src/council/preflight.d.ts +7 -0
  51. package/dist/src/council/preflight.js +14 -2
  52. package/dist/src/council/prompts.d.ts +30 -3
  53. package/dist/src/council/prompts.js +234 -64
  54. package/dist/src/council/stance-recall.d.ts +42 -0
  55. package/dist/src/council/stance-recall.js +57 -0
  56. package/dist/src/council/strip-think.d.ts +17 -0
  57. package/dist/src/council/strip-think.js +33 -0
  58. package/dist/src/council/types.d.ts +128 -0
  59. package/dist/src/ee/artifact-cache.d.ts +16 -0
  60. package/dist/src/ee/artifact-cache.js +32 -0
  61. package/dist/src/ee/auth.d.ts +1 -0
  62. package/dist/src/ee/auth.js +15 -2
  63. package/dist/src/ee/bridge.d.ts +10 -0
  64. package/dist/src/ee/bridge.js +58 -0
  65. package/dist/src/ee/client.js +81 -18
  66. package/dist/src/ee/export-transcripts.d.ts +1 -0
  67. package/dist/src/ee/export-transcripts.js +8 -10
  68. package/dist/src/ee/extract-session.js +29 -0
  69. package/dist/src/ee/extract-style.d.ts +58 -0
  70. package/dist/src/ee/extract-style.js +270 -0
  71. package/dist/src/ee/recall-ledger.d.ts +9 -0
  72. package/dist/src/ee/recall-ledger.js +3 -0
  73. package/dist/src/ee/scope.d.ts +1 -0
  74. package/dist/src/ee/scope.js +26 -1
  75. package/dist/src/ee/search.d.ts +7 -0
  76. package/dist/src/ee/search.js +24 -0
  77. package/dist/src/ee/transcript-emit.js +2 -0
  78. package/dist/src/ee/types.d.ts +22 -0
  79. package/dist/src/ee/who-am-i-brain.d.ts +35 -0
  80. package/dist/src/ee/who-am-i-brain.js +220 -0
  81. package/dist/src/ee/who-am-i.d.ts +10 -3
  82. package/dist/src/ee/who-am-i.js +12 -0
  83. package/dist/src/ee/workflow-event.d.ts +48 -0
  84. package/dist/src/ee/workflow-event.js +81 -0
  85. package/dist/src/flow/compaction/compress.d.ts +3 -3
  86. package/dist/src/flow/compaction/compress.js +45 -8
  87. package/dist/src/flow/compaction/extract.d.ts +4 -7
  88. package/dist/src/flow/compaction/extract.js +50 -10
  89. package/dist/src/flow/compaction/index.d.ts +13 -1
  90. package/dist/src/flow/compaction/index.js +70 -3
  91. package/dist/src/flow/compaction/input-guard.d.ts +24 -0
  92. package/dist/src/flow/compaction/input-guard.js +43 -0
  93. package/dist/src/flow/fold-planning.d.ts +36 -0
  94. package/dist/src/flow/fold-planning.js +83 -0
  95. package/dist/src/flow/hierarchy.d.ts +146 -0
  96. package/dist/src/flow/hierarchy.js +427 -0
  97. package/dist/src/flow/index.d.ts +1 -0
  98. package/dist/src/flow/index.js +2 -0
  99. package/dist/src/flow/run-artifacts.d.ts +102 -0
  100. package/dist/src/flow/run-artifacts.js +208 -0
  101. package/dist/src/generated/version.d.ts +1 -1
  102. package/dist/src/generated/version.js +1 -1
  103. package/dist/src/gsd/assessment-schema.d.ts +44 -0
  104. package/dist/src/gsd/assessment-schema.js +134 -0
  105. package/dist/src/gsd/capability-registry.d.ts +45 -0
  106. package/dist/src/gsd/capability-registry.js +337 -0
  107. package/dist/src/gsd/complexity-assessor.d.ts +39 -0
  108. package/dist/src/gsd/complexity-assessor.js +152 -0
  109. package/dist/src/gsd/config-bridge.d.ts +7 -0
  110. package/dist/src/gsd/config-bridge.js +114 -0
  111. package/dist/src/gsd/config-loader.d.ts +27 -0
  112. package/dist/src/gsd/config-loader.js +50 -0
  113. package/dist/src/gsd/council-context.d.ts +44 -0
  114. package/dist/src/gsd/council-context.js +114 -0
  115. package/dist/src/gsd/ee-closure.d.ts +28 -0
  116. package/dist/src/gsd/ee-closure.js +49 -0
  117. package/dist/src/gsd/flags.d.ts +55 -0
  118. package/dist/src/gsd/flags.js +83 -0
  119. package/dist/src/gsd/gsd-dispatch.d.ts +58 -0
  120. package/dist/src/gsd/gsd-dispatch.js +131 -0
  121. package/dist/src/gsd/gsd-runtime.d.ts +22 -0
  122. package/dist/src/gsd/gsd-runtime.js +37 -0
  123. package/dist/src/gsd/host-adapter.d.ts +11 -0
  124. package/dist/src/gsd/host-adapter.js +29 -0
  125. package/dist/src/gsd/index.d.ts +24 -1
  126. package/dist/src/gsd/index.js +27 -0
  127. package/dist/src/gsd/loop-host-contract.d.ts +21 -0
  128. package/dist/src/gsd/loop-host-contract.js +39 -0
  129. package/dist/src/gsd/loop-host.d.ts +69 -0
  130. package/dist/src/gsd/loop-host.js +245 -0
  131. package/dist/src/gsd/loop-resolver.d.ts +36 -0
  132. package/dist/src/gsd/loop-resolver.js +79 -0
  133. package/dist/src/gsd/model-tier.d.ts +13 -0
  134. package/dist/src/gsd/model-tier.js +45 -0
  135. package/dist/src/gsd/mutation-gate.d.ts +16 -0
  136. package/dist/src/gsd/mutation-gate.js +41 -0
  137. package/dist/src/gsd/native-roadmap.d.ts +89 -0
  138. package/dist/src/gsd/native-roadmap.js +343 -0
  139. package/dist/src/gsd/native-state.d.ts +47 -0
  140. package/dist/src/gsd/native-state.js +220 -0
  141. package/dist/src/gsd/paths.d.ts +23 -0
  142. package/dist/src/gsd/paths.js +66 -0
  143. package/dist/src/gsd/phase-dag.d.ts +12 -0
  144. package/dist/src/gsd/phase-dag.js +94 -0
  145. package/dist/src/gsd/phase-sync.d.ts +42 -0
  146. package/dist/src/gsd/phase-sync.js +321 -0
  147. package/dist/src/gsd/pil-gate-context.d.ts +13 -0
  148. package/dist/src/gsd/pil-gate-context.js +64 -0
  149. package/dist/src/gsd/pil-gate-critic.d.ts +19 -0
  150. package/dist/src/gsd/pil-gate-critic.js +74 -0
  151. package/dist/src/gsd/plan-council-prompts.d.ts +25 -0
  152. package/dist/src/gsd/plan-council-prompts.js +79 -0
  153. package/dist/src/gsd/plan-council.d.ts +44 -0
  154. package/dist/src/gsd/plan-council.js +251 -0
  155. package/dist/src/gsd/plan-gate-vocabulary.d.ts +40 -0
  156. package/dist/src/gsd/plan-gate-vocabulary.js +64 -0
  157. package/dist/src/gsd/product-workspace.d.ts +13 -0
  158. package/dist/src/gsd/product-workspace.js +124 -0
  159. package/dist/src/gsd/ship-bridge.d.ts +25 -0
  160. package/dist/src/gsd/ship-bridge.js +65 -0
  161. package/dist/src/gsd/state-document.d.ts +40 -0
  162. package/dist/src/gsd/state-document.js +163 -0
  163. package/dist/src/gsd/verdict-schema.d.ts +39 -0
  164. package/dist/src/gsd/verdict-schema.js +144 -0
  165. package/dist/src/gsd/verify-context.d.ts +22 -0
  166. package/dist/src/gsd/verify-context.js +27 -0
  167. package/dist/src/gsd/verify-council-prompts.d.ts +19 -0
  168. package/dist/src/gsd/verify-council-prompts.js +85 -0
  169. package/dist/src/gsd/verify-council.d.ts +25 -0
  170. package/dist/src/gsd/verify-council.js +119 -0
  171. package/dist/src/gsd/verify-gate-vocabulary.d.ts +25 -0
  172. package/dist/src/gsd/verify-gate-vocabulary.js +46 -0
  173. package/dist/src/gsd/workflow-engine.d.ts +60 -0
  174. package/dist/src/gsd/workflow-engine.js +207 -0
  175. package/dist/src/gsd/workflow-tools.d.ts +13 -0
  176. package/dist/src/gsd/workflow-tools.js +277 -0
  177. package/dist/src/hooks/index.js +1 -1
  178. package/dist/src/index.js +44 -11
  179. package/dist/src/maintain/pr-builder.js +23 -13
  180. package/dist/src/mcp/auto-setup.js +57 -32
  181. package/dist/src/mcp/client-pool.js +1 -1
  182. package/dist/src/mcp/ee-tools.js +1 -0
  183. package/dist/src/mcp/research-onboarding.js +8 -7
  184. package/dist/src/mcp/runtime.js +34 -2
  185. package/dist/src/models/catalog-client.d.ts +87 -0
  186. package/dist/src/models/catalog-client.js +105 -38
  187. package/dist/src/models/catalog.json +528 -265
  188. package/dist/src/models/registry.d.ts +22 -7
  189. package/dist/src/models/registry.js +73 -10
  190. package/dist/src/ops/doctor.js +1 -1
  191. package/dist/src/orchestrator/auto-commit.js +1 -1
  192. package/dist/src/orchestrator/batch-turn-runner.js +2 -2
  193. package/dist/src/orchestrator/cache-prefix.d.ts +67 -0
  194. package/dist/src/orchestrator/cache-prefix.js +83 -0
  195. package/dist/src/orchestrator/compact-request.d.ts +32 -0
  196. package/dist/src/orchestrator/compact-request.js +41 -0
  197. package/dist/src/orchestrator/compaction.d.ts +10 -0
  198. package/dist/src/orchestrator/compaction.js +27 -7
  199. package/dist/src/orchestrator/council-manager.d.ts +12 -3
  200. package/dist/src/orchestrator/council-manager.js +65 -24
  201. package/dist/src/orchestrator/cross-turn-dedup.d.ts +6 -0
  202. package/dist/src/orchestrator/cross-turn-dedup.js +22 -1
  203. package/dist/src/orchestrator/error-utils.d.ts +29 -0
  204. package/dist/src/orchestrator/error-utils.js +132 -24
  205. package/dist/src/orchestrator/grounding-check.js +39 -1
  206. package/dist/src/orchestrator/message-processor.js +242 -33
  207. package/dist/src/orchestrator/orchestrator.d.ts +39 -3
  208. package/dist/src/orchestrator/orchestrator.js +651 -102
  209. package/dist/src/orchestrator/preprocessor.js +1 -1
  210. package/dist/src/orchestrator/proactive-compact-detector.d.ts +26 -0
  211. package/dist/src/orchestrator/proactive-compact-detector.js +36 -0
  212. package/dist/src/orchestrator/prompts.js +17 -17
  213. package/dist/src/orchestrator/reactive-delegation.d.ts +39 -0
  214. package/dist/src/orchestrator/reactive-delegation.js +59 -0
  215. package/dist/src/orchestrator/retry-classifier.d.ts +2 -1
  216. package/dist/src/orchestrator/retry-classifier.js +46 -2
  217. package/dist/src/orchestrator/safety-intercept.d.ts +45 -0
  218. package/dist/src/orchestrator/safety-intercept.js +55 -0
  219. package/dist/src/orchestrator/scope-reminder.js +1 -1
  220. package/dist/src/orchestrator/session-experience.d.ts +2 -1
  221. package/dist/src/orchestrator/session-experience.js +2 -1
  222. package/dist/src/orchestrator/should-run-gate.d.ts +5 -0
  223. package/dist/src/orchestrator/should-run-gate.js +18 -0
  224. package/dist/src/orchestrator/stall-watchdog.d.ts +24 -3
  225. package/dist/src/orchestrator/stall-watchdog.js +47 -13
  226. package/dist/src/orchestrator/stream-runner.js +62 -29
  227. package/dist/src/orchestrator/sub-agent-cap.d.ts +13 -1
  228. package/dist/src/orchestrator/sub-agent-cap.js +16 -1
  229. package/dist/src/orchestrator/sub-agent-model-tier.d.ts +13 -1
  230. package/dist/src/orchestrator/sub-agent-model-tier.js +16 -2
  231. package/dist/src/orchestrator/subagent-compactor.d.ts +53 -1
  232. package/dist/src/orchestrator/subagent-compactor.js +126 -15
  233. package/dist/src/orchestrator/tool-engine.d.ts +22 -0
  234. package/dist/src/orchestrator/tool-engine.js +620 -56
  235. package/dist/src/orchestrator/tool-limit-auto-recover.d.ts +22 -0
  236. package/dist/src/orchestrator/tool-limit-auto-recover.js +30 -0
  237. package/dist/src/orchestrator/turn-runner-deps.d.ts +19 -0
  238. package/dist/src/orchestrator/turn-watchdog.d.ts +37 -0
  239. package/dist/src/orchestrator/turn-watchdog.js +55 -0
  240. package/dist/src/pil/agent-operating-contract.d.ts +1 -1
  241. package/dist/src/pil/agent-operating-contract.js +1 -1
  242. package/dist/src/pil/cheap-model-playbook.d.ts +1 -1
  243. package/dist/src/pil/cheap-model-playbook.js +5 -1
  244. package/dist/src/pil/cheap-model-workbooks.d.ts +1 -1
  245. package/dist/src/pil/cheap-model-workbooks.js +1 -1
  246. package/dist/src/pil/discovery-types.d.ts +1 -0
  247. package/dist/src/pil/discovery.js +16 -11
  248. package/dist/src/pil/layer1-intent.d.ts +18 -6
  249. package/dist/src/pil/layer1-intent.js +66 -757
  250. package/dist/src/pil/layer15-context-scan.js +15 -1
  251. package/dist/src/pil/layer3-ee-injection.js +23 -8
  252. package/dist/src/pil/layer4-gsd.js +69 -16
  253. package/dist/src/pil/layer5-context.js +7 -3
  254. package/dist/src/pil/layer6-output.d.ts +23 -0
  255. package/dist/src/pil/layer6-output.js +5 -1
  256. package/dist/src/pil/llm-classify.d.ts +33 -2
  257. package/dist/src/pil/llm-classify.js +123 -131
  258. package/dist/src/pil/native-capabilities-workbook.d.ts +1 -1
  259. package/dist/src/pil/native-capabilities-workbook.js +1 -0
  260. package/dist/src/pil/pipeline.js +34 -2
  261. package/dist/src/pil/response-tools.js +5 -3
  262. package/dist/src/pil/schema.d.ts +1 -0
  263. package/dist/src/pil/schema.js +2 -0
  264. package/dist/src/pil/types.d.ts +18 -0
  265. package/dist/src/playbook/directives.d.ts +4 -0
  266. package/dist/src/playbook/directives.js +17 -5
  267. package/dist/src/product-loop/backlog-builder.d.ts +14 -1
  268. package/dist/src/product-loop/backlog-builder.js +30 -6
  269. package/dist/src/product-loop/discovery-context-format.js +3 -1
  270. package/dist/src/product-loop/discovery-ecosystem.js +4 -1
  271. package/dist/src/product-loop/discovery-interview.js +32 -3
  272. package/dist/src/product-loop/discovery-schema.js +5 -1
  273. package/dist/src/product-loop/ideal-trace.d.ts +7 -0
  274. package/dist/src/product-loop/ideal-trace.js +64 -0
  275. package/dist/src/product-loop/index.d.ts +13 -1
  276. package/dist/src/product-loop/index.js +333 -52
  277. package/dist/src/product-loop/loop-driver.d.ts +7 -0
  278. package/dist/src/product-loop/loop-driver.js +310 -99
  279. package/dist/src/product-loop/phase-plan.d.ts +5 -0
  280. package/dist/src/product-loop/phase-plan.js +39 -2
  281. package/dist/src/product-loop/phase-runner.js +9 -1
  282. package/dist/src/product-loop/sprint-runner.d.ts +111 -0
  283. package/dist/src/product-loop/sprint-runner.js +559 -16
  284. package/dist/src/product-loop/types.d.ts +36 -5
  285. package/dist/src/providers/adapter.d.ts +1 -1
  286. package/dist/src/providers/adapter.js +3 -4
  287. package/dist/src/providers/auth/browser-flow.d.ts +1 -1
  288. package/dist/src/providers/auth/browser-flow.js +1 -1
  289. package/dist/src/providers/auth/openai-oauth.js +1 -1
  290. package/dist/src/providers/auth/registry.js +0 -34
  291. package/dist/src/providers/auth/token-store.js +4 -1
  292. package/dist/src/providers/auth/types.d.ts +1 -1
  293. package/dist/src/providers/auth/types.js +1 -1
  294. package/dist/src/providers/capabilities.d.ts +24 -5
  295. package/dist/src/providers/capabilities.js +42 -24
  296. package/dist/src/providers/endpoints.d.ts +2 -2
  297. package/dist/src/providers/endpoints.js +11 -10
  298. package/dist/src/providers/keychain.d.ts +1 -1
  299. package/dist/src/providers/keychain.js +7 -9
  300. package/dist/src/providers/mcp-vision-bridge.js +56 -146
  301. package/dist/src/providers/openai-compatible.js +8 -1
  302. package/dist/src/providers/pricing.d.ts +2 -2
  303. package/dist/src/providers/pricing.js +3 -13
  304. package/dist/src/providers/runtime.d.ts +27 -2
  305. package/dist/src/providers/runtime.js +78 -15
  306. package/dist/src/providers/strategies/base.strategy.d.ts +16 -0
  307. package/dist/src/providers/strategies/base.strategy.js +24 -1
  308. package/dist/src/providers/strategies/{siliconflow.strategy.d.ts → opencode-go.strategy.d.ts} +3 -3
  309. package/dist/src/providers/strategies/opencode-go.strategy.js +83 -0
  310. package/dist/src/providers/strategies/registry.js +4 -4
  311. package/dist/src/providers/strategies/thinking-mode.d.ts +109 -0
  312. package/dist/src/providers/strategies/thinking-mode.js +280 -1
  313. package/dist/src/providers/strategies/zai.strategy.d.ts +14 -0
  314. package/dist/src/providers/strategies/zai.strategy.js +44 -0
  315. package/dist/src/providers/types.d.ts +5 -6
  316. package/dist/src/providers/types.js +2 -2
  317. package/dist/src/providers/vision-backend.d.ts +47 -0
  318. package/dist/src/providers/vision-backend.js +258 -0
  319. package/dist/src/providers/vision-proxy.d.ts +22 -9
  320. package/dist/src/providers/vision-proxy.js +63 -132
  321. package/dist/src/providers/wire-debug.js +95 -0
  322. package/dist/src/router/decide.d.ts +13 -0
  323. package/dist/src/router/decide.js +138 -36
  324. package/dist/src/router/peak-hour.d.ts +38 -0
  325. package/dist/src/router/peak-hour.js +107 -0
  326. package/dist/src/router/step-router.js +3 -2
  327. package/dist/src/router/warm.js +4 -5
  328. package/dist/src/scaffold/continuation-prompt.d.ts +11 -0
  329. package/dist/src/scaffold/continuation-prompt.js +26 -0
  330. package/dist/src/scaffold/point-to-existing.d.ts +21 -0
  331. package/dist/src/scaffold/point-to-existing.js +25 -0
  332. package/dist/src/self-qa/agentic-loop.js +3 -3
  333. package/dist/src/{ui/state → state}/active-run.d.ts +19 -0
  334. package/dist/src/{ui/state → state}/active-run.js +21 -0
  335. package/dist/src/{ui/status-bar/store.d.ts → state/status-bar-store.d.ts} +4 -1
  336. package/dist/src/{ui/status-bar/store.js → state/status-bar-store.js} +12 -9
  337. package/dist/src/state/turn-trace.d.ts +43 -0
  338. package/dist/src/state/turn-trace.js +32 -0
  339. package/dist/src/storage/db.js +2 -1
  340. package/dist/src/storage/index.d.ts +1 -1
  341. package/dist/src/storage/index.js +1 -1
  342. package/dist/src/storage/interaction-log.d.ts +1 -1
  343. package/dist/src/storage/migrations.js +71 -1
  344. package/dist/src/storage/sessions.d.ts +28 -10
  345. package/dist/src/storage/sessions.js +78 -21
  346. package/dist/src/storage/transcript-view.js +1 -1
  347. package/dist/src/storage/transcript.d.ts +51 -0
  348. package/dist/src/storage/transcript.js +284 -13
  349. package/dist/src/tools/file.d.ts +15 -0
  350. package/dist/src/tools/file.js +32 -0
  351. package/dist/src/tools/native-tools.js +5 -0
  352. package/dist/src/tools/registry.d.ts +3 -0
  353. package/dist/src/tools/registry.js +460 -22
  354. package/dist/src/tools/research.d.ts +29 -0
  355. package/dist/src/tools/research.js +233 -0
  356. package/dist/src/types/index.d.ts +118 -3
  357. package/dist/src/ui/app.js +0 -0
  358. package/dist/src/ui/cards/product-status-card.js +1 -1
  359. package/dist/src/ui/components/bubble-body-guard.d.ts +27 -0
  360. package/dist/src/ui/components/bubble-body-guard.js +50 -0
  361. package/dist/src/ui/components/context-rail.d.ts +26 -0
  362. package/dist/src/ui/components/context-rail.js +33 -0
  363. package/dist/src/ui/components/council-conclusion-card.d.ts +73 -0
  364. package/dist/src/ui/components/council-conclusion-card.js +420 -0
  365. package/dist/src/ui/components/council-debate-pill.d.ts +36 -0
  366. package/dist/src/ui/components/council-debate-pill.js +34 -0
  367. package/dist/src/ui/components/council-info-card.js +2 -2
  368. package/dist/src/ui/components/council-leader-bubble.d.ts +10 -3
  369. package/dist/src/ui/components/council-leader-bubble.js +21 -11
  370. package/dist/src/ui/components/council-message-bubble.d.ts +15 -2
  371. package/dist/src/ui/components/council-message-bubble.js +16 -15
  372. package/dist/src/ui/components/council-phase-timeline.d.ts +9 -1
  373. package/dist/src/ui/components/council-phase-timeline.js +49 -15
  374. package/dist/src/ui/components/council-placeholder-bubble.d.ts +16 -9
  375. package/dist/src/ui/components/council-placeholder-bubble.js +32 -29
  376. package/dist/src/ui/components/council-question-card.js +12 -12
  377. package/dist/src/ui/components/council-rail-rounds.d.ts +26 -0
  378. package/dist/src/ui/components/council-rail-rounds.js +57 -0
  379. package/dist/src/ui/components/council-round-group.d.ts +38 -0
  380. package/dist/src/ui/components/council-round-group.js +88 -0
  381. package/dist/src/ui/components/council-status-list.d.ts +3 -1
  382. package/dist/src/ui/components/council-status-list.js +36 -24
  383. package/dist/src/ui/components/council-synthesis-banner.d.ts +7 -2
  384. package/dist/src/ui/components/council-synthesis-banner.js +20 -5
  385. package/dist/src/ui/components/halt-recovery-card.js +9 -5
  386. package/dist/src/ui/components/jump-to-latest-pill.d.ts +11 -0
  387. package/dist/src/ui/components/jump-to-latest-pill.js +14 -0
  388. package/dist/src/ui/components/prompt-box.js +18 -16
  389. package/dist/src/ui/components/session-tree-card.d.ts +14 -0
  390. package/dist/src/ui/components/session-tree-card.js +46 -0
  391. package/dist/src/ui/components/slash-inline-menu.d.ts +12 -0
  392. package/dist/src/ui/components/slash-inline-menu.js +26 -5
  393. package/dist/src/ui/components/task-list-panel.d.ts +14 -1
  394. package/dist/src/ui/components/task-list-panel.js +22 -2
  395. package/dist/src/ui/containers/modals-layer.d.ts +2 -1
  396. package/dist/src/ui/hooks/use-session-picker.d.ts +3 -3
  397. package/dist/src/ui/mcp-modal.js +2 -4
  398. package/dist/src/ui/modals/api-key-modal.js +1 -1
  399. package/dist/src/ui/modals/connect-modal.js +4 -3
  400. package/dist/src/ui/modals/session-picker-modal.d.ts +2 -2
  401. package/dist/src/ui/modals/session-picker-modal.js +3 -5
  402. package/dist/src/ui/picker-providers.d.ts +1 -1
  403. package/dist/src/ui/picker-providers.js +1 -1
  404. package/dist/src/ui/primitives/index.d.ts +1 -0
  405. package/dist/src/ui/primitives/index.js +2 -0
  406. package/dist/src/ui/primitives/semantic-primitives.d.ts +76 -0
  407. package/dist/src/ui/primitives/semantic-primitives.js +81 -0
  408. package/dist/src/ui/slash/compact.js +5 -7
  409. package/dist/src/ui/slash/cost.js +1 -1
  410. package/dist/src/ui/slash/council.js +19 -1
  411. package/dist/src/ui/slash/debug.d.ts +3 -31
  412. package/dist/src/ui/slash/debug.js +9 -20
  413. package/dist/src/ui/slash/ideal.d.ts +6 -2
  414. package/dist/src/ui/slash/ideal.js +97 -7
  415. package/dist/src/ui/slash/menu-items.d.ts +7 -0
  416. package/dist/src/ui/slash/menu-items.js +12 -18
  417. package/dist/src/ui/slash/registry.d.ts +2 -0
  418. package/dist/src/ui/slash/registry.js +4 -0
  419. package/dist/src/ui/status-bar/cache-hit.d.ts +6 -0
  420. package/dist/src/ui/status-bar/cache-hit.js +9 -0
  421. package/dist/src/ui/status-bar/index.d.ts +1 -1
  422. package/dist/src/ui/status-bar/index.js +7 -3
  423. package/dist/src/ui/status-bar/usd-meter.d.ts +5 -4
  424. package/dist/src/ui/status-bar/usd-meter.js +6 -4
  425. package/dist/src/ui/theme.d.ts +1 -0
  426. package/dist/src/ui/theme.js +2 -0
  427. package/dist/src/ui/types.d.ts +7 -0
  428. package/dist/src/ui/use-app-logic.js +0 -0
  429. package/dist/src/ui/utils/format.d.ts +14 -0
  430. package/dist/src/ui/utils/format.js +23 -3
  431. package/dist/src/usage/downgrade.js +2 -2
  432. package/dist/src/usage/product-ledger.js +2 -2
  433. package/dist/src/utils/install-manager.js +2 -1
  434. package/dist/src/utils/logger.js +2 -2
  435. package/dist/src/utils/permission-mode.js +5 -3
  436. package/dist/src/utils/redactor.js +1 -1
  437. package/dist/src/utils/settings.d.ts +153 -5
  438. package/dist/src/utils/settings.js +233 -29
  439. package/dist/src/utils/visible-retry.d.ts +11 -0
  440. package/dist/src/utils/visible-retry.js +10 -1
  441. package/dist/src/verify/entrypoint.d.ts +1 -1
  442. package/dist/src/verify/entrypoint.js +1 -1
  443. package/dist/src/verify/recipes.d.ts +13 -0
  444. package/dist/src/verify/recipes.js +15 -0
  445. package/package.json +135 -132
  446. package/dist/src/providers/auth/gcloud.d.ts +0 -28
  447. package/dist/src/providers/auth/gcloud.js +0 -102
  448. package/dist/src/providers/auth/gemini-oauth.d.ts +0 -82
  449. package/dist/src/providers/auth/gemini-oauth.js +0 -472
  450. package/dist/src/providers/gemini.d.ts +0 -11
  451. package/dist/src/providers/gemini.js +0 -45
  452. package/dist/src/providers/siliconflow-sse-repair.d.ts +0 -58
  453. package/dist/src/providers/siliconflow-sse-repair.js +0 -177
  454. package/dist/src/providers/strategies/google.strategy.d.ts +0 -22
  455. package/dist/src/providers/strategies/google.strategy.js +0 -174
  456. package/dist/src/providers/strategies/siliconflow.strategy.js +0 -29
  457. package/dist/src/ui/containers/chat-feed.d.ts +0 -40
  458. package/dist/src/ui/containers/chat-feed.js +0 -66
package/LICENSE CHANGED
@@ -1,9 +1,21 @@
1
- Copyright (c) 2026 muonroi. All rights reserved.
1
+ MIT License
2
2
 
3
- Commercial / Proprietary License
3
+ Copyright (c) 2026 muonroi
4
4
 
5
- This software and associated documentation files (the "Software") are proprietary to muonroi.
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
6
11
 
7
- Unauthorized copying, modification, distribution, transmission, performance, display or other use of this Software, via any medium, is strictly prohibited.
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
8
14
 
9
- The Software is provided "as is", without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with the software or the use or other dealings in the Software.
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -1,5 +1,13 @@
1
1
  import { type Predicate } from "./predicate.js";
2
- import type { LiveEvent, LiveFrame, UINode } from "./protocol.js";
2
+ import type { LiveEvent, LiveFrame, UINode, VisualFrame } from "./protocol.js";
3
+ import { type VisualQualityReport } from "./visual-quality.js";
4
+ /** A single decoded cell from the rendered grid (returned by `visual_cell`). */
5
+ export type VisualCell = {
6
+ char: string;
7
+ fg: string;
8
+ bg: string;
9
+ attrs: number;
10
+ };
3
11
  type WaitConditionIdle = {
4
12
  idle: true;
5
13
  };
@@ -31,6 +39,9 @@ type DriverDeps = {
31
39
  type Ingested = {
32
40
  kind: "frame";
33
41
  frame: LiveFrame;
42
+ } | {
43
+ kind: "visual";
44
+ frame: VisualFrame;
34
45
  } | {
35
46
  kind: "idle";
36
47
  } | {
@@ -69,6 +80,21 @@ export type Driver = {
69
80
  */
70
81
  events: (filter?: EventFilter) => AsyncIterable<LiveEvent>;
71
82
  render_text: () => string;
83
+ /** Latest VisualFrame — the ACTUAL rendered cell grid (colors + attributes),
84
+ * or null if the TUI has not emitted one (renderer not attached). */
85
+ snapshot_visual: () => VisualFrame | null;
86
+ /** Render the visual grid as plain text (what a human reads on screen) —
87
+ * faithful to the rendered characters, unlike render_text (semantic tree). */
88
+ render_visual: () => string;
89
+ /** Decode the cell at (row, col) — char + fg/bg hex + attribute bits — from
90
+ * the latest VisualFrame, accounting for wide (2-col) glyphs. Null if out of
91
+ * range or no visual frame yet. */
92
+ visual_cell: (row: number, col: number) => VisualCell | null;
93
+ /** Programmatic visual-quality heuristics over the latest VisualFrame:
94
+ * near-empty-row ratio, blank-row runs, whitespace density, and mojibake —
95
+ * the exact "messy render" signals the semantic tree is blind to. Null if no
96
+ * visual frame yet. */
97
+ visual_quality: () => VisualQualityReport | null;
72
98
  _ingest: (m: Ingested) => void;
73
99
  /** Called when the TUI exits. Cleanly terminates all active `events()` iterables. */
74
100
  _closeAllSubscribers: () => void;
@@ -1,5 +1,6 @@
1
1
  import { evaluatePredicate, predicateSchema } from "./predicate.js";
2
2
  import { matchSelector } from "./selector.js";
3
+ import { computeVisualQuality } from "./visual-quality.js";
3
4
  // ---------------------------------------------------------------------------
4
5
  // Constants
5
6
  // ---------------------------------------------------------------------------
@@ -12,6 +13,7 @@ const PER_SUBSCRIBER_QUEUE_CAP = 256;
12
13
  // ---------------------------------------------------------------------------
13
14
  export function createDriver(deps) {
14
15
  let latestFrame = null;
16
+ let latestVisualFrame = null;
15
17
  let lastIdleAt = -1;
16
18
  const eventBuffer = [];
17
19
  const waiters = new Set();
@@ -276,10 +278,54 @@ export function createDriver(deps) {
276
278
  return "(no frame)";
277
279
  return latestFrame.nodes.map((n) => renderNode(n, 0)).join("\n");
278
280
  },
281
+ snapshot_visual() {
282
+ return latestVisualFrame;
283
+ },
284
+ render_visual() {
285
+ if (!latestVisualFrame)
286
+ return "(no visual frame)";
287
+ return latestVisualFrame.lines
288
+ .map((ln) => ln.spans
289
+ .map((s) => s.text)
290
+ .join("")
291
+ .replace(/\s+$/, ""))
292
+ .join("\n");
293
+ },
294
+ visual_cell(row, col) {
295
+ const frame = latestVisualFrame;
296
+ if (!frame)
297
+ return null;
298
+ const line = frame.lines[row];
299
+ if (!line)
300
+ return null;
301
+ // Walk spans accumulating display columns; a wide glyph spans 2 columns
302
+ // but its span.width already reflects that, so index within the run by
303
+ // string position while tracking the column cursor.
304
+ let colCursor = 0;
305
+ for (const span of line.spans) {
306
+ const chars = [...span.text];
307
+ for (const ch of chars) {
308
+ // A char occupies span.width / chars.length columns on average; for
309
+ // the common width===text-length case this is 1. Round to stay integral.
310
+ const chCols = Math.max(1, Math.round(span.width / Math.max(1, chars.length)));
311
+ if (col >= colCursor && col < colCursor + chCols) {
312
+ return { char: ch, fg: span.fg, bg: span.bg, attrs: span.attrs };
313
+ }
314
+ colCursor += chCols;
315
+ }
316
+ }
317
+ return null;
318
+ },
319
+ visual_quality() {
320
+ return latestVisualFrame ? computeVisualQuality(latestVisualFrame) : null;
321
+ },
279
322
  _ingest(m) {
280
323
  if (m.kind === "frame") {
281
324
  latestFrame = m.frame;
282
325
  }
326
+ else if (m.kind === "visual") {
327
+ latestVisualFrame = m.frame;
328
+ }
283
329
  else if (m.kind === "idle") {
284
330
  lastIdleAt = Date.now();
285
331
  }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * event-tee.ts — optional JSONL sink for harness LiveEvents.
3
+ *
4
+ * Why: the MCP wake mechanism that re-invokes an external agent is a background
5
+ * task (e.g. a milestone watcher), which is a SEPARATE process from the MCP
6
+ * server. That watcher can only read files — it cannot reach the in-process
7
+ * driver that holds the real UI. So a generic, reusable milestone watcher needs
8
+ * the harness to persist events to a file it can tail.
9
+ *
10
+ * The DB only records debate-END rows (synthesis/debate_complete), so a
11
+ * DB-poll watcher is blind to mid-flight modal pauses (askcard-open) that never
12
+ * write a DB row. Teeing every LiveEvent to a JSONL file closes that gap for
13
+ * ANY event kind.
14
+ *
15
+ * Ephemeral events (toast, disconnect, retry, ee-*) flash and vanish before an
16
+ * agent can wake + render, so for those kinds — and ONLY those — we attach a
17
+ * plain-text visual snapshot captured at emit time, so the watcher's wake
18
+ * payload carries the exact frame. Persistent states (askcard, post-debate
19
+ * card) stay put, so the agent renders them fresh on demand; embedding a stale
20
+ * snapshot for those would be worse, not better.
21
+ *
22
+ * Gated entirely behind MUONROI_HARNESS_EVENT_LOG — unset means zero behavior
23
+ * change (clean checkouts are byte-identical to before this file existed).
24
+ */
25
+ import type { LiveEvent } from "./protocol.js";
26
+ /**
27
+ * Event kinds that flash and disappear before an agent can wake + render.
28
+ * Only these carry an at-emit visual snapshot in the teed line.
29
+ */
30
+ export declare const EPHEMERAL_KINDS: ReadonlySet<string>;
31
+ /** One teed JSONL record. */
32
+ export interface TeedEventLine {
33
+ ts: number;
34
+ kind: string;
35
+ event: LiveEvent;
36
+ /** Present only for EPHEMERAL_KINDS — plain-text visual frame at emit time. */
37
+ visualText?: string;
38
+ }
39
+ /**
40
+ * Build an event-tee sink. Returns null when MUONROI_HARNESS_EVENT_LOG is unset
41
+ * or blank (no-op path — caller skips it entirely).
42
+ *
43
+ * @param getVisualText - lazily renders the current visual frame to plain text
44
+ * (driver.render_visual). Called ONLY for ephemeral kinds, so persistent
45
+ * events pay nothing.
46
+ * @param envValue - value of MUONROI_HARNESS_EVENT_LOG (injectable for tests).
47
+ */
48
+ export declare function createEventTee(getVisualText: () => string | null, envValue?: string): ((event: LiveEvent) => void) | null;
@@ -0,0 +1,77 @@
1
+ /**
2
+ * event-tee.ts — optional JSONL sink for harness LiveEvents.
3
+ *
4
+ * Why: the MCP wake mechanism that re-invokes an external agent is a background
5
+ * task (e.g. a milestone watcher), which is a SEPARATE process from the MCP
6
+ * server. That watcher can only read files — it cannot reach the in-process
7
+ * driver that holds the real UI. So a generic, reusable milestone watcher needs
8
+ * the harness to persist events to a file it can tail.
9
+ *
10
+ * The DB only records debate-END rows (synthesis/debate_complete), so a
11
+ * DB-poll watcher is blind to mid-flight modal pauses (askcard-open) that never
12
+ * write a DB row. Teeing every LiveEvent to a JSONL file closes that gap for
13
+ * ANY event kind.
14
+ *
15
+ * Ephemeral events (toast, disconnect, retry, ee-*) flash and vanish before an
16
+ * agent can wake + render, so for those kinds — and ONLY those — we attach a
17
+ * plain-text visual snapshot captured at emit time, so the watcher's wake
18
+ * payload carries the exact frame. Persistent states (askcard, post-debate
19
+ * card) stay put, so the agent renders them fresh on demand; embedding a stale
20
+ * snapshot for those would be worse, not better.
21
+ *
22
+ * Gated entirely behind MUONROI_HARNESS_EVENT_LOG — unset means zero behavior
23
+ * change (clean checkouts are byte-identical to before this file existed).
24
+ */
25
+ import { appendFileSync } from "node:fs";
26
+ /**
27
+ * Event kinds that flash and disappear before an agent can wake + render.
28
+ * Only these carry an at-emit visual snapshot in the teed line.
29
+ */
30
+ export const EPHEMERAL_KINDS = new Set([
31
+ "toast",
32
+ "disconnect",
33
+ "stream-retry",
34
+ "ee-timeout",
35
+ "ee-error",
36
+ "grounding-flag",
37
+ ]);
38
+ /**
39
+ * Build an event-tee sink. Returns null when MUONROI_HARNESS_EVENT_LOG is unset
40
+ * or blank (no-op path — caller skips it entirely).
41
+ *
42
+ * @param getVisualText - lazily renders the current visual frame to plain text
43
+ * (driver.render_visual). Called ONLY for ephemeral kinds, so persistent
44
+ * events pay nothing.
45
+ * @param envValue - value of MUONROI_HARNESS_EVENT_LOG (injectable for tests).
46
+ */
47
+ export function createEventTee(getVisualText, envValue) {
48
+ const path = (envValue ?? "").trim();
49
+ if (!path)
50
+ return null;
51
+ return (event) => {
52
+ // The idle sentinel arrives as { t: "idle" } — never here. Guard anyway so
53
+ // a malformed event can't crash the ingest loop.
54
+ const kind = event.kind;
55
+ if (typeof kind !== "string")
56
+ return;
57
+ const line = { ts: Date.now(), kind, event };
58
+ if (EPHEMERAL_KINDS.has(kind)) {
59
+ try {
60
+ const visual = getVisualText();
61
+ if (visual)
62
+ line.visualText = visual;
63
+ }
64
+ catch (err) {
65
+ // A visual-render failure must not drop the event record itself.
66
+ console.error(`[event-tee] visual snapshot failed for kind=${kind}: ${err?.message}`);
67
+ }
68
+ }
69
+ try {
70
+ appendFileSync(path, `${JSON.stringify(line)}\n`);
71
+ }
72
+ catch (err) {
73
+ console.error(`[event-tee] append failed (path=${path}, kind=${kind}): ${err?.message}`);
74
+ }
75
+ };
76
+ }
77
+ //# sourceMappingURL=event-tee.js.map
@@ -13,6 +13,7 @@
13
13
  */
14
14
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
15
15
  import { type Driver } from "./driver.js";
16
+ import type { LiveEvent } from "./protocol.js";
16
17
  /**
17
18
  * The result returned by a HarnessSpawn implementation.
18
19
  * Both the POSIX fd-3/4 and Windows named-pipe transports satisfy this shape.
@@ -95,6 +96,16 @@ export declare function registerAsyncTools(server: McpServer, getDriver: () => D
95
96
  export declare function createMcpHarnessServer({ spawn }: {
96
97
  spawn: HarnessSpawn;
97
98
  }): McpServer;
99
+ /**
100
+ * Build the onLine sidechannel handler: parse each JSON line, ingest frames /
101
+ * visuals / idle / events into the driver, and tee events to the optional JSONL
102
+ * sink. Extracted (and exported) so the frame/event/tee wiring is unit-testable
103
+ * without a live TUI or an MCP protocol handshake.
104
+ *
105
+ * @param driver the driver to ingest into (only _ingest is used).
106
+ * @param eventTee optional sink from createEventTee (null → no tee).
107
+ */
108
+ export declare function makeLineHandler(driver: Pick<Driver, "_ingest">, eventTee: ((event: LiveEvent) => void) | null): (line: string) => void;
98
109
  /**
99
110
  * Run the MCP harness driver over stdio with the OpenTUI spawn implementation
100
111
  * injected by the consumer.
@@ -18,6 +18,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
18
18
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
19
19
  import { z } from "zod";
20
20
  import { createDriver } from "./driver.js";
21
+ import { createEventTee } from "./event-tee.js";
21
22
  import { PROTOCOL_VERSION } from "./protocol.js";
22
23
  const ARG_ALLOW = /^(--agent-[a-z-]+(=.*)?|--mock-llm(=.+)?|--profile=[a-zA-Z0-9_-]+)$/;
23
24
  const ENV_KEY_RE = /^[A-Z_][A-Z0-9_]{0,63}$/;
@@ -133,7 +134,20 @@ export function validateMockLlmPath(value) {
133
134
  const sep = process.platform === "win32" ? "\\" : "/";
134
135
  return real === root || real.startsWith(root + sep);
135
136
  }
136
- const FEATURES = ["capabilities", "snapshot", "press", "type", "wait_for", "query", "expect", "render_text"];
137
+ const FEATURES = [
138
+ "capabilities",
139
+ "snapshot",
140
+ "press",
141
+ "type",
142
+ "wait_for",
143
+ "query",
144
+ "expect",
145
+ "render_text",
146
+ "render_visual",
147
+ "snapshot_visual",
148
+ "cell",
149
+ "visual_quality",
150
+ ];
137
151
  export function buildCapabilitiesPayload() {
138
152
  return {
139
153
  protocol: PROTOCOL_VERSION,
@@ -203,6 +217,42 @@ export function registerReadTools(server, getDriver) {
203
217
  return noDriver();
204
218
  return { content: [{ type: "text", text: d.render_text() }] };
205
219
  });
220
+ server.registerTool("tui.render_visual", {
221
+ description: "Render the ACTUAL rendered cell grid as plain text — the characters a human reads on screen. Unlike tui.render_text (semantic tree), this reflects the real render. Requires agent-mode with the renderer attached.",
222
+ inputSchema: {},
223
+ }, async () => {
224
+ const d = getDriver();
225
+ if (!d)
226
+ return noDriver();
227
+ return { content: [{ type: "text", text: d.render_visual() }] };
228
+ });
229
+ server.registerTool("tui.snapshot_visual", {
230
+ description: "Return the latest VisualFrame — the real rendered cell grid with per-cell char + fg/bg hex + attribute bits. null until the renderer emits one.",
231
+ inputSchema: {},
232
+ }, async () => {
233
+ const d = getDriver();
234
+ if (!d)
235
+ return noDriver();
236
+ return { content: [{ type: "text", text: JSON.stringify(d.snapshot_visual()) }] };
237
+ });
238
+ server.registerTool("tui.cell", {
239
+ description: "Decode the rendered cell at (row, col): char + fg/bg hex + attribute bits, from the latest VisualFrame. Use to assert the colors/formatting a human actually sees.",
240
+ inputSchema: { row: z.number().int().min(0), col: z.number().int().min(0) },
241
+ }, async ({ row, col }) => {
242
+ const d = getDriver();
243
+ if (!d)
244
+ return noDriver();
245
+ return { content: [{ type: "text", text: JSON.stringify(d.visual_cell(row, col)) }] };
246
+ });
247
+ server.registerTool("tui.visual_quality", {
248
+ description: "Heuristic visual-quality report over the rendered grid: near-empty-row ratio, blank-row runs, whitespace density, mojibake, a 0-100 score and issues[]. Catches 'messy render' the semantic tree cannot see.",
249
+ inputSchema: {},
250
+ }, async () => {
251
+ const d = getDriver();
252
+ if (!d)
253
+ return noDriver();
254
+ return { content: [{ type: "text", text: JSON.stringify(d.visual_quality()) }] };
255
+ });
206
256
  }
207
257
  export function registerActionTools(server, getDriver) {
208
258
  const noDriver = () => ({
@@ -459,22 +509,13 @@ export function createMcpHarnessServer({ spawn }) {
459
509
  sendKey: (k) => sendLine(JSON.stringify({ op: "press", key: k })),
460
510
  sendType: (t) => sendLine(JSON.stringify({ op: "type", text: t })),
461
511
  });
512
+ // Optional JSONL event sink for external milestone watchers (null unless
513
+ // MUONROI_HARNESS_EVENT_LOG is set). Ephemeral kinds carry an at-emit
514
+ // visual snapshot so flash events aren't lost before an agent wakes.
515
+ const eventTee = createEventTee(() => driver.render_visual(), process.env["MUONROI_HARNESS_EVENT_LOG"]);
462
516
  // onLine already delivers complete newline-stripped lines — no extra
463
517
  // splitting required.
464
- const unsub = onLine((line) => {
465
- try {
466
- const msg = JSON.parse(line);
467
- if (msg.mode === "live")
468
- driver._ingest({ kind: "frame", frame: msg });
469
- else if (msg.t === "idle")
470
- driver._ingest({ kind: "idle" });
471
- else if (msg.t === "event")
472
- driver._ingest({ kind: "event", event: msg });
473
- }
474
- catch {
475
- // ignore malformed lines
476
- }
477
- });
518
+ const unsub = onLine(makeLineHandler(driver, eventTee));
478
519
  spawnResult.exited.then(() => {
479
520
  unsub();
480
521
  if (currentPid === proc.pid) {
@@ -491,6 +532,37 @@ export function createMcpHarnessServer({ spawn }) {
491
532
  registerAsyncTools(server, () => currentDriver, { onStop });
492
533
  return server;
493
534
  }
535
+ /**
536
+ * Build the onLine sidechannel handler: parse each JSON line, ingest frames /
537
+ * visuals / idle / events into the driver, and tee events to the optional JSONL
538
+ * sink. Extracted (and exported) so the frame/event/tee wiring is unit-testable
539
+ * without a live TUI or an MCP protocol handshake.
540
+ *
541
+ * @param driver the driver to ingest into (only _ingest is used).
542
+ * @param eventTee optional sink from createEventTee (null → no tee).
543
+ */
544
+ export function makeLineHandler(driver, eventTee) {
545
+ return (line) => {
546
+ try {
547
+ const msg = JSON.parse(line);
548
+ if (msg.mode === "live")
549
+ driver._ingest({ kind: "frame", frame: msg });
550
+ else if (msg.mode === "visual")
551
+ driver._ingest({ kind: "visual", frame: msg });
552
+ else if (msg.t === "idle")
553
+ driver._ingest({ kind: "idle" });
554
+ else if (msg.t === "event") {
555
+ const event = msg;
556
+ driver._ingest({ kind: "event", event });
557
+ // Tee AFTER ingest so render_visual reflects the frame at this event.
558
+ eventTee?.(event);
559
+ }
560
+ }
561
+ catch {
562
+ // ignore malformed lines
563
+ }
564
+ };
565
+ }
494
566
  /**
495
567
  * Run the MCP harness driver over stdio with the OpenTUI spawn implementation
496
568
  * injected by the consumer.
@@ -1,5 +1,17 @@
1
1
  export declare const PROTOCOL_VERSION: "0.4.0";
2
- export type Role = "dialog" | "textbox" | "listbox" | "listitem" | "button" | "checkbox" | "radio" | "radiogroup" | "tab" | "tablist" | "tree" | "treeitem" | "table" | "row" | "cell" | "progressbar" | "spinner" | "log" | "statusbar" | "menu" | "menuitem" | "toast" | "tooltip" | "region";
2
+ /**
3
+ * Known accessibility-style roles for semantic blocks. Closed vocabulary —
4
+ * `Role` (below) additionally admits namespaced `x-*` custom roles so new UI
5
+ * surfaces can be introduced without a protocol version bump (additive-only).
6
+ */
7
+ export type KnownRole = "dialog" | "textbox" | "listbox" | "listitem" | "button" | "checkbox" | "radio" | "radiogroup" | "tab" | "tablist" | "tree" | "treeitem" | "table" | "row" | "cell" | "progressbar" | "spinner" | "log" | "statusbar" | "menu" | "menuitem" | "toast" | "tooltip" | "region" | "editor" | "diff" | "gutter" | "panel";
8
+ /**
9
+ * A semantic role. Either a well-known {@link KnownRole} or a namespaced
10
+ * `x-<custom>` role. The `x-` prefix keeps custom roles greppable and prevents
11
+ * silent collisions with future well-known roles — additive versioning via the
12
+ * `tui.capabilities` handshake rather than a breaking protocol bump.
13
+ */
14
+ export type Role = KnownRole | `x-${string}`;
3
15
  export type UINode = {
4
16
  id: string;
5
17
  role: Role;
@@ -23,6 +35,58 @@ export type LiveFrame = {
23
35
  modals?: string[];
24
36
  nodes: UINode[];
25
37
  };
38
+ /**
39
+ * Text-attribute bit flags in {@link VisualSpan.attrs}. Mirrors the low byte of
40
+ * OpenTUI's `TextAttributes` (`ATTRIBUTE_BASE_MASK = 255`), so a consumer can
41
+ * decode `attrs & VisualAttr.BOLD` etc. without importing the renderer.
42
+ */
43
+ export declare const VisualAttr: {
44
+ readonly BOLD: 1;
45
+ readonly DIM: 2;
46
+ readonly ITALIC: 4;
47
+ readonly UNDERLINE: 8;
48
+ readonly BLINK: 16;
49
+ readonly INVERSE: 32;
50
+ readonly HIDDEN: 64;
51
+ readonly STRIKETHROUGH: 128;
52
+ };
53
+ /** One coalesced run of identically-styled cells on a visual line. */
54
+ export type VisualSpan = {
55
+ /** The run's text (wide-char continuation cells omitted; `width` reflects columns). */
56
+ text: string;
57
+ /** Foreground color as "#rrggbb" (or "#rrggbbaa" when alpha < 255). */
58
+ fg: string;
59
+ /** Background color as "#rrggbb" (or "#rrggbbaa"). */
60
+ bg: string;
61
+ /** Text-attribute bitmask — decode with {@link VisualAttr}. */
62
+ attrs: number;
63
+ /** Display columns this run occupies (accounts for wide CJK/emoji). */
64
+ width: number;
65
+ };
66
+ /** One row of the rendered grid, as style-coalesced spans. */
67
+ export type VisualLine = {
68
+ spans: VisualSpan[];
69
+ };
70
+ /**
71
+ * Ground-truth snapshot of the ACTUAL rendered terminal cell grid — the real
72
+ * characters, colors, and attributes OpenTUI painted, read directly from
73
+ * `CliRenderer.currentRenderBuffer.getSpanLines()`. Unlike {@link LiveFrame}
74
+ * (semantic structure), this is what a human SEES on screen. No OCR — the cell
75
+ * buffer is authoritative. Emitted on the same sidechannel as LiveFrame,
76
+ * deduped by content hash, and (to bound `getSpanLines` allocation) only
77
+ * captured when a semantic frame also changes.
78
+ */
79
+ export type VisualFrame = {
80
+ mode: "visual";
81
+ version: typeof PROTOCOL_VERSION;
82
+ seq: number;
83
+ ts: number;
84
+ cols: number;
85
+ rows: number;
86
+ /** Cursor [col, row], or null when hidden/unknown. */
87
+ cursor: [number, number] | null;
88
+ lines: VisualLine[];
89
+ };
26
90
  export type LiveEvent = {
27
91
  t: "event";
28
92
  kind: "stream.delta";
@@ -226,4 +290,4 @@ export type DesignSpec = {
226
290
  notes?: string;
227
291
  }>;
228
292
  };
229
- export type HarnessMessage = LiveFrame | LiveEvent;
293
+ export type HarnessMessage = LiveFrame | VisualFrame | LiveEvent;
@@ -1,2 +1,17 @@
1
1
  export const PROTOCOL_VERSION = "0.4.0";
2
+ /**
3
+ * Text-attribute bit flags in {@link VisualSpan.attrs}. Mirrors the low byte of
4
+ * OpenTUI's `TextAttributes` (`ATTRIBUTE_BASE_MASK = 255`), so a consumer can
5
+ * decode `attrs & VisualAttr.BOLD` etc. without importing the renderer.
6
+ */
7
+ export const VisualAttr = {
8
+ BOLD: 1,
9
+ DIM: 2,
10
+ ITALIC: 4,
11
+ UNDERLINE: 8,
12
+ BLINK: 16,
13
+ INVERSE: 32,
14
+ HIDDEN: 64,
15
+ STRIKETHROUGH: 128,
16
+ };
2
17
  //# sourceMappingURL=protocol.js.map
@@ -0,0 +1,58 @@
1
+ /**
2
+ * visual-quality.ts — programmatic visual-quality heuristics over a VisualFrame.
3
+ *
4
+ * P1 of Harness TUI v2. Given the ground-truth rendered cell grid, compute cheap
5
+ * ground-truth signals a human would call "messy" — WITHOUT a vision model:
6
+ * - blank / near-blank row ratios (the council "↳" + "· Skip" garbage)
7
+ * - long runs of consecutive blank rows (dead space)
8
+ * - whitespace density
9
+ * - mojibake (U+FFFD replacement chars + misdecoded-UTF8 clusters like `💡`)
10
+ *
11
+ * These are the exact defects the semantic tree (LiveFrame) is blind to — the
12
+ * 2217600e1f27 export had 12 near-empty rows + mojibake that no existing
13
+ * self-verify check could see. A composite `score` + `issues[]` lets self-QA
14
+ * fail a render regression automatically.
15
+ *
16
+ * Thresholds are conservative defaults; tune per surface. This is a heuristic,
17
+ * not a proof — a low score flags "look closer", it does not assert a bug.
18
+ */
19
+ import type { VisualFrame } from "./protocol.js";
20
+ export interface VisualQualityThresholds {
21
+ /** Rows with <= this many non-space chars count as "near-empty". */
22
+ nearEmptyMaxChars: number;
23
+ /** Flag when the near-empty-row ratio exceeds this. */
24
+ nearEmptyRowPct: number;
25
+ /** Flag when consecutive blank rows exceed this run length. */
26
+ maxConsecutiveBlankRows: number;
27
+ /** Flag when any mojibake is detected at/above this count. */
28
+ mojibakeCount: number;
29
+ /** Flag when whitespace density exceeds this fraction. */
30
+ whitespacePct: number;
31
+ }
32
+ export declare const DEFAULT_VISUAL_QUALITY_THRESHOLDS: VisualQualityThresholds;
33
+ export interface VisualQualityReport {
34
+ rows: number;
35
+ cols: number;
36
+ /** Non-space characters across the whole grid. */
37
+ contentChars: number;
38
+ /** Rows that are entirely whitespace. */
39
+ blankRows: number;
40
+ /** Rows with 1..nearEmptyMaxChars non-space chars (skeleton noise). */
41
+ nearEmptyRows: number;
42
+ nearEmptyRowPct: number;
43
+ /** Longest run of consecutive fully-blank rows. */
44
+ maxConsecutiveBlankRows: number;
45
+ whitespacePct: number;
46
+ /** Count of mojibake signals (U+FFFD + misdecoded-UTF8 clusters). */
47
+ mojibakeCount: number;
48
+ /** Sample of offending text fragments (for diagnostics). */
49
+ mojibakeSamples: string[];
50
+ /** 0-100 composite; lower = messier. */
51
+ score: number;
52
+ /** Human-readable problems that tripped a threshold. */
53
+ issues: string[];
54
+ }
55
+ /**
56
+ * Compute visual-quality signals for a rendered frame. Pure + deterministic.
57
+ */
58
+ export declare function computeVisualQuality(frame: VisualFrame, thresholds?: VisualQualityThresholds): VisualQualityReport;