muonroi-cli 1.8.3 → 1.8.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (477) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +133 -122
  3. package/dist/packages/agent-harness-core/src/driver.d.ts +27 -1
  4. package/dist/packages/agent-harness-core/src/driver.js +46 -0
  5. package/dist/packages/agent-harness-core/src/event-tee.d.ts +48 -0
  6. package/dist/packages/agent-harness-core/src/event-tee.js +77 -0
  7. package/dist/packages/agent-harness-core/src/mcp-server.d.ts +11 -0
  8. package/dist/packages/agent-harness-core/src/mcp-server.js +87 -15
  9. package/dist/packages/agent-harness-core/src/protocol.d.ts +66 -2
  10. package/dist/packages/agent-harness-core/src/protocol.js +15 -0
  11. package/dist/packages/agent-harness-core/src/visual-quality.d.ts +58 -0
  12. package/dist/packages/agent-harness-core/src/visual-quality.js +141 -0
  13. package/dist/packages/agent-harness-opentui/src/agent-mode.d.ts +6 -0
  14. package/dist/packages/agent-harness-opentui/src/agent-mode.js +14 -1
  15. package/dist/packages/agent-harness-opentui/src/input-bridge.d.ts +2 -10
  16. package/dist/packages/agent-harness-opentui/src/input-bridge.js +103 -16
  17. package/dist/packages/agent-harness-opentui/src/install.d.ts +8 -0
  18. package/dist/packages/agent-harness-opentui/src/install.js +10 -0
  19. package/dist/packages/agent-harness-opentui/src/semantic.js +12 -10
  20. package/dist/packages/agent-harness-opentui/src/visual-capture.d.ts +56 -0
  21. package/dist/packages/agent-harness-opentui/src/visual-capture.js +103 -0
  22. package/dist/src/agent-harness/mock-model.d.ts +28 -0
  23. package/dist/src/agent-harness/mock-model.js +63 -1
  24. package/dist/src/agent-harness/test-spawn.js +31 -0
  25. package/dist/src/cli/config/screen-providers.js +1 -1
  26. package/dist/src/cli/cost-forensics.d.ts +10 -0
  27. package/dist/src/cli/cost-forensics.js +30 -15
  28. package/dist/src/cli/keys-bundle.d.ts +1 -1
  29. package/dist/src/cli/keys-bundle.js +1 -1
  30. package/dist/src/cli/keys.d.ts +2 -2
  31. package/dist/src/cli/keys.js +19 -81
  32. package/dist/src/council/clarifier.d.ts +28 -2
  33. package/dist/src/council/clarifier.js +81 -15
  34. package/dist/src/council/context.js +49 -15
  35. package/dist/src/council/debate-checkpoint.d.ts +129 -0
  36. package/dist/src/council/debate-checkpoint.js +176 -0
  37. package/dist/src/council/debate-planner.js +51 -3
  38. package/dist/src/council/debate-summary.d.ts +25 -0
  39. package/dist/src/council/debate-summary.js +85 -0
  40. package/dist/src/council/debate.d.ts +169 -2
  41. package/dist/src/council/debate.js +1210 -134
  42. package/dist/src/council/index.d.ts +85 -1
  43. package/dist/src/council/index.js +634 -196
  44. package/dist/src/council/leader.d.ts +26 -0
  45. package/dist/src/council/leader.js +150 -9
  46. package/dist/src/council/llm.d.ts +32 -0
  47. package/dist/src/council/llm.js +231 -38
  48. package/dist/src/council/panel-select.d.ts +30 -0
  49. package/dist/src/council/panel-select.js +72 -0
  50. package/dist/src/council/planner.js +23 -0
  51. package/dist/src/council/preflight.d.ts +7 -0
  52. package/dist/src/council/preflight.js +14 -2
  53. package/dist/src/council/prompts.d.ts +30 -3
  54. package/dist/src/council/prompts.js +254 -84
  55. package/dist/src/council/stance-recall.d.ts +42 -0
  56. package/dist/src/council/stance-recall.js +57 -0
  57. package/dist/src/council/strip-think.d.ts +17 -0
  58. package/dist/src/council/strip-think.js +33 -0
  59. package/dist/src/council/types.d.ts +128 -0
  60. package/dist/src/ee/artifact-cache.d.ts +16 -0
  61. package/dist/src/ee/artifact-cache.js +32 -0
  62. package/dist/src/ee/auth.d.ts +1 -0
  63. package/dist/src/ee/auth.js +15 -2
  64. package/dist/src/ee/bridge.d.ts +10 -0
  65. package/dist/src/ee/bridge.js +58 -0
  66. package/dist/src/ee/client.js +81 -18
  67. package/dist/src/ee/export-transcripts.d.ts +1 -0
  68. package/dist/src/ee/export-transcripts.js +8 -10
  69. package/dist/src/ee/extract-session.js +29 -0
  70. package/dist/src/ee/extract-style.d.ts +58 -0
  71. package/dist/src/ee/extract-style.js +270 -0
  72. package/dist/src/ee/recall-ledger.d.ts +9 -0
  73. package/dist/src/ee/recall-ledger.js +3 -0
  74. package/dist/src/ee/scope.d.ts +1 -0
  75. package/dist/src/ee/scope.js +26 -1
  76. package/dist/src/ee/search.d.ts +7 -0
  77. package/dist/src/ee/search.js +24 -0
  78. package/dist/src/ee/transcript-emit.js +2 -0
  79. package/dist/src/ee/types.d.ts +22 -0
  80. package/dist/src/ee/who-am-i-brain.d.ts +35 -0
  81. package/dist/src/ee/who-am-i-brain.js +220 -0
  82. package/dist/src/ee/who-am-i.d.ts +10 -3
  83. package/dist/src/ee/who-am-i.js +12 -0
  84. package/dist/src/ee/workflow-event.d.ts +48 -0
  85. package/dist/src/ee/workflow-event.js +81 -0
  86. package/dist/src/flow/compaction/compress.d.ts +3 -3
  87. package/dist/src/flow/compaction/compress.js +45 -8
  88. package/dist/src/flow/compaction/extract.d.ts +4 -7
  89. package/dist/src/flow/compaction/extract.js +50 -10
  90. package/dist/src/flow/compaction/index.d.ts +13 -1
  91. package/dist/src/flow/compaction/index.js +70 -3
  92. package/dist/src/flow/compaction/input-guard.d.ts +24 -0
  93. package/dist/src/flow/compaction/input-guard.js +43 -0
  94. package/dist/src/flow/fold-planning.d.ts +36 -0
  95. package/dist/src/flow/fold-planning.js +83 -0
  96. package/dist/src/flow/hierarchy.d.ts +146 -0
  97. package/dist/src/flow/hierarchy.js +427 -0
  98. package/dist/src/flow/index.d.ts +1 -0
  99. package/dist/src/flow/index.js +2 -0
  100. package/dist/src/flow/run-artifacts.d.ts +102 -0
  101. package/dist/src/flow/run-artifacts.js +208 -0
  102. package/dist/src/generated/version.d.ts +1 -1
  103. package/dist/src/generated/version.js +1 -1
  104. package/dist/src/gsd/assessment-schema.d.ts +44 -0
  105. package/dist/src/gsd/assessment-schema.js +134 -0
  106. package/dist/src/gsd/capability-registry.d.ts +45 -0
  107. package/dist/src/gsd/capability-registry.js +337 -0
  108. package/dist/src/gsd/complexity-assessor.d.ts +39 -0
  109. package/dist/src/gsd/complexity-assessor.js +152 -0
  110. package/dist/src/gsd/config-bridge.d.ts +7 -0
  111. package/dist/src/gsd/config-bridge.js +114 -0
  112. package/dist/src/gsd/config-loader.d.ts +27 -0
  113. package/dist/src/gsd/config-loader.js +50 -0
  114. package/dist/src/gsd/council-context.d.ts +44 -0
  115. package/dist/src/gsd/council-context.js +114 -0
  116. package/dist/src/gsd/ee-closure.d.ts +28 -0
  117. package/dist/src/gsd/ee-closure.js +49 -0
  118. package/dist/src/gsd/flags.d.ts +55 -0
  119. package/dist/src/gsd/flags.js +83 -0
  120. package/dist/src/gsd/gsd-dispatch.d.ts +58 -0
  121. package/dist/src/gsd/gsd-dispatch.js +131 -0
  122. package/dist/src/gsd/gsd-runtime.d.ts +22 -0
  123. package/dist/src/gsd/gsd-runtime.js +37 -0
  124. package/dist/src/gsd/host-adapter.d.ts +11 -0
  125. package/dist/src/gsd/host-adapter.js +29 -0
  126. package/dist/src/gsd/index.d.ts +24 -1
  127. package/dist/src/gsd/index.js +27 -0
  128. package/dist/src/gsd/loop-host-contract.d.ts +21 -0
  129. package/dist/src/gsd/loop-host-contract.js +39 -0
  130. package/dist/src/gsd/loop-host.d.ts +69 -0
  131. package/dist/src/gsd/loop-host.js +245 -0
  132. package/dist/src/gsd/loop-resolver.d.ts +36 -0
  133. package/dist/src/gsd/loop-resolver.js +79 -0
  134. package/dist/src/gsd/model-tier.d.ts +13 -0
  135. package/dist/src/gsd/model-tier.js +45 -0
  136. package/dist/src/gsd/mutation-gate.d.ts +16 -0
  137. package/dist/src/gsd/mutation-gate.js +41 -0
  138. package/dist/src/gsd/native-roadmap.d.ts +89 -0
  139. package/dist/src/gsd/native-roadmap.js +343 -0
  140. package/dist/src/gsd/native-state.d.ts +47 -0
  141. package/dist/src/gsd/native-state.js +220 -0
  142. package/dist/src/gsd/paths.d.ts +23 -0
  143. package/dist/src/gsd/paths.js +66 -0
  144. package/dist/src/gsd/phase-dag.d.ts +12 -0
  145. package/dist/src/gsd/phase-dag.js +94 -0
  146. package/dist/src/gsd/phase-sync.d.ts +42 -0
  147. package/dist/src/gsd/phase-sync.js +321 -0
  148. package/dist/src/gsd/pil-gate-context.d.ts +13 -0
  149. package/dist/src/gsd/pil-gate-context.js +64 -0
  150. package/dist/src/gsd/pil-gate-critic.d.ts +19 -0
  151. package/dist/src/gsd/pil-gate-critic.js +74 -0
  152. package/dist/src/gsd/plan-council-prompts.d.ts +25 -0
  153. package/dist/src/gsd/plan-council-prompts.js +79 -0
  154. package/dist/src/gsd/plan-council.d.ts +44 -0
  155. package/dist/src/gsd/plan-council.js +251 -0
  156. package/dist/src/gsd/plan-gate-vocabulary.d.ts +40 -0
  157. package/dist/src/gsd/plan-gate-vocabulary.js +64 -0
  158. package/dist/src/gsd/product-workspace.d.ts +13 -0
  159. package/dist/src/gsd/product-workspace.js +124 -0
  160. package/dist/src/gsd/ship-bridge.d.ts +25 -0
  161. package/dist/src/gsd/ship-bridge.js +65 -0
  162. package/dist/src/gsd/state-document.d.ts +40 -0
  163. package/dist/src/gsd/state-document.js +163 -0
  164. package/dist/src/gsd/verdict-schema.d.ts +39 -0
  165. package/dist/src/gsd/verdict-schema.js +144 -0
  166. package/dist/src/gsd/verify-context.d.ts +22 -0
  167. package/dist/src/gsd/verify-context.js +27 -0
  168. package/dist/src/gsd/verify-council-prompts.d.ts +19 -0
  169. package/dist/src/gsd/verify-council-prompts.js +85 -0
  170. package/dist/src/gsd/verify-council.d.ts +25 -0
  171. package/dist/src/gsd/verify-council.js +119 -0
  172. package/dist/src/gsd/verify-gate-vocabulary.d.ts +25 -0
  173. package/dist/src/gsd/verify-gate-vocabulary.js +46 -0
  174. package/dist/src/gsd/workflow-engine.d.ts +60 -0
  175. package/dist/src/gsd/workflow-engine.js +207 -0
  176. package/dist/src/gsd/workflow-tools.d.ts +13 -0
  177. package/dist/src/gsd/workflow-tools.js +277 -0
  178. package/dist/src/hooks/index.js +1 -1
  179. package/dist/src/index.js +44 -11
  180. package/dist/src/maintain/pr-builder.js +23 -13
  181. package/dist/src/mcp/auto-setup.js +57 -32
  182. package/dist/src/mcp/client-pool.js +1 -1
  183. package/dist/src/mcp/ee-tools.js +1 -0
  184. package/dist/src/mcp/oauth-callback.js +2 -2
  185. package/dist/src/mcp/research-onboarding.js +8 -7
  186. package/dist/src/mcp/runtime.js +34 -2
  187. package/dist/src/mcp/setup-guide-text.d.ts +1 -1
  188. package/dist/src/mcp/setup-guide-text.js +77 -76
  189. package/dist/src/models/catalog-client.d.ts +87 -0
  190. package/dist/src/models/catalog-client.js +105 -38
  191. package/dist/src/models/catalog.json +528 -265
  192. package/dist/src/models/registry.d.ts +22 -7
  193. package/dist/src/models/registry.js +73 -10
  194. package/dist/src/ops/doctor.js +8 -8
  195. package/dist/src/orchestrator/auto-commit.js +1 -1
  196. package/dist/src/orchestrator/batch-turn-runner.js +2 -2
  197. package/dist/src/orchestrator/cache-prefix.d.ts +67 -0
  198. package/dist/src/orchestrator/cache-prefix.js +83 -0
  199. package/dist/src/orchestrator/compact-request.d.ts +32 -0
  200. package/dist/src/orchestrator/compact-request.js +41 -0
  201. package/dist/src/orchestrator/compaction.d.ts +10 -0
  202. package/dist/src/orchestrator/compaction.js +27 -7
  203. package/dist/src/orchestrator/council-manager.d.ts +12 -3
  204. package/dist/src/orchestrator/council-manager.js +65 -24
  205. package/dist/src/orchestrator/cross-turn-dedup.d.ts +6 -0
  206. package/dist/src/orchestrator/cross-turn-dedup.js +22 -1
  207. package/dist/src/orchestrator/error-utils.d.ts +29 -0
  208. package/dist/src/orchestrator/error-utils.js +132 -24
  209. package/dist/src/orchestrator/grounding-check.js +39 -1
  210. package/dist/src/orchestrator/message-processor.js +242 -33
  211. package/dist/src/orchestrator/orchestrator.d.ts +39 -3
  212. package/dist/src/orchestrator/orchestrator.js +651 -102
  213. package/dist/src/orchestrator/preprocessor.js +1 -1
  214. package/dist/src/orchestrator/proactive-compact-detector.d.ts +26 -0
  215. package/dist/src/orchestrator/proactive-compact-detector.js +36 -0
  216. package/dist/src/orchestrator/prompts.js +159 -159
  217. package/dist/src/orchestrator/reactive-delegation.d.ts +39 -0
  218. package/dist/src/orchestrator/reactive-delegation.js +59 -0
  219. package/dist/src/orchestrator/retry-classifier.d.ts +2 -1
  220. package/dist/src/orchestrator/retry-classifier.js +46 -2
  221. package/dist/src/orchestrator/safety-intercept.d.ts +45 -0
  222. package/dist/src/orchestrator/safety-intercept.js +55 -0
  223. package/dist/src/orchestrator/scope-reminder.js +1 -1
  224. package/dist/src/orchestrator/session-experience.d.ts +2 -1
  225. package/dist/src/orchestrator/session-experience.js +2 -1
  226. package/dist/src/orchestrator/should-run-gate.d.ts +5 -0
  227. package/dist/src/orchestrator/should-run-gate.js +18 -0
  228. package/dist/src/orchestrator/stall-watchdog.d.ts +24 -3
  229. package/dist/src/orchestrator/stall-watchdog.js +47 -13
  230. package/dist/src/orchestrator/stream-runner.js +62 -29
  231. package/dist/src/orchestrator/sub-agent-cap.d.ts +13 -1
  232. package/dist/src/orchestrator/sub-agent-cap.js +16 -1
  233. package/dist/src/orchestrator/sub-agent-model-tier.d.ts +13 -1
  234. package/dist/src/orchestrator/sub-agent-model-tier.js +16 -2
  235. package/dist/src/orchestrator/subagent-compactor.d.ts +53 -1
  236. package/dist/src/orchestrator/subagent-compactor.js +126 -15
  237. package/dist/src/orchestrator/tool-engine.d.ts +26 -0
  238. package/dist/src/orchestrator/tool-engine.js +669 -56
  239. package/dist/src/orchestrator/tool-limit-auto-recover.d.ts +22 -0
  240. package/dist/src/orchestrator/tool-limit-auto-recover.js +30 -0
  241. package/dist/src/orchestrator/turn-runner-deps.d.ts +19 -0
  242. package/dist/src/orchestrator/turn-watchdog.d.ts +37 -0
  243. package/dist/src/orchestrator/turn-watchdog.js +55 -0
  244. package/dist/src/pil/agent-operating-contract.d.ts +1 -1
  245. package/dist/src/pil/agent-operating-contract.js +1 -1
  246. package/dist/src/pil/cheap-model-playbook.d.ts +1 -1
  247. package/dist/src/pil/cheap-model-playbook.js +5 -1
  248. package/dist/src/pil/cheap-model-workbooks.d.ts +1 -1
  249. package/dist/src/pil/cheap-model-workbooks.js +1 -1
  250. package/dist/src/pil/discovery-types.d.ts +1 -0
  251. package/dist/src/pil/discovery.js +16 -11
  252. package/dist/src/pil/layer1-intent.d.ts +18 -6
  253. package/dist/src/pil/layer1-intent.js +66 -757
  254. package/dist/src/pil/layer15-context-scan.js +15 -1
  255. package/dist/src/pil/layer2_5-ponytail.js +8 -8
  256. package/dist/src/pil/layer3-ee-injection.js +23 -8
  257. package/dist/src/pil/layer4-gsd.js +69 -16
  258. package/dist/src/pil/layer5-context.js +7 -3
  259. package/dist/src/pil/layer6-output.d.ts +23 -0
  260. package/dist/src/pil/layer6-output.js +5 -1
  261. package/dist/src/pil/llm-classify.d.ts +33 -2
  262. package/dist/src/pil/llm-classify.js +123 -131
  263. package/dist/src/pil/native-capabilities-workbook.d.ts +1 -1
  264. package/dist/src/pil/native-capabilities-workbook.js +1 -0
  265. package/dist/src/pil/pipeline.js +34 -2
  266. package/dist/src/pil/response-tools.js +5 -3
  267. package/dist/src/pil/schema.d.ts +1 -0
  268. package/dist/src/pil/schema.js +2 -0
  269. package/dist/src/pil/types.d.ts +18 -0
  270. package/dist/src/playbook/directives.d.ts +4 -0
  271. package/dist/src/playbook/directives.js +17 -5
  272. package/dist/src/product-loop/backlog-builder.d.ts +14 -1
  273. package/dist/src/product-loop/backlog-builder.js +30 -6
  274. package/dist/src/product-loop/discovery-context-format.js +3 -1
  275. package/dist/src/product-loop/discovery-ecosystem.js +4 -1
  276. package/dist/src/product-loop/discovery-interview.js +32 -3
  277. package/dist/src/product-loop/discovery-schema.js +5 -1
  278. package/dist/src/product-loop/done-gate.js +3 -3
  279. package/dist/src/product-loop/ideal-trace.d.ts +7 -0
  280. package/dist/src/product-loop/ideal-trace.js +64 -0
  281. package/dist/src/product-loop/index.d.ts +13 -1
  282. package/dist/src/product-loop/index.js +333 -52
  283. package/dist/src/product-loop/loop-driver.d.ts +7 -0
  284. package/dist/src/product-loop/loop-driver.js +327 -116
  285. package/dist/src/product-loop/phase-plan.d.ts +5 -0
  286. package/dist/src/product-loop/phase-plan.js +39 -2
  287. package/dist/src/product-loop/phase-runner.js +9 -1
  288. package/dist/src/product-loop/progress-snapshot.js +4 -4
  289. package/dist/src/product-loop/sprint-runner.d.ts +111 -0
  290. package/dist/src/product-loop/sprint-runner.js +559 -16
  291. package/dist/src/product-loop/types.d.ts +36 -5
  292. package/dist/src/providers/adapter.d.ts +1 -1
  293. package/dist/src/providers/adapter.js +3 -4
  294. package/dist/src/providers/auth/browser-flow.d.ts +1 -1
  295. package/dist/src/providers/auth/browser-flow.js +1 -1
  296. package/dist/src/providers/auth/openai-oauth.js +1 -1
  297. package/dist/src/providers/auth/registry.js +0 -34
  298. package/dist/src/providers/auth/token-store.js +4 -1
  299. package/dist/src/providers/auth/types.d.ts +1 -1
  300. package/dist/src/providers/auth/types.js +1 -1
  301. package/dist/src/providers/capabilities.d.ts +24 -5
  302. package/dist/src/providers/capabilities.js +42 -24
  303. package/dist/src/providers/endpoints.d.ts +2 -2
  304. package/dist/src/providers/endpoints.js +11 -10
  305. package/dist/src/providers/keychain.d.ts +1 -1
  306. package/dist/src/providers/keychain.js +7 -9
  307. package/dist/src/providers/mcp-vision-bridge.js +82 -172
  308. package/dist/src/providers/openai-compatible.js +8 -1
  309. package/dist/src/providers/pricing.d.ts +2 -2
  310. package/dist/src/providers/pricing.js +3 -13
  311. package/dist/src/providers/runtime.d.ts +27 -2
  312. package/dist/src/providers/runtime.js +78 -15
  313. package/dist/src/providers/strategies/base.strategy.d.ts +16 -0
  314. package/dist/src/providers/strategies/base.strategy.js +24 -1
  315. package/dist/src/providers/strategies/{siliconflow.strategy.d.ts → opencode-go.strategy.d.ts} +3 -3
  316. package/dist/src/providers/strategies/opencode-go.strategy.js +83 -0
  317. package/dist/src/providers/strategies/registry.js +4 -4
  318. package/dist/src/providers/strategies/thinking-mode.d.ts +109 -0
  319. package/dist/src/providers/strategies/thinking-mode.js +280 -1
  320. package/dist/src/providers/strategies/zai.strategy.d.ts +14 -0
  321. package/dist/src/providers/strategies/zai.strategy.js +44 -0
  322. package/dist/src/providers/types.d.ts +5 -6
  323. package/dist/src/providers/types.js +2 -2
  324. package/dist/src/providers/vision-backend.d.ts +47 -0
  325. package/dist/src/providers/vision-backend.js +258 -0
  326. package/dist/src/providers/vision-proxy.d.ts +22 -9
  327. package/dist/src/providers/vision-proxy.js +63 -132
  328. package/dist/src/providers/wire-debug.js +95 -0
  329. package/dist/src/reporter/index.js +1 -1
  330. package/dist/src/router/decide.d.ts +13 -0
  331. package/dist/src/router/decide.js +138 -36
  332. package/dist/src/router/peak-hour.d.ts +38 -0
  333. package/dist/src/router/peak-hour.js +107 -0
  334. package/dist/src/router/step-router.js +3 -2
  335. package/dist/src/router/warm.js +4 -5
  336. package/dist/src/scaffold/bb-ecosystem-apply.js +47 -47
  337. package/dist/src/scaffold/bb-quality-gate.js +5 -5
  338. package/dist/src/scaffold/continuation-prompt.d.ts +11 -0
  339. package/dist/src/scaffold/continuation-prompt.js +86 -60
  340. package/dist/src/scaffold/init-new.js +453 -453
  341. package/dist/src/scaffold/point-to-existing.d.ts +21 -0
  342. package/dist/src/scaffold/point-to-existing.js +25 -0
  343. package/dist/src/self-qa/agentic-loop.js +22 -22
  344. package/dist/src/{ui/state → state}/active-run.d.ts +19 -0
  345. package/dist/src/{ui/state → state}/active-run.js +21 -0
  346. package/dist/src/{ui/status-bar/store.d.ts → state/status-bar-store.d.ts} +4 -1
  347. package/dist/src/{ui/status-bar/store.js → state/status-bar-store.js} +12 -9
  348. package/dist/src/state/turn-trace.d.ts +43 -0
  349. package/dist/src/state/turn-trace.js +32 -0
  350. package/dist/src/storage/db.js +2 -1
  351. package/dist/src/storage/index.d.ts +1 -1
  352. package/dist/src/storage/index.js +1 -1
  353. package/dist/src/storage/interaction-log.d.ts +1 -1
  354. package/dist/src/storage/interaction-log.js +5 -5
  355. package/dist/src/storage/migrations.js +196 -126
  356. package/dist/src/storage/session-experience-store.js +4 -4
  357. package/dist/src/storage/sessions.d.ts +28 -10
  358. package/dist/src/storage/sessions.js +112 -55
  359. package/dist/src/storage/transcript-view.js +1 -1
  360. package/dist/src/storage/transcript.d.ts +51 -0
  361. package/dist/src/storage/transcript.js +383 -112
  362. package/dist/src/storage/usage.js +14 -14
  363. package/dist/src/storage/workspaces.js +12 -12
  364. package/dist/src/tools/file.d.ts +15 -0
  365. package/dist/src/tools/file.js +32 -0
  366. package/dist/src/tools/native-tools.js +5 -0
  367. package/dist/src/tools/registry.d.ts +3 -0
  368. package/dist/src/tools/registry.js +460 -22
  369. package/dist/src/tools/research.d.ts +29 -0
  370. package/dist/src/tools/research.js +233 -0
  371. package/dist/src/types/index.d.ts +118 -3
  372. package/dist/src/ui/app.js +0 -0
  373. package/dist/src/ui/cards/product-status-card.js +1 -1
  374. package/dist/src/ui/components/bubble-body-guard.d.ts +27 -0
  375. package/dist/src/ui/components/bubble-body-guard.js +50 -0
  376. package/dist/src/ui/components/context-rail.d.ts +26 -0
  377. package/dist/src/ui/components/context-rail.js +33 -0
  378. package/dist/src/ui/components/council-conclusion-card.d.ts +73 -0
  379. package/dist/src/ui/components/council-conclusion-card.js +420 -0
  380. package/dist/src/ui/components/council-debate-pill.d.ts +36 -0
  381. package/dist/src/ui/components/council-debate-pill.js +34 -0
  382. package/dist/src/ui/components/council-info-card.js +2 -2
  383. package/dist/src/ui/components/council-leader-bubble.d.ts +10 -3
  384. package/dist/src/ui/components/council-leader-bubble.js +21 -11
  385. package/dist/src/ui/components/council-message-bubble.d.ts +15 -2
  386. package/dist/src/ui/components/council-message-bubble.js +16 -15
  387. package/dist/src/ui/components/council-phase-timeline.d.ts +9 -1
  388. package/dist/src/ui/components/council-phase-timeline.js +49 -15
  389. package/dist/src/ui/components/council-placeholder-bubble.d.ts +16 -9
  390. package/dist/src/ui/components/council-placeholder-bubble.js +32 -29
  391. package/dist/src/ui/components/council-question-card.js +12 -12
  392. package/dist/src/ui/components/council-rail-rounds.d.ts +26 -0
  393. package/dist/src/ui/components/council-rail-rounds.js +57 -0
  394. package/dist/src/ui/components/council-round-group.d.ts +38 -0
  395. package/dist/src/ui/components/council-round-group.js +88 -0
  396. package/dist/src/ui/components/council-status-list.d.ts +3 -1
  397. package/dist/src/ui/components/council-status-list.js +36 -24
  398. package/dist/src/ui/components/council-synthesis-banner.d.ts +7 -2
  399. package/dist/src/ui/components/council-synthesis-banner.js +20 -5
  400. package/dist/src/ui/components/halt-recovery-card.js +9 -5
  401. package/dist/src/ui/components/jump-to-latest-pill.d.ts +11 -0
  402. package/dist/src/ui/components/jump-to-latest-pill.js +14 -0
  403. package/dist/src/ui/components/prompt-box.js +18 -16
  404. package/dist/src/ui/components/session-tree-card.d.ts +14 -0
  405. package/dist/src/ui/components/session-tree-card.js +46 -0
  406. package/dist/src/ui/components/slash-inline-menu.d.ts +12 -0
  407. package/dist/src/ui/components/slash-inline-menu.js +26 -5
  408. package/dist/src/ui/components/task-list-panel.d.ts +14 -1
  409. package/dist/src/ui/components/task-list-panel.js +22 -2
  410. package/dist/src/ui/containers/modals-layer.d.ts +2 -1
  411. package/dist/src/ui/hooks/use-session-picker.d.ts +3 -3
  412. package/dist/src/ui/mcp-modal.js +2 -4
  413. package/dist/src/ui/modals/api-key-modal.js +1 -1
  414. package/dist/src/ui/modals/connect-modal.js +4 -3
  415. package/dist/src/ui/modals/session-picker-modal.d.ts +2 -2
  416. package/dist/src/ui/modals/session-picker-modal.js +3 -5
  417. package/dist/src/ui/picker-providers.d.ts +1 -1
  418. package/dist/src/ui/picker-providers.js +1 -1
  419. package/dist/src/ui/primitives/index.d.ts +1 -0
  420. package/dist/src/ui/primitives/index.js +2 -0
  421. package/dist/src/ui/primitives/semantic-primitives.d.ts +76 -0
  422. package/dist/src/ui/primitives/semantic-primitives.js +81 -0
  423. package/dist/src/ui/slash/compact.js +5 -7
  424. package/dist/src/ui/slash/cost.js +1 -1
  425. package/dist/src/ui/slash/council-inspect.js +4 -4
  426. package/dist/src/ui/slash/council.js +19 -1
  427. package/dist/src/ui/slash/debug.d.ts +3 -31
  428. package/dist/src/ui/slash/debug.js +9 -20
  429. package/dist/src/ui/slash/ideal.d.ts +6 -2
  430. package/dist/src/ui/slash/ideal.js +97 -7
  431. package/dist/src/ui/slash/menu-items.d.ts +7 -0
  432. package/dist/src/ui/slash/menu-items.js +12 -18
  433. package/dist/src/ui/slash/registry.d.ts +2 -0
  434. package/dist/src/ui/slash/registry.js +4 -0
  435. package/dist/src/ui/status-bar/cache-hit.d.ts +6 -0
  436. package/dist/src/ui/status-bar/cache-hit.js +9 -0
  437. package/dist/src/ui/status-bar/index.d.ts +1 -1
  438. package/dist/src/ui/status-bar/index.js +7 -3
  439. package/dist/src/ui/status-bar/usd-meter.d.ts +5 -4
  440. package/dist/src/ui/status-bar/usd-meter.js +6 -4
  441. package/dist/src/ui/theme.d.ts +1 -0
  442. package/dist/src/ui/theme.js +2 -0
  443. package/dist/src/ui/types.d.ts +7 -0
  444. package/dist/src/ui/use-app-logic.js +0 -0
  445. package/dist/src/ui/utils/format.d.ts +14 -0
  446. package/dist/src/ui/utils/format.js +23 -3
  447. package/dist/src/usage/downgrade.js +2 -2
  448. package/dist/src/usage/product-ledger.js +2 -2
  449. package/dist/src/utils/clipboard-image.js +23 -23
  450. package/dist/src/utils/install-manager.js +14 -11
  451. package/dist/src/utils/logger.js +2 -2
  452. package/dist/src/utils/permission-mode.js +5 -3
  453. package/dist/src/utils/redactor.js +1 -1
  454. package/dist/src/utils/settings.d.ts +153 -5
  455. package/dist/src/utils/settings.js +233 -29
  456. package/dist/src/utils/side-question.js +2 -2
  457. package/dist/src/utils/skills.js +3 -3
  458. package/dist/src/utils/visible-retry.d.ts +11 -0
  459. package/dist/src/utils/visible-retry.js +10 -1
  460. package/dist/src/verify/entrypoint.d.ts +1 -1
  461. package/dist/src/verify/entrypoint.js +1 -1
  462. package/dist/src/verify/recipes.d.ts +13 -0
  463. package/dist/src/verify/recipes.js +15 -0
  464. package/package.json +135 -132
  465. package/dist/src/providers/auth/gcloud.d.ts +0 -28
  466. package/dist/src/providers/auth/gcloud.js +0 -102
  467. package/dist/src/providers/auth/gemini-oauth.d.ts +0 -82
  468. package/dist/src/providers/auth/gemini-oauth.js +0 -472
  469. package/dist/src/providers/gemini.d.ts +0 -11
  470. package/dist/src/providers/gemini.js +0 -45
  471. package/dist/src/providers/siliconflow-sse-repair.d.ts +0 -58
  472. package/dist/src/providers/siliconflow-sse-repair.js +0 -177
  473. package/dist/src/providers/strategies/google.strategy.d.ts +0 -22
  474. package/dist/src/providers/strategies/google.strategy.js +0 -174
  475. package/dist/src/providers/strategies/siliconflow.strategy.js +0 -29
  476. package/dist/src/ui/containers/chat-feed.d.ts +0 -40
  477. package/dist/src/ui/containers/chat-feed.js +0 -66
@@ -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;
@@ -0,0 +1,141 @@
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
+ export const DEFAULT_VISUAL_QUALITY_THRESHOLDS = {
20
+ nearEmptyMaxChars: 2,
21
+ nearEmptyRowPct: 0.5,
22
+ maxConsecutiveBlankRows: 4,
23
+ mojibakeCount: 1,
24
+ whitespacePct: 0.92,
25
+ };
26
+ // Characters that legitimately never (or almost never) appear in this TUI but
27
+ // are produced when UTF-8 bytes are re-decoded as CP437/latin1. Presence in a
28
+ // 2+ cluster is a strong mojibake signal (e.g. 💡 → "≡ƒÆí", em-dash → "ΓÇô").
29
+ const MOJIBAKE_SIGNAL = new Set([
30
+ ..."≡ƒÆÇÃÂ’ÿÖÜúÑŒ¬".split(""),
31
+ "�", // replacement char
32
+ ]);
33
+ function rowText(line) {
34
+ return line.spans.map((s) => s.text).join("");
35
+ }
36
+ function countMojibake(text) {
37
+ let count = 0;
38
+ let sample;
39
+ // Definitive: U+FFFD replacement chars.
40
+ for (const ch of text) {
41
+ if (ch === "�")
42
+ count++;
43
+ }
44
+ // Clusters of 2+ consecutive signal chars — mojibake arrives in runs, which
45
+ // keeps ordinary accented text (single é, ü) from tripping the heuristic.
46
+ const chars = [...text];
47
+ let run = 0;
48
+ for (let i = 0; i <= chars.length; i++) {
49
+ const isSignal = i < chars.length && MOJIBAKE_SIGNAL.has(chars[i]) && chars[i] !== "�";
50
+ if (isSignal) {
51
+ run++;
52
+ }
53
+ else {
54
+ if (run >= 2) {
55
+ count += run;
56
+ if (!sample)
57
+ sample = chars.slice(Math.max(0, i - run - 4), i + 2).join("");
58
+ }
59
+ run = 0;
60
+ }
61
+ }
62
+ return { count, sample };
63
+ }
64
+ /**
65
+ * Compute visual-quality signals for a rendered frame. Pure + deterministic.
66
+ */
67
+ export function computeVisualQuality(frame, thresholds = DEFAULT_VISUAL_QUALITY_THRESHOLDS) {
68
+ const rows = frame.lines.length;
69
+ const cols = frame.cols;
70
+ let contentChars = 0;
71
+ let totalCells = 0;
72
+ let blankRows = 0;
73
+ let nearEmptyRows = 0;
74
+ let maxConsecutiveBlankRows = 0;
75
+ let curBlankRun = 0;
76
+ let mojibakeCount = 0;
77
+ const mojibakeSamples = [];
78
+ for (const line of frame.lines) {
79
+ const text = rowText(line);
80
+ const nonSpace = [...text].filter((c) => c.trim().length > 0).length;
81
+ contentChars += nonSpace;
82
+ totalCells += Math.max(text.length, cols);
83
+ if (nonSpace === 0) {
84
+ blankRows++;
85
+ curBlankRun++;
86
+ maxConsecutiveBlankRows = Math.max(maxConsecutiveBlankRows, curBlankRun);
87
+ }
88
+ else {
89
+ curBlankRun = 0;
90
+ }
91
+ // "Low-content" rows = blank OR only a glyph or two (skeleton noise like the
92
+ // council "↳" / "· Skip" rows, and the blank separators between them). Both
93
+ // are visual garbage, so they count together against the total row budget.
94
+ if (nonSpace <= thresholds.nearEmptyMaxChars)
95
+ nearEmptyRows++;
96
+ const mj = countMojibake(text);
97
+ if (mj.count > 0) {
98
+ mojibakeCount += mj.count;
99
+ if (mj.sample && mojibakeSamples.length < 5)
100
+ mojibakeSamples.push(mj.sample);
101
+ }
102
+ }
103
+ // Ratio of low-content (blank or near-empty) rows over the whole frame.
104
+ const nearEmptyRowPct = rows > 0 ? nearEmptyRows / rows : 0;
105
+ const whitespacePct = totalCells > 0 ? 1 - contentChars / totalCells : 1;
106
+ const issues = [];
107
+ if (nearEmptyRowPct > thresholds.nearEmptyRowPct) {
108
+ issues.push(`${nearEmptyRows}/${rows} rows are blank or near-empty (<=${thresholds.nearEmptyMaxChars} chars) — skeleton/garbage rows`);
109
+ }
110
+ if (maxConsecutiveBlankRows > thresholds.maxConsecutiveBlankRows) {
111
+ issues.push(`${maxConsecutiveBlankRows} consecutive blank rows — large dead gap`);
112
+ }
113
+ if (mojibakeCount >= thresholds.mojibakeCount) {
114
+ issues.push(`${mojibakeCount} mojibake signal(s) detected (misdecoded UTF-8 / U+FFFD)${mojibakeSamples.length ? ` e.g. ${JSON.stringify(mojibakeSamples[0])}` : ""}`);
115
+ }
116
+ if (whitespacePct > thresholds.whitespacePct && contentChars > 0) {
117
+ issues.push(`${(whitespacePct * 100).toFixed(0)}% of the grid is whitespace — very sparse render`);
118
+ }
119
+ // Composite score: start at 100, subtract weighted penalties.
120
+ let score = 100;
121
+ score -= Math.min(40, Math.round(nearEmptyRowPct * 60));
122
+ score -= Math.min(25, Math.max(0, maxConsecutiveBlankRows - thresholds.maxConsecutiveBlankRows) * 4);
123
+ score -= Math.min(30, mojibakeCount * 6);
124
+ score -= whitespacePct > thresholds.whitespacePct ? 10 : 0;
125
+ score = Math.max(0, Math.min(100, score));
126
+ return {
127
+ rows,
128
+ cols,
129
+ contentChars,
130
+ blankRows,
131
+ nearEmptyRows,
132
+ nearEmptyRowPct,
133
+ maxConsecutiveBlankRows,
134
+ whitespacePct,
135
+ mojibakeCount,
136
+ mojibakeSamples,
137
+ score,
138
+ issues,
139
+ };
140
+ }
141
+ //# sourceMappingURL=visual-quality.js.map
@@ -17,6 +17,7 @@
17
17
  */
18
18
  import type { LiveEvent } from "@muonroi/agent-harness-core/protocol";
19
19
  import type { SemanticRegistry } from "./reconciler-hook.js";
20
+ import { type RendererLike } from "./visual-capture.js";
20
21
  export type AgentModeOptions = {
21
22
  cols: number;
22
23
  rows: number;
@@ -40,6 +41,11 @@ export type AgentModeRuntime = {
40
41
  onCommand: (h: (cmd: unknown) => void) => void;
41
42
  /** Get the current timestamp (real or fake clock). */
42
43
  now: () => number;
44
+ /**
45
+ * Attach the OpenTUI renderer once it exists (constructed AFTER this runtime
46
+ * — see src/index.ts). Enables VisualFrame capture of the real cell grid.
47
+ */
48
+ attachRenderer: (renderer: RendererLike) => void;
43
49
  /** Tear down: close streams, dispose idle detector. */
44
50
  dispose: () => void;
45
51
  };
@@ -22,6 +22,7 @@ import { createIdleDetector } from "@muonroi/agent-harness-core/idle";
22
22
  import { createLineSplitter, createSidechannelWriter } from "@muonroi/agent-harness-core/transports/sidechannel";
23
23
  import { installOpenTUIHarness } from "./install.js";
24
24
  import { createSemanticRegistry } from "./reconciler-hook.js";
25
+ import { createVisualCaptureHook } from "./visual-capture.js";
25
26
  // ---------------------------------------------------------------------------
26
27
  // Factory
27
28
  // ---------------------------------------------------------------------------
@@ -90,6 +91,11 @@ export async function startAgentMode(opts) {
90
91
  const now = () => (opts.fakeClock ? 0 : Date.now());
91
92
  // --- Semantic registry ---------------------------------------------------
92
93
  const registry = createSemanticRegistry();
94
+ // --- Visual capture (real rendered cell grid) ----------------------------
95
+ // The renderer does not exist yet — it is constructed after this runtime and
96
+ // handed back via attachRenderer(). The hook reads it late-bound.
97
+ let rendererRef;
98
+ const visualHook = createVisualCaptureHook(() => rendererRef);
93
99
  // --- Idle detector -------------------------------------------------------
94
100
  const idle = createIdleDetector({
95
101
  quiescenceMs: opts.idleMs,
@@ -116,6 +122,9 @@ export async function startAgentMode(opts) {
116
122
  // becomes deterministic (seq*16). Without this, ts = Date.now() and the
117
123
  // determinism spec sees timestamps differ between runs.
118
124
  fakeClock: opts.fakeClock,
125
+ // Emit a VisualFrame (real rendered cell grid) alongside each semantic
126
+ // change. No-op until attachRenderer() supplies the renderer.
127
+ captureVisual: (seq, ts) => visualHook.capture(seq, ts),
119
128
  });
120
129
  // --- Command channel (in stream → handlers) ------------------------------
121
130
  const commandHandlers = [];
@@ -165,6 +174,10 @@ export async function startAgentMode(opts) {
165
174
  const onCommand = (h) => {
166
175
  commandHandlers.push(h);
167
176
  };
177
+ const attachRenderer = (renderer) => {
178
+ rendererRef = renderer;
179
+ visualHook.resetDedup(); // force the first visual frame after attach
180
+ };
168
181
  const dispose = () => {
169
182
  harnessHandle.uninstall(); // stops poll interval; transport.close() ends outStream
170
183
  idle.dispose();
@@ -173,6 +186,6 @@ export async function startAgentMode(opts) {
173
186
  inStream.destroy();
174
187
  }
175
188
  };
176
- return { registry, capture, emitEvent, onCommand, now, dispose };
189
+ return { registry, capture, emitEvent, onCommand, now, attachRenderer, dispose };
177
190
  }
178
191
  //# sourceMappingURL=agent-mode.js.map
@@ -13,15 +13,7 @@
13
13
  * raw byte. We construct ParsedKey directly so we don't depend on terminal
14
14
  * escape sequence conventions.
15
15
  */
16
+ import type { KeyEvent } from "@opentui/core";
16
17
  import type { AgentModeRuntime } from "./agent-mode.js";
17
- /**
18
- * Subscribe to agentRuntime.onCommand and inject synthetic key events into
19
- * OpenTUI's keyHandler. Safe no-op when agentRuntime is undefined (normal user
20
- * mode).
21
- *
22
- * Special key `__focus__:<id>` is intentionally NOT translated to OpenTUI input
23
- * here — it is a driver-level signal currently ignored at this layer. (Focus
24
- * routing in OpenTUI is owned by individual components; a global focus dispatch
25
- * is out of scope for this bridge.)
26
- */
18
+ export declare function keyForNamed(key: string): KeyEvent | null;
27
19
  export declare function useAgentInputBridge(agentRuntime: AgentModeRuntime | undefined): void;