muonroi-cli 1.8.4 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (562) hide show
  1. package/LICENSE +17 -5
  2. package/dist/packages/agent-harness-core/src/driver.d.ts +27 -1
  3. package/dist/packages/agent-harness-core/src/driver.js +46 -0
  4. package/dist/packages/agent-harness-core/src/event-filter.js +11 -0
  5. package/dist/packages/agent-harness-core/src/event-redact.js +7 -0
  6. package/dist/packages/agent-harness-core/src/event-tee.d.ts +64 -0
  7. package/dist/packages/agent-harness-core/src/event-tee.js +104 -0
  8. package/dist/packages/agent-harness-core/src/mcp-server.d.ts +25 -0
  9. package/dist/packages/agent-harness-core/src/mcp-server.js +169 -21
  10. package/dist/packages/agent-harness-core/src/predicate.d.ts +1 -1
  11. package/dist/packages/agent-harness-core/src/protocol.d.ts +90 -4
  12. package/dist/packages/agent-harness-core/src/protocol.js +15 -0
  13. package/dist/packages/agent-harness-core/src/visual-quality.d.ts +58 -0
  14. package/dist/packages/agent-harness-core/src/visual-quality.js +141 -0
  15. package/dist/packages/agent-harness-opentui/src/agent-mode.d.ts +6 -0
  16. package/dist/packages/agent-harness-opentui/src/agent-mode.js +14 -1
  17. package/dist/packages/agent-harness-opentui/src/input-bridge.d.ts +2 -10
  18. package/dist/packages/agent-harness-opentui/src/input-bridge.js +103 -16
  19. package/dist/packages/agent-harness-opentui/src/install.d.ts +8 -0
  20. package/dist/packages/agent-harness-opentui/src/install.js +10 -0
  21. package/dist/packages/agent-harness-opentui/src/semantic.js +12 -10
  22. package/dist/packages/agent-harness-opentui/src/visual-capture.d.ts +56 -0
  23. package/dist/packages/agent-harness-opentui/src/visual-capture.js +103 -0
  24. package/dist/src/agent-harness/mock-model.d.ts +38 -0
  25. package/dist/src/agent-harness/mock-model.js +69 -3
  26. package/dist/src/agent-harness/test-spawn.js +31 -0
  27. package/dist/src/chat/chat-keychain.d.ts +7 -12
  28. package/dist/src/chat/chat-keychain.js +19 -86
  29. package/dist/src/cli/config/screen-providers.js +1 -1
  30. package/dist/src/cli/cost-forensics.d.ts +10 -0
  31. package/dist/src/cli/cost-forensics.js +18 -3
  32. package/dist/src/cli/keys-bundle.d.ts +1 -1
  33. package/dist/src/cli/keys-bundle.js +1 -1
  34. package/dist/src/cli/keys.d.ts +10 -47
  35. package/dist/src/cli/keys.js +31 -399
  36. package/dist/src/council/clarifier.d.ts +31 -3
  37. package/dist/src/council/clarifier.js +220 -32
  38. package/dist/src/council/context.js +49 -15
  39. package/dist/src/council/debate-checkpoint.d.ts +129 -0
  40. package/dist/src/council/debate-checkpoint.js +176 -0
  41. package/dist/src/council/debate-planner.js +54 -5
  42. package/dist/src/council/debate-summary.d.ts +25 -0
  43. package/dist/src/council/debate-summary.js +85 -0
  44. package/dist/src/council/debate.d.ts +169 -2
  45. package/dist/src/council/debate.js +1265 -135
  46. package/dist/src/council/index.d.ts +108 -1
  47. package/dist/src/council/index.js +670 -197
  48. package/dist/src/council/leader.d.ts +26 -0
  49. package/dist/src/council/leader.js +150 -9
  50. package/dist/src/council/llm.d.ts +94 -0
  51. package/dist/src/council/llm.js +348 -55
  52. package/dist/src/council/panel-select.d.ts +30 -0
  53. package/dist/src/council/panel-select.js +82 -0
  54. package/dist/src/council/planner.js +40 -0
  55. package/dist/src/council/preflight.d.ts +17 -0
  56. package/dist/src/council/preflight.js +50 -2
  57. package/dist/src/council/prompts.d.ts +39 -4
  58. package/dist/src/council/prompts.js +256 -69
  59. package/dist/src/council/stance-recall.d.ts +42 -0
  60. package/dist/src/council/stance-recall.js +57 -0
  61. package/dist/src/council/strip-think.d.ts +17 -0
  62. package/dist/src/council/strip-think.js +33 -0
  63. package/dist/src/council/types.d.ts +138 -0
  64. package/dist/src/ee/artifact-cache.d.ts +16 -0
  65. package/dist/src/ee/artifact-cache.js +32 -0
  66. package/dist/src/ee/auth.d.ts +20 -0
  67. package/dist/src/ee/auth.js +54 -2
  68. package/dist/src/ee/bridge.d.ts +10 -0
  69. package/dist/src/ee/bridge.js +58 -0
  70. package/dist/src/ee/client.js +109 -21
  71. package/dist/src/ee/ee-onboarding.js +6 -26
  72. package/dist/src/ee/export-transcripts.d.ts +1 -0
  73. package/dist/src/ee/export-transcripts.js +8 -10
  74. package/dist/src/ee/extract-session.js +29 -0
  75. package/dist/src/ee/extract-style.d.ts +58 -0
  76. package/dist/src/ee/extract-style.js +270 -0
  77. package/dist/src/ee/recall-ledger.d.ts +9 -0
  78. package/dist/src/ee/recall-ledger.js +3 -0
  79. package/dist/src/ee/scope.d.ts +1 -0
  80. package/dist/src/ee/scope.js +26 -1
  81. package/dist/src/ee/search.d.ts +7 -0
  82. package/dist/src/ee/search.js +24 -0
  83. package/dist/src/ee/transcript-emit.js +2 -0
  84. package/dist/src/ee/types.d.ts +22 -0
  85. package/dist/src/ee/who-am-i-brain.d.ts +35 -0
  86. package/dist/src/ee/who-am-i-brain.js +220 -0
  87. package/dist/src/ee/who-am-i.d.ts +10 -3
  88. package/dist/src/ee/who-am-i.js +12 -0
  89. package/dist/src/ee/workflow-event.d.ts +48 -0
  90. package/dist/src/ee/workflow-event.js +81 -0
  91. package/dist/src/flow/compaction/compress.d.ts +3 -3
  92. package/dist/src/flow/compaction/compress.js +58 -8
  93. package/dist/src/flow/compaction/extract.d.ts +4 -7
  94. package/dist/src/flow/compaction/extract.js +50 -10
  95. package/dist/src/flow/compaction/index.d.ts +14 -1
  96. package/dist/src/flow/compaction/index.js +96 -3
  97. package/dist/src/flow/compaction/input-guard.d.ts +24 -0
  98. package/dist/src/flow/compaction/input-guard.js +43 -0
  99. package/dist/src/flow/compaction/progress.d.ts +35 -0
  100. package/dist/src/flow/compaction/progress.js +35 -0
  101. package/dist/src/flow/fold-planning.d.ts +36 -0
  102. package/dist/src/flow/fold-planning.js +83 -0
  103. package/dist/src/flow/hierarchy.d.ts +146 -0
  104. package/dist/src/flow/hierarchy.js +427 -0
  105. package/dist/src/flow/index.d.ts +1 -0
  106. package/dist/src/flow/index.js +2 -0
  107. package/dist/src/flow/run-artifacts.d.ts +102 -0
  108. package/dist/src/flow/run-artifacts.js +208 -0
  109. package/dist/src/generated/version.d.ts +1 -1
  110. package/dist/src/generated/version.js +1 -1
  111. package/dist/src/gsd/assessment-schema.d.ts +44 -0
  112. package/dist/src/gsd/assessment-schema.js +134 -0
  113. package/dist/src/gsd/capability-registry.d.ts +45 -0
  114. package/dist/src/gsd/capability-registry.js +337 -0
  115. package/dist/src/gsd/complexity-assessor.d.ts +39 -0
  116. package/dist/src/gsd/complexity-assessor.js +152 -0
  117. package/dist/src/gsd/config-bridge.d.ts +7 -0
  118. package/dist/src/gsd/config-bridge.js +114 -0
  119. package/dist/src/gsd/config-loader.d.ts +27 -0
  120. package/dist/src/gsd/config-loader.js +50 -0
  121. package/dist/src/gsd/council-context.d.ts +44 -0
  122. package/dist/src/gsd/council-context.js +114 -0
  123. package/dist/src/gsd/ee-closure.d.ts +28 -0
  124. package/dist/src/gsd/ee-closure.js +49 -0
  125. package/dist/src/gsd/flags.d.ts +66 -0
  126. package/dist/src/gsd/flags.js +102 -0
  127. package/dist/src/gsd/gsd-dispatch.d.ts +58 -0
  128. package/dist/src/gsd/gsd-dispatch.js +131 -0
  129. package/dist/src/gsd/gsd-runtime.d.ts +22 -0
  130. package/dist/src/gsd/gsd-runtime.js +37 -0
  131. package/dist/src/gsd/host-adapter.d.ts +11 -0
  132. package/dist/src/gsd/host-adapter.js +29 -0
  133. package/dist/src/gsd/index.d.ts +24 -1
  134. package/dist/src/gsd/index.js +27 -0
  135. package/dist/src/gsd/loop-host-contract.d.ts +21 -0
  136. package/dist/src/gsd/loop-host-contract.js +39 -0
  137. package/dist/src/gsd/loop-host.d.ts +69 -0
  138. package/dist/src/gsd/loop-host.js +245 -0
  139. package/dist/src/gsd/loop-resolver.d.ts +36 -0
  140. package/dist/src/gsd/loop-resolver.js +79 -0
  141. package/dist/src/gsd/model-tier.d.ts +13 -0
  142. package/dist/src/gsd/model-tier.js +45 -0
  143. package/dist/src/gsd/mutation-gate.d.ts +16 -0
  144. package/dist/src/gsd/mutation-gate.js +41 -0
  145. package/dist/src/gsd/native-roadmap.d.ts +89 -0
  146. package/dist/src/gsd/native-roadmap.js +343 -0
  147. package/dist/src/gsd/native-state.d.ts +47 -0
  148. package/dist/src/gsd/native-state.js +220 -0
  149. package/dist/src/gsd/paths.d.ts +23 -0
  150. package/dist/src/gsd/paths.js +66 -0
  151. package/dist/src/gsd/phase-dag.d.ts +12 -0
  152. package/dist/src/gsd/phase-dag.js +94 -0
  153. package/dist/src/gsd/phase-sync.d.ts +42 -0
  154. package/dist/src/gsd/phase-sync.js +321 -0
  155. package/dist/src/gsd/pil-gate-context.d.ts +13 -0
  156. package/dist/src/gsd/pil-gate-context.js +64 -0
  157. package/dist/src/gsd/pil-gate-critic.d.ts +19 -0
  158. package/dist/src/gsd/pil-gate-critic.js +74 -0
  159. package/dist/src/gsd/plan-council-prompts.d.ts +25 -0
  160. package/dist/src/gsd/plan-council-prompts.js +79 -0
  161. package/dist/src/gsd/plan-council.d.ts +44 -0
  162. package/dist/src/gsd/plan-council.js +283 -0
  163. package/dist/src/gsd/plan-gate-vocabulary.d.ts +40 -0
  164. package/dist/src/gsd/plan-gate-vocabulary.js +64 -0
  165. package/dist/src/gsd/product-workspace.d.ts +13 -0
  166. package/dist/src/gsd/product-workspace.js +124 -0
  167. package/dist/src/gsd/ship-bridge.d.ts +25 -0
  168. package/dist/src/gsd/ship-bridge.js +65 -0
  169. package/dist/src/gsd/state-document.d.ts +40 -0
  170. package/dist/src/gsd/state-document.js +163 -0
  171. package/dist/src/gsd/verdict-schema.d.ts +39 -0
  172. package/dist/src/gsd/verdict-schema.js +144 -0
  173. package/dist/src/gsd/verify-context.d.ts +22 -0
  174. package/dist/src/gsd/verify-context.js +27 -0
  175. package/dist/src/gsd/verify-council-prompts.d.ts +19 -0
  176. package/dist/src/gsd/verify-council-prompts.js +85 -0
  177. package/dist/src/gsd/verify-council.d.ts +25 -0
  178. package/dist/src/gsd/verify-council.js +119 -0
  179. package/dist/src/gsd/verify-gate-vocabulary.d.ts +25 -0
  180. package/dist/src/gsd/verify-gate-vocabulary.js +46 -0
  181. package/dist/src/gsd/workflow-engine.d.ts +60 -0
  182. package/dist/src/gsd/workflow-engine.js +207 -0
  183. package/dist/src/gsd/workflow-tools.d.ts +13 -0
  184. package/dist/src/gsd/workflow-tools.js +277 -0
  185. package/dist/src/headless/council-answers.js +4 -0
  186. package/dist/src/hooks/index.js +1 -1
  187. package/dist/src/index.js +172 -270
  188. package/dist/src/lsp/builtins.js +3 -1
  189. package/dist/src/lsp/manager.d.ts +5 -1
  190. package/dist/src/lsp/manager.js +249 -3
  191. package/dist/src/lsp/npm-cache.d.ts +11 -1
  192. package/dist/src/lsp/npm-cache.js +17 -1
  193. package/dist/src/lsp/runtime.d.ts +6 -1
  194. package/dist/src/lsp/runtime.js +17 -1
  195. package/dist/src/lsp/types.d.ts +83 -1
  196. package/dist/src/lsp/types.js +10 -0
  197. package/dist/src/maintain/pr-builder.js +23 -13
  198. package/dist/src/mcp/auto-setup.js +57 -32
  199. package/dist/src/mcp/client-pool.js +44 -16
  200. package/dist/src/mcp/lsp-tools.d.ts +5 -1
  201. package/dist/src/mcp/lsp-tools.js +93 -2
  202. package/dist/src/mcp/mcp-keychain.d.ts +3 -5
  203. package/dist/src/mcp/mcp-keychain.js +9 -49
  204. package/dist/src/mcp/research-onboarding.js +8 -7
  205. package/dist/src/mcp/runtime.js +34 -2
  206. package/dist/src/mcp/setup-guide-text.d.ts +1 -1
  207. package/dist/src/mcp/setup-guide-text.js +22 -2
  208. package/dist/src/mcp/tools-server.d.ts +10 -0
  209. package/dist/src/mcp/tools-server.js +10 -2
  210. package/dist/src/models/catalog-client.d.ts +87 -0
  211. package/dist/src/models/catalog-client.js +105 -38
  212. package/dist/src/models/catalog.json +528 -265
  213. package/dist/src/models/registry.d.ts +22 -7
  214. package/dist/src/models/registry.js +73 -10
  215. package/dist/src/ops/doctor.js +1 -1
  216. package/dist/src/orchestrator/ask-user.d.ts +61 -0
  217. package/dist/src/orchestrator/ask-user.js +65 -0
  218. package/dist/src/orchestrator/auto-commit.js +1 -1
  219. package/dist/src/orchestrator/batch-turn-runner.js +2 -2
  220. package/dist/src/orchestrator/cache-prefix.d.ts +67 -0
  221. package/dist/src/orchestrator/cache-prefix.js +83 -0
  222. package/dist/src/orchestrator/compact-request.d.ts +32 -0
  223. package/dist/src/orchestrator/compact-request.js +41 -0
  224. package/dist/src/orchestrator/compaction.d.ts +12 -3
  225. package/dist/src/orchestrator/compaction.js +35 -15
  226. package/dist/src/orchestrator/council-manager.d.ts +12 -3
  227. package/dist/src/orchestrator/council-manager.js +74 -32
  228. package/dist/src/orchestrator/council-request.d.ts +49 -0
  229. package/dist/src/orchestrator/council-request.js +62 -0
  230. package/dist/src/orchestrator/cross-turn-dedup.d.ts +6 -0
  231. package/dist/src/orchestrator/cross-turn-dedup.js +22 -1
  232. package/dist/src/orchestrator/error-utils.d.ts +29 -0
  233. package/dist/src/orchestrator/error-utils.js +132 -24
  234. package/dist/src/orchestrator/grounding-check.js +39 -1
  235. package/dist/src/orchestrator/interactive-pause.d.ts +26 -0
  236. package/dist/src/orchestrator/interactive-pause.js +36 -0
  237. package/dist/src/orchestrator/message-processor.d.ts +4 -0
  238. package/dist/src/orchestrator/message-processor.js +268 -41
  239. package/dist/src/orchestrator/orchestrator.d.ts +64 -3
  240. package/dist/src/orchestrator/orchestrator.js +823 -120
  241. package/dist/src/orchestrator/preprocessor.js +3 -3
  242. package/dist/src/orchestrator/proactive-compact-detector.d.ts +26 -0
  243. package/dist/src/orchestrator/proactive-compact-detector.js +36 -0
  244. package/dist/src/orchestrator/prompts.js +17 -17
  245. package/dist/src/orchestrator/reactive-delegation.d.ts +39 -0
  246. package/dist/src/orchestrator/reactive-delegation.js +59 -0
  247. package/dist/src/orchestrator/retry-classifier.d.ts +2 -1
  248. package/dist/src/orchestrator/retry-classifier.js +46 -2
  249. package/dist/src/orchestrator/safety-askcard.d.ts +1 -1
  250. package/dist/src/orchestrator/safety-askcard.js +5 -2
  251. package/dist/src/orchestrator/safety-intercept.d.ts +50 -0
  252. package/dist/src/orchestrator/safety-intercept.js +62 -0
  253. package/dist/src/orchestrator/scope-reminder.js +1 -1
  254. package/dist/src/orchestrator/session-experience.d.ts +2 -1
  255. package/dist/src/orchestrator/session-experience.js +2 -1
  256. package/dist/src/orchestrator/should-run-gate.d.ts +5 -0
  257. package/dist/src/orchestrator/should-run-gate.js +18 -0
  258. package/dist/src/orchestrator/stall-watchdog.d.ts +31 -3
  259. package/dist/src/orchestrator/stall-watchdog.js +65 -10
  260. package/dist/src/orchestrator/stream-runner.d.ts +13 -3
  261. package/dist/src/orchestrator/stream-runner.js +115 -49
  262. package/dist/src/orchestrator/sub-agent-cap.d.ts +13 -1
  263. package/dist/src/orchestrator/sub-agent-cap.js +16 -1
  264. package/dist/src/orchestrator/sub-agent-model-tier.d.ts +13 -1
  265. package/dist/src/orchestrator/sub-agent-model-tier.js +16 -2
  266. package/dist/src/orchestrator/subagent-compactor.d.ts +53 -1
  267. package/dist/src/orchestrator/subagent-compactor.js +126 -15
  268. package/dist/src/orchestrator/tool-engine.d.ts +41 -0
  269. package/dist/src/orchestrator/tool-engine.js +846 -66
  270. package/dist/src/orchestrator/tool-limit-auto-recover.d.ts +22 -0
  271. package/dist/src/orchestrator/tool-limit-auto-recover.js +30 -0
  272. package/dist/src/orchestrator/turn-runner-deps.d.ts +19 -0
  273. package/dist/src/orchestrator/turn-watchdog.d.ts +44 -0
  274. package/dist/src/orchestrator/turn-watchdog.js +84 -0
  275. package/dist/src/pil/agent-operating-contract.d.ts +1 -1
  276. package/dist/src/pil/agent-operating-contract.js +6 -4
  277. package/dist/src/pil/cheap-model-playbook.d.ts +1 -1
  278. package/dist/src/pil/cheap-model-playbook.js +5 -1
  279. package/dist/src/pil/cheap-model-workbooks.d.ts +1 -1
  280. package/dist/src/pil/cheap-model-workbooks.js +1 -1
  281. package/dist/src/pil/discovery-types.d.ts +1 -0
  282. package/dist/src/pil/discovery.d.ts +1 -1
  283. package/dist/src/pil/discovery.js +18 -13
  284. package/dist/src/pil/layer1-intent.d.ts +18 -6
  285. package/dist/src/pil/layer1-intent.js +66 -757
  286. package/dist/src/pil/layer15-context-scan.js +15 -1
  287. package/dist/src/pil/layer1_5-complexity-size.d.ts +7 -0
  288. package/dist/src/pil/layer1_5-complexity-size.js +31 -5
  289. package/dist/src/pil/layer3-ee-injection.js +23 -8
  290. package/dist/src/pil/layer4-gsd.js +69 -16
  291. package/dist/src/pil/layer5-context.js +7 -3
  292. package/dist/src/pil/layer6-output.d.ts +23 -0
  293. package/dist/src/pil/layer6-output.js +5 -1
  294. package/dist/src/pil/llm-classify.d.ts +111 -5
  295. package/dist/src/pil/llm-classify.js +421 -189
  296. package/dist/src/pil/native-capabilities-workbook.d.ts +1 -1
  297. package/dist/src/pil/native-capabilities-workbook.js +8 -0
  298. package/dist/src/pil/pipeline.js +36 -2
  299. package/dist/src/pil/repo-grounding-probe.d.ts +15 -0
  300. package/dist/src/pil/repo-grounding-probe.js +136 -0
  301. package/dist/src/pil/repo-structure-hints.d.ts +7 -0
  302. package/dist/src/pil/repo-structure-hints.js +45 -0
  303. package/dist/src/pil/response-tools.js +5 -3
  304. package/dist/src/pil/schema.d.ts +1 -0
  305. package/dist/src/pil/schema.js +2 -0
  306. package/dist/src/pil/types.d.ts +18 -0
  307. package/dist/src/playbook/directives.d.ts +4 -0
  308. package/dist/src/playbook/directives.js +17 -5
  309. package/dist/src/product-loop/artifact-io.js +4 -0
  310. package/dist/src/product-loop/backlog-builder.d.ts +14 -1
  311. package/dist/src/product-loop/backlog-builder.js +30 -6
  312. package/dist/src/product-loop/criteria-seed.d.ts +51 -0
  313. package/dist/src/product-loop/criteria-seed.js +200 -0
  314. package/dist/src/product-loop/discovery-context-format.js +3 -1
  315. package/dist/src/product-loop/discovery-ecosystem.js +4 -1
  316. package/dist/src/product-loop/discovery-interview.d.ts +9 -0
  317. package/dist/src/product-loop/discovery-interview.js +60 -12
  318. package/dist/src/product-loop/discovery-recommender.js +2 -1
  319. package/dist/src/product-loop/discovery-schema.js +19 -2
  320. package/dist/src/product-loop/discovery-triage.d.ts +23 -0
  321. package/dist/src/product-loop/discovery-triage.js +109 -0
  322. package/dist/src/product-loop/gather.js +150 -2
  323. package/dist/src/product-loop/ideal-trace.d.ts +7 -0
  324. package/dist/src/product-loop/ideal-trace.js +64 -0
  325. package/dist/src/product-loop/index.d.ts +13 -1
  326. package/dist/src/product-loop/index.js +340 -52
  327. package/dist/src/product-loop/loop-driver.d.ts +7 -0
  328. package/dist/src/product-loop/loop-driver.js +330 -106
  329. package/dist/src/product-loop/phase-plan.d.ts +21 -0
  330. package/dist/src/product-loop/phase-plan.js +81 -6
  331. package/dist/src/product-loop/phase-rituals.d.ts +3 -0
  332. package/dist/src/product-loop/phase-rituals.js +8 -3
  333. package/dist/src/product-loop/phase-runner.js +39 -12
  334. package/dist/src/product-loop/plan-adherence-review.d.ts +26 -0
  335. package/dist/src/product-loop/plan-adherence-review.js +144 -0
  336. package/dist/src/product-loop/sprint-runner.d.ts +173 -0
  337. package/dist/src/product-loop/sprint-runner.js +863 -19
  338. package/dist/src/product-loop/types.d.ts +61 -5
  339. package/dist/src/providers/adapter.d.ts +1 -1
  340. package/dist/src/providers/adapter.js +3 -4
  341. package/dist/src/providers/anthropic.d.ts +9 -8
  342. package/dist/src/providers/anthropic.js +13 -47
  343. package/dist/src/providers/auth/browser-flow.d.ts +1 -1
  344. package/dist/src/providers/auth/browser-flow.js +1 -1
  345. package/dist/src/providers/auth/grok-oauth.d.ts +1 -0
  346. package/dist/src/providers/auth/grok-oauth.js +30 -5
  347. package/dist/src/providers/auth/openai-oauth.d.ts +1 -0
  348. package/dist/src/providers/auth/openai-oauth.js +15 -1
  349. package/dist/src/providers/auth/registry.js +0 -34
  350. package/dist/src/providers/auth/token-store.d.ts +9 -9
  351. package/dist/src/providers/auth/token-store.js +8 -67
  352. package/dist/src/providers/auth/types.d.ts +9 -1
  353. package/dist/src/providers/auth/types.js +1 -1
  354. package/dist/src/providers/capabilities.d.ts +24 -5
  355. package/dist/src/providers/capabilities.js +42 -24
  356. package/dist/src/providers/endpoints.d.ts +2 -2
  357. package/dist/src/providers/endpoints.js +11 -10
  358. package/dist/src/providers/env-store.d.ts +17 -0
  359. package/dist/src/providers/env-store.js +228 -0
  360. package/dist/src/providers/keychain.d.ts +22 -18
  361. package/dist/src/providers/keychain.js +127 -140
  362. package/dist/src/providers/mcp-vision-bridge.js +56 -146
  363. package/dist/src/providers/openai-compatible.js +8 -1
  364. package/dist/src/providers/pricing.d.ts +2 -2
  365. package/dist/src/providers/pricing.js +3 -13
  366. package/dist/src/providers/runtime.d.ts +43 -3
  367. package/dist/src/providers/runtime.js +88 -14
  368. package/dist/src/providers/strategies/base.strategy.d.ts +16 -0
  369. package/dist/src/providers/strategies/base.strategy.js +24 -1
  370. package/dist/src/providers/strategies/{siliconflow.strategy.d.ts → opencode-go.strategy.d.ts} +3 -3
  371. package/dist/src/providers/strategies/opencode-go.strategy.js +83 -0
  372. package/dist/src/providers/strategies/registry.js +4 -4
  373. package/dist/src/providers/strategies/thinking-mode.d.ts +109 -0
  374. package/dist/src/providers/strategies/thinking-mode.js +288 -1
  375. package/dist/src/providers/strategies/xai.strategy.js +27 -0
  376. package/dist/src/providers/strategies/zai.strategy.d.ts +14 -0
  377. package/dist/src/providers/strategies/zai.strategy.js +44 -0
  378. package/dist/src/providers/types.d.ts +5 -6
  379. package/dist/src/providers/types.js +2 -2
  380. package/dist/src/providers/vision-backend.d.ts +47 -0
  381. package/dist/src/providers/vision-backend.js +258 -0
  382. package/dist/src/providers/vision-proxy.d.ts +22 -9
  383. package/dist/src/providers/vision-proxy.js +63 -132
  384. package/dist/src/providers/warm.d.ts +65 -0
  385. package/dist/src/providers/warm.js +145 -0
  386. package/dist/src/providers/wire-debug.js +95 -0
  387. package/dist/src/router/decide.d.ts +13 -0
  388. package/dist/src/router/decide.js +138 -36
  389. package/dist/src/router/peak-hour.d.ts +38 -0
  390. package/dist/src/router/peak-hour.js +107 -0
  391. package/dist/src/router/step-router.js +3 -2
  392. package/dist/src/router/warm.js +4 -5
  393. package/dist/src/scaffold/continuation-prompt.d.ts +11 -0
  394. package/dist/src/scaffold/continuation-prompt.js +26 -0
  395. package/dist/src/scaffold/point-to-existing.d.ts +21 -0
  396. package/dist/src/scaffold/point-to-existing.js +25 -0
  397. package/dist/src/self-qa/agentic-loop.js +6 -5
  398. package/dist/src/{ui/state → state}/active-run.d.ts +19 -0
  399. package/dist/src/{ui/state → state}/active-run.js +21 -0
  400. package/dist/src/{ui/status-bar/store.d.ts → state/status-bar-store.d.ts} +4 -1
  401. package/dist/src/{ui/status-bar/store.js → state/status-bar-store.js} +12 -9
  402. package/dist/src/state/turn-trace.d.ts +43 -0
  403. package/dist/src/state/turn-trace.js +32 -0
  404. package/dist/src/storage/db.js +2 -1
  405. package/dist/src/storage/index.d.ts +1 -1
  406. package/dist/src/storage/index.js +1 -1
  407. package/dist/src/storage/interaction-log.d.ts +1 -1
  408. package/dist/src/storage/migrations.js +71 -1
  409. package/dist/src/storage/sessions.d.ts +28 -10
  410. package/dist/src/storage/sessions.js +78 -21
  411. package/dist/src/storage/transcript-view.js +1 -1
  412. package/dist/src/storage/transcript.d.ts +51 -0
  413. package/dist/src/storage/transcript.js +340 -15
  414. package/dist/src/tools/file.d.ts +15 -0
  415. package/dist/src/tools/file.js +32 -0
  416. package/dist/src/tools/git-safety.d.ts +19 -0
  417. package/dist/src/tools/git-safety.js +168 -0
  418. package/dist/src/tools/native-tools.d.ts +1 -1
  419. package/dist/src/tools/native-tools.js +81 -1
  420. package/dist/src/tools/registry.d.ts +20 -0
  421. package/dist/src/tools/registry.js +576 -23
  422. package/dist/src/tools/research.d.ts +29 -0
  423. package/dist/src/tools/research.js +233 -0
  424. package/dist/src/types/index.d.ts +147 -4
  425. package/dist/src/ui/app.js +0 -0
  426. package/dist/src/ui/cards/product-status-card.js +1 -1
  427. package/dist/src/ui/components/agent-rail-activities.d.ts +26 -0
  428. package/dist/src/ui/components/agent-rail-activities.js +47 -0
  429. package/dist/src/ui/components/bubble-body-guard.d.ts +27 -0
  430. package/dist/src/ui/components/bubble-body-guard.js +50 -0
  431. package/dist/src/ui/components/compact-progress-card.d.ts +24 -0
  432. package/dist/src/ui/components/compact-progress-card.js +42 -0
  433. package/dist/src/ui/components/context-rail.d.ts +26 -0
  434. package/dist/src/ui/components/context-rail.js +33 -0
  435. package/dist/src/ui/components/council-conclusion-card.d.ts +73 -0
  436. package/dist/src/ui/components/council-conclusion-card.js +420 -0
  437. package/dist/src/ui/components/council-debate-pill.d.ts +36 -0
  438. package/dist/src/ui/components/council-debate-pill.js +34 -0
  439. package/dist/src/ui/components/council-info-card.js +2 -2
  440. package/dist/src/ui/components/council-leader-bubble.d.ts +10 -3
  441. package/dist/src/ui/components/council-leader-bubble.js +21 -11
  442. package/dist/src/ui/components/council-message-bubble.d.ts +15 -2
  443. package/dist/src/ui/components/council-message-bubble.js +16 -15
  444. package/dist/src/ui/components/council-phase-timeline.d.ts +9 -1
  445. package/dist/src/ui/components/council-phase-timeline.js +66 -17
  446. package/dist/src/ui/components/council-placeholder-bubble.d.ts +16 -9
  447. package/dist/src/ui/components/council-placeholder-bubble.js +32 -29
  448. package/dist/src/ui/components/council-question-card.js +13 -12
  449. package/dist/src/ui/components/council-rail-rounds.d.ts +26 -0
  450. package/dist/src/ui/components/council-rail-rounds.js +57 -0
  451. package/dist/src/ui/components/council-round-group.d.ts +38 -0
  452. package/dist/src/ui/components/council-round-group.js +88 -0
  453. package/dist/src/ui/components/council-status-list.d.ts +3 -1
  454. package/dist/src/ui/components/council-status-list.js +36 -24
  455. package/dist/src/ui/components/council-synthesis-banner.d.ts +7 -2
  456. package/dist/src/ui/components/council-synthesis-banner.js +20 -5
  457. package/dist/src/ui/components/halt-recovery-card.js +9 -5
  458. package/dist/src/ui/components/jump-to-latest-pill.d.ts +11 -0
  459. package/dist/src/ui/components/jump-to-latest-pill.js +14 -0
  460. package/dist/src/ui/components/message-view.d.ts +15 -0
  461. package/dist/src/ui/components/message-view.js +50 -1
  462. package/dist/src/ui/components/prompt-box.js +18 -16
  463. package/dist/src/ui/components/session-tree-card.d.ts +14 -0
  464. package/dist/src/ui/components/session-tree-card.js +46 -0
  465. package/dist/src/ui/components/slash-inline-menu.d.ts +12 -0
  466. package/dist/src/ui/components/slash-inline-menu.js +26 -5
  467. package/dist/src/ui/components/task-list-panel.d.ts +14 -1
  468. package/dist/src/ui/components/task-list-panel.js +22 -2
  469. package/dist/src/ui/components/tool-group.d.ts +15 -3
  470. package/dist/src/ui/components/tool-group.js +69 -11
  471. package/dist/src/ui/containers/modals-layer.d.ts +4 -2
  472. package/dist/src/ui/containers/modals-layer.js +2 -2
  473. package/dist/src/ui/council-harness-event.d.ts +57 -0
  474. package/dist/src/ui/council-harness-event.js +46 -0
  475. package/dist/src/ui/heartbeat-debug.d.ts +29 -0
  476. package/dist/src/ui/heartbeat-debug.js +45 -0
  477. package/dist/src/ui/hooks/use-session-picker.d.ts +3 -3
  478. package/dist/src/ui/mcp-modal.js +2 -4
  479. package/dist/src/ui/modals/api-key-modal.js +1 -1
  480. package/dist/src/ui/modals/connect-modal.js +4 -3
  481. package/dist/src/ui/modals/model-picker-modal.d.ts +8 -18
  482. package/dist/src/ui/modals/model-picker-modal.js +8 -10
  483. package/dist/src/ui/modals/session-picker-modal.d.ts +2 -2
  484. package/dist/src/ui/modals/session-picker-modal.js +3 -5
  485. package/dist/src/ui/picker-providers.d.ts +1 -1
  486. package/dist/src/ui/picker-providers.js +1 -1
  487. package/dist/src/ui/primitives/index.d.ts +1 -0
  488. package/dist/src/ui/primitives/index.js +2 -0
  489. package/dist/src/ui/primitives/semantic-primitives.d.ts +76 -0
  490. package/dist/src/ui/primitives/semantic-primitives.js +81 -0
  491. package/dist/src/ui/slash/compact.js +5 -7
  492. package/dist/src/ui/slash/cost.js +1 -1
  493. package/dist/src/ui/slash/council.js +19 -1
  494. package/dist/src/ui/slash/debug.d.ts +3 -31
  495. package/dist/src/ui/slash/debug.js +9 -20
  496. package/dist/src/ui/slash/ee.js +81 -0
  497. package/dist/src/ui/slash/ideal.d.ts +6 -2
  498. package/dist/src/ui/slash/ideal.js +97 -7
  499. package/dist/src/ui/slash/menu-items.d.ts +7 -0
  500. package/dist/src/ui/slash/menu-items.js +23 -20
  501. package/dist/src/ui/slash/registry.d.ts +2 -0
  502. package/dist/src/ui/slash/registry.js +4 -0
  503. package/dist/src/ui/status-bar/cache-hit.d.ts +6 -0
  504. package/dist/src/ui/status-bar/cache-hit.js +9 -0
  505. package/dist/src/ui/status-bar/index.d.ts +1 -1
  506. package/dist/src/ui/status-bar/index.js +7 -3
  507. package/dist/src/ui/status-bar/usd-meter.d.ts +5 -4
  508. package/dist/src/ui/status-bar/usd-meter.js +6 -4
  509. package/dist/src/ui/theme.d.ts +1 -0
  510. package/dist/src/ui/theme.js +2 -0
  511. package/dist/src/ui/types.d.ts +7 -0
  512. package/dist/src/ui/use-app-logic.js +0 -0
  513. package/dist/src/ui/utils/agent-activities.d.ts +39 -0
  514. package/dist/src/ui/utils/agent-activities.js +96 -0
  515. package/dist/src/ui/utils/format.d.ts +14 -0
  516. package/dist/src/ui/utils/format.js +23 -3
  517. package/dist/src/ui/utils/group-tool-entries.d.ts +26 -0
  518. package/dist/src/ui/utils/group-tool-entries.js +111 -0
  519. package/dist/src/ui/utils/tool-summary.d.ts +21 -0
  520. package/dist/src/ui/utils/tool-summary.js +91 -0
  521. package/dist/src/usage/downgrade.js +2 -2
  522. package/dist/src/usage/product-ledger.js +2 -2
  523. package/dist/src/utils/event-loop-monitor.d.ts +85 -0
  524. package/dist/src/utils/event-loop-monitor.js +107 -0
  525. package/dist/src/utils/install-manager.js +2 -1
  526. package/dist/src/utils/llm-deadline.d.ts +14 -0
  527. package/dist/src/utils/llm-deadline.js +19 -0
  528. package/dist/src/utils/logger.js +2 -2
  529. package/dist/src/utils/loop-profiler.d.ts +102 -0
  530. package/dist/src/utils/loop-profiler.js +202 -0
  531. package/dist/src/utils/permission-mode.js +5 -3
  532. package/dist/src/utils/redactor.js +1 -1
  533. package/dist/src/utils/settings.d.ts +180 -5
  534. package/dist/src/utils/settings.js +271 -31
  535. package/dist/src/utils/side-question.d.ts +1 -2
  536. package/dist/src/utils/side-question.js +2 -2
  537. package/dist/src/utils/visible-retry.d.ts +11 -0
  538. package/dist/src/utils/visible-retry.js +10 -1
  539. package/dist/src/verify/entrypoint.d.ts +1 -1
  540. package/dist/src/verify/entrypoint.js +52 -17
  541. package/dist/src/verify/orchestrator.d.ts +1 -1
  542. package/dist/src/verify/orchestrator.js +20 -3
  543. package/dist/src/verify/recipes.d.ts +13 -0
  544. package/dist/src/verify/recipes.js +15 -0
  545. package/package.json +134 -132
  546. package/dist/src/cli/bw-vault.d.ts +0 -55
  547. package/dist/src/cli/bw-vault.js +0 -133
  548. package/dist/src/mcp/ee-tools.d.ts +0 -46
  549. package/dist/src/mcp/ee-tools.js +0 -193
  550. package/dist/src/providers/auth/gcloud.d.ts +0 -28
  551. package/dist/src/providers/auth/gcloud.js +0 -102
  552. package/dist/src/providers/auth/gemini-oauth.d.ts +0 -82
  553. package/dist/src/providers/auth/gemini-oauth.js +0 -472
  554. package/dist/src/providers/gemini.d.ts +0 -11
  555. package/dist/src/providers/gemini.js +0 -45
  556. package/dist/src/providers/siliconflow-sse-repair.d.ts +0 -58
  557. package/dist/src/providers/siliconflow-sse-repair.js +0 -177
  558. package/dist/src/providers/strategies/google.strategy.d.ts +0 -22
  559. package/dist/src/providers/strategies/google.strategy.js +0 -174
  560. package/dist/src/providers/strategies/siliconflow.strategy.js +0 -29
  561. package/dist/src/ui/containers/chat-feed.d.ts +0 -40
  562. package/dist/src/ui/containers/chat-feed.js +0 -66
@@ -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, resolveEventLogPath } 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}$/;
@@ -49,6 +50,25 @@ export function sanitizeEnv(env) {
49
50
  }
50
51
  return out;
51
52
  }
53
+ /**
54
+ * Build the child TUI's environment for tui.start.
55
+ *
56
+ * Starts from the driver's own env so PATH/HOME survive, overlays the
57
+ * caller-supplied keys, THEN strips the dangerous ones. The merge-before-strip
58
+ * order keeps the security posture (NODE_OPTIONS/LD_PRELOAD/… still removed)
59
+ * while guaranteeing PATH is present: bun drops the fd 3/4 stdio channels when a
60
+ * child is spawned with a partial env that lacks PATH, which yields a null
61
+ * inWrite and crashes the whole driver on spawn. `base` defaults to process.env
62
+ * and is injectable for tests.
63
+ */
64
+ export function buildChildEnv(callerEnv = {}, base = process.env) {
65
+ const merged = {};
66
+ for (const [k, v] of Object.entries(base)) {
67
+ if (v !== undefined)
68
+ merged[k] = v;
69
+ }
70
+ return sanitizeEnv({ ...merged, ...callerEnv });
71
+ }
52
72
  const REPO_ROOT = process.cwd();
53
73
  /**
54
74
  * Opt-in extra cwd roots for tui.start, layered ON TOP of the default
@@ -133,11 +153,30 @@ export function validateMockLlmPath(value) {
133
153
  const sep = process.platform === "win32" ? "\\" : "/";
134
154
  return real === root || real.startsWith(root + sep);
135
155
  }
136
- const FEATURES = ["capabilities", "snapshot", "press", "type", "wait_for", "query", "expect", "render_text"];
156
+ const FEATURES = [
157
+ "capabilities",
158
+ "snapshot",
159
+ "press",
160
+ "type",
161
+ "wait_for",
162
+ "query",
163
+ "expect",
164
+ "render_text",
165
+ "render_visual",
166
+ "snapshot_visual",
167
+ "cell",
168
+ "visual_quality",
169
+ "wait_for_event",
170
+ "event_log",
171
+ ];
137
172
  export function buildCapabilitiesPayload() {
138
173
  return {
139
174
  protocol: PROTOCOL_VERSION,
140
175
  features: FEATURES,
176
+ // A default-on sink nobody can locate is still opt-in. Reporting the
177
+ // resolved path here is what makes it discoverable without the caller
178
+ // reproducing the env/tmpdir/pid rule.
179
+ eventLogPath: resolveEventLogPath(process.env["MUONROI_HARNESS_EVENT_LOG"]),
141
180
  };
142
181
  }
143
182
  export function registerReadTools(server, getDriver) {
@@ -197,12 +236,54 @@ export function registerReadTools(server, getDriver) {
197
236
  return noDriver();
198
237
  return { content: [{ type: "text", text: String(d.count(selector)) }] };
199
238
  });
200
- server.registerTool("tui.render_text", { description: "ASCII debug render of the current frame.", inputSchema: {} }, async () => {
239
+ server.registerTool("tui.render_text", {
240
+ // "debug render" undersold it: this is the primary way to see what is on
241
+ // screen right now — every node, modal and message in the semantic tree.
242
+ description: "See the current screen as a semantic tree (every node, modal, message, and focus state). " +
243
+ "Use tui.render_visual instead for the literal characters a human reads.",
244
+ inputSchema: {},
245
+ }, async () => {
201
246
  const d = getDriver();
202
247
  if (!d)
203
248
  return noDriver();
204
249
  return { content: [{ type: "text", text: d.render_text() }] };
205
250
  });
251
+ server.registerTool("tui.render_visual", {
252
+ 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.",
253
+ inputSchema: {},
254
+ }, async () => {
255
+ const d = getDriver();
256
+ if (!d)
257
+ return noDriver();
258
+ return { content: [{ type: "text", text: d.render_visual() }] };
259
+ });
260
+ server.registerTool("tui.snapshot_visual", {
261
+ 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.",
262
+ inputSchema: {},
263
+ }, async () => {
264
+ const d = getDriver();
265
+ if (!d)
266
+ return noDriver();
267
+ return { content: [{ type: "text", text: JSON.stringify(d.snapshot_visual()) }] };
268
+ });
269
+ server.registerTool("tui.cell", {
270
+ 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.",
271
+ inputSchema: { row: z.number().int().min(0), col: z.number().int().min(0) },
272
+ }, async ({ row, col }) => {
273
+ const d = getDriver();
274
+ if (!d)
275
+ return noDriver();
276
+ return { content: [{ type: "text", text: JSON.stringify(d.visual_cell(row, col)) }] };
277
+ });
278
+ server.registerTool("tui.visual_quality", {
279
+ 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.",
280
+ inputSchema: {},
281
+ }, async () => {
282
+ const d = getDriver();
283
+ if (!d)
284
+ return noDriver();
285
+ return { content: [{ type: "text", text: JSON.stringify(d.visual_quality()) }] };
286
+ });
206
287
  }
207
288
  export function registerActionTools(server, getDriver) {
208
289
  const noDriver = () => ({
@@ -268,21 +349,53 @@ export function registerAsyncTools(server, getDriver, deps) {
268
349
  const waitConditionShape = {
269
350
  selector: z.string().max(500).optional(),
270
351
  idle: z.boolean().optional(),
352
+ event: z.string().max(64).optional(),
271
353
  };
354
+ /**
355
+ * Rebuild the driver's WaitArgs from validated MCP input.
356
+ *
357
+ * Built key-by-key rather than passed through, because the driver dispatches
358
+ * on `"selector" in args` BEFORE `"event" in args` — an explicitly-`undefined`
359
+ * `selector` key would still win that check and silently wait on nothing.
360
+ */
361
+ function toWaitArgs(c) {
362
+ if (typeof c.selector === "string")
363
+ return { selector: c.selector };
364
+ if (typeof c.event === "string")
365
+ return { event: c.event };
366
+ if (c.idle)
367
+ return { idle: true };
368
+ return {};
369
+ }
272
370
  server.registerTool("tui.wait_for", {
273
- description: "Wait until a selector matches or the TUI is idle (or both for all=).",
371
+ description: "Block until a selector matches, a LiveEvent of the given kind arrives, or the TUI is idle " +
372
+ "(all= requires every condition). Prefer this over polling: the wait resolves on the driver's " +
373
+ "own event stream, so a modal pause (event='askcard-open') or a sprint halt wakes it immediately.",
274
374
  inputSchema: {
275
375
  selector: z.string().max(500).optional(),
276
376
  idle: z.boolean().optional(),
377
+ // The driver has always supported an `event` condition (buildCheck in
378
+ // driver.ts); only this MCP schema withheld it, so external agents fell
379
+ // back to polling a DB that never records askcard-open at all.
380
+ event: z.string().max(64).optional(),
277
381
  all: z.array(z.object(waitConditionShape)).max(10).optional(),
278
- timeoutMs: z.number().int().min(0).max(60_000).optional(),
382
+ // 10 min, matching the longest legitimate single wait (a council debate
383
+ // phase measured 119.5s, and an unattended /ideal sprint runs longer).
384
+ // At 60s a caller had to re-issue the wait repeatedly — polling wearing
385
+ // a wait_for costume.
386
+ timeoutMs: z.number().int().min(0).max(600_000).optional(),
279
387
  },
280
388
  }, async (input) => {
281
389
  const d = getDriver();
282
390
  if (!d)
283
391
  return noDriver();
392
+ const args = input.all
393
+ ? { all: input.all.map(toWaitArgs) }
394
+ : toWaitArgs(input);
395
+ if (typeof input.timeoutMs === "number")
396
+ args.timeoutMs = input.timeoutMs;
284
397
  try {
285
- await d.wait_for(input);
398
+ await d.wait_for(args);
286
399
  return { content: [{ type: "text", text: "ok" }] };
287
400
  }
288
401
  catch (e) {
@@ -305,7 +418,17 @@ export function registerAsyncTools(server, getDriver, deps) {
305
418
  return { content: [{ type: "text", text: String(ok) }] };
306
419
  });
307
420
  server.registerTool("tui.last_event", {
308
- description: "Return the most recent event of the given kind (null if none).",
421
+ // Keyword-loaded on purpose: deferred-tool search matches this text, and
422
+ // the terse original ("Return the most recent event of the given kind")
423
+ // did not rank for "is the TUI waiting for input or hung" — the exact
424
+ // question it answers. An unfindable tool is an absent one.
425
+ description: "Check what a live run is doing: is it waiting on a human (kind='askcard-open' — the " +
426
+ "modal/prompt/approval question), what stage it reached (kind='sprint-stage'), did it " +
427
+ "fail or hang (kind='sprint-halt' / 'toast'), how the council progressed " +
428
+ "(kind='council-step'). Returns the most recent event of that kind with its FULL payload " +
429
+ "(null if none) — including the complete askcard question text, which tui.query truncates. " +
430
+ "Use this instead of polling a database or log file: modal pauses write no DB row, so a " +
431
+ "poller cannot tell 'waiting for a human' from 'hung'. Pair with tui.wait_for to block.",
309
432
  // Full protocol event set (minus the idle sentinel) so an external agent can
310
433
  // observe lifecycle events — council/sprint/route/askcard, not just toasts.
311
434
  // The Driver accepts any kind; this enum is the MCP-boundary validation.
@@ -423,7 +546,9 @@ export function createMcpHarnessServer({ spawn }) {
423
546
  isError: true,
424
547
  };
425
548
  }
426
- const sanitizedEnv = sanitizeEnv(input.env ?? {});
549
+ // See buildChildEnv: merge process.env (keeps PATH so bun allocates the
550
+ // fd 3/4 transport) with caller keys, then strip dangerous vars.
551
+ const sanitizedEnv = buildChildEnv(input.env ?? {});
427
552
  // Build final arg list.
428
553
  const finalArgs = [...input.args];
429
554
  if (input.mockLlmDir && !finalArgs.some((a) => a.startsWith("--mock-llm"))) {
@@ -459,22 +584,14 @@ export function createMcpHarnessServer({ spawn }) {
459
584
  sendKey: (k) => sendLine(JSON.stringify({ op: "press", key: k })),
460
585
  sendType: (t) => sendLine(JSON.stringify({ op: "type", text: t })),
461
586
  });
587
+ // JSONL event sink for external milestone watchers — on by default, null
588
+ // only when MUONROI_HARNESS_EVENT_LOG explicitly disables it. Ephemeral
589
+ // kinds carry an at-emit visual snapshot so flash events aren't lost
590
+ // before an agent wakes. Path is reported by tui.capabilities.
591
+ const eventTee = createEventTee(() => driver.render_visual(), process.env["MUONROI_HARNESS_EVENT_LOG"]);
462
592
  // onLine already delivers complete newline-stripped lines — no extra
463
593
  // 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
- });
594
+ const unsub = onLine(makeLineHandler(driver, eventTee));
478
595
  spawnResult.exited.then(() => {
479
596
  unsub();
480
597
  if (currentPid === proc.pid) {
@@ -491,6 +608,37 @@ export function createMcpHarnessServer({ spawn }) {
491
608
  registerAsyncTools(server, () => currentDriver, { onStop });
492
609
  return server;
493
610
  }
611
+ /**
612
+ * Build the onLine sidechannel handler: parse each JSON line, ingest frames /
613
+ * visuals / idle / events into the driver, and tee events to the optional JSONL
614
+ * sink. Extracted (and exported) so the frame/event/tee wiring is unit-testable
615
+ * without a live TUI or an MCP protocol handshake.
616
+ *
617
+ * @param driver the driver to ingest into (only _ingest is used).
618
+ * @param eventTee optional sink from createEventTee (null → no tee).
619
+ */
620
+ export function makeLineHandler(driver, eventTee) {
621
+ return (line) => {
622
+ try {
623
+ const msg = JSON.parse(line);
624
+ if (msg.mode === "live")
625
+ driver._ingest({ kind: "frame", frame: msg });
626
+ else if (msg.mode === "visual")
627
+ driver._ingest({ kind: "visual", frame: msg });
628
+ else if (msg.t === "idle")
629
+ driver._ingest({ kind: "idle" });
630
+ else if (msg.t === "event") {
631
+ const event = msg;
632
+ driver._ingest({ kind: "event", event });
633
+ // Tee AFTER ingest so render_visual reflects the frame at this event.
634
+ eventTee?.(event);
635
+ }
636
+ }
637
+ catch {
638
+ // ignore malformed lines
639
+ }
640
+ };
641
+ }
494
642
  /**
495
643
  * Run the MCP harness driver over stdio with the OpenTUI spawn implementation
496
644
  * injected by the consumer.
@@ -2,8 +2,8 @@ import { z } from "zod";
2
2
  import type { UINode } from "./protocol.js";
3
3
  declare const FieldOp: z.ZodObject<{
4
4
  field: z.ZodEnum<{
5
- name: "name";
6
5
  value: "value";
6
+ name: "name";
7
7
  state: "state";
8
8
  }>;
9
9
  op: z.ZodEnum<{
@@ -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";
@@ -67,12 +131,34 @@ export type LiveEvent = {
67
131
  kind: "council-speaker";
68
132
  /** The council role label (e.g. "architect", "security", "qa"). */
69
133
  role: string;
70
- /** "start" — speaker began their turn; "done" — speaker finished. */
71
- status: "start" | "done";
134
+ /**
135
+ * "start" speaker began; "tick" — 1s progress heartbeat (long phases
136
+ * like research emit these with an advancing `elapsedMs`); "done" —
137
+ * speaker finished. A harness poller distinguishes ALIVE (tick with
138
+ * advancing elapsedMs) from HUNG (elapsedMs frozen) via `tui_last_event`.
139
+ */
140
+ status: "start" | "tick" | "done";
72
141
  /** Round number if available from the status chunk. */
73
142
  round?: number;
74
143
  /** Correlation ID linking this speaker event to the enclosing council run. */
75
144
  correlationId: string;
145
+ /** Milliseconds elapsed in this speaker's turn/phase; advances on ticks. */
146
+ elapsedMs?: number;
147
+ /**
148
+ * Chars (text + reasoning deltas) streamed since this phase began.
149
+ * `elapsedMs` can freeze on a HEALTHY call — its tick generator only
150
+ * advances when the consumer pulls, and a round awaiting its pairs via
151
+ * Promise.all does not pull. This counter is pushed from the token stream
152
+ * itself, so growth here proves liveness regardless of pumping.
153
+ */
154
+ streamedChars?: number;
155
+ /**
156
+ * Age in ms of the most recent stream delta. Small + growing
157
+ * `streamedChars` = SLOW BUT ALIVE (e.g. a reasoning model emitting
158
+ * reasoning tokens for minutes before any text); growing age + static
159
+ * chars = genuinely STUCK.
160
+ */
161
+ lastDeltaAgeMs?: number;
76
162
  } | {
77
163
  t: "event";
78
164
  kind: "council-turn-length";
@@ -226,4 +312,4 @@ export type DesignSpec = {
226
312
  notes?: string;
227
313
  }>;
228
314
  };
229
- export type HarnessMessage = LiveFrame | LiveEvent;
315
+ 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
  };