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
@@ -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;
@@ -35,7 +35,54 @@ const NAMED = {
35
35
  PageUp: { name: "pageup", sequence: "\x1b[5~", raw: "\x1b[5~" },
36
36
  PageDown: { name: "pagedown", sequence: "\x1b[6~", raw: "\x1b[6~" },
37
37
  };
38
- function makeKey(partial) {
38
+ // Case-insensitive view of NAMED. The map above is capitalized, but drivers
39
+ // naturally send lowercase ("enter", "escape", "tab", "space") — those missed
40
+ // the exact-case lookup, so keyForNamed returned null and the keystroke was a
41
+ // SILENT no-op (Enter never submitted, Escape never dismissed a modal). Resolve
42
+ // on the lowercased base so every casing works. Includes a couple of common
43
+ // short aliases drivers reach for.
44
+ const NAMED_LC = {
45
+ ...Object.fromEntries(Object.entries(NAMED).map(([k, v]) => [k.toLowerCase(), v])),
46
+ ret: NAMED.Return,
47
+ del: NAMED.Delete,
48
+ spacebar: NAMED.Space,
49
+ pgup: NAMED.PageUp,
50
+ pgdn: NAMED.PageDown,
51
+ };
52
+ /**
53
+ * Strip modifier prefixes (`C-` ctrl, `M-` meta/alt, `S-` shift) off a harness
54
+ * key string so combos like `C-b` or `C-o` can be driven. Prefixes may stack in
55
+ * any order (`C-S-tab`). The remainder is the base key (named or single char).
56
+ */
57
+ function parseModifiers(key) {
58
+ const mods = {};
59
+ let base = key;
60
+ // Only consume a prefix when a base remains after it, so a literal "C" or a
61
+ // hyphen key is not mis-parsed.
62
+ for (;;) {
63
+ if (base.length > 2 && base[1] === "-") {
64
+ const p = base[0];
65
+ if (p === "C" || p === "c") {
66
+ mods.ctrl = true;
67
+ base = base.slice(2);
68
+ continue;
69
+ }
70
+ if (p === "M" || p === "m") {
71
+ mods.meta = true;
72
+ base = base.slice(2);
73
+ continue;
74
+ }
75
+ if (p === "S") {
76
+ mods.shift = true;
77
+ base = base.slice(2);
78
+ continue;
79
+ }
80
+ }
81
+ break;
82
+ }
83
+ return { mods, base };
84
+ }
85
+ function makeKey(partial, mods = {}) {
39
86
  // Some app key-handlers conditionally call key.preventDefault()/stopPropagation()
40
87
  // (typed `?.()` so they tolerate undefined). When the host's keypress arrives
41
88
  // through a terminal, OpenTUI attaches these methods; in agent-mode we
@@ -48,9 +95,9 @@ function makeKey(partial) {
48
95
  let _propagationStopped = false;
49
96
  const ev = {
50
97
  name: partial.name,
51
- ctrl: false,
52
- meta: false,
53
- shift: false,
98
+ ctrl: mods.ctrl ?? false,
99
+ meta: mods.meta ?? false,
100
+ shift: mods.shift ?? false,
54
101
  option: false,
55
102
  sequence: partial.sequence,
56
103
  number: false,
@@ -72,18 +119,22 @@ function makeKey(partial) {
72
119
  };
73
120
  return ev;
74
121
  }
75
- function keyForChar(ch) {
76
- return makeKey({ name: ch, sequence: ch, raw: ch });
122
+ function keyForChar(ch, mods = {}) {
123
+ return makeKey({ name: ch, sequence: ch, raw: ch }, mods);
77
124
  }
78
- function keyForNamed(key) {
79
- const m = NAMED[key];
125
+ export function keyForNamed(key) {
126
+ const { mods, base } = parseModifiers(key);
127
+ // Exact case first (fast path), then case-insensitive so "enter"/"Enter"/
128
+ // "ENTER"/"return" all resolve. Single-letter bases never collide with a
129
+ // NAMED word, so they still fall through to the literal-char path below.
130
+ const m = NAMED[base] ?? NAMED_LC[base.toLowerCase()];
80
131
  if (!m) {
81
132
  // Unknown named key — fall back to treating it as a literal character if it's 1 char.
82
- if (key.length === 1)
83
- return keyForChar(key);
133
+ if (base.length === 1)
134
+ return keyForChar(base, mods);
84
135
  return null;
85
136
  }
86
- return makeKey(m);
137
+ return makeKey(m, mods);
87
138
  }
88
139
  /**
89
140
  * Subscribe to agentRuntime.onCommand and inject synthetic key events into
@@ -95,15 +146,51 @@ function keyForNamed(key) {
95
146
  * routing in OpenTUI is owned by individual components; a global focus dispatch
96
147
  * is out of scope for this bridge.)
97
148
  */
149
+ // Runtimes that already have the input bridge wired — module-level so the guard
150
+ // survives App remounts (a per-component ref would reset and re-register).
151
+ const mountedRuntimes = new WeakSet();
98
152
  export function useAgentInputBridge(agentRuntime) {
99
- const { keyHandler } = useAppContext();
153
+ const ctx = useAppContext();
154
+ const keyHandler = ctx.keyHandler;
155
+ // Focused renderables (the composer textarea) subscribe to keypresses via
156
+ // renderer._internalKeyInput.onInternal("keypress"), NOT via the public
157
+ // keyHandler (useAppContext().keyHandler === renderer.keyInput). On the real
158
+ // TTY renderer both are the SAME InternalKeyHandler, but under the agent-mode
159
+ // headless renderer they are DISTINCT objects — so emitting only on keyHandler
160
+ // reached app-level useKeyboard shortcuts yet never the textarea, and typed
161
+ // text silently vanished (the tier-2 harness-drive bug). Prefer
162
+ // _internalKeyInput: its emitWithPriority dispatches to BOTH the focused
163
+ // renderable AND global listeners registered on it.
164
+ const internal = ctx.renderer?._internalKeyInput;
100
165
  useEffect(() => {
101
- if (!agentRuntime || !keyHandler)
166
+ if (!agentRuntime)
167
+ return;
168
+ // onCommand has no unsubscribe (it only pushes to a handler array), so
169
+ // registering twice doubles every keystroke (observed: a typed prompt
170
+ // arrived duplicated). A per-component ref is not enough — the App can
171
+ // remount (e.g. hero → chat transition) giving a fresh ref while the prior
172
+ // handler stays registered on the same long-lived runtime. Track mounted
173
+ // runtimes in a module-level WeakSet so registration is once-per-runtime
174
+ // across remounts.
175
+ if (mountedRuntimes.has(agentRuntime))
176
+ return;
177
+ const target = internal ?? keyHandler;
178
+ if (!target)
102
179
  return;
103
180
  // Toast-only stub in normal interactive mode provides emitEvent but not
104
181
  // onCommand. Guard so the bridge no-ops cleanly instead of crashing the TUI.
105
182
  if (typeof agentRuntime.onCommand !== "function")
106
183
  return;
184
+ mountedRuntimes.add(agentRuntime);
185
+ // Emit to the renderable channel (the focused textarea), and also to the
186
+ // public keyHandler when it is a distinct object so global shortcuts still
187
+ // fire. The `!== target` guard avoids double-dispatch on the real renderer
188
+ // where the two are identical.
189
+ const emitKey = (k) => {
190
+ target.emit("keypress", k);
191
+ if (keyHandler && keyHandler !== target)
192
+ keyHandler.emit("keypress", k);
193
+ };
107
194
  agentRuntime.onCommand((cmd) => {
108
195
  if (!cmd || typeof cmd !== "object")
109
196
  return;
@@ -113,14 +200,14 @@ export function useAgentInputBridge(agentRuntime) {
113
200
  return;
114
201
  const k = keyForNamed(c.key);
115
202
  if (k)
116
- keyHandler.emit("keypress", k);
203
+ emitKey(k);
117
204
  }
118
205
  else if (c.op === "type" && typeof c.text === "string") {
119
206
  for (const ch of c.text) {
120
- keyHandler.emit("keypress", keyForChar(ch));
207
+ emitKey(keyForChar(ch));
121
208
  }
122
209
  }
123
210
  });
124
- }, [agentRuntime, keyHandler]);
211
+ }, [agentRuntime, internal, keyHandler]);
125
212
  }
126
213
  //# sourceMappingURL=input-bridge.js.map
@@ -41,6 +41,14 @@ export interface InstallOpenTUIHarnessOptions {
41
41
  * the `--agent-fake-clock` CLI flag.
42
42
  */
43
43
  fakeClock?: boolean;
44
+ /**
45
+ * Optional visual-frame capture. When provided, a VisualFrame (the actual
46
+ * rendered cell grid — colors + attributes) is captured and sent right after
47
+ * each SEMANTIC frame change. Piggybacking on semantic changes bounds the
48
+ * per-frame `getSpanLines()` allocation to real UI updates instead of every
49
+ * poll tick. Returns null on dedup / unavailable renderer.
50
+ */
51
+ captureVisual?: (seq: number, ts: number) => object | null;
44
52
  }
45
53
  export interface OpenTUIHarnessHandle {
46
54
  /**
@@ -28,6 +28,7 @@ export function installOpenTUIHarness(opts) {
28
28
  const fps = opts.fps ?? 60;
29
29
  const intervalMs = Math.max(1, Math.round(1000 / fps));
30
30
  let seq = 0;
31
+ let vseq = 0;
31
32
  const fakeClock = opts.fakeClock ?? false;
32
33
  const hook = createReconcilerHook({
33
34
  registry: opts.registry,
@@ -43,6 +44,15 @@ export function installOpenTUIHarness(opts) {
43
44
  try {
44
45
  opts.transport.send(`${JSON.stringify(frame)}\n`);
45
46
  opts.onFrame?.(frame);
47
+ // Piggyback the visual (rendered cell-grid) frame on semantic changes so
48
+ // getSpanLines() only runs when the UI actually changed, not every tick.
49
+ if (opts.captureVisual) {
50
+ const vframe = opts.captureVisual(vseq, fakeClock ? vseq * 16 : Date.now());
51
+ if (vframe !== null) {
52
+ vseq++;
53
+ opts.transport.send(`${JSON.stringify(vframe)}\n`);
54
+ }
55
+ }
46
56
  }
47
57
  catch {
48
58
  // Drop frame on transport error — survivor of next tick will retry.
@@ -31,22 +31,24 @@ export function Semantic(props) {
31
31
  const unregister = registry.register({ ...nodeRef.current, parentId });
32
32
  return unregister;
33
33
  }, [registry, parentId, node.id]);
34
- // Updates: re-register on prop change. This calls register() with the new
35
- // node fields; Map.set on an existing key preserves insertion order.
36
- // biome-ignore lint/correctness/useExhaustiveDependencies: nodeKey is the intentional cache-bust for node fields
34
+ // Updates: patch fields on prop change via registry.update(). nodeKey is the
35
+ // intentional cache-bust dep it serializes the node fields so this effect
36
+ // re-runs whenever any field changes; Map.set on an existing key preserves
37
+ // insertion order.
37
38
  React.useEffect(() => {
38
39
  if (!registry)
39
40
  return;
40
41
  if (!nodeKey)
41
42
  return;
42
43
  // Skip the very first run — the mount effect above already registered.
43
- // We detect "subsequent run" by checking if the entry exists in the
44
- // registry. On mount, the order is: this effect runs after the mount
45
- // effect, so the entry IS present. We then call register() again with
46
- // the same node that's a no-op write that updates fields without
47
- // changing position. On subsequent renders with different nodeKey,
48
- // register() updates the fields in place.
49
- registry.register({ ...nodeRef.current, parentId });
44
+ // On mount this effect runs after the mount effect, so the entry IS
45
+ // present; update() patches fields in place (Map.set on an existing key
46
+ // preserves insertion order). On subsequent renders with a different
47
+ // nodeKey it patches the changed fields. Using update() (not register())
48
+ // is the cheaper intended path it merges instead of rebuilding the
49
+ // entry, and it is a no-op if the entry was already unmounted.
50
+ const { id: _id, ...patch } = nodeRef.current;
51
+ registry.update(nodeRef.current.id, patch);
50
52
  // No cleanup — the mount effect's cleanup is the single source of
51
53
  // truth for unmounting. If we returned a cleanup here, it would race
52
54
  // with re-renders and re-delete the entry.
@@ -0,0 +1,56 @@
1
+ /**
2
+ * visual-capture.ts — read OpenTUI's ACTUAL rendered cell grid.
3
+ *
4
+ * The semantic harness (reconciler-hook) reports STRUCTURE (id/role/name/props)
5
+ * but is blind to what a human SEES — colors, bold/italic, alignment, blank
6
+ * rows, mojibake. This module snapshots the ground-truth render buffer instead:
7
+ * OpenTUI's `CliRenderer.currentRenderBuffer` is a cell grid it already paints,
8
+ * and `OptimizedBuffer.getSpanLines()` returns per-cell char + RGBA fg/bg +
9
+ * attribute bits (verified against @opentui/core@0.1.107 buffer.d.ts:43 +
10
+ * lib/RGBA.d.ts). No OCR — the buffer is authoritative and in-process.
11
+ *
12
+ * Consumers get a `VisualFrame` (protocol) they can query per-cell, render to
13
+ * annotated text, or (P2) rasterize to PNG for a vision model.
14
+ */
15
+ import { type VisualFrame } from "@muonroi/agent-harness-core/protocol";
16
+ interface RgbaLike {
17
+ toInts?: () => [number, number, number, number];
18
+ r?: number;
19
+ g?: number;
20
+ b?: number;
21
+ a?: number;
22
+ }
23
+ interface CapturedSpanLike {
24
+ text: string;
25
+ fg: RgbaLike;
26
+ bg: RgbaLike;
27
+ attributes: number;
28
+ width: number;
29
+ }
30
+ interface CapturedLineLike {
31
+ spans: CapturedSpanLike[];
32
+ }
33
+ interface OptimizedBufferLike {
34
+ width: number;
35
+ height: number;
36
+ getSpanLines: () => CapturedLineLike[];
37
+ }
38
+ export interface RendererLike {
39
+ currentRenderBuffer?: OptimizedBufferLike;
40
+ nextRenderBuffer?: OptimizedBufferLike;
41
+ }
42
+ export interface VisualCaptureHook {
43
+ /** Build a VisualFrame from the renderer's current buffer. Returns null when
44
+ * no renderer is attached, the buffer is unreadable, or content is unchanged
45
+ * (content-hash dedup — same contract as the semantic reconciler hook). */
46
+ capture(seq: number, ts: number): VisualFrame | null;
47
+ /** Reset dedup state so the next capture always emits. */
48
+ resetDedup(): void;
49
+ }
50
+ /**
51
+ * Create a visual-capture hook. `getRenderer` is a late-bound accessor because
52
+ * the OpenTUI renderer is constructed AFTER the agent-mode runtime (which owns
53
+ * this hook) — see src/index.ts attachRenderer wiring.
54
+ */
55
+ export declare function createVisualCaptureHook(getRenderer: () => RendererLike | undefined): VisualCaptureHook;
56
+ export {};
@@ -0,0 +1,103 @@
1
+ /**
2
+ * visual-capture.ts — read OpenTUI's ACTUAL rendered cell grid.
3
+ *
4
+ * The semantic harness (reconciler-hook) reports STRUCTURE (id/role/name/props)
5
+ * but is blind to what a human SEES — colors, bold/italic, alignment, blank
6
+ * rows, mojibake. This module snapshots the ground-truth render buffer instead:
7
+ * OpenTUI's `CliRenderer.currentRenderBuffer` is a cell grid it already paints,
8
+ * and `OptimizedBuffer.getSpanLines()` returns per-cell char + RGBA fg/bg +
9
+ * attribute bits (verified against @opentui/core@0.1.107 buffer.d.ts:43 +
10
+ * lib/RGBA.d.ts). No OCR — the buffer is authoritative and in-process.
11
+ *
12
+ * Consumers get a `VisualFrame` (protocol) they can query per-cell, render to
13
+ * annotated text, or (P2) rasterize to PNG for a vision model.
14
+ */
15
+ import { createHash } from "node:crypto";
16
+ import { PROTOCOL_VERSION, } from "@muonroi/agent-harness-core/protocol";
17
+ /** Low byte of OpenTUI's TextAttributes (ATTRIBUTE_BASE_MASK = 255). */
18
+ const ATTR_MASK = 255;
19
+ function clampByte(n) {
20
+ return Math.max(0, Math.min(255, Math.round(n)));
21
+ }
22
+ /**
23
+ * Convert an OpenTUI RGBA to a "#rrggbb" (or "#rrggbbaa" when translucent) hex
24
+ * string. Prefers `toInts()` (0-255 ints); falls back to component getters,
25
+ * scaling 0-1 floats up to 0-255 when the renderer exposes normalized channels.
26
+ */
27
+ function toHex(c) {
28
+ let r = 0;
29
+ let g = 0;
30
+ let b = 0;
31
+ let a = 255;
32
+ if (c && typeof c.toInts === "function") {
33
+ const ints = c.toInts();
34
+ r = ints[0];
35
+ g = ints[1];
36
+ b = ints[2];
37
+ a = ints[3];
38
+ }
39
+ else if (c) {
40
+ const scale = (v) => (v === undefined ? 0 : v <= 1 ? v * 255 : v);
41
+ r = scale(c.r);
42
+ g = scale(c.g);
43
+ b = scale(c.b);
44
+ a = c.a === undefined ? 255 : c.a <= 1 ? c.a * 255 : c.a;
45
+ }
46
+ const h = (n) => clampByte(n).toString(16).padStart(2, "0");
47
+ return a < 255 ? `#${h(r)}${h(g)}${h(b)}${h(a)}` : `#${h(r)}${h(g)}${h(b)}`;
48
+ }
49
+ /**
50
+ * Create a visual-capture hook. `getRenderer` is a late-bound accessor because
51
+ * the OpenTUI renderer is constructed AFTER the agent-mode runtime (which owns
52
+ * this hook) — see src/index.ts attachRenderer wiring.
53
+ */
54
+ export function createVisualCaptureHook(getRenderer) {
55
+ let lastHash;
56
+ function capture(seq, ts) {
57
+ const renderer = getRenderer();
58
+ const buffer = renderer?.currentRenderBuffer ?? renderer?.nextRenderBuffer;
59
+ if (!buffer || typeof buffer.getSpanLines !== "function")
60
+ return null;
61
+ let captured;
62
+ try {
63
+ captured = buffer.getSpanLines();
64
+ }
65
+ catch (err) {
66
+ // getSpanLines allocates + decodes native Zig memory; a mid-resize/destroy
67
+ // race can throw. Skip this frame (the next tick retries). Debug-gated log
68
+ // only — unconditional logging here would spam the render loop.
69
+ if (process.env.MUONROI_DEBUG_VISUAL) {
70
+ console.error(`[visual-capture] getSpanLines failed: ${err?.message}`);
71
+ }
72
+ return null;
73
+ }
74
+ const lines = captured.map((ln) => ({
75
+ spans: (ln.spans ?? []).map((s) => ({
76
+ text: s.text,
77
+ fg: toHex(s.fg),
78
+ bg: toHex(s.bg),
79
+ attrs: (s.attributes ?? 0) & ATTR_MASK,
80
+ width: s.width,
81
+ })),
82
+ }));
83
+ const hash = createHash("sha1").update(JSON.stringify(lines)).digest("hex");
84
+ if (lastHash === hash)
85
+ return null;
86
+ lastHash = hash;
87
+ return {
88
+ mode: "visual",
89
+ version: PROTOCOL_VERSION,
90
+ seq,
91
+ ts,
92
+ cols: buffer.width,
93
+ rows: buffer.height,
94
+ cursor: null,
95
+ lines,
96
+ };
97
+ }
98
+ function resetDedup() {
99
+ lastHash = undefined;
100
+ }
101
+ return { capture, resetDedup };
102
+ }
103
+ //# sourceMappingURL=visual-capture.js.map
@@ -48,6 +48,44 @@ export interface MockModelFixture {
48
48
  provider?: string;
49
49
  /** Reported model id. Default "mock-model". */
50
50
  modelId?: string;
51
+ /**
52
+ * Response for PIL's model-first intent classifier (src/pil/llm-classify.ts).
53
+ * Since the no-regex refactor (2026-07-07) PIL classifies EVERY turn by
54
+ * calling the model with a distinctive system prompt ("Reply with ONE line of
55
+ * EIGHT lowercase words…") and parses a comma-separated line. The generic
56
+ * fixture stream ("Hello back!" / arbitrary chunks) does not parse → the turn
57
+ * degrades to intentKind=UNKNOWN, which skips the gsd gate and breaks routing
58
+ * in E2E specs. The mock detects that classify call by its system prompt and
59
+ * returns THIS line instead of consuming the turn fixture — so every spec gets
60
+ * a sane classification for free. Override per-fixture when a spec needs a
61
+ * specific taskType/intent/depth. Format:
62
+ * <taskType>,<style>,<intent>,<deliverable>,<depth>,<scope>,<lang>,<clarity>
63
+ */
64
+ classify?: string;
65
+ /**
66
+ * Milliseconds to wait before each streamed chunk. Default 0 (instant).
67
+ *
68
+ * A real provider takes seconds; the mock is instant, which makes anything
69
+ * that only exists WHILE a call is in flight — a progress card, a spinner —
70
+ * impossible to observe from a spec. Set this to hold the in-flight state open
71
+ * long enough to assert on it. Keep it small: it is wall-clock in every spec
72
+ * that sets it.
73
+ */
74
+ chunkDelayMs?: number;
75
+ /**
76
+ * Opt in to the PIL-classifier intercept described on `classify`. When true,
77
+ * a `doStream` call carrying the classifier's system prompt is answered with
78
+ * `classify` (or DEFAULT_CLASSIFY_LINE) WITHOUT consuming a turn fixture.
79
+ *
80
+ * This is a convenience for the file-based E2E harness (`loadMockModelFromDir`
81
+ * sets it true by default) whose fixtures supply agent turn-rounds and must
82
+ * not spend a round on the classify call. Direct in-memory callers
83
+ * (`createMockModel`/`installMockModel`, e.g. llm-classify.test.ts) leave it
84
+ * OFF so the fixture stream they configure IS delivered to the classifier —
85
+ * otherwise every classification would collapse to DEFAULT_CLASSIFY_LINE.
86
+ * Setting `classify` implies opting in.
87
+ */
88
+ autoClassify?: boolean;
51
89
  }
52
90
  export interface MockModelHandle {
53
91
  /** The mock model instance — assign to `globalThis.__muonroiMockModel`. */
@@ -25,6 +25,40 @@
25
25
  import { APICallError } from "@ai-sdk/provider";
26
26
  import { simulateReadableStream } from "ai";
27
27
  import { MockLanguageModelV3 } from "ai/test";
28
+ /**
29
+ * System-prompt substring that uniquely identifies PIL's model-first intent
30
+ * classifier call (see SYSTEM_PROMPT in src/pil/llm-classify.ts). Kept in sync
31
+ * with that constant's opening line.
32
+ */
33
+ const CLASSIFY_SIGNATURE = "Reply with ONE line of EIGHT lowercase words";
34
+ /**
35
+ * Safe default classification: a non-chitchat coding task of standard depth.
36
+ * `intent=task` keeps the toolset + gsd gate active; `standard` depth avoids
37
+ * forcing heavy-only paths. First word MUST be a valid taskType.
38
+ */
39
+ const DEFAULT_CLASSIFY_LINE = "generate, balanced, task, code, standard, local, english, clear";
40
+ /** Concatenated text of every `system` message in a doStream call's prompt. */
41
+ function systemPromptText(options) {
42
+ const prompt = options.prompt;
43
+ if (!Array.isArray(prompt))
44
+ return "";
45
+ const parts = [];
46
+ for (const msg of prompt) {
47
+ const m = msg;
48
+ if (m.role !== "system")
49
+ continue;
50
+ if (typeof m.content === "string")
51
+ parts.push(m.content);
52
+ else if (Array.isArray(m.content)) {
53
+ for (const c of m.content) {
54
+ const t = c.text;
55
+ if (typeof t === "string")
56
+ parts.push(t);
57
+ }
58
+ }
59
+ }
60
+ return parts.join("\n");
61
+ }
28
62
  function isNestedArray(v) {
29
63
  return Array.isArray(v) && v.length > 0 && Array.isArray(v[0]);
30
64
  }
@@ -44,10 +78,35 @@ export function createMockModel(fx) {
44
78
  let genIdx = 0;
45
79
  const provider = fx.provider ?? "mock";
46
80
  const modelId = fx.modelId ?? "mock-model";
81
+ const classifyLine = fx.classify ?? DEFAULT_CLASSIFY_LINE;
82
+ // Intercept the PIL classifier call ONLY when the fixture opts in (E2E
83
+ // harness fixtures do, via loadMockModelFromDir). Direct in-memory callers
84
+ // (llm-classify.test.ts) leave it off so the stream they configure IS
85
+ // delivered to the classifier — intercepting would override their reply with
86
+ // DEFAULT_CLASSIFY_LINE and make every classification collapse to "generate".
87
+ const interceptClassify = fx.autoClassify === true || fx.classify !== undefined;
88
+ const chunkDelayMs = Math.max(0, fx.chunkDelayMs ?? 0);
47
89
  const model = new MockLanguageModelV3({
48
90
  provider,
49
91
  modelId,
50
- doStream: async () => {
92
+ doStream: async (options) => {
93
+ // PIL model-first classifier call — answer with the parseable eight-word
94
+ // line and DO NOT advance the turn-fixture cursor, so the classify call
95
+ // (which fires on every turn) never steals the round chunks meant for the
96
+ // agent's actual reply. Without this the generic fixture text fails
97
+ // parseResponse → intentKind=UNKNOWN → gsd gate/routing break in E2E.
98
+ if (interceptClassify && systemPromptText(options).includes(CLASSIFY_SIGNATURE)) {
99
+ if (process.env.MUONROI_DEBUG_MOCK_MODEL === "1") {
100
+ process.stderr.write(`[mock-model] doStream classify → "${classifyLine}"\n`);
101
+ }
102
+ return {
103
+ stream: simulateReadableStream({
104
+ chunks: textOnlyStream(classifyLine),
105
+ initialDelayInMs: null,
106
+ chunkDelayInMs: null,
107
+ }),
108
+ };
109
+ }
51
110
  const chunks = streams[Math.min(callIdx, streams.length - 1)];
52
111
  callIdx += 1;
53
112
  if (process.env.MUONROI_DEBUG_MOCK_MODEL === "1") {
@@ -58,8 +117,11 @@ export function createMockModel(fx) {
58
117
  return {
59
118
  stream: simulateReadableStream({
60
119
  chunks,
61
- initialDelayInMs: null,
62
- chunkDelayInMs: null,
120
+ // Only the turn stream honours the delay — the classify intercept above
121
+ // fires on EVERY turn, so pacing it would tax specs that just want a
122
+ // slow answer.
123
+ initialDelayInMs: chunkDelayMs || null,
124
+ chunkDelayInMs: chunkDelayMs || null,
63
125
  }),
64
126
  };
65
127
  },
@@ -397,6 +459,10 @@ export async function loadMockModelFromDir(dir) {
397
459
  // to createMockModel, so the AI SDK stream-loop and orchestrator see a
398
460
  // consistent Error shape regardless of fixture serialization.
399
461
  const normalized = {
462
+ // File-based E2E fixtures supply agent turn-rounds; auto-answer the PIL
463
+ // classifier call so it never consumes a round (a fixture may still set
464
+ // autoClassify:false to opt out, or classify:"…" to force a line).
465
+ autoClassify: true,
400
466
  ...raw.model,
401
467
  stream: isNestedArray(raw.model.stream)
402
468
  ? raw.model.stream.map(normalizeStreamChunks)